1b3dbf4a5SMacpaul Lin /*
2b3dbf4a5SMacpaul Lin * Faraday FTGMAC100 Ethernet
3b3dbf4a5SMacpaul Lin *
4b3dbf4a5SMacpaul Lin * (C) Copyright 2009 Faraday Technology
5b3dbf4a5SMacpaul Lin * Po-Yu Chuang <ratbert@faraday-tech.com>
6b3dbf4a5SMacpaul Lin *
7b3dbf4a5SMacpaul Lin * (C) Copyright 2010 Andes Technology
8b3dbf4a5SMacpaul Lin * Macpaul Lin <macpaul@andestech.com>
9b3dbf4a5SMacpaul Lin *
101a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
11b3dbf4a5SMacpaul Lin */
12b3dbf4a5SMacpaul Lin
13b3dbf4a5SMacpaul Lin #include <config.h>
14b3dbf4a5SMacpaul Lin #include <common.h>
15b3dbf4a5SMacpaul Lin #include <malloc.h>
16b3dbf4a5SMacpaul Lin #include <net.h>
17b3dbf4a5SMacpaul Lin #include <asm/io.h>
18a8f9cd18SKuo-Jung Su #include <asm/dma-mapping.h>
19b3dbf4a5SMacpaul Lin #include <linux/mii.h>
20b3dbf4a5SMacpaul Lin
21b3dbf4a5SMacpaul Lin #include "ftgmac100.h"
22b3dbf4a5SMacpaul Lin
23b3dbf4a5SMacpaul Lin #define ETH_ZLEN 60
24a8f9cd18SKuo-Jung Su #define CFG_XBUF_SIZE 1536
25b3dbf4a5SMacpaul Lin
26b3dbf4a5SMacpaul Lin /* RBSR - hw default init value is also 0x640 */
27b3dbf4a5SMacpaul Lin #define RBSR_DEFAULT_VALUE 0x640
28b3dbf4a5SMacpaul Lin
29b3dbf4a5SMacpaul Lin /* PKTBUFSTX/PKTBUFSRX must both be power of 2 */
30b3dbf4a5SMacpaul Lin #define PKTBUFSTX 4 /* must be power of 2 */
31b3dbf4a5SMacpaul Lin
32b3dbf4a5SMacpaul Lin struct ftgmac100_data {
33a8f9cd18SKuo-Jung Su ulong txdes_dma;
34a8f9cd18SKuo-Jung Su struct ftgmac100_txdes *txdes;
35a8f9cd18SKuo-Jung Su ulong rxdes_dma;
36a8f9cd18SKuo-Jung Su struct ftgmac100_rxdes *rxdes;
37b3dbf4a5SMacpaul Lin int tx_index;
38b3dbf4a5SMacpaul Lin int rx_index;
39b3dbf4a5SMacpaul Lin int phy_addr;
40b3dbf4a5SMacpaul Lin };
41b3dbf4a5SMacpaul Lin
42b3dbf4a5SMacpaul Lin /*
43b3dbf4a5SMacpaul Lin * struct mii_bus functions
44b3dbf4a5SMacpaul Lin */
ftgmac100_mdiobus_read(struct eth_device * dev,int phy_addr,int regnum)45b3dbf4a5SMacpaul Lin static int ftgmac100_mdiobus_read(struct eth_device *dev, int phy_addr,
46b3dbf4a5SMacpaul Lin int regnum)
47b3dbf4a5SMacpaul Lin {
48b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
49b3dbf4a5SMacpaul Lin int phycr;
50b3dbf4a5SMacpaul Lin int i;
51b3dbf4a5SMacpaul Lin
52b3dbf4a5SMacpaul Lin phycr = readl(&ftgmac100->phycr);
53b3dbf4a5SMacpaul Lin
54b3dbf4a5SMacpaul Lin /* preserve MDC cycle threshold */
55b3dbf4a5SMacpaul Lin phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK;
56b3dbf4a5SMacpaul Lin
57b3dbf4a5SMacpaul Lin phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr)
58b3dbf4a5SMacpaul Lin | FTGMAC100_PHYCR_REGAD(regnum)
59b3dbf4a5SMacpaul Lin | FTGMAC100_PHYCR_MIIRD;
60b3dbf4a5SMacpaul Lin
61b3dbf4a5SMacpaul Lin writel(phycr, &ftgmac100->phycr);
62b3dbf4a5SMacpaul Lin
63b3dbf4a5SMacpaul Lin for (i = 0; i < 10; i++) {
64b3dbf4a5SMacpaul Lin phycr = readl(&ftgmac100->phycr);
65b3dbf4a5SMacpaul Lin
66b3dbf4a5SMacpaul Lin if ((phycr & FTGMAC100_PHYCR_MIIRD) == 0) {
67b3dbf4a5SMacpaul Lin int data;
68b3dbf4a5SMacpaul Lin
69b3dbf4a5SMacpaul Lin data = readl(&ftgmac100->phydata);
70b3dbf4a5SMacpaul Lin return FTGMAC100_PHYDATA_MIIRDATA(data);
71b3dbf4a5SMacpaul Lin }
72b3dbf4a5SMacpaul Lin
73b3dbf4a5SMacpaul Lin mdelay(10);
74b3dbf4a5SMacpaul Lin }
75b3dbf4a5SMacpaul Lin
76b3dbf4a5SMacpaul Lin debug("mdio read timed out\n");
77b3dbf4a5SMacpaul Lin return -1;
78b3dbf4a5SMacpaul Lin }
79b3dbf4a5SMacpaul Lin
ftgmac100_mdiobus_write(struct eth_device * dev,int phy_addr,int regnum,u16 value)80b3dbf4a5SMacpaul Lin static int ftgmac100_mdiobus_write(struct eth_device *dev, int phy_addr,
81b3dbf4a5SMacpaul Lin int regnum, u16 value)
82b3dbf4a5SMacpaul Lin {
83b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
84b3dbf4a5SMacpaul Lin int phycr;
85b3dbf4a5SMacpaul Lin int data;
86b3dbf4a5SMacpaul Lin int i;
87b3dbf4a5SMacpaul Lin
88b3dbf4a5SMacpaul Lin phycr = readl(&ftgmac100->phycr);
89b3dbf4a5SMacpaul Lin
90b3dbf4a5SMacpaul Lin /* preserve MDC cycle threshold */
91b3dbf4a5SMacpaul Lin phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK;
92b3dbf4a5SMacpaul Lin
93b3dbf4a5SMacpaul Lin phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr)
94b3dbf4a5SMacpaul Lin | FTGMAC100_PHYCR_REGAD(regnum)
95b3dbf4a5SMacpaul Lin | FTGMAC100_PHYCR_MIIWR;
96b3dbf4a5SMacpaul Lin
97b3dbf4a5SMacpaul Lin data = FTGMAC100_PHYDATA_MIIWDATA(value);
98b3dbf4a5SMacpaul Lin
99b3dbf4a5SMacpaul Lin writel(data, &ftgmac100->phydata);
100b3dbf4a5SMacpaul Lin writel(phycr, &ftgmac100->phycr);
101b3dbf4a5SMacpaul Lin
102b3dbf4a5SMacpaul Lin for (i = 0; i < 10; i++) {
103b3dbf4a5SMacpaul Lin phycr = readl(&ftgmac100->phycr);
104b3dbf4a5SMacpaul Lin
105b3dbf4a5SMacpaul Lin if ((phycr & FTGMAC100_PHYCR_MIIWR) == 0) {
106b3dbf4a5SMacpaul Lin debug("(phycr & FTGMAC100_PHYCR_MIIWR) == 0: " \
107b3dbf4a5SMacpaul Lin "phy_addr: %x\n", phy_addr);
108b3dbf4a5SMacpaul Lin return 0;
109b3dbf4a5SMacpaul Lin }
110b3dbf4a5SMacpaul Lin
111b3dbf4a5SMacpaul Lin mdelay(1);
112b3dbf4a5SMacpaul Lin }
113b3dbf4a5SMacpaul Lin
114b3dbf4a5SMacpaul Lin debug("mdio write timed out\n");
115b3dbf4a5SMacpaul Lin return -1;
116b3dbf4a5SMacpaul Lin }
117b3dbf4a5SMacpaul Lin
ftgmac100_phy_read(struct eth_device * dev,int addr,int reg,u16 * value)118b3dbf4a5SMacpaul Lin int ftgmac100_phy_read(struct eth_device *dev, int addr, int reg, u16 *value)
119b3dbf4a5SMacpaul Lin {
120b3dbf4a5SMacpaul Lin *value = ftgmac100_mdiobus_read(dev , addr, reg);
121b3dbf4a5SMacpaul Lin
122b3dbf4a5SMacpaul Lin if (*value == -1)
123b3dbf4a5SMacpaul Lin return -1;
124b3dbf4a5SMacpaul Lin
125b3dbf4a5SMacpaul Lin return 0;
126b3dbf4a5SMacpaul Lin }
127b3dbf4a5SMacpaul Lin
ftgmac100_phy_write(struct eth_device * dev,int addr,int reg,u16 value)128b3dbf4a5SMacpaul Lin int ftgmac100_phy_write(struct eth_device *dev, int addr, int reg, u16 value)
129b3dbf4a5SMacpaul Lin {
130b3dbf4a5SMacpaul Lin if (ftgmac100_mdiobus_write(dev, addr, reg, value) == -1)
131b3dbf4a5SMacpaul Lin return -1;
132b3dbf4a5SMacpaul Lin
133b3dbf4a5SMacpaul Lin return 0;
134b3dbf4a5SMacpaul Lin }
135b3dbf4a5SMacpaul Lin
ftgmac100_phy_reset(struct eth_device * dev)136b3dbf4a5SMacpaul Lin static int ftgmac100_phy_reset(struct eth_device *dev)
137b3dbf4a5SMacpaul Lin {
138b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv;
139b3dbf4a5SMacpaul Lin int i;
140b3dbf4a5SMacpaul Lin u16 status, adv;
141b3dbf4a5SMacpaul Lin
142b3dbf4a5SMacpaul Lin adv = ADVERTISE_CSMA | ADVERTISE_ALL;
143b3dbf4a5SMacpaul Lin
144b3dbf4a5SMacpaul Lin ftgmac100_phy_write(dev, priv->phy_addr, MII_ADVERTISE, adv);
145b3dbf4a5SMacpaul Lin
146b3dbf4a5SMacpaul Lin printf("%s: Starting autonegotiation...\n", dev->name);
147b3dbf4a5SMacpaul Lin
148b3dbf4a5SMacpaul Lin ftgmac100_phy_write(dev, priv->phy_addr,
149b3dbf4a5SMacpaul Lin MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART));
150b3dbf4a5SMacpaul Lin
151b3dbf4a5SMacpaul Lin for (i = 0; i < 100000 / 100; i++) {
152b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &status);
153b3dbf4a5SMacpaul Lin
154b3dbf4a5SMacpaul Lin if (status & BMSR_ANEGCOMPLETE)
155b3dbf4a5SMacpaul Lin break;
156b3dbf4a5SMacpaul Lin mdelay(1);
157b3dbf4a5SMacpaul Lin }
158b3dbf4a5SMacpaul Lin
159b3dbf4a5SMacpaul Lin if (status & BMSR_ANEGCOMPLETE) {
160b3dbf4a5SMacpaul Lin printf("%s: Autonegotiation complete\n", dev->name);
161b3dbf4a5SMacpaul Lin } else {
162b3dbf4a5SMacpaul Lin printf("%s: Autonegotiation timed out (status=0x%04x)\n",
163b3dbf4a5SMacpaul Lin dev->name, status);
164b3dbf4a5SMacpaul Lin return 0;
165b3dbf4a5SMacpaul Lin }
166b3dbf4a5SMacpaul Lin
167b3dbf4a5SMacpaul Lin return 1;
168b3dbf4a5SMacpaul Lin }
169b3dbf4a5SMacpaul Lin
ftgmac100_phy_init(struct eth_device * dev)170b3dbf4a5SMacpaul Lin static int ftgmac100_phy_init(struct eth_device *dev)
171b3dbf4a5SMacpaul Lin {
172b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv;
173b3dbf4a5SMacpaul Lin
174b3dbf4a5SMacpaul Lin int phy_addr;
175b3dbf4a5SMacpaul Lin u16 phy_id, status, adv, lpa, stat_ge;
176b3dbf4a5SMacpaul Lin int media, speed, duplex;
177b3dbf4a5SMacpaul Lin int i;
178b3dbf4a5SMacpaul Lin
179b3dbf4a5SMacpaul Lin /* Check if the PHY is up to snuff... */
180b3dbf4a5SMacpaul Lin for (phy_addr = 0; phy_addr < CONFIG_PHY_MAX_ADDR; phy_addr++) {
181b3dbf4a5SMacpaul Lin
182b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, phy_addr, MII_PHYSID1, &phy_id);
183b3dbf4a5SMacpaul Lin
184b3dbf4a5SMacpaul Lin /*
185b3dbf4a5SMacpaul Lin * When it is unable to found PHY,
186b3dbf4a5SMacpaul Lin * the interface usually return 0xffff or 0x0000
187b3dbf4a5SMacpaul Lin */
188b3dbf4a5SMacpaul Lin if (phy_id != 0xffff && phy_id != 0x0) {
189b3dbf4a5SMacpaul Lin printf("%s: found PHY at 0x%02x\n",
190b3dbf4a5SMacpaul Lin dev->name, phy_addr);
191b3dbf4a5SMacpaul Lin priv->phy_addr = phy_addr;
192b3dbf4a5SMacpaul Lin break;
193b3dbf4a5SMacpaul Lin }
194b3dbf4a5SMacpaul Lin }
195b3dbf4a5SMacpaul Lin
196b3dbf4a5SMacpaul Lin if (phy_id == 0xffff || phy_id == 0x0) {
197b3dbf4a5SMacpaul Lin printf("%s: no PHY present\n", dev->name);
198b3dbf4a5SMacpaul Lin return 0;
199b3dbf4a5SMacpaul Lin }
200b3dbf4a5SMacpaul Lin
201b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &status);
202b3dbf4a5SMacpaul Lin
203b3dbf4a5SMacpaul Lin if (!(status & BMSR_LSTATUS)) {
204b3dbf4a5SMacpaul Lin /* Try to re-negotiate if we don't have link already. */
205b3dbf4a5SMacpaul Lin ftgmac100_phy_reset(dev);
206b3dbf4a5SMacpaul Lin
207b3dbf4a5SMacpaul Lin for (i = 0; i < 100000 / 100; i++) {
208b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr,
209b3dbf4a5SMacpaul Lin MII_BMSR, &status);
210b3dbf4a5SMacpaul Lin if (status & BMSR_LSTATUS)
211b3dbf4a5SMacpaul Lin break;
212b3dbf4a5SMacpaul Lin udelay(100);
213b3dbf4a5SMacpaul Lin }
214b3dbf4a5SMacpaul Lin }
215b3dbf4a5SMacpaul Lin
216b3dbf4a5SMacpaul Lin if (!(status & BMSR_LSTATUS)) {
217b3dbf4a5SMacpaul Lin printf("%s: link down\n", dev->name);
218b3dbf4a5SMacpaul Lin return 0;
219b3dbf4a5SMacpaul Lin }
220b3dbf4a5SMacpaul Lin
221b3dbf4a5SMacpaul Lin #ifdef CONFIG_FTGMAC100_EGIGA
222b3dbf4a5SMacpaul Lin /* 1000 Base-T Status Register */
223b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr,
224b3dbf4a5SMacpaul Lin MII_STAT1000, &stat_ge);
225b3dbf4a5SMacpaul Lin
226b3dbf4a5SMacpaul Lin speed = (stat_ge & (LPA_1000FULL | LPA_1000HALF)
227b3dbf4a5SMacpaul Lin ? 1 : 0);
228b3dbf4a5SMacpaul Lin
229b3dbf4a5SMacpaul Lin duplex = ((stat_ge & LPA_1000FULL)
230b3dbf4a5SMacpaul Lin ? 1 : 0);
231b3dbf4a5SMacpaul Lin
232b3dbf4a5SMacpaul Lin if (speed) { /* Speed is 1000 */
233b3dbf4a5SMacpaul Lin printf("%s: link up, 1000bps %s-duplex\n",
234b3dbf4a5SMacpaul Lin dev->name, duplex ? "full" : "half");
235b3dbf4a5SMacpaul Lin return 0;
236b3dbf4a5SMacpaul Lin }
237b3dbf4a5SMacpaul Lin #endif
238b3dbf4a5SMacpaul Lin
239b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_ADVERTISE, &adv);
240b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_LPA, &lpa);
241b3dbf4a5SMacpaul Lin
242b3dbf4a5SMacpaul Lin media = mii_nway_result(lpa & adv);
243b3dbf4a5SMacpaul Lin speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? 1 : 0);
244b3dbf4a5SMacpaul Lin duplex = (media & ADVERTISE_FULL) ? 1 : 0;
245b3dbf4a5SMacpaul Lin
246b3dbf4a5SMacpaul Lin printf("%s: link up, %sMbps %s-duplex\n",
247b3dbf4a5SMacpaul Lin dev->name, speed ? "100" : "10", duplex ? "full" : "half");
248b3dbf4a5SMacpaul Lin
249b3dbf4a5SMacpaul Lin return 1;
250b3dbf4a5SMacpaul Lin }
251b3dbf4a5SMacpaul Lin
ftgmac100_update_link_speed(struct eth_device * dev)252b3dbf4a5SMacpaul Lin static int ftgmac100_update_link_speed(struct eth_device *dev)
253b3dbf4a5SMacpaul Lin {
254b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
255b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv;
256b3dbf4a5SMacpaul Lin
257b3dbf4a5SMacpaul Lin unsigned short stat_fe;
258b3dbf4a5SMacpaul Lin unsigned short stat_ge;
259b3dbf4a5SMacpaul Lin unsigned int maccr;
260b3dbf4a5SMacpaul Lin
261b3dbf4a5SMacpaul Lin #ifdef CONFIG_FTGMAC100_EGIGA
262b3dbf4a5SMacpaul Lin /* 1000 Base-T Status Register */
263b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_STAT1000, &stat_ge);
264b3dbf4a5SMacpaul Lin #endif
265b3dbf4a5SMacpaul Lin
266b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &stat_fe);
267b3dbf4a5SMacpaul Lin
268b3dbf4a5SMacpaul Lin if (!(stat_fe & BMSR_LSTATUS)) /* link status up? */
269b3dbf4a5SMacpaul Lin return 0;
270b3dbf4a5SMacpaul Lin
271b3dbf4a5SMacpaul Lin /* read MAC control register and clear related bits */
272b3dbf4a5SMacpaul Lin maccr = readl(&ftgmac100->maccr) &
273b3dbf4a5SMacpaul Lin ~(FTGMAC100_MACCR_GIGA_MODE |
274b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_FAST_MODE |
275b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_FULLDUP);
276b3dbf4a5SMacpaul Lin
277b3dbf4a5SMacpaul Lin #ifdef CONFIG_FTGMAC100_EGIGA
278b3dbf4a5SMacpaul Lin if (stat_ge & LPA_1000FULL) {
279b3dbf4a5SMacpaul Lin /* set gmac for 1000BaseTX and Full Duplex */
280b3dbf4a5SMacpaul Lin maccr |= FTGMAC100_MACCR_GIGA_MODE | FTGMAC100_MACCR_FULLDUP;
281b3dbf4a5SMacpaul Lin }
282b3dbf4a5SMacpaul Lin
283b3dbf4a5SMacpaul Lin if (stat_ge & LPA_1000HALF) {
284b3dbf4a5SMacpaul Lin /* set gmac for 1000BaseTX and Half Duplex */
285b3dbf4a5SMacpaul Lin maccr |= FTGMAC100_MACCR_GIGA_MODE;
286b3dbf4a5SMacpaul Lin }
287b3dbf4a5SMacpaul Lin #endif
288b3dbf4a5SMacpaul Lin
289b3dbf4a5SMacpaul Lin if (stat_fe & BMSR_100FULL) {
290b3dbf4a5SMacpaul Lin /* set MII for 100BaseTX and Full Duplex */
291b3dbf4a5SMacpaul Lin maccr |= FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_FULLDUP;
292b3dbf4a5SMacpaul Lin }
293b3dbf4a5SMacpaul Lin
294b3dbf4a5SMacpaul Lin if (stat_fe & BMSR_10FULL) {
295b3dbf4a5SMacpaul Lin /* set MII for 10BaseT and Full Duplex */
296b3dbf4a5SMacpaul Lin maccr |= FTGMAC100_MACCR_FULLDUP;
297b3dbf4a5SMacpaul Lin }
298b3dbf4a5SMacpaul Lin
299b3dbf4a5SMacpaul Lin if (stat_fe & BMSR_100HALF) {
300b3dbf4a5SMacpaul Lin /* set MII for 100BaseTX and Half Duplex */
301b3dbf4a5SMacpaul Lin maccr |= FTGMAC100_MACCR_FAST_MODE;
302b3dbf4a5SMacpaul Lin }
303b3dbf4a5SMacpaul Lin
304b3dbf4a5SMacpaul Lin if (stat_fe & BMSR_10HALF) {
305b3dbf4a5SMacpaul Lin /* set MII for 10BaseT and Half Duplex */
306b3dbf4a5SMacpaul Lin /* we have already clear these bits, do nothing */
307b3dbf4a5SMacpaul Lin ;
308b3dbf4a5SMacpaul Lin }
309b3dbf4a5SMacpaul Lin
310b3dbf4a5SMacpaul Lin /* update MII config into maccr */
311b3dbf4a5SMacpaul Lin writel(maccr, &ftgmac100->maccr);
312b3dbf4a5SMacpaul Lin
313b3dbf4a5SMacpaul Lin return 1;
314b3dbf4a5SMacpaul Lin }
315b3dbf4a5SMacpaul Lin
316b3dbf4a5SMacpaul Lin /*
317b3dbf4a5SMacpaul Lin * Reset MAC
318b3dbf4a5SMacpaul Lin */
ftgmac100_reset(struct eth_device * dev)319b3dbf4a5SMacpaul Lin static void ftgmac100_reset(struct eth_device *dev)
320b3dbf4a5SMacpaul Lin {
321b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
322b3dbf4a5SMacpaul Lin
323b3dbf4a5SMacpaul Lin debug("%s()\n", __func__);
324b3dbf4a5SMacpaul Lin
325b3dbf4a5SMacpaul Lin writel(FTGMAC100_MACCR_SW_RST, &ftgmac100->maccr);
326b3dbf4a5SMacpaul Lin
327b3dbf4a5SMacpaul Lin while (readl(&ftgmac100->maccr) & FTGMAC100_MACCR_SW_RST)
328b3dbf4a5SMacpaul Lin ;
329b3dbf4a5SMacpaul Lin }
330b3dbf4a5SMacpaul Lin
331b3dbf4a5SMacpaul Lin /*
332b3dbf4a5SMacpaul Lin * Set MAC address
333b3dbf4a5SMacpaul Lin */
ftgmac100_set_mac(struct eth_device * dev,const unsigned char * mac)334b3dbf4a5SMacpaul Lin static void ftgmac100_set_mac(struct eth_device *dev,
335b3dbf4a5SMacpaul Lin const unsigned char *mac)
336b3dbf4a5SMacpaul Lin {
337b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
338b3dbf4a5SMacpaul Lin unsigned int maddr = mac[0] << 8 | mac[1];
339b3dbf4a5SMacpaul Lin unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
340b3dbf4a5SMacpaul Lin
341b3dbf4a5SMacpaul Lin debug("%s(%x %x)\n", __func__, maddr, laddr);
342b3dbf4a5SMacpaul Lin
343b3dbf4a5SMacpaul Lin writel(maddr, &ftgmac100->mac_madr);
344b3dbf4a5SMacpaul Lin writel(laddr, &ftgmac100->mac_ladr);
345b3dbf4a5SMacpaul Lin }
346b3dbf4a5SMacpaul Lin
ftgmac100_set_mac_from_env(struct eth_device * dev)347b3dbf4a5SMacpaul Lin static void ftgmac100_set_mac_from_env(struct eth_device *dev)
348b3dbf4a5SMacpaul Lin {
349*35affd7aSSimon Glass eth_env_get_enetaddr("ethaddr", dev->enetaddr);
350b3dbf4a5SMacpaul Lin
351b3dbf4a5SMacpaul Lin ftgmac100_set_mac(dev, dev->enetaddr);
352b3dbf4a5SMacpaul Lin }
353b3dbf4a5SMacpaul Lin
354b3dbf4a5SMacpaul Lin /*
355b3dbf4a5SMacpaul Lin * disable transmitter, receiver
356b3dbf4a5SMacpaul Lin */
ftgmac100_halt(struct eth_device * dev)357b3dbf4a5SMacpaul Lin static void ftgmac100_halt(struct eth_device *dev)
358b3dbf4a5SMacpaul Lin {
359b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
360b3dbf4a5SMacpaul Lin
361b3dbf4a5SMacpaul Lin debug("%s()\n", __func__);
362b3dbf4a5SMacpaul Lin
363b3dbf4a5SMacpaul Lin writel(0, &ftgmac100->maccr);
364b3dbf4a5SMacpaul Lin }
365b3dbf4a5SMacpaul Lin
ftgmac100_init(struct eth_device * dev,bd_t * bd)366b3dbf4a5SMacpaul Lin static int ftgmac100_init(struct eth_device *dev, bd_t *bd)
367b3dbf4a5SMacpaul Lin {
368b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
369b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv;
370a8f9cd18SKuo-Jung Su struct ftgmac100_txdes *txdes;
371a8f9cd18SKuo-Jung Su struct ftgmac100_rxdes *rxdes;
372b3dbf4a5SMacpaul Lin unsigned int maccr;
373a8f9cd18SKuo-Jung Su void *buf;
374b3dbf4a5SMacpaul Lin int i;
375b3dbf4a5SMacpaul Lin
376b3dbf4a5SMacpaul Lin debug("%s()\n", __func__);
377b3dbf4a5SMacpaul Lin
378a8f9cd18SKuo-Jung Su if (!priv->txdes) {
379a8f9cd18SKuo-Jung Su txdes = dma_alloc_coherent(
380a8f9cd18SKuo-Jung Su sizeof(*txdes) * PKTBUFSTX, &priv->txdes_dma);
381a8f9cd18SKuo-Jung Su if (!txdes)
382a8f9cd18SKuo-Jung Su panic("ftgmac100: out of memory\n");
383a8f9cd18SKuo-Jung Su memset(txdes, 0, sizeof(*txdes) * PKTBUFSTX);
384a8f9cd18SKuo-Jung Su priv->txdes = txdes;
385a8f9cd18SKuo-Jung Su }
386a8f9cd18SKuo-Jung Su txdes = priv->txdes;
387a8f9cd18SKuo-Jung Su
388a8f9cd18SKuo-Jung Su if (!priv->rxdes) {
389a8f9cd18SKuo-Jung Su rxdes = dma_alloc_coherent(
390a8f9cd18SKuo-Jung Su sizeof(*rxdes) * PKTBUFSRX, &priv->rxdes_dma);
391a8f9cd18SKuo-Jung Su if (!rxdes)
392a8f9cd18SKuo-Jung Su panic("ftgmac100: out of memory\n");
393a8f9cd18SKuo-Jung Su memset(rxdes, 0, sizeof(*rxdes) * PKTBUFSRX);
394a8f9cd18SKuo-Jung Su priv->rxdes = rxdes;
395a8f9cd18SKuo-Jung Su }
396a8f9cd18SKuo-Jung Su rxdes = priv->rxdes;
397a8f9cd18SKuo-Jung Su
398b3dbf4a5SMacpaul Lin /* set the ethernet address */
399b3dbf4a5SMacpaul Lin ftgmac100_set_mac_from_env(dev);
400b3dbf4a5SMacpaul Lin
401b3dbf4a5SMacpaul Lin /* disable all interrupts */
402b3dbf4a5SMacpaul Lin writel(0, &ftgmac100->ier);
403b3dbf4a5SMacpaul Lin
404b3dbf4a5SMacpaul Lin /* initialize descriptors */
405b3dbf4a5SMacpaul Lin priv->tx_index = 0;
406b3dbf4a5SMacpaul Lin priv->rx_index = 0;
407b3dbf4a5SMacpaul Lin
408b3dbf4a5SMacpaul Lin txdes[PKTBUFSTX - 1].txdes0 = FTGMAC100_TXDES0_EDOTR;
409b3dbf4a5SMacpaul Lin rxdes[PKTBUFSRX - 1].rxdes0 = FTGMAC100_RXDES0_EDORR;
410b3dbf4a5SMacpaul Lin
411b3dbf4a5SMacpaul Lin for (i = 0; i < PKTBUFSTX; i++) {
412b3dbf4a5SMacpaul Lin /* TXBUF_BADR */
413a8f9cd18SKuo-Jung Su if (!txdes[i].txdes2) {
414a8f9cd18SKuo-Jung Su buf = memalign(ARCH_DMA_MINALIGN, CFG_XBUF_SIZE);
415a8f9cd18SKuo-Jung Su if (!buf)
416a8f9cd18SKuo-Jung Su panic("ftgmac100: out of memory\n");
417a8f9cd18SKuo-Jung Su txdes[i].txdes3 = virt_to_phys(buf);
418a8f9cd18SKuo-Jung Su txdes[i].txdes2 = (uint)buf;
419a8f9cd18SKuo-Jung Su }
420b3dbf4a5SMacpaul Lin txdes[i].txdes1 = 0;
421b3dbf4a5SMacpaul Lin }
422b3dbf4a5SMacpaul Lin
423b3dbf4a5SMacpaul Lin for (i = 0; i < PKTBUFSRX; i++) {
424b3dbf4a5SMacpaul Lin /* RXBUF_BADR */
425a8f9cd18SKuo-Jung Su if (!rxdes[i].rxdes2) {
4261fd92db8SJoe Hershberger buf = net_rx_packets[i];
427a8f9cd18SKuo-Jung Su rxdes[i].rxdes3 = virt_to_phys(buf);
428a8f9cd18SKuo-Jung Su rxdes[i].rxdes2 = (uint)buf;
429a8f9cd18SKuo-Jung Su }
430b3dbf4a5SMacpaul Lin rxdes[i].rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY;
431b3dbf4a5SMacpaul Lin }
432b3dbf4a5SMacpaul Lin
433b3dbf4a5SMacpaul Lin /* transmit ring */
434a8f9cd18SKuo-Jung Su writel(priv->txdes_dma, &ftgmac100->txr_badr);
435b3dbf4a5SMacpaul Lin
436b3dbf4a5SMacpaul Lin /* receive ring */
437a8f9cd18SKuo-Jung Su writel(priv->rxdes_dma, &ftgmac100->rxr_badr);
438b3dbf4a5SMacpaul Lin
439b3dbf4a5SMacpaul Lin /* poll receive descriptor automatically */
440b3dbf4a5SMacpaul Lin writel(FTGMAC100_APTC_RXPOLL_CNT(1), &ftgmac100->aptc);
441b3dbf4a5SMacpaul Lin
442b3dbf4a5SMacpaul Lin /* config receive buffer size register */
443b3dbf4a5SMacpaul Lin writel(FTGMAC100_RBSR_SIZE(RBSR_DEFAULT_VALUE), &ftgmac100->rbsr);
444b3dbf4a5SMacpaul Lin
445b3dbf4a5SMacpaul Lin /* enable transmitter, receiver */
446b3dbf4a5SMacpaul Lin maccr = FTGMAC100_MACCR_TXMAC_EN |
447b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_RXMAC_EN |
448b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_TXDMA_EN |
449b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_RXDMA_EN |
450b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_CRC_APD |
451b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_FULLDUP |
452b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_RX_RUNT |
453b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_RX_BROADPKT;
454b3dbf4a5SMacpaul Lin
455b3dbf4a5SMacpaul Lin writel(maccr, &ftgmac100->maccr);
456b3dbf4a5SMacpaul Lin
457b3dbf4a5SMacpaul Lin if (!ftgmac100_phy_init(dev)) {
458b3dbf4a5SMacpaul Lin if (!ftgmac100_update_link_speed(dev))
459b3dbf4a5SMacpaul Lin return -1;
460b3dbf4a5SMacpaul Lin }
461b3dbf4a5SMacpaul Lin
462b3dbf4a5SMacpaul Lin return 0;
463b3dbf4a5SMacpaul Lin }
464b3dbf4a5SMacpaul Lin
465b3dbf4a5SMacpaul Lin /*
466b3dbf4a5SMacpaul Lin * Get a data block via Ethernet
467b3dbf4a5SMacpaul Lin */
ftgmac100_recv(struct eth_device * dev)468b3dbf4a5SMacpaul Lin static int ftgmac100_recv(struct eth_device *dev)
469b3dbf4a5SMacpaul Lin {
470b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv;
471b3dbf4a5SMacpaul Lin struct ftgmac100_rxdes *curr_des;
472b3dbf4a5SMacpaul Lin unsigned short rxlen;
473b3dbf4a5SMacpaul Lin
474b3dbf4a5SMacpaul Lin curr_des = &priv->rxdes[priv->rx_index];
475b3dbf4a5SMacpaul Lin
476b3dbf4a5SMacpaul Lin if (!(curr_des->rxdes0 & FTGMAC100_RXDES0_RXPKT_RDY))
477b3dbf4a5SMacpaul Lin return -1;
478b3dbf4a5SMacpaul Lin
479b3dbf4a5SMacpaul Lin if (curr_des->rxdes0 & (FTGMAC100_RXDES0_RX_ERR |
480b3dbf4a5SMacpaul Lin FTGMAC100_RXDES0_CRC_ERR |
481b3dbf4a5SMacpaul Lin FTGMAC100_RXDES0_FTL |
482b3dbf4a5SMacpaul Lin FTGMAC100_RXDES0_RUNT |
483b3dbf4a5SMacpaul Lin FTGMAC100_RXDES0_RX_ODD_NB)) {
484b3dbf4a5SMacpaul Lin return -1;
485b3dbf4a5SMacpaul Lin }
486b3dbf4a5SMacpaul Lin
487b3dbf4a5SMacpaul Lin rxlen = FTGMAC100_RXDES0_VDBC(curr_des->rxdes0);
488b3dbf4a5SMacpaul Lin
489b3dbf4a5SMacpaul Lin debug("%s(): RX buffer %d, %x received\n",
490b3dbf4a5SMacpaul Lin __func__, priv->rx_index, rxlen);
491b3dbf4a5SMacpaul Lin
492a8f9cd18SKuo-Jung Su /* invalidate d-cache */
493a8f9cd18SKuo-Jung Su dma_map_single((void *)curr_des->rxdes2, rxlen, DMA_FROM_DEVICE);
494a8f9cd18SKuo-Jung Su
495b3dbf4a5SMacpaul Lin /* pass the packet up to the protocol layers. */
4961fd92db8SJoe Hershberger net_process_received_packet((void *)curr_des->rxdes2, rxlen);
497b3dbf4a5SMacpaul Lin
498b3dbf4a5SMacpaul Lin /* release buffer to DMA */
499b3dbf4a5SMacpaul Lin curr_des->rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY;
500b3dbf4a5SMacpaul Lin
501b3dbf4a5SMacpaul Lin priv->rx_index = (priv->rx_index + 1) % PKTBUFSRX;
502b3dbf4a5SMacpaul Lin
503b3dbf4a5SMacpaul Lin return 0;
504b3dbf4a5SMacpaul Lin }
505b3dbf4a5SMacpaul Lin
506b3dbf4a5SMacpaul Lin /*
507b3dbf4a5SMacpaul Lin * Send a data block via Ethernet
508b3dbf4a5SMacpaul Lin */
ftgmac100_send(struct eth_device * dev,void * packet,int length)50910cbe3b6SJoe Hershberger static int ftgmac100_send(struct eth_device *dev, void *packet, int length)
510b3dbf4a5SMacpaul Lin {
511b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
512b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv;
513b3dbf4a5SMacpaul Lin struct ftgmac100_txdes *curr_des = &priv->txdes[priv->tx_index];
514b3dbf4a5SMacpaul Lin
515b3dbf4a5SMacpaul Lin if (curr_des->txdes0 & FTGMAC100_TXDES0_TXDMA_OWN) {
516b3dbf4a5SMacpaul Lin debug("%s(): no TX descriptor available\n", __func__);
517b3dbf4a5SMacpaul Lin return -1;
518b3dbf4a5SMacpaul Lin }
519b3dbf4a5SMacpaul Lin
520b3dbf4a5SMacpaul Lin debug("%s(%x, %x)\n", __func__, (int)packet, length);
521b3dbf4a5SMacpaul Lin
522b3dbf4a5SMacpaul Lin length = (length < ETH_ZLEN) ? ETH_ZLEN : length;
523b3dbf4a5SMacpaul Lin
524a8f9cd18SKuo-Jung Su memcpy((void *)curr_des->txdes2, (void *)packet, length);
525a8f9cd18SKuo-Jung Su dma_map_single((void *)curr_des->txdes2, length, DMA_TO_DEVICE);
526b3dbf4a5SMacpaul Lin
527b3dbf4a5SMacpaul Lin /* only one descriptor on TXBUF */
528b3dbf4a5SMacpaul Lin curr_des->txdes0 &= FTGMAC100_TXDES0_EDOTR;
529b3dbf4a5SMacpaul Lin curr_des->txdes0 |= FTGMAC100_TXDES0_FTS |
530b3dbf4a5SMacpaul Lin FTGMAC100_TXDES0_LTS |
531b3dbf4a5SMacpaul Lin FTGMAC100_TXDES0_TXBUF_SIZE(length) |
532b3dbf4a5SMacpaul Lin FTGMAC100_TXDES0_TXDMA_OWN ;
533b3dbf4a5SMacpaul Lin
534b3dbf4a5SMacpaul Lin /* start transmit */
535b3dbf4a5SMacpaul Lin writel(1, &ftgmac100->txpd);
536b3dbf4a5SMacpaul Lin
537b3dbf4a5SMacpaul Lin debug("%s(): packet sent\n", __func__);
538b3dbf4a5SMacpaul Lin
539b3dbf4a5SMacpaul Lin priv->tx_index = (priv->tx_index + 1) % PKTBUFSTX;
540b3dbf4a5SMacpaul Lin
541b3dbf4a5SMacpaul Lin return 0;
542b3dbf4a5SMacpaul Lin }
543b3dbf4a5SMacpaul Lin
ftgmac100_initialize(bd_t * bd)544b3dbf4a5SMacpaul Lin int ftgmac100_initialize(bd_t *bd)
545b3dbf4a5SMacpaul Lin {
546b3dbf4a5SMacpaul Lin struct eth_device *dev;
547b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv;
548b3dbf4a5SMacpaul Lin
549b3dbf4a5SMacpaul Lin dev = malloc(sizeof *dev);
550b3dbf4a5SMacpaul Lin if (!dev) {
551b3dbf4a5SMacpaul Lin printf("%s(): failed to allocate dev\n", __func__);
552b3dbf4a5SMacpaul Lin goto out;
553b3dbf4a5SMacpaul Lin }
554b3dbf4a5SMacpaul Lin
555b3dbf4a5SMacpaul Lin /* Transmit and receive descriptors should align to 16 bytes */
556b3dbf4a5SMacpaul Lin priv = memalign(16, sizeof(struct ftgmac100_data));
557b3dbf4a5SMacpaul Lin if (!priv) {
558b3dbf4a5SMacpaul Lin printf("%s(): failed to allocate priv\n", __func__);
559b3dbf4a5SMacpaul Lin goto free_dev;
560b3dbf4a5SMacpaul Lin }
561b3dbf4a5SMacpaul Lin
562b3dbf4a5SMacpaul Lin memset(dev, 0, sizeof(*dev));
563b3dbf4a5SMacpaul Lin memset(priv, 0, sizeof(*priv));
564b3dbf4a5SMacpaul Lin
565192bc694SBen Whitten strcpy(dev->name, "FTGMAC100");
566b3dbf4a5SMacpaul Lin dev->iobase = CONFIG_FTGMAC100_BASE;
567b3dbf4a5SMacpaul Lin dev->init = ftgmac100_init;
568b3dbf4a5SMacpaul Lin dev->halt = ftgmac100_halt;
569b3dbf4a5SMacpaul Lin dev->send = ftgmac100_send;
570b3dbf4a5SMacpaul Lin dev->recv = ftgmac100_recv;
571b3dbf4a5SMacpaul Lin dev->priv = priv;
572b3dbf4a5SMacpaul Lin
573b3dbf4a5SMacpaul Lin eth_register(dev);
574b3dbf4a5SMacpaul Lin
57555b438a9SMacpaul Lin ftgmac100_reset(dev);
57655b438a9SMacpaul Lin
577b3dbf4a5SMacpaul Lin return 1;
578b3dbf4a5SMacpaul Lin
579b3dbf4a5SMacpaul Lin free_dev:
580b3dbf4a5SMacpaul Lin free(dev);
581b3dbf4a5SMacpaul Lin out:
582b3dbf4a5SMacpaul Lin return 0;
583b3dbf4a5SMacpaul Lin }
584