+static int ethoc_free_pkt_common(struct ethoc *priv)
+{
+ struct ethoc_bd bd;
+ u32 i = priv->cur_rx % priv->num_rx;
+ u32 entry = priv->num_tx + i;
+ void *src;
+
+ ethoc_read_bd(priv, entry, &bd);
+
+ if (priv->packet)
+ src = priv->packet + entry * PKTSIZE_ALIGN;
+ else
+ src = net_rx_packets[i];
+ /* clear the buffer descriptor so it can be reused */
+ flush_dcache_range((ulong)src,
+ (ulong)src + PKTSIZE_ALIGN);
+ bd.stat &= ~RX_BD_STATS;
+ bd.stat |= RX_BD_EMPTY;
+ ethoc_write_bd(priv, entry, &bd);
+ priv->cur_rx++;
+
+ return 0;
+}
+
+#ifdef CONFIG_PHYLIB
+
+static int ethoc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+ struct ethoc *priv = bus->priv;
+ int rc;
+
+ ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg));
+ ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ);
+
+ rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS),
+ MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false);
+
+ if (rc == 0) {
+ u32 data = ethoc_read(priv, MIIRX_DATA);
+
+ /* reset MII command register */
+ ethoc_write(priv, MIICOMMAND, 0);
+ return data;
+ }
+ return rc;
+}
+
+static int ethoc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
+ u16 val)
+{
+ struct ethoc *priv = bus->priv;
+ int rc;
+
+ ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(addr, reg));
+ ethoc_write(priv, MIITX_DATA, val);
+ ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE);
+
+ rc = wait_for_bit(__func__, ethoc_reg(priv, MIISTATUS),
+ MIISTATUS_BUSY, false, CONFIG_SYS_HZ, false);
+
+ if (rc == 0) {
+ /* reset MII command register */
+ ethoc_write(priv, MIICOMMAND, 0);
+ }
+ return rc;
+}
+
+static int ethoc_mdio_init(const char *name, struct ethoc *priv)
+{
+ struct mii_dev *bus = mdio_alloc();
+ int ret;
+
+ if (!bus) {
+ printf("Failed to allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->read = ethoc_mdio_read;
+ bus->write = ethoc_mdio_write;
+ snprintf(bus->name, sizeof(bus->name), "%s", name);
+ bus->priv = priv;
+
+ ret = mdio_register(bus);
+ if (ret < 0)
+ return ret;
+
+ priv->bus = miiphy_get_dev_by_name(name);
+ return 0;
+}
+
+static int ethoc_phy_init(struct ethoc *priv, void *dev)
+{
+ struct phy_device *phydev;
+ int mask = 0xffffffff;
+
+#ifdef CONFIG_PHY_ADDR
+ mask = 1 << CONFIG_PHY_ADDR;
+#endif
+
+ phydev = phy_find_by_mask(priv->bus, mask, PHY_INTERFACE_MODE_MII);
+ if (!phydev)
+ return -ENODEV;
+
+ phy_connect_dev(phydev, dev);
+
+ phydev->supported &= PHY_BASIC_FEATURES;
+ phydev->advertising = phydev->supported;
+
+ priv->phydev = phydev;
+ phy_config(phydev);
+
+ return 0;
+}
+
+#else
+
+static inline int ethoc_mdio_init(const char *name, struct ethoc *priv)
+{
+ return 0;
+}
+
+static inline int ethoc_phy_init(struct ethoc *priv, void *dev)
+{
+ return 0;
+}
+
+#endif
+
+#ifdef CONFIG_DM_ETH
+
+static int ethoc_write_hwaddr(struct udevice *dev)
+{
+ struct ethoc_eth_pdata *pdata = dev_get_platdata(dev);
+ struct ethoc *priv = dev_get_priv(dev);
+ u8 *mac = pdata->eth_pdata.enetaddr;
+
+ return ethoc_write_hwaddr_common(priv, mac);
+}
+
+static int ethoc_send(struct udevice *dev, void *packet, int length)
+{
+ return ethoc_send_common(dev_get_priv(dev), packet, length);
+}
+
+static int ethoc_free_pkt(struct udevice *dev, uchar *packet, int length)
+{
+ return ethoc_free_pkt_common(dev_get_priv(dev));
+}
+
+static int ethoc_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+ struct ethoc *priv = dev_get_priv(dev);
+
+ if (flags & ETH_RECV_CHECK_DEVICE)
+ if (!ethoc_is_new_packet_received(priv))
+ return -EAGAIN;
+
+ return ethoc_rx_common(priv, packetp);
+}
+
+static int ethoc_start(struct udevice *dev)
+{
+ return ethoc_init_common(dev_get_priv(dev));
+}
+
+static void ethoc_stop(struct udevice *dev)
+{
+ ethoc_stop_common(dev_get_priv(dev));
+}
+
+static int ethoc_ofdata_to_platdata(struct udevice *dev)
+{
+ struct ethoc_eth_pdata *pdata = dev_get_platdata(dev);
+ fdt_addr_t addr;
+
+ pdata->eth_pdata.iobase = dev_get_addr(dev);
+ addr = dev_get_addr_index(dev, 1);
+ if (addr != FDT_ADDR_T_NONE)
+ pdata->packet_base = addr;
+ return 0;
+}
+
+static int ethoc_probe(struct udevice *dev)
+{
+ struct ethoc_eth_pdata *pdata = dev_get_platdata(dev);
+ struct ethoc *priv = dev_get_priv(dev);
+
+ priv->iobase = ioremap(pdata->eth_pdata.iobase, ETHOC_IOSIZE);
+ if (pdata->packet_base) {
+ priv->packet_phys = pdata->packet_base;
+ priv->packet = ioremap(pdata->packet_base,
+ (1 + PKTBUFSRX) * PKTSIZE_ALIGN);
+ }
+
+ ethoc_mdio_init(dev->name, priv);
+ ethoc_phy_init(priv, dev);
+
+ return 0;
+}
+
+static int ethoc_remove(struct udevice *dev)
+{
+ struct ethoc *priv = dev_get_priv(dev);
+
+#ifdef CONFIG_PHYLIB
+ free(priv->phydev);
+ mdio_unregister(priv->bus);
+ mdio_free(priv->bus);
+#endif
+ iounmap(priv->iobase);
+ return 0;
+}
+
+static const struct eth_ops ethoc_ops = {
+ .start = ethoc_start,
+ .stop = ethoc_stop,
+ .send = ethoc_send,
+ .recv = ethoc_recv,
+ .free_pkt = ethoc_free_pkt,
+ .write_hwaddr = ethoc_write_hwaddr,
+};
+
+static const struct udevice_id ethoc_ids[] = {
+ { .compatible = "opencores,ethoc" },
+ { }
+};
+
+U_BOOT_DRIVER(ethoc) = {
+ .name = "ethoc",
+ .id = UCLASS_ETH,
+ .of_match = ethoc_ids,
+ .ofdata_to_platdata = ethoc_ofdata_to_platdata,
+ .probe = ethoc_probe,
+ .remove = ethoc_remove,
+ .ops = ðoc_ops,
+ .priv_auto_alloc_size = sizeof(struct ethoc),
+ .platdata_auto_alloc_size = sizeof(struct ethoc_eth_pdata),
+};
+
+#else
+
+static int ethoc_init(struct eth_device *dev, bd_t *bd)
+{
+ struct ethoc *priv = (struct ethoc *)dev->priv;
+
+ return ethoc_init_common(priv);
+}
+
+static int ethoc_write_hwaddr(struct eth_device *dev)
+{
+ struct ethoc *priv = (struct ethoc *)dev->priv;
+ u8 *mac = dev->enetaddr;
+
+ return ethoc_write_hwaddr_common(priv, mac);
+}
+
+static int ethoc_send(struct eth_device *dev, void *packet, int length)
+{
+ return ethoc_send_common(dev->priv, packet, length);
+}
+