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