]>
Commit | Line | Data |
---|---|---|
62cbc408 IY |
1 | /* |
2 | * Dave Ethernet Controller driver | |
3 | * | |
4 | * Copyright (C) 2008 Dave S.r.l. <www.dave.eu> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <common.h> | |
12 | ||
13 | #ifndef CONFIG_DNET_AUTONEG_TIMEOUT | |
14 | #define CONFIG_DNET_AUTONEG_TIMEOUT 5000000 /* default value */ | |
15 | #endif | |
16 | ||
17 | #include <net.h> | |
18 | #include <malloc.h> | |
19 | #include <linux/mii.h> | |
20 | ||
21 | #include <miiphy.h> | |
22 | #include <asm/io.h> | |
d780e74f | 23 | #include <asm/unaligned.h> |
62cbc408 IY |
24 | |
25 | #include "dnet.h" | |
26 | ||
27 | struct dnet_device { | |
28 | struct dnet_registers *regs; | |
29 | const struct device *dev; | |
30 | struct eth_device netdev; | |
31 | unsigned short phy_addr; | |
32 | }; | |
33 | ||
34 | /* get struct dnet_device from given struct netdev */ | |
35 | #define to_dnet(_nd) container_of(_nd, struct dnet_device, netdev) | |
36 | ||
37 | /* function for reading internal MAC register */ | |
38 | u16 dnet_readw_mac(struct dnet_device *dnet, u16 reg) | |
39 | { | |
40 | u16 data_read; | |
41 | ||
42 | /* issue a read */ | |
43 | writel(reg, &dnet->regs->MACREG_ADDR); | |
44 | ||
45 | /* since a read/write op to the MAC is very slow, | |
46 | * we must wait before reading the data */ | |
47 | udelay(1); | |
48 | ||
49 | /* read data read from the MAC register */ | |
50 | data_read = readl(&dnet->regs->MACREG_DATA); | |
51 | ||
52 | /* all done */ | |
53 | return data_read; | |
54 | } | |
55 | ||
56 | /* function for writing internal MAC register */ | |
57 | void dnet_writew_mac(struct dnet_device *dnet, u16 reg, u16 val) | |
58 | { | |
59 | /* load data to write */ | |
60 | writel(val, &dnet->regs->MACREG_DATA); | |
61 | ||
62 | /* issue a write */ | |
63 | writel(reg | DNET_INTERNAL_WRITE, &dnet->regs->MACREG_ADDR); | |
64 | ||
65 | /* since a read/write op to the MAC is very slow, | |
66 | * we must wait before exiting */ | |
67 | udelay(1); | |
68 | } | |
69 | ||
70 | static void dnet_mdio_write(struct dnet_device *dnet, u8 reg, u16 value) | |
71 | { | |
72 | u16 tmp; | |
73 | ||
74 | debug(DRIVERNAME "dnet_mdio_write %02x:%02x <- %04x\n", | |
75 | dnet->phy_addr, reg, value); | |
76 | ||
77 | while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & | |
78 | DNET_INTERNAL_GMII_MNG_CMD_FIN)) | |
79 | ; | |
80 | ||
81 | /* prepare for a write operation */ | |
82 | tmp = (1 << 13); | |
83 | ||
84 | /* only 5 bits allowed for register offset */ | |
85 | reg &= 0x1f; | |
86 | ||
87 | /* prepare reg_value for a write */ | |
88 | tmp |= (dnet->phy_addr << 8); | |
89 | tmp |= reg; | |
90 | ||
91 | /* write data to write first */ | |
92 | dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_DAT_REG, value); | |
93 | ||
94 | /* write control word */ | |
95 | dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG, tmp); | |
96 | ||
97 | while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & | |
98 | DNET_INTERNAL_GMII_MNG_CMD_FIN)) | |
99 | ; | |
100 | } | |
101 | ||
102 | static u16 dnet_mdio_read(struct dnet_device *dnet, u8 reg) | |
103 | { | |
104 | u16 value; | |
105 | ||
106 | while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & | |
107 | DNET_INTERNAL_GMII_MNG_CMD_FIN)) | |
108 | ; | |
109 | ||
110 | /* only 5 bits allowed for register offset*/ | |
111 | reg &= 0x1f; | |
112 | ||
113 | /* prepare reg_value for a read */ | |
114 | value = (dnet->phy_addr << 8); | |
115 | value |= reg; | |
116 | ||
117 | /* write control word */ | |
118 | dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG, value); | |
119 | ||
120 | /* wait for end of transfer */ | |
121 | while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & | |
122 | DNET_INTERNAL_GMII_MNG_CMD_FIN)) | |
123 | ; | |
124 | ||
125 | value = dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_DAT_REG); | |
126 | ||
127 | debug(DRIVERNAME "dnet_mdio_read %02x:%02x <- %04x\n", | |
128 | dnet->phy_addr, reg, value); | |
129 | ||
130 | return value; | |
131 | } | |
132 | ||
133 | static int dnet_send(struct eth_device *netdev, volatile void *packet, | |
134 | int length) | |
135 | { | |
136 | struct dnet_device *dnet = to_dnet(netdev); | |
d780e74f | 137 | int i, wrsz; |
62cbc408 IY |
138 | unsigned int *bufp; |
139 | unsigned int tx_cmd; | |
140 | ||
141 | debug(DRIVERNAME "[%s] Sending %u bytes\n", __func__, length); | |
142 | ||
62cbc408 IY |
143 | bufp = (unsigned int *) (((u32)packet) & 0xFFFFFFFC); |
144 | wrsz = (u32)length + 3; | |
145 | wrsz += ((u32)packet) & 0x3; | |
146 | wrsz >>= 2; | |
147 | tx_cmd = ((((unsigned int)(packet)) & 0x03) << 16) | (u32)length; | |
148 | ||
149 | /* check if there is enough room for the current frame */ | |
150 | if (wrsz < (DNET_FIFO_SIZE - readl(&dnet->regs->TX_FIFO_WCNT))) { | |
151 | for (i = 0; i < wrsz; i++) | |
152 | writel(*bufp++, &dnet->regs->TX_DATA_FIFO); | |
153 | /* | |
154 | * inform MAC that a packet's written and ready | |
155 | * to be shipped out | |
156 | */ | |
157 | writel(tx_cmd, &dnet->regs->TX_LEN_FIFO); | |
158 | } else { | |
159 | printf(DRIVERNAME "No free space (actual %d, required %d " | |
160 | "(words))\n", DNET_FIFO_SIZE - | |
161 | readl(&dnet->regs->TX_FIFO_WCNT), wrsz); | |
162 | } | |
163 | ||
164 | /* No one cares anyway */ | |
165 | return 0; | |
166 | } | |
167 | ||
168 | ||
169 | static int dnet_recv(struct eth_device *netdev) | |
170 | { | |
171 | struct dnet_device *dnet = to_dnet(netdev); | |
172 | unsigned int *data_ptr; | |
173 | int pkt_len, poll, i; | |
174 | u32 cmd_word; | |
175 | ||
176 | debug("Waiting for pkt (polling)\n"); | |
177 | poll = 50; | |
178 | while ((readl(&dnet->regs->RX_FIFO_WCNT) >> 16) == 0) { | |
179 | udelay(10); /* wait 10 usec */ | |
180 | if (--poll == 0) | |
181 | return 0; /* no pkt available */ | |
182 | } | |
183 | ||
184 | cmd_word = readl(&dnet->regs->RX_LEN_FIFO); | |
185 | pkt_len = cmd_word & 0xFFFF; | |
186 | ||
187 | debug("Got pkt with size %d bytes\n", pkt_len); | |
188 | ||
189 | if (cmd_word & 0xDF180000) | |
190 | printf("%s packet receive error %x\n", __func__, cmd_word); | |
191 | ||
192 | data_ptr = (unsigned int *) NetRxPackets[0]; | |
193 | ||
194 | for (i = 0; i < (pkt_len + 3) >> 2; i++) | |
195 | *data_ptr++ = readl(&dnet->regs->RX_DATA_FIFO); | |
196 | ||
197 | NetReceive(NetRxPackets[0], pkt_len + 5); /* ok + 5 ?? */ | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
202 | static void dnet_set_hwaddr(struct eth_device *netdev) | |
203 | { | |
204 | struct dnet_device *dnet = to_dnet(netdev); | |
205 | u16 tmp; | |
206 | ||
d780e74f | 207 | tmp = get_unaligned_be16(netdev->enetaddr); |
62cbc408 | 208 | dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_0_REG, tmp); |
d780e74f | 209 | tmp = get_unaligned_be16(&netdev->enetaddr[2]); |
62cbc408 | 210 | dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_1_REG, tmp); |
d780e74f | 211 | tmp = get_unaligned_be16(&netdev->enetaddr[4]); |
62cbc408 IY |
212 | dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_2_REG, tmp); |
213 | } | |
214 | ||
215 | static void dnet_phy_reset(struct dnet_device *dnet) | |
216 | { | |
217 | struct eth_device *netdev = &dnet->netdev; | |
218 | int i; | |
219 | u16 status, adv; | |
220 | ||
221 | adv = ADVERTISE_CSMA | ADVERTISE_ALL; | |
222 | dnet_mdio_write(dnet, MII_ADVERTISE, adv); | |
223 | printf("%s: Starting autonegotiation...\n", netdev->name); | |
224 | dnet_mdio_write(dnet, MII_BMCR, (BMCR_ANENABLE | |
225 | | BMCR_ANRESTART)); | |
226 | ||
227 | for (i = 0; i < CONFIG_DNET_AUTONEG_TIMEOUT / 100; i++) { | |
228 | status = dnet_mdio_read(dnet, MII_BMSR); | |
229 | if (status & BMSR_ANEGCOMPLETE) | |
230 | break; | |
231 | udelay(100); | |
232 | } | |
233 | ||
234 | if (status & BMSR_ANEGCOMPLETE) | |
235 | printf("%s: Autonegotiation complete\n", netdev->name); | |
236 | else | |
237 | printf("%s: Autonegotiation timed out (status=0x%04x)\n", | |
238 | netdev->name, status); | |
239 | } | |
240 | ||
241 | static int dnet_phy_init(struct dnet_device *dnet) | |
242 | { | |
243 | struct eth_device *netdev = &dnet->netdev; | |
244 | u16 phy_id, status, adv, lpa; | |
245 | int media, speed, duplex; | |
246 | int i; | |
247 | u32 ctl_reg; | |
248 | ||
249 | /* Find a PHY */ | |
250 | for (i = 0; i < 32; i++) { | |
251 | dnet->phy_addr = i; | |
252 | phy_id = dnet_mdio_read(dnet, MII_PHYSID1); | |
253 | if (phy_id != 0xffff) { | |
254 | /* ok we found it */ | |
255 | printf("Found PHY at address %d PHYID (%04x:%04x)\n", | |
256 | i, phy_id, | |
257 | dnet_mdio_read(dnet, MII_PHYSID2)); | |
258 | break; | |
259 | } | |
260 | } | |
261 | ||
262 | /* Check if the PHY is up to snuff... */ | |
263 | phy_id = dnet_mdio_read(dnet, MII_PHYSID1); | |
264 | if (phy_id == 0xffff) { | |
265 | printf("%s: No PHY present\n", netdev->name); | |
266 | return -1; | |
267 | } | |
268 | ||
269 | status = dnet_mdio_read(dnet, MII_BMSR); | |
270 | if (!(status & BMSR_LSTATUS)) { | |
271 | /* Try to re-negotiate if we don't have link already. */ | |
272 | dnet_phy_reset(dnet); | |
273 | ||
274 | for (i = 0; i < CONFIG_DNET_AUTONEG_TIMEOUT / 100; i++) { | |
275 | status = dnet_mdio_read(dnet, MII_BMSR); | |
276 | if (status & BMSR_LSTATUS) | |
277 | break; | |
278 | udelay(100); | |
279 | } | |
280 | } | |
281 | ||
282 | if (!(status & BMSR_LSTATUS)) { | |
283 | printf("%s: link down (status: 0x%04x)\n", | |
284 | netdev->name, status); | |
285 | return -1; | |
286 | } else { | |
287 | adv = dnet_mdio_read(dnet, MII_ADVERTISE); | |
288 | lpa = dnet_mdio_read(dnet, MII_LPA); | |
289 | media = mii_nway_result(lpa & adv); | |
290 | speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) | |
291 | ? 1 : 0); | |
292 | duplex = (media & ADVERTISE_FULL) ? 1 : 0; | |
293 | /* 1000BaseT ethernet is not supported */ | |
294 | printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n", | |
295 | netdev->name, | |
296 | speed ? "100" : "10", | |
297 | duplex ? "full" : "half", | |
298 | lpa); | |
299 | ||
300 | ctl_reg = dnet_readw_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG); | |
301 | ||
302 | if (duplex) | |
303 | ctl_reg &= ~(DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP); | |
304 | else | |
305 | ctl_reg |= DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP; | |
306 | ||
307 | dnet_writew_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG, ctl_reg); | |
308 | ||
309 | return 0; | |
310 | } | |
311 | } | |
312 | ||
313 | static int dnet_init(struct eth_device *netdev, bd_t *bd) | |
314 | { | |
315 | struct dnet_device *dnet = to_dnet(netdev); | |
316 | u32 config; | |
317 | ||
318 | /* | |
319 | * dnet_halt should have been called at some point before now, | |
320 | * so we'll assume the controller is idle. | |
321 | */ | |
322 | ||
323 | /* set hardware address */ | |
324 | dnet_set_hwaddr(netdev); | |
325 | ||
326 | if (dnet_phy_init(dnet) < 0) | |
327 | return -1; | |
328 | ||
329 | /* flush rx/tx fifos */ | |
330 | writel(DNET_SYS_CTL_RXFIFOFLUSH | DNET_SYS_CTL_TXFIFOFLUSH, | |
331 | &dnet->regs->SYS_CTL); | |
332 | udelay(1000); | |
333 | writel(0, &dnet->regs->SYS_CTL); | |
334 | ||
335 | config = dnet_readw_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG); | |
336 | ||
337 | config |= DNET_INTERNAL_RXTX_CONTROL_RXPAUSE | | |
338 | DNET_INTERNAL_RXTX_CONTROL_RXBROADCAST | | |
339 | DNET_INTERNAL_RXTX_CONTROL_DROPCONTROL | | |
340 | DNET_INTERNAL_RXTX_CONTROL_DISCFXFCS; | |
341 | ||
342 | dnet_writew_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG, config); | |
343 | ||
344 | /* Enable TX and RX */ | |
345 | dnet_writew_mac(dnet, DNET_INTERNAL_MODE_REG, | |
346 | DNET_INTERNAL_MODE_RXEN | DNET_INTERNAL_MODE_TXEN); | |
347 | ||
348 | return 0; | |
349 | } | |
350 | ||
351 | static void dnet_halt(struct eth_device *netdev) | |
352 | { | |
353 | struct dnet_device *dnet = to_dnet(netdev); | |
354 | ||
355 | /* Disable TX and RX */ | |
356 | dnet_writew_mac(dnet, DNET_INTERNAL_MODE_REG, 0); | |
357 | } | |
358 | ||
359 | int dnet_eth_initialize(int id, void *regs, unsigned int phy_addr) | |
360 | { | |
361 | struct dnet_device *dnet; | |
362 | struct eth_device *netdev; | |
363 | unsigned int dev_capa; | |
364 | ||
365 | dnet = malloc(sizeof(struct dnet_device)); | |
366 | if (!dnet) { | |
367 | printf("Error: Failed to allocate memory for DNET%d\n", id); | |
368 | return -1; | |
369 | } | |
370 | memset(dnet, 0, sizeof(struct dnet_device)); | |
371 | ||
372 | netdev = &dnet->netdev; | |
373 | ||
374 | dnet->regs = (struct dnet_registers *)regs; | |
375 | dnet->phy_addr = phy_addr; | |
376 | ||
377 | sprintf(netdev->name, "dnet%d", id); | |
378 | netdev->init = dnet_init; | |
379 | netdev->halt = dnet_halt; | |
380 | netdev->send = dnet_send; | |
381 | netdev->recv = dnet_recv; | |
382 | ||
383 | dev_capa = readl(&dnet->regs->VERCAPS) & 0xFFFF; | |
384 | debug("%s: has %smdio, %sirq, %sgigabit, %sdma \n", netdev->name, | |
385 | (dev_capa & DNET_HAS_MDIO) ? "" : "no ", | |
386 | (dev_capa & DNET_HAS_IRQ) ? "" : "no ", | |
387 | (dev_capa & DNET_HAS_GIGABIT) ? "" : "no ", | |
388 | (dev_capa & DNET_HAS_DMA) ? "" : "no "); | |
389 | ||
390 | eth_register(netdev); | |
391 | ||
392 | return 0; | |
393 | } |