blob: 1e34436772d3018403c7615331b600dc3595e660 [file] [log] [blame]
Vipin KUMAR1f873122010-06-29 10:53:34 +05301/*
2 * (C) Copyright 2010
3 * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
4 *
5 * See file CREDITS for list of people who contributed to this
6 * project.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21 * MA 02111-1307 USA
22 */
23
24/*
25 * Designware ethernet IP driver for u-boot
26 */
27
28#include <common.h>
29#include <miiphy.h>
30#include <malloc.h>
31#include <linux/err.h>
32#include <asm/io.h>
33#include "designware.h"
34
35static void tx_descs_init(struct eth_device *dev)
36{
37 struct dw_eth_dev *priv = dev->priv;
38 struct eth_dma_regs *dma_p = priv->dma_regs_p;
39 struct dmamacdescr *desc_table_p = &priv->tx_mac_descrtable[0];
40 char *txbuffs = &priv->txbuffs[0];
41 struct dmamacdescr *desc_p;
42 u32 idx;
43
44 for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) {
45 desc_p = &desc_table_p[idx];
46 desc_p->dmamac_addr = &txbuffs[idx * CONFIG_ETH_BUFSIZE];
47 desc_p->dmamac_next = &desc_table_p[idx + 1];
48
49#if defined(CONFIG_DW_ALTDESCRIPTOR)
50 desc_p->txrx_status &= ~(DESC_TXSTS_TXINT | DESC_TXSTS_TXLAST |
51 DESC_TXSTS_TXFIRST | DESC_TXSTS_TXCRCDIS | \
52 DESC_TXSTS_TXCHECKINSCTRL | \
53 DESC_TXSTS_TXRINGEND | DESC_TXSTS_TXPADDIS);
54
55 desc_p->txrx_status |= DESC_TXSTS_TXCHAIN;
56 desc_p->dmamac_cntl = 0;
57 desc_p->txrx_status &= ~(DESC_TXSTS_MSK | DESC_TXSTS_OWNBYDMA);
58#else
59 desc_p->dmamac_cntl = DESC_TXCTRL_TXCHAIN;
60 desc_p->txrx_status = 0;
61#endif
62 }
63
64 /* Correcting the last pointer of the chain */
65 desc_p->dmamac_next = &desc_table_p[0];
66
67 writel((ulong)&desc_table_p[0], &dma_p->txdesclistaddr);
68}
69
70static void rx_descs_init(struct eth_device *dev)
71{
72 struct dw_eth_dev *priv = dev->priv;
73 struct eth_dma_regs *dma_p = priv->dma_regs_p;
74 struct dmamacdescr *desc_table_p = &priv->rx_mac_descrtable[0];
75 char *rxbuffs = &priv->rxbuffs[0];
76 struct dmamacdescr *desc_p;
77 u32 idx;
78
79 for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) {
80 desc_p = &desc_table_p[idx];
81 desc_p->dmamac_addr = &rxbuffs[idx * CONFIG_ETH_BUFSIZE];
82 desc_p->dmamac_next = &desc_table_p[idx + 1];
83
84 desc_p->dmamac_cntl =
85 (MAC_MAX_FRAME_SZ & DESC_RXCTRL_SIZE1MASK) | \
86 DESC_RXCTRL_RXCHAIN;
87
88 desc_p->txrx_status = DESC_RXSTS_OWNBYDMA;
89 }
90
91 /* Correcting the last pointer of the chain */
92 desc_p->dmamac_next = &desc_table_p[0];
93
94 writel((ulong)&desc_table_p[0], &dma_p->rxdesclistaddr);
95}
96
97static void descs_init(struct eth_device *dev)
98{
99 tx_descs_init(dev);
100 rx_descs_init(dev);
101}
102
103static int mac_reset(struct eth_device *dev)
104{
105 struct dw_eth_dev *priv = dev->priv;
106 struct eth_mac_regs *mac_p = priv->mac_regs_p;
107 struct eth_dma_regs *dma_p = priv->dma_regs_p;
108
109 int timeout = CONFIG_MACRESET_TIMEOUT;
110
111 writel(DMAMAC_SRST, &dma_p->busmode);
112 writel(MII_PORTSELECT, &mac_p->conf);
113
114 do {
115 if (!(readl(&dma_p->busmode) & DMAMAC_SRST))
116 return 0;
117 udelay(1000);
118 } while (timeout--);
119
120 return -1;
121}
122
123static int dw_write_hwaddr(struct eth_device *dev)
124{
125 struct dw_eth_dev *priv = dev->priv;
126 struct eth_mac_regs *mac_p = priv->mac_regs_p;
127 u32 macid_lo, macid_hi;
128 u8 *mac_id = &dev->enetaddr[0];
129
130 macid_lo = mac_id[0] + (mac_id[1] << 8) + \
131 (mac_id[2] << 16) + (mac_id[3] << 24);
132 macid_hi = mac_id[4] + (mac_id[5] << 8);
133
134 writel(macid_hi, &mac_p->macaddr0hi);
135 writel(macid_lo, &mac_p->macaddr0lo);
136
137 return 0;
138}
139
140static int dw_eth_init(struct eth_device *dev, bd_t *bis)
141{
142 struct dw_eth_dev *priv = dev->priv;
143 struct eth_mac_regs *mac_p = priv->mac_regs_p;
144 struct eth_dma_regs *dma_p = priv->dma_regs_p;
145 u32 conf;
146
147 /* Reset ethernet hardware */
148 if (mac_reset(dev) < 0)
149 return -1;
150
151 writel(FIXEDBURST | PRIORXTX_41 | BURST_16,
152 &dma_p->busmode);
153
154 writel(FLUSHTXFIFO | readl(&dma_p->opmode), &dma_p->opmode);
155 writel(STOREFORWARD | TXSECONDFRAME, &dma_p->opmode);
156
157 conf = FRAMEBURSTENABLE | DISABLERXOWN;
158
159 if (priv->speed != SPEED_1000M)
160 conf |= MII_PORTSELECT;
161
162 if (priv->duplex == FULL_DUPLEX)
163 conf |= FULLDPLXMODE;
164
165 writel(conf, &mac_p->conf);
166
167 descs_init(dev);
168
169 /*
170 * Start/Enable xfer at dma as well as mac level
171 */
172 writel(readl(&dma_p->opmode) | RXSTART, &dma_p->opmode);
173 writel(readl(&dma_p->opmode) | TXSTART, &dma_p->opmode);
174
175 writel(readl(&mac_p->conf) | RXENABLE, &mac_p->conf);
176 writel(readl(&mac_p->conf) | TXENABLE, &mac_p->conf);
177
178 return 0;
179}
180
181static int dw_eth_send(struct eth_device *dev, volatile void *packet,
182 int length)
183{
184 struct dw_eth_dev *priv = dev->priv;
185 struct eth_dma_regs *dma_p = priv->dma_regs_p;
186 u32 desc_num = priv->tx_currdescnum;
187 struct dmamacdescr *desc_p = &priv->tx_mac_descrtable[desc_num];
188
189 /* Check if the descriptor is owned by CPU */
190 if (desc_p->txrx_status & DESC_TXSTS_OWNBYDMA) {
191 printf("CPU not owner of tx frame\n");
192 return -1;
193 }
194
195 memcpy((void *)desc_p->dmamac_addr, (void *)packet, length);
196
197#if defined(CONFIG_DW_ALTDESCRIPTOR)
198 desc_p->txrx_status |= DESC_TXSTS_TXFIRST | DESC_TXSTS_TXLAST;
199 desc_p->dmamac_cntl |= (length << DESC_TXCTRL_SIZE1SHFT) & \
200 DESC_TXCTRL_SIZE1MASK;
201
202 desc_p->txrx_status &= ~(DESC_TXSTS_MSK);
203 desc_p->txrx_status |= DESC_TXSTS_OWNBYDMA;
204#else
205 desc_p->dmamac_cntl |= ((length << DESC_TXCTRL_SIZE1SHFT) & \
206 DESC_TXCTRL_SIZE1MASK) | DESC_TXCTRL_TXLAST | \
207 DESC_TXCTRL_TXFIRST;
208
209 desc_p->txrx_status = DESC_TXSTS_OWNBYDMA;
210#endif
211
212 /* Test the wrap-around condition. */
213 if (++desc_num >= CONFIG_TX_DESCR_NUM)
214 desc_num = 0;
215
216 priv->tx_currdescnum = desc_num;
217
218 /* Start the transmission */
219 writel(POLL_DATA, &dma_p->txpolldemand);
220
221 return 0;
222}
223
224static int dw_eth_recv(struct eth_device *dev)
225{
226 struct dw_eth_dev *priv = dev->priv;
227 u32 desc_num = priv->rx_currdescnum;
228 struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num];
229
230 u32 status = desc_p->txrx_status;
231 int length = 0;
232
233 /* Check if the owner is the CPU */
234 if (!(status & DESC_RXSTS_OWNBYDMA)) {
235
236 length = (status & DESC_RXSTS_FRMLENMSK) >> \
237 DESC_RXSTS_FRMLENSHFT;
238
239 NetReceive(desc_p->dmamac_addr, length);
240
241 /*
242 * Make the current descriptor valid again and go to
243 * the next one
244 */
245 desc_p->txrx_status |= DESC_RXSTS_OWNBYDMA;
246
247 /* Test the wrap-around condition. */
248 if (++desc_num >= CONFIG_RX_DESCR_NUM)
249 desc_num = 0;
250 }
251
252 priv->rx_currdescnum = desc_num;
253
254 return length;
255}
256
257static void dw_eth_halt(struct eth_device *dev)
258{
259 struct dw_eth_dev *priv = dev->priv;
260
261 mac_reset(dev);
262 priv->tx_currdescnum = priv->rx_currdescnum = 0;
263}
264
265static int eth_mdio_read(struct eth_device *dev, u8 addr, u8 reg, u16 *val)
266{
267 struct dw_eth_dev *priv = dev->priv;
268 struct eth_mac_regs *mac_p = priv->mac_regs_p;
269 u32 miiaddr;
270 int timeout = CONFIG_MDIO_TIMEOUT;
271
272 miiaddr = ((addr << MIIADDRSHIFT) & MII_ADDRMSK) | \
273 ((reg << MIIREGSHIFT) & MII_REGMSK);
274
275 writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr);
276
277 do {
278 if (!(readl(&mac_p->miiaddr) & MII_BUSY)) {
279 *val = readl(&mac_p->miidata);
280 return 0;
281 }
282 udelay(1000);
283 } while (timeout--);
284
285 return -1;
286}
287
288static int eth_mdio_write(struct eth_device *dev, u8 addr, u8 reg, u16 val)
289{
290 struct dw_eth_dev *priv = dev->priv;
291 struct eth_mac_regs *mac_p = priv->mac_regs_p;
292 u32 miiaddr;
293 int ret = -1, timeout = CONFIG_MDIO_TIMEOUT;
294 u16 value;
295
296 writel(val, &mac_p->miidata);
297 miiaddr = ((addr << MIIADDRSHIFT) & MII_ADDRMSK) | \
298 ((reg << MIIREGSHIFT) & MII_REGMSK) | MII_WRITE;
299
300 writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr);
301
302 do {
303 if (!(readl(&mac_p->miiaddr) & MII_BUSY))
304 ret = 0;
305 udelay(1000);
306 } while (timeout--);
307
308 /* Needed as a fix for ST-Phy */
309 eth_mdio_read(dev, addr, reg, &value);
310
311 return ret;
312}
313
314#if defined(CONFIG_DW_SEARCH_PHY)
315static int find_phy(struct eth_device *dev)
316{
317 int phy_addr = 0;
318 u16 ctrl, oldctrl;
319
320 do {
Mike Frysingerd63ee712010-12-23 15:40:12 -0500321 eth_mdio_read(dev, phy_addr, MII_BMCR, &ctrl);
322 oldctrl = ctrl & BMCR_ANENABLE;
Vipin KUMAR1f873122010-06-29 10:53:34 +0530323
Mike Frysingerd63ee712010-12-23 15:40:12 -0500324 ctrl ^= BMCR_ANENABLE;
325 eth_mdio_write(dev, phy_addr, MII_BMCR, ctrl);
326 eth_mdio_read(dev, phy_addr, MII_BMCR, &ctrl);
327 ctrl &= BMCR_ANENABLE;
Vipin KUMAR1f873122010-06-29 10:53:34 +0530328
329 if (ctrl == oldctrl) {
330 phy_addr++;
331 } else {
Mike Frysingerd63ee712010-12-23 15:40:12 -0500332 ctrl ^= BMCR_ANENABLE;
333 eth_mdio_write(dev, phy_addr, MII_BMCR, ctrl);
Vipin KUMAR1f873122010-06-29 10:53:34 +0530334
335 return phy_addr;
336 }
337 } while (phy_addr < 32);
338
339 return -1;
340}
341#endif
342
343static int dw_reset_phy(struct eth_device *dev)
344{
345 struct dw_eth_dev *priv = dev->priv;
346 u16 ctrl;
347 int timeout = CONFIG_PHYRESET_TIMEOUT;
348 u32 phy_addr = priv->address;
349
Mike Frysingerd63ee712010-12-23 15:40:12 -0500350 eth_mdio_write(dev, phy_addr, MII_BMCR, BMCR_RESET);
Vipin KUMAR1f873122010-06-29 10:53:34 +0530351 do {
Mike Frysingerd63ee712010-12-23 15:40:12 -0500352 eth_mdio_read(dev, phy_addr, MII_BMCR, &ctrl);
353 if (!(ctrl & BMCR_RESET))
Vipin KUMAR1f873122010-06-29 10:53:34 +0530354 break;
355 udelay(1000);
356 } while (timeout--);
357
358 if (timeout < 0)
359 return -1;
360
361#ifdef CONFIG_PHY_RESET_DELAY
362 udelay(CONFIG_PHY_RESET_DELAY);
363#endif
364 return 0;
365}
366
367static int configure_phy(struct eth_device *dev)
368{
369 struct dw_eth_dev *priv = dev->priv;
370 int phy_addr;
Mike Frysinger352f9fd2011-06-02 05:19:37 +0000371 u16 bmcr;
Vipin KUMAR1f873122010-06-29 10:53:34 +0530372#if defined(CONFIG_DW_AUTONEG)
373 u16 bmsr;
374 u32 timeout;
375 u16 anlpar, btsr;
Mike Frysinger352f9fd2011-06-02 05:19:37 +0000376#else
377 u16 ctrl;
Vipin KUMAR1f873122010-06-29 10:53:34 +0530378#endif
379
380#if defined(CONFIG_DW_SEARCH_PHY)
381 phy_addr = find_phy(dev);
382 if (phy_addr > 0)
383 priv->address = phy_addr;
384 else
385 return -1;
Mike Frysingerc7c60102011-06-02 05:19:38 +0000386#else
387 phy_addr = priv->address;
Vipin KUMAR1f873122010-06-29 10:53:34 +0530388#endif
389 if (dw_reset_phy(dev) < 0)
390 return -1;
391
392#if defined(CONFIG_DW_AUTONEG)
Mike Frysingerd63ee712010-12-23 15:40:12 -0500393 bmcr = BMCR_ANENABLE | BMCR_ANRESTART | BMCR_SPEED100 | \
394 BMCR_FULLDPLX | BMCR_SPEED1000;
Vipin KUMAR1f873122010-06-29 10:53:34 +0530395#else
Mike Frysingerd63ee712010-12-23 15:40:12 -0500396 bmcr = BMCR_SPEED100 | BMCR_FULLDPLX;
Vipin KUMAR1f873122010-06-29 10:53:34 +0530397
398#if defined(CONFIG_DW_SPEED10M)
Mike Frysingerd63ee712010-12-23 15:40:12 -0500399 bmcr &= ~BMCR_SPEED100;
Vipin KUMAR1f873122010-06-29 10:53:34 +0530400#endif
401#if defined(CONFIG_DW_DUPLEXHALF)
Mike Frysingerd63ee712010-12-23 15:40:12 -0500402 bmcr &= ~BMCR_FULLDPLX;
Vipin KUMAR1f873122010-06-29 10:53:34 +0530403#endif
404#endif
Mike Frysingerd63ee712010-12-23 15:40:12 -0500405 if (eth_mdio_write(dev, phy_addr, MII_BMCR, bmcr) < 0)
Vipin KUMAR1f873122010-06-29 10:53:34 +0530406 return -1;
407
408 /* Read the phy status register and populate priv structure */
409#if defined(CONFIG_DW_AUTONEG)
410 timeout = CONFIG_AUTONEG_TIMEOUT;
411 do {
Mike Frysingerd63ee712010-12-23 15:40:12 -0500412 eth_mdio_read(dev, phy_addr, MII_BMSR, &bmsr);
413 if (bmsr & BMSR_ANEGCOMPLETE)
Vipin KUMAR1f873122010-06-29 10:53:34 +0530414 break;
415 udelay(1000);
416 } while (timeout--);
417
Mike Frysingerd63ee712010-12-23 15:40:12 -0500418 eth_mdio_read(dev, phy_addr, MII_LPA, &anlpar);
419 eth_mdio_read(dev, phy_addr, MII_STAT1000, &btsr);
Vipin KUMAR1f873122010-06-29 10:53:34 +0530420
421 if (btsr & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) {
422 priv->speed = SPEED_1000M;
423 if (btsr & PHY_1000BTSR_1000FD)
424 priv->duplex = FULL_DUPLEX;
425 else
426 priv->duplex = HALF_DUPLEX;
427 } else {
Mike Frysingerd63ee712010-12-23 15:40:12 -0500428 if (anlpar & LPA_100)
Vipin KUMAR1f873122010-06-29 10:53:34 +0530429 priv->speed = SPEED_100M;
430 else
431 priv->speed = SPEED_10M;
432
Mike Frysingerd63ee712010-12-23 15:40:12 -0500433 if (anlpar & (LPA_10FULL | LPA_100FULL))
Vipin KUMAR1f873122010-06-29 10:53:34 +0530434 priv->duplex = FULL_DUPLEX;
435 else
436 priv->duplex = HALF_DUPLEX;
437 }
438#else
Mike Frysingerd63ee712010-12-23 15:40:12 -0500439 if (eth_mdio_read(dev, phy_addr, MII_BMCR, &ctrl) < 0)
Vipin KUMAR1f873122010-06-29 10:53:34 +0530440 return -1;
441
Mike Frysingerd63ee712010-12-23 15:40:12 -0500442 if (ctrl & BMCR_FULLDPLX)
Vipin KUMAR1f873122010-06-29 10:53:34 +0530443 priv->duplex = FULL_DUPLEX;
444 else
445 priv->duplex = HALF_DUPLEX;
446
Mike Frysingerd63ee712010-12-23 15:40:12 -0500447 if (ctrl & BMCR_SPEED1000)
Vipin KUMAR1f873122010-06-29 10:53:34 +0530448 priv->speed = SPEED_1000M;
Mike Frysingerd63ee712010-12-23 15:40:12 -0500449 else if (ctrl & BMCR_SPEED100)
Vipin KUMAR1f873122010-06-29 10:53:34 +0530450 priv->speed = SPEED_100M;
451 else
452 priv->speed = SPEED_10M;
453#endif
454 return 0;
455}
456
457#if defined(CONFIG_MII)
Mike Frysinger5ff5fdb2010-07-27 18:35:08 -0400458static int dw_mii_read(const char *devname, u8 addr, u8 reg, u16 *val)
Vipin KUMAR1f873122010-06-29 10:53:34 +0530459{
460 struct eth_device *dev;
461
462 dev = eth_get_dev_by_name(devname);
463 if (dev)
464 eth_mdio_read(dev, addr, reg, val);
465
466 return 0;
467}
468
Mike Frysinger5ff5fdb2010-07-27 18:35:08 -0400469static int dw_mii_write(const char *devname, u8 addr, u8 reg, u16 val)
Vipin KUMAR1f873122010-06-29 10:53:34 +0530470{
471 struct eth_device *dev;
472
473 dev = eth_get_dev_by_name(devname);
474 if (dev)
475 eth_mdio_write(dev, addr, reg, val);
476
477 return 0;
478}
479#endif
480
481int designware_initialize(u32 id, ulong base_addr, u32 phy_addr)
482{
483 struct eth_device *dev;
484 struct dw_eth_dev *priv;
485
486 dev = (struct eth_device *) malloc(sizeof(struct eth_device));
487 if (!dev)
488 return -ENOMEM;
489
490 /*
491 * Since the priv structure contains the descriptors which need a strict
492 * buswidth alignment, memalign is used to allocate memory
493 */
494 priv = (struct dw_eth_dev *) memalign(16, sizeof(struct dw_eth_dev));
495 if (!priv) {
496 free(dev);
497 return -ENOMEM;
498 }
499
500 memset(dev, 0, sizeof(struct eth_device));
501 memset(priv, 0, sizeof(struct dw_eth_dev));
502
503 sprintf(dev->name, "mii%d", id);
504 dev->iobase = (int)base_addr;
505 dev->priv = priv;
506
Simon Glass62b36c92011-06-13 16:13:10 -0700507 eth_getenv_enetaddr_by_index("eth", id, &dev->enetaddr[0]);
Vipin KUMAR1f873122010-06-29 10:53:34 +0530508
509 priv->dev = dev;
510 priv->mac_regs_p = (struct eth_mac_regs *)base_addr;
511 priv->dma_regs_p = (struct eth_dma_regs *)(base_addr +
512 DW_DMA_BASE_OFFSET);
513 priv->address = phy_addr;
514
515 if (mac_reset(dev) < 0)
516 return -1;
517
518 if (configure_phy(dev) < 0) {
519 printf("Phy could not be configured\n");
520 return -1;
521 }
522
523 dev->init = dw_eth_init;
524 dev->send = dw_eth_send;
525 dev->recv = dw_eth_recv;
526 dev->halt = dw_eth_halt;
527 dev->write_hwaddr = dw_write_hwaddr;
528
529 eth_register(dev);
530
531#if defined(CONFIG_MII)
532 miiphy_register(dev->name, dw_mii_read, dw_mii_write);
533#endif
534 return 1;
535}