xref: /rk3399_rockchip-uboot/drivers/net/designware.c (revision 74cb708d5853854d28547b917d4b8a5588ee7324)
15b1b1883SVipin KUMAR /*
25b1b1883SVipin KUMAR  * (C) Copyright 2010
35b1b1883SVipin KUMAR  * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
45b1b1883SVipin KUMAR  *
51a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
65b1b1883SVipin KUMAR  */
75b1b1883SVipin KUMAR 
85b1b1883SVipin KUMAR /*
95b1b1883SVipin KUMAR  * Designware ethernet IP driver for u-boot
105b1b1883SVipin KUMAR  */
115b1b1883SVipin KUMAR 
125b1b1883SVipin KUMAR #include <common.h>
135b1b1883SVipin KUMAR #include <miiphy.h>
145b1b1883SVipin KUMAR #include <malloc.h>
15ef76025aSStefan Roese #include <linux/compiler.h>
165b1b1883SVipin KUMAR #include <linux/err.h>
175b1b1883SVipin KUMAR #include <asm/io.h>
185b1b1883SVipin KUMAR #include "designware.h"
195b1b1883SVipin KUMAR 
2013edd170SVipin Kumar static int configure_phy(struct eth_device *dev);
2113edd170SVipin Kumar 
225b1b1883SVipin KUMAR static void tx_descs_init(struct eth_device *dev)
235b1b1883SVipin KUMAR {
245b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
255b1b1883SVipin KUMAR 	struct eth_dma_regs *dma_p = priv->dma_regs_p;
265b1b1883SVipin KUMAR 	struct dmamacdescr *desc_table_p = &priv->tx_mac_descrtable[0];
275b1b1883SVipin KUMAR 	char *txbuffs = &priv->txbuffs[0];
285b1b1883SVipin KUMAR 	struct dmamacdescr *desc_p;
295b1b1883SVipin KUMAR 	u32 idx;
305b1b1883SVipin KUMAR 
315b1b1883SVipin KUMAR 	for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) {
325b1b1883SVipin KUMAR 		desc_p = &desc_table_p[idx];
335b1b1883SVipin KUMAR 		desc_p->dmamac_addr = &txbuffs[idx * CONFIG_ETH_BUFSIZE];
345b1b1883SVipin KUMAR 		desc_p->dmamac_next = &desc_table_p[idx + 1];
355b1b1883SVipin KUMAR 
365b1b1883SVipin KUMAR #if defined(CONFIG_DW_ALTDESCRIPTOR)
375b1b1883SVipin KUMAR 		desc_p->txrx_status &= ~(DESC_TXSTS_TXINT | DESC_TXSTS_TXLAST |
385b1b1883SVipin KUMAR 				DESC_TXSTS_TXFIRST | DESC_TXSTS_TXCRCDIS | \
395b1b1883SVipin KUMAR 				DESC_TXSTS_TXCHECKINSCTRL | \
405b1b1883SVipin KUMAR 				DESC_TXSTS_TXRINGEND | DESC_TXSTS_TXPADDIS);
415b1b1883SVipin KUMAR 
425b1b1883SVipin KUMAR 		desc_p->txrx_status |= DESC_TXSTS_TXCHAIN;
435b1b1883SVipin KUMAR 		desc_p->dmamac_cntl = 0;
445b1b1883SVipin KUMAR 		desc_p->txrx_status &= ~(DESC_TXSTS_MSK | DESC_TXSTS_OWNBYDMA);
455b1b1883SVipin KUMAR #else
465b1b1883SVipin KUMAR 		desc_p->dmamac_cntl = DESC_TXCTRL_TXCHAIN;
475b1b1883SVipin KUMAR 		desc_p->txrx_status = 0;
485b1b1883SVipin KUMAR #endif
495b1b1883SVipin KUMAR 	}
505b1b1883SVipin KUMAR 
515b1b1883SVipin KUMAR 	/* Correcting the last pointer of the chain */
525b1b1883SVipin KUMAR 	desc_p->dmamac_next = &desc_table_p[0];
535b1b1883SVipin KUMAR 
545b1b1883SVipin KUMAR 	writel((ulong)&desc_table_p[0], &dma_p->txdesclistaddr);
55*74cb708dSAlexey Brodkin 	priv->tx_currdescnum = 0;
565b1b1883SVipin KUMAR }
575b1b1883SVipin KUMAR 
585b1b1883SVipin KUMAR static void rx_descs_init(struct eth_device *dev)
595b1b1883SVipin KUMAR {
605b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
615b1b1883SVipin KUMAR 	struct eth_dma_regs *dma_p = priv->dma_regs_p;
625b1b1883SVipin KUMAR 	struct dmamacdescr *desc_table_p = &priv->rx_mac_descrtable[0];
635b1b1883SVipin KUMAR 	char *rxbuffs = &priv->rxbuffs[0];
645b1b1883SVipin KUMAR 	struct dmamacdescr *desc_p;
655b1b1883SVipin KUMAR 	u32 idx;
665b1b1883SVipin KUMAR 
675b1b1883SVipin KUMAR 	for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) {
685b1b1883SVipin KUMAR 		desc_p = &desc_table_p[idx];
695b1b1883SVipin KUMAR 		desc_p->dmamac_addr = &rxbuffs[idx * CONFIG_ETH_BUFSIZE];
705b1b1883SVipin KUMAR 		desc_p->dmamac_next = &desc_table_p[idx + 1];
715b1b1883SVipin KUMAR 
725b1b1883SVipin KUMAR 		desc_p->dmamac_cntl =
735b1b1883SVipin KUMAR 			(MAC_MAX_FRAME_SZ & DESC_RXCTRL_SIZE1MASK) | \
745b1b1883SVipin KUMAR 				      DESC_RXCTRL_RXCHAIN;
755b1b1883SVipin KUMAR 
765b1b1883SVipin KUMAR 		desc_p->txrx_status = DESC_RXSTS_OWNBYDMA;
775b1b1883SVipin KUMAR 	}
785b1b1883SVipin KUMAR 
795b1b1883SVipin KUMAR 	/* Correcting the last pointer of the chain */
805b1b1883SVipin KUMAR 	desc_p->dmamac_next = &desc_table_p[0];
815b1b1883SVipin KUMAR 
825b1b1883SVipin KUMAR 	writel((ulong)&desc_table_p[0], &dma_p->rxdesclistaddr);
83*74cb708dSAlexey Brodkin 	priv->rx_currdescnum = 0;
845b1b1883SVipin KUMAR }
855b1b1883SVipin KUMAR 
865b1b1883SVipin KUMAR static void descs_init(struct eth_device *dev)
875b1b1883SVipin KUMAR {
885b1b1883SVipin KUMAR 	tx_descs_init(dev);
895b1b1883SVipin KUMAR 	rx_descs_init(dev);
905b1b1883SVipin KUMAR }
915b1b1883SVipin KUMAR 
925b1b1883SVipin KUMAR static int mac_reset(struct eth_device *dev)
935b1b1883SVipin KUMAR {
945b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
955b1b1883SVipin KUMAR 	struct eth_mac_regs *mac_p = priv->mac_regs_p;
965b1b1883SVipin KUMAR 	struct eth_dma_regs *dma_p = priv->dma_regs_p;
975b1b1883SVipin KUMAR 
98cafabe19SAmit Virdi 	ulong start;
995b1b1883SVipin KUMAR 	int timeout = CONFIG_MACRESET_TIMEOUT;
1005b1b1883SVipin KUMAR 
101227ad7b2SAlexey Brodkin 	writel(readl(&dma_p->busmode) | DMAMAC_SRST, &dma_p->busmode);
1027091915aSVipin Kumar 
1037091915aSVipin Kumar 	if (priv->interface != PHY_INTERFACE_MODE_RGMII)
1045b1b1883SVipin KUMAR 		writel(MII_PORTSELECT, &mac_p->conf);
1055b1b1883SVipin KUMAR 
106cafabe19SAmit Virdi 	start = get_timer(0);
107cafabe19SAmit Virdi 	while (get_timer(start) < timeout) {
1085b1b1883SVipin KUMAR 		if (!(readl(&dma_p->busmode) & DMAMAC_SRST))
1095b1b1883SVipin KUMAR 			return 0;
110cafabe19SAmit Virdi 
111cafabe19SAmit Virdi 		/* Try again after 10usec */
112cafabe19SAmit Virdi 		udelay(10);
113cafabe19SAmit Virdi 	};
1145b1b1883SVipin KUMAR 
1155b1b1883SVipin KUMAR 	return -1;
1165b1b1883SVipin KUMAR }
1175b1b1883SVipin KUMAR 
1185b1b1883SVipin KUMAR static int dw_write_hwaddr(struct eth_device *dev)
1195b1b1883SVipin KUMAR {
1205b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
1215b1b1883SVipin KUMAR 	struct eth_mac_regs *mac_p = priv->mac_regs_p;
1225b1b1883SVipin KUMAR 	u32 macid_lo, macid_hi;
1235b1b1883SVipin KUMAR 	u8 *mac_id = &dev->enetaddr[0];
1245b1b1883SVipin KUMAR 
1255b1b1883SVipin KUMAR 	macid_lo = mac_id[0] + (mac_id[1] << 8) + \
1265b1b1883SVipin KUMAR 		   (mac_id[2] << 16) + (mac_id[3] << 24);
1275b1b1883SVipin KUMAR 	macid_hi = mac_id[4] + (mac_id[5] << 8);
1285b1b1883SVipin KUMAR 
1295b1b1883SVipin KUMAR 	writel(macid_hi, &mac_p->macaddr0hi);
1305b1b1883SVipin KUMAR 	writel(macid_lo, &mac_p->macaddr0lo);
1315b1b1883SVipin KUMAR 
1325b1b1883SVipin KUMAR 	return 0;
1335b1b1883SVipin KUMAR }
1345b1b1883SVipin KUMAR 
1355b1b1883SVipin KUMAR static int dw_eth_init(struct eth_device *dev, bd_t *bis)
1365b1b1883SVipin KUMAR {
1375b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
1385b1b1883SVipin KUMAR 	struct eth_mac_regs *mac_p = priv->mac_regs_p;
1395b1b1883SVipin KUMAR 	struct eth_dma_regs *dma_p = priv->dma_regs_p;
1405b1b1883SVipin KUMAR 	u32 conf;
1415b1b1883SVipin KUMAR 
14213edd170SVipin Kumar 	if (priv->phy_configured != 1)
14313edd170SVipin Kumar 		configure_phy(dev);
14413edd170SVipin Kumar 
145ef76025aSStefan Roese 	/* Print link status only once */
146ef76025aSStefan Roese 	if (!priv->link_printed) {
147ef76025aSStefan Roese 		printf("ENET Speed is %d Mbps - %s duplex connection\n",
148ef76025aSStefan Roese 		       priv->speed, (priv->duplex == HALF) ? "HALF" : "FULL");
149ef76025aSStefan Roese 		priv->link_printed = 1;
150ef76025aSStefan Roese 	}
151ef76025aSStefan Roese 
1525b1b1883SVipin KUMAR 	/* Reset ethernet hardware */
1535b1b1883SVipin KUMAR 	if (mac_reset(dev) < 0)
1545b1b1883SVipin KUMAR 		return -1;
1555b1b1883SVipin KUMAR 
156c7f6dbe7SVipin KUMAR 	/* Resore the HW MAC address as it has been lost during MAC reset */
157c7f6dbe7SVipin KUMAR 	dw_write_hwaddr(dev);
158c7f6dbe7SVipin KUMAR 
1595b1b1883SVipin KUMAR 	writel(FIXEDBURST | PRIORXTX_41 | BURST_16,
1605b1b1883SVipin KUMAR 			&dma_p->busmode);
1615b1b1883SVipin KUMAR 
16266f119e5SDinh Nguyen 	writel(readl(&dma_p->opmode) | FLUSHTXFIFO | STOREFORWARD |
16366f119e5SDinh Nguyen 		TXSECONDFRAME, &dma_p->opmode);
1645b1b1883SVipin KUMAR 
1655b1b1883SVipin KUMAR 	conf = FRAMEBURSTENABLE | DISABLERXOWN;
1665b1b1883SVipin KUMAR 
167ef76025aSStefan Roese 	if (priv->speed != 1000)
1685b1b1883SVipin KUMAR 		conf |= MII_PORTSELECT;
1695b1b1883SVipin KUMAR 
1709afc1af0SVipin Kumar 	if ((priv->interface != PHY_INTERFACE_MODE_MII) &&
1719afc1af0SVipin Kumar 		(priv->interface != PHY_INTERFACE_MODE_GMII)) {
1729afc1af0SVipin Kumar 
173ef76025aSStefan Roese 		if (priv->speed == 100)
1749afc1af0SVipin Kumar 			conf |= FES_100;
1759afc1af0SVipin Kumar 	}
1769afc1af0SVipin Kumar 
177ef76025aSStefan Roese 	if (priv->duplex == FULL)
1785b1b1883SVipin KUMAR 		conf |= FULLDPLXMODE;
1795b1b1883SVipin KUMAR 
1805b1b1883SVipin KUMAR 	writel(conf, &mac_p->conf);
1815b1b1883SVipin KUMAR 
1825b1b1883SVipin KUMAR 	descs_init(dev);
1835b1b1883SVipin KUMAR 
1845b1b1883SVipin KUMAR 	/*
1855b1b1883SVipin KUMAR 	 * Start/Enable xfer at dma as well as mac level
1865b1b1883SVipin KUMAR 	 */
1875b1b1883SVipin KUMAR 	writel(readl(&dma_p->opmode) | RXSTART, &dma_p->opmode);
1885b1b1883SVipin KUMAR 	writel(readl(&dma_p->opmode) | TXSTART, &dma_p->opmode);
1895b1b1883SVipin KUMAR 
190aa51005cSArmando Visconti 	writel(readl(&mac_p->conf) | RXENABLE | TXENABLE, &mac_p->conf);
1915b1b1883SVipin KUMAR 
1925b1b1883SVipin KUMAR 	return 0;
1935b1b1883SVipin KUMAR }
1945b1b1883SVipin KUMAR 
19510cbe3b6SJoe Hershberger static int dw_eth_send(struct eth_device *dev, void *packet, int length)
1965b1b1883SVipin KUMAR {
1975b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
1985b1b1883SVipin KUMAR 	struct eth_dma_regs *dma_p = priv->dma_regs_p;
1995b1b1883SVipin KUMAR 	u32 desc_num = priv->tx_currdescnum;
2005b1b1883SVipin KUMAR 	struct dmamacdescr *desc_p = &priv->tx_mac_descrtable[desc_num];
2015b1b1883SVipin KUMAR 
2025b1b1883SVipin KUMAR 	/* Check if the descriptor is owned by CPU */
2035b1b1883SVipin KUMAR 	if (desc_p->txrx_status & DESC_TXSTS_OWNBYDMA) {
2045b1b1883SVipin KUMAR 		printf("CPU not owner of tx frame\n");
2055b1b1883SVipin KUMAR 		return -1;
2065b1b1883SVipin KUMAR 	}
2075b1b1883SVipin KUMAR 
20810cbe3b6SJoe Hershberger 	memcpy((void *)desc_p->dmamac_addr, packet, length);
2095b1b1883SVipin KUMAR 
2105b1b1883SVipin KUMAR #if defined(CONFIG_DW_ALTDESCRIPTOR)
2115b1b1883SVipin KUMAR 	desc_p->txrx_status |= DESC_TXSTS_TXFIRST | DESC_TXSTS_TXLAST;
2125b1b1883SVipin KUMAR 	desc_p->dmamac_cntl |= (length << DESC_TXCTRL_SIZE1SHFT) & \
2135b1b1883SVipin KUMAR 			       DESC_TXCTRL_SIZE1MASK;
2145b1b1883SVipin KUMAR 
2155b1b1883SVipin KUMAR 	desc_p->txrx_status &= ~(DESC_TXSTS_MSK);
2165b1b1883SVipin KUMAR 	desc_p->txrx_status |= DESC_TXSTS_OWNBYDMA;
2175b1b1883SVipin KUMAR #else
2185b1b1883SVipin KUMAR 	desc_p->dmamac_cntl |= ((length << DESC_TXCTRL_SIZE1SHFT) & \
2195b1b1883SVipin KUMAR 			       DESC_TXCTRL_SIZE1MASK) | DESC_TXCTRL_TXLAST | \
2205b1b1883SVipin KUMAR 			       DESC_TXCTRL_TXFIRST;
2215b1b1883SVipin KUMAR 
2225b1b1883SVipin KUMAR 	desc_p->txrx_status = DESC_TXSTS_OWNBYDMA;
2235b1b1883SVipin KUMAR #endif
2245b1b1883SVipin KUMAR 
2255b1b1883SVipin KUMAR 	/* Test the wrap-around condition. */
2265b1b1883SVipin KUMAR 	if (++desc_num >= CONFIG_TX_DESCR_NUM)
2275b1b1883SVipin KUMAR 		desc_num = 0;
2285b1b1883SVipin KUMAR 
2295b1b1883SVipin KUMAR 	priv->tx_currdescnum = desc_num;
2305b1b1883SVipin KUMAR 
2315b1b1883SVipin KUMAR 	/* Start the transmission */
2325b1b1883SVipin KUMAR 	writel(POLL_DATA, &dma_p->txpolldemand);
2335b1b1883SVipin KUMAR 
2345b1b1883SVipin KUMAR 	return 0;
2355b1b1883SVipin KUMAR }
2365b1b1883SVipin KUMAR 
2375b1b1883SVipin KUMAR static int dw_eth_recv(struct eth_device *dev)
2385b1b1883SVipin KUMAR {
2395b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
2405b1b1883SVipin KUMAR 	u32 desc_num = priv->rx_currdescnum;
2415b1b1883SVipin KUMAR 	struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num];
2425b1b1883SVipin KUMAR 
2435b1b1883SVipin KUMAR 	u32 status = desc_p->txrx_status;
2445b1b1883SVipin KUMAR 	int length = 0;
2455b1b1883SVipin KUMAR 
2465b1b1883SVipin KUMAR 	/* Check  if the owner is the CPU */
2475b1b1883SVipin KUMAR 	if (!(status & DESC_RXSTS_OWNBYDMA)) {
2485b1b1883SVipin KUMAR 
2495b1b1883SVipin KUMAR 		length = (status & DESC_RXSTS_FRMLENMSK) >> \
2505b1b1883SVipin KUMAR 			 DESC_RXSTS_FRMLENSHFT;
2515b1b1883SVipin KUMAR 
2525b1b1883SVipin KUMAR 		NetReceive(desc_p->dmamac_addr, length);
2535b1b1883SVipin KUMAR 
2545b1b1883SVipin KUMAR 		/*
2555b1b1883SVipin KUMAR 		 * Make the current descriptor valid again and go to
2565b1b1883SVipin KUMAR 		 * the next one
2575b1b1883SVipin KUMAR 		 */
2585b1b1883SVipin KUMAR 		desc_p->txrx_status |= DESC_RXSTS_OWNBYDMA;
2595b1b1883SVipin KUMAR 
2605b1b1883SVipin KUMAR 		/* Test the wrap-around condition. */
2615b1b1883SVipin KUMAR 		if (++desc_num >= CONFIG_RX_DESCR_NUM)
2625b1b1883SVipin KUMAR 			desc_num = 0;
2635b1b1883SVipin KUMAR 	}
2645b1b1883SVipin KUMAR 
2655b1b1883SVipin KUMAR 	priv->rx_currdescnum = desc_num;
2665b1b1883SVipin KUMAR 
2675b1b1883SVipin KUMAR 	return length;
2685b1b1883SVipin KUMAR }
2695b1b1883SVipin KUMAR 
2705b1b1883SVipin KUMAR static void dw_eth_halt(struct eth_device *dev)
2715b1b1883SVipin KUMAR {
2725b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
2735b1b1883SVipin KUMAR 
2745b1b1883SVipin KUMAR 	mac_reset(dev);
2755b1b1883SVipin KUMAR 	priv->tx_currdescnum = priv->rx_currdescnum = 0;
2765b1b1883SVipin KUMAR }
2775b1b1883SVipin KUMAR 
2785b1b1883SVipin KUMAR static int eth_mdio_read(struct eth_device *dev, u8 addr, u8 reg, u16 *val)
2795b1b1883SVipin KUMAR {
2805b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
2815b1b1883SVipin KUMAR 	struct eth_mac_regs *mac_p = priv->mac_regs_p;
282cafabe19SAmit Virdi 	ulong start;
2835b1b1883SVipin KUMAR 	u32 miiaddr;
2845b1b1883SVipin KUMAR 	int timeout = CONFIG_MDIO_TIMEOUT;
2855b1b1883SVipin KUMAR 
2865b1b1883SVipin KUMAR 	miiaddr = ((addr << MIIADDRSHIFT) & MII_ADDRMSK) | \
2875b1b1883SVipin KUMAR 		  ((reg << MIIREGSHIFT) & MII_REGMSK);
2885b1b1883SVipin KUMAR 
2895b1b1883SVipin KUMAR 	writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr);
2905b1b1883SVipin KUMAR 
291cafabe19SAmit Virdi 	start = get_timer(0);
292cafabe19SAmit Virdi 	while (get_timer(start) < timeout) {
2935b1b1883SVipin KUMAR 		if (!(readl(&mac_p->miiaddr) & MII_BUSY)) {
2945b1b1883SVipin KUMAR 			*val = readl(&mac_p->miidata);
2955b1b1883SVipin KUMAR 			return 0;
2965b1b1883SVipin KUMAR 		}
297cafabe19SAmit Virdi 
298cafabe19SAmit Virdi 		/* Try again after 10usec */
299cafabe19SAmit Virdi 		udelay(10);
300cafabe19SAmit Virdi 	};
3015b1b1883SVipin KUMAR 
3025b1b1883SVipin KUMAR 	return -1;
3035b1b1883SVipin KUMAR }
3045b1b1883SVipin KUMAR 
3055b1b1883SVipin KUMAR static int eth_mdio_write(struct eth_device *dev, u8 addr, u8 reg, u16 val)
3065b1b1883SVipin KUMAR {
3075b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
3085b1b1883SVipin KUMAR 	struct eth_mac_regs *mac_p = priv->mac_regs_p;
309cafabe19SAmit Virdi 	ulong start;
3105b1b1883SVipin KUMAR 	u32 miiaddr;
3115b1b1883SVipin KUMAR 	int ret = -1, timeout = CONFIG_MDIO_TIMEOUT;
3125b1b1883SVipin KUMAR 	u16 value;
3135b1b1883SVipin KUMAR 
3145b1b1883SVipin KUMAR 	writel(val, &mac_p->miidata);
3155b1b1883SVipin KUMAR 	miiaddr = ((addr << MIIADDRSHIFT) & MII_ADDRMSK) | \
3165b1b1883SVipin KUMAR 		  ((reg << MIIREGSHIFT) & MII_REGMSK) | MII_WRITE;
3175b1b1883SVipin KUMAR 
3185b1b1883SVipin KUMAR 	writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr);
3195b1b1883SVipin KUMAR 
320cafabe19SAmit Virdi 	start = get_timer(0);
321cafabe19SAmit Virdi 	while (get_timer(start) < timeout) {
322c7f6dbe7SVipin KUMAR 		if (!(readl(&mac_p->miiaddr) & MII_BUSY)) {
3235b1b1883SVipin KUMAR 			ret = 0;
324c7f6dbe7SVipin KUMAR 			break;
325c7f6dbe7SVipin KUMAR 		}
326cafabe19SAmit Virdi 
327cafabe19SAmit Virdi 		/* Try again after 10usec */
328cafabe19SAmit Virdi 		udelay(10);
329cafabe19SAmit Virdi 	};
3305b1b1883SVipin KUMAR 
3315b1b1883SVipin KUMAR 	/* Needed as a fix for ST-Phy */
3325b1b1883SVipin KUMAR 	eth_mdio_read(dev, addr, reg, &value);
3335b1b1883SVipin KUMAR 
3345b1b1883SVipin KUMAR 	return ret;
3355b1b1883SVipin KUMAR }
3365b1b1883SVipin KUMAR 
3375b1b1883SVipin KUMAR #if defined(CONFIG_DW_SEARCH_PHY)
3385b1b1883SVipin KUMAR static int find_phy(struct eth_device *dev)
3395b1b1883SVipin KUMAR {
3405b1b1883SVipin KUMAR 	int phy_addr = 0;
3415b1b1883SVipin KUMAR 	u16 ctrl, oldctrl;
3425b1b1883SVipin KUMAR 
3435b1b1883SVipin KUMAR 	do {
3448ef583a0SMike Frysinger 		eth_mdio_read(dev, phy_addr, MII_BMCR, &ctrl);
3458ef583a0SMike Frysinger 		oldctrl = ctrl & BMCR_ANENABLE;
3465b1b1883SVipin KUMAR 
3478ef583a0SMike Frysinger 		ctrl ^= BMCR_ANENABLE;
3488ef583a0SMike Frysinger 		eth_mdio_write(dev, phy_addr, MII_BMCR, ctrl);
3498ef583a0SMike Frysinger 		eth_mdio_read(dev, phy_addr, MII_BMCR, &ctrl);
3508ef583a0SMike Frysinger 		ctrl &= BMCR_ANENABLE;
3515b1b1883SVipin KUMAR 
3525b1b1883SVipin KUMAR 		if (ctrl == oldctrl) {
3535b1b1883SVipin KUMAR 			phy_addr++;
3545b1b1883SVipin KUMAR 		} else {
3558ef583a0SMike Frysinger 			ctrl ^= BMCR_ANENABLE;
3568ef583a0SMike Frysinger 			eth_mdio_write(dev, phy_addr, MII_BMCR, ctrl);
3575b1b1883SVipin KUMAR 
3585b1b1883SVipin KUMAR 			return phy_addr;
3595b1b1883SVipin KUMAR 		}
3605b1b1883SVipin KUMAR 	} while (phy_addr < 32);
3615b1b1883SVipin KUMAR 
3625b1b1883SVipin KUMAR 	return -1;
3635b1b1883SVipin KUMAR }
3645b1b1883SVipin KUMAR #endif
3655b1b1883SVipin KUMAR 
3665b1b1883SVipin KUMAR static int dw_reset_phy(struct eth_device *dev)
3675b1b1883SVipin KUMAR {
3685b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
3695b1b1883SVipin KUMAR 	u16 ctrl;
370cafabe19SAmit Virdi 	ulong start;
3715b1b1883SVipin KUMAR 	int timeout = CONFIG_PHYRESET_TIMEOUT;
3725b1b1883SVipin KUMAR 	u32 phy_addr = priv->address;
3735b1b1883SVipin KUMAR 
3748ef583a0SMike Frysinger 	eth_mdio_write(dev, phy_addr, MII_BMCR, BMCR_RESET);
375cafabe19SAmit Virdi 
376cafabe19SAmit Virdi 	start = get_timer(0);
377cafabe19SAmit Virdi 	while (get_timer(start) < timeout) {
3788ef583a0SMike Frysinger 		eth_mdio_read(dev, phy_addr, MII_BMCR, &ctrl);
3798ef583a0SMike Frysinger 		if (!(ctrl & BMCR_RESET))
3805b1b1883SVipin KUMAR 			break;
3815b1b1883SVipin KUMAR 
382cafabe19SAmit Virdi 		/* Try again after 10usec */
383cafabe19SAmit Virdi 		udelay(10);
384cafabe19SAmit Virdi 	};
385cafabe19SAmit Virdi 
386cafabe19SAmit Virdi 	if (get_timer(start) >= CONFIG_PHYRESET_TIMEOUT)
3875b1b1883SVipin KUMAR 		return -1;
3885b1b1883SVipin KUMAR 
3895b1b1883SVipin KUMAR #ifdef CONFIG_PHY_RESET_DELAY
3905b1b1883SVipin KUMAR 	udelay(CONFIG_PHY_RESET_DELAY);
3915b1b1883SVipin KUMAR #endif
3925b1b1883SVipin KUMAR 	return 0;
3935b1b1883SVipin KUMAR }
3945b1b1883SVipin KUMAR 
395ef76025aSStefan Roese /*
396ef76025aSStefan Roese  * Add weak default function for board specific PHY configuration
397ef76025aSStefan Roese  */
398ef76025aSStefan Roese int __weak designware_board_phy_init(struct eth_device *dev, int phy_addr,
399ef76025aSStefan Roese 		int (*mii_write)(struct eth_device *, u8, u8, u16),
400ef76025aSStefan Roese 		int dw_reset_phy(struct eth_device *))
401ef76025aSStefan Roese {
402ef76025aSStefan Roese 	return 0;
403ef76025aSStefan Roese }
404ef76025aSStefan Roese 
4055b1b1883SVipin KUMAR static int configure_phy(struct eth_device *dev)
4065b1b1883SVipin KUMAR {
4075b1b1883SVipin KUMAR 	struct dw_eth_dev *priv = dev->priv;
4085b1b1883SVipin KUMAR 	int phy_addr;
409ee7f5bfdSMike Frysinger 	u16 bmcr;
4105b1b1883SVipin KUMAR #if defined(CONFIG_DW_AUTONEG)
4115b1b1883SVipin KUMAR 	u16 bmsr;
4125b1b1883SVipin KUMAR 	u32 timeout;
413cafabe19SAmit Virdi 	ulong start;
4145b1b1883SVipin KUMAR #endif
4155b1b1883SVipin KUMAR 
4165b1b1883SVipin KUMAR #if defined(CONFIG_DW_SEARCH_PHY)
4175b1b1883SVipin KUMAR 	phy_addr = find_phy(dev);
418024333c9SVipin KUMAR 	if (phy_addr >= 0)
4195b1b1883SVipin KUMAR 		priv->address = phy_addr;
4205b1b1883SVipin KUMAR 	else
4215b1b1883SVipin KUMAR 		return -1;
422f0ece9e9SMike Frysinger #else
423f0ece9e9SMike Frysinger 	phy_addr = priv->address;
4245b1b1883SVipin KUMAR #endif
425ef76025aSStefan Roese 
426ef76025aSStefan Roese 	/*
427ef76025aSStefan Roese 	 * Some boards need board specific PHY initialization. This is
428ef76025aSStefan Roese 	 * after the main driver init code but before the auto negotiation
429ef76025aSStefan Roese 	 * is run.
430ef76025aSStefan Roese 	 */
431ef76025aSStefan Roese 	if (designware_board_phy_init(dev, phy_addr,
432ef76025aSStefan Roese 				      eth_mdio_write, dw_reset_phy) < 0)
433ef76025aSStefan Roese 		return -1;
434ef76025aSStefan Roese 
4355b1b1883SVipin KUMAR 	if (dw_reset_phy(dev) < 0)
4365b1b1883SVipin KUMAR 		return -1;
4375b1b1883SVipin KUMAR 
4385b1b1883SVipin KUMAR #if defined(CONFIG_DW_AUTONEG)
43920a5dde1SArmando Visconti 	/* Set Auto-Neg Advertisement capabilities to 10/100 half/full */
44020a5dde1SArmando Visconti 	eth_mdio_write(dev, phy_addr, MII_ADVERTISE, 0x1E1);
44120a5dde1SArmando Visconti 
442e25c90b4SVikas Manocha 	bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
4435b1b1883SVipin KUMAR #else
4448ef583a0SMike Frysinger 	bmcr = BMCR_SPEED100 | BMCR_FULLDPLX;
4455b1b1883SVipin KUMAR 
4465b1b1883SVipin KUMAR #if defined(CONFIG_DW_SPEED10M)
4478ef583a0SMike Frysinger 	bmcr &= ~BMCR_SPEED100;
4485b1b1883SVipin KUMAR #endif
4495b1b1883SVipin KUMAR #if defined(CONFIG_DW_DUPLEXHALF)
4508ef583a0SMike Frysinger 	bmcr &= ~BMCR_FULLDPLX;
4515b1b1883SVipin KUMAR #endif
4525b1b1883SVipin KUMAR #endif
4538ef583a0SMike Frysinger 	if (eth_mdio_write(dev, phy_addr, MII_BMCR, bmcr) < 0)
4545b1b1883SVipin KUMAR 		return -1;
4555b1b1883SVipin KUMAR 
4565b1b1883SVipin KUMAR 	/* Read the phy status register and populate priv structure */
4575b1b1883SVipin KUMAR #if defined(CONFIG_DW_AUTONEG)
4585b1b1883SVipin KUMAR 	timeout = CONFIG_AUTONEG_TIMEOUT;
459cafabe19SAmit Virdi 	start = get_timer(0);
460ef76025aSStefan Roese 	puts("Waiting for PHY auto negotiation to complete");
461cafabe19SAmit Virdi 	while (get_timer(start) < timeout) {
4628ef583a0SMike Frysinger 		eth_mdio_read(dev, phy_addr, MII_BMSR, &bmsr);
463ef76025aSStefan Roese 		if (bmsr & BMSR_ANEGCOMPLETE) {
464ef76025aSStefan Roese 			priv->phy_configured = 1;
4655b1b1883SVipin KUMAR 			break;
466ef76025aSStefan Roese 		}
467cafabe19SAmit Virdi 
468ef76025aSStefan Roese 		/* Print dot all 1s to show progress */
469ef76025aSStefan Roese 		if ((get_timer(start) % 1000) == 0)
470ef76025aSStefan Roese 			putc('.');
471ef76025aSStefan Roese 
472ef76025aSStefan Roese 		/* Try again after 1msec */
473ef76025aSStefan Roese 		udelay(1000);
474cafabe19SAmit Virdi 	};
4755b1b1883SVipin KUMAR 
476ef76025aSStefan Roese 	if (!(bmsr & BMSR_ANEGCOMPLETE))
477ef76025aSStefan Roese 		puts(" TIMEOUT!\n");
478ef76025aSStefan Roese 	else
479ef76025aSStefan Roese 		puts(" done\n");
4805b1b1883SVipin KUMAR #else
48113edd170SVipin Kumar 	priv->phy_configured = 1;
482ef76025aSStefan Roese #endif
483ef76025aSStefan Roese 
484ef76025aSStefan Roese 	priv->speed = miiphy_speed(dev->name, phy_addr);
485ef76025aSStefan Roese 	priv->duplex = miiphy_duplex(dev->name, phy_addr);
48613edd170SVipin Kumar 
4875b1b1883SVipin KUMAR 	return 0;
4885b1b1883SVipin KUMAR }
4895b1b1883SVipin KUMAR 
4905b1b1883SVipin KUMAR #if defined(CONFIG_MII)
4915700bb63SMike Frysinger static int dw_mii_read(const char *devname, u8 addr, u8 reg, u16 *val)
4925b1b1883SVipin KUMAR {
4935b1b1883SVipin KUMAR 	struct eth_device *dev;
4945b1b1883SVipin KUMAR 
4955b1b1883SVipin KUMAR 	dev = eth_get_dev_by_name(devname);
4965b1b1883SVipin KUMAR 	if (dev)
4975b1b1883SVipin KUMAR 		eth_mdio_read(dev, addr, reg, val);
4985b1b1883SVipin KUMAR 
4995b1b1883SVipin KUMAR 	return 0;
5005b1b1883SVipin KUMAR }
5015b1b1883SVipin KUMAR 
5025700bb63SMike Frysinger static int dw_mii_write(const char *devname, u8 addr, u8 reg, u16 val)
5035b1b1883SVipin KUMAR {
5045b1b1883SVipin KUMAR 	struct eth_device *dev;
5055b1b1883SVipin KUMAR 
5065b1b1883SVipin KUMAR 	dev = eth_get_dev_by_name(devname);
5075b1b1883SVipin KUMAR 	if (dev)
5085b1b1883SVipin KUMAR 		eth_mdio_write(dev, addr, reg, val);
5095b1b1883SVipin KUMAR 
5105b1b1883SVipin KUMAR 	return 0;
5115b1b1883SVipin KUMAR }
5125b1b1883SVipin KUMAR #endif
5135b1b1883SVipin KUMAR 
5149afc1af0SVipin Kumar int designware_initialize(u32 id, ulong base_addr, u32 phy_addr, u32 interface)
5155b1b1883SVipin KUMAR {
5165b1b1883SVipin KUMAR 	struct eth_device *dev;
5175b1b1883SVipin KUMAR 	struct dw_eth_dev *priv;
5185b1b1883SVipin KUMAR 
5195b1b1883SVipin KUMAR 	dev = (struct eth_device *) malloc(sizeof(struct eth_device));
5205b1b1883SVipin KUMAR 	if (!dev)
5215b1b1883SVipin KUMAR 		return -ENOMEM;
5225b1b1883SVipin KUMAR 
5235b1b1883SVipin KUMAR 	/*
5245b1b1883SVipin KUMAR 	 * Since the priv structure contains the descriptors which need a strict
5255b1b1883SVipin KUMAR 	 * buswidth alignment, memalign is used to allocate memory
5265b1b1883SVipin KUMAR 	 */
5275b1b1883SVipin KUMAR 	priv = (struct dw_eth_dev *) memalign(16, sizeof(struct dw_eth_dev));
5285b1b1883SVipin KUMAR 	if (!priv) {
5295b1b1883SVipin KUMAR 		free(dev);
5305b1b1883SVipin KUMAR 		return -ENOMEM;
5315b1b1883SVipin KUMAR 	}
5325b1b1883SVipin KUMAR 
5335b1b1883SVipin KUMAR 	memset(dev, 0, sizeof(struct eth_device));
5345b1b1883SVipin KUMAR 	memset(priv, 0, sizeof(struct dw_eth_dev));
5355b1b1883SVipin KUMAR 
5365b1b1883SVipin KUMAR 	sprintf(dev->name, "mii%d", id);
5375b1b1883SVipin KUMAR 	dev->iobase = (int)base_addr;
5385b1b1883SVipin KUMAR 	dev->priv = priv;
5395b1b1883SVipin KUMAR 
5407616e785SSimon Glass 	eth_getenv_enetaddr_by_index("eth", id, &dev->enetaddr[0]);
5415b1b1883SVipin KUMAR 
5425b1b1883SVipin KUMAR 	priv->dev = dev;
5435b1b1883SVipin KUMAR 	priv->mac_regs_p = (struct eth_mac_regs *)base_addr;
5445b1b1883SVipin KUMAR 	priv->dma_regs_p = (struct eth_dma_regs *)(base_addr +
5455b1b1883SVipin KUMAR 			DW_DMA_BASE_OFFSET);
5465b1b1883SVipin KUMAR 	priv->address = phy_addr;
54713edd170SVipin Kumar 	priv->phy_configured = 0;
5489afc1af0SVipin Kumar 	priv->interface = interface;
5495b1b1883SVipin KUMAR 
5505b1b1883SVipin KUMAR 	dev->init = dw_eth_init;
5515b1b1883SVipin KUMAR 	dev->send = dw_eth_send;
5525b1b1883SVipin KUMAR 	dev->recv = dw_eth_recv;
5535b1b1883SVipin KUMAR 	dev->halt = dw_eth_halt;
5545b1b1883SVipin KUMAR 	dev->write_hwaddr = dw_write_hwaddr;
5555b1b1883SVipin KUMAR 
5565b1b1883SVipin KUMAR 	eth_register(dev);
5575b1b1883SVipin KUMAR 
5585b1b1883SVipin KUMAR #if defined(CONFIG_MII)
5595b1b1883SVipin KUMAR 	miiphy_register(dev->name, dw_mii_read, dw_mii_write);
5605b1b1883SVipin KUMAR #endif
5615b1b1883SVipin KUMAR 	return 1;
5625b1b1883SVipin KUMAR }
563