]>
Commit | Line | Data |
---|---|---|
594d57d0 MK |
1 | /* |
2 | * Cirrus Logic EP93xx ethernet MAC / MII driver. | |
3 | * | |
4 | * Copyright (C) 2010, 2009 | |
5 | * Matthias Kaehlcke <[email protected]> | |
6 | * | |
7 | * Copyright (C) 2004, 2005 | |
8 | * Cory T. Tusar, Videon Central, Inc., <[email protected]> | |
9 | * | |
10 | * Based on the original eth.[ch] Cirrus Logic EP93xx Rev D. Ethernet Driver, | |
11 | * which is | |
12 | * | |
13 | * (C) Copyright 2002 2003 | |
14 | * Adam Bezanson, Network Audio Technologies, Inc. | |
15 | * <[email protected]> | |
16 | * | |
1a459660 | 17 | * SPDX-License-Identifier: GPL-2.0+ |
594d57d0 MK |
18 | */ |
19 | ||
20 | #include <command.h> | |
21 | #include <common.h> | |
22 | #include <asm/arch/ep93xx.h> | |
23 | #include <asm/io.h> | |
24 | #include <malloc.h> | |
25 | #include <miiphy.h> | |
26 | #include <linux/types.h> | |
27 | #include "ep93xx_eth.h" | |
28 | ||
29 | #define GET_PRIV(eth_dev) ((struct ep93xx_priv *)(eth_dev)->priv) | |
30 | #define GET_REGS(eth_dev) (GET_PRIV(eth_dev)->regs) | |
31 | ||
32 | /* ep93xx_miiphy ops forward declarations */ | |
5a49f174 JH |
33 | static int ep93xx_miiphy_read(struct mii_dev *bus, int addr, int devad, |
34 | int reg); | |
35 | static int ep93xx_miiphy_write(struct mii_dev *bus, int addr, int devad, | |
36 | int reg, u16 value); | |
594d57d0 MK |
37 | |
38 | #if defined(EP93XX_MAC_DEBUG) | |
39 | /** | |
40 | * Dump ep93xx_mac values to the terminal. | |
41 | */ | |
42 | static void dump_dev(struct eth_device *dev) | |
43 | { | |
44 | struct ep93xx_priv *priv = GET_PRIV(dev); | |
45 | int i; | |
46 | ||
47 | printf("\ndump_dev()\n"); | |
48 | printf(" rx_dq.base %p\n", priv->rx_dq.base); | |
49 | printf(" rx_dq.current %p\n", priv->rx_dq.current); | |
50 | printf(" rx_dq.end %p\n", priv->rx_dq.end); | |
51 | printf(" rx_sq.base %p\n", priv->rx_sq.base); | |
52 | printf(" rx_sq.current %p\n", priv->rx_sq.current); | |
53 | printf(" rx_sq.end %p\n", priv->rx_sq.end); | |
54 | ||
55 | for (i = 0; i < NUMRXDESC; i++) | |
1fd92db8 | 56 | printf(" rx_buffer[%2.d] %p\n", i, net_rx_packets[i]); |
594d57d0 MK |
57 | |
58 | printf(" tx_dq.base %p\n", priv->tx_dq.base); | |
59 | printf(" tx_dq.current %p\n", priv->tx_dq.current); | |
60 | printf(" tx_dq.end %p\n", priv->tx_dq.end); | |
61 | printf(" tx_sq.base %p\n", priv->tx_sq.base); | |
62 | printf(" tx_sq.current %p\n", priv->tx_sq.current); | |
63 | printf(" tx_sq.end %p\n", priv->tx_sq.end); | |
64 | } | |
65 | ||
66 | /** | |
67 | * Dump all RX status queue entries to the terminal. | |
68 | */ | |
69 | static void dump_rx_status_queue(struct eth_device *dev) | |
70 | { | |
71 | struct ep93xx_priv *priv = GET_PRIV(dev); | |
72 | int i; | |
73 | ||
74 | printf("\ndump_rx_status_queue()\n"); | |
75 | printf(" descriptor address word1 word2\n"); | |
76 | for (i = 0; i < NUMRXDESC; i++) { | |
77 | printf(" [ %p ] %08X %08X\n", | |
78 | priv->rx_sq.base + i, | |
79 | (priv->rx_sq.base + i)->word1, | |
80 | (priv->rx_sq.base + i)->word2); | |
81 | } | |
82 | } | |
83 | ||
84 | /** | |
85 | * Dump all RX descriptor queue entries to the terminal. | |
86 | */ | |
87 | static void dump_rx_descriptor_queue(struct eth_device *dev) | |
88 | { | |
89 | struct ep93xx_priv *priv = GET_PRIV(dev); | |
90 | int i; | |
91 | ||
92 | printf("\ndump_rx_descriptor_queue()\n"); | |
93 | printf(" descriptor address word1 word2\n"); | |
94 | for (i = 0; i < NUMRXDESC; i++) { | |
95 | printf(" [ %p ] %08X %08X\n", | |
96 | priv->rx_dq.base + i, | |
97 | (priv->rx_dq.base + i)->word1, | |
98 | (priv->rx_dq.base + i)->word2); | |
99 | } | |
100 | } | |
101 | ||
102 | /** | |
103 | * Dump all TX descriptor queue entries to the terminal. | |
104 | */ | |
105 | static void dump_tx_descriptor_queue(struct eth_device *dev) | |
106 | { | |
107 | struct ep93xx_priv *priv = GET_PRIV(dev); | |
108 | int i; | |
109 | ||
110 | printf("\ndump_tx_descriptor_queue()\n"); | |
111 | printf(" descriptor address word1 word2\n"); | |
112 | for (i = 0; i < NUMTXDESC; i++) { | |
113 | printf(" [ %p ] %08X %08X\n", | |
114 | priv->tx_dq.base + i, | |
115 | (priv->tx_dq.base + i)->word1, | |
116 | (priv->tx_dq.base + i)->word2); | |
117 | } | |
118 | } | |
119 | ||
120 | /** | |
121 | * Dump all TX status queue entries to the terminal. | |
122 | */ | |
123 | static void dump_tx_status_queue(struct eth_device *dev) | |
124 | { | |
125 | struct ep93xx_priv *priv = GET_PRIV(dev); | |
126 | int i; | |
127 | ||
128 | printf("\ndump_tx_status_queue()\n"); | |
129 | printf(" descriptor address word1\n"); | |
130 | for (i = 0; i < NUMTXDESC; i++) { | |
131 | printf(" [ %p ] %08X\n", | |
132 | priv->rx_sq.base + i, | |
133 | (priv->rx_sq.base + i)->word1); | |
134 | } | |
135 | } | |
136 | #else | |
137 | #define dump_dev(x) | |
138 | #define dump_rx_descriptor_queue(x) | |
139 | #define dump_rx_status_queue(x) | |
140 | #define dump_tx_descriptor_queue(x) | |
141 | #define dump_tx_status_queue(x) | |
142 | #endif /* defined(EP93XX_MAC_DEBUG) */ | |
143 | ||
144 | /** | |
145 | * Reset the EP93xx MAC by twiddling the soft reset bit and spinning until | |
146 | * it's cleared. | |
147 | */ | |
148 | static void ep93xx_mac_reset(struct eth_device *dev) | |
149 | { | |
150 | struct mac_regs *mac = GET_REGS(dev); | |
151 | uint32_t value; | |
152 | ||
153 | debug("+ep93xx_mac_reset"); | |
154 | ||
155 | value = readl(&mac->selfctl); | |
156 | value |= SELFCTL_RESET; | |
157 | writel(value, &mac->selfctl); | |
158 | ||
159 | while (readl(&mac->selfctl) & SELFCTL_RESET) | |
160 | ; /* noop */ | |
161 | ||
162 | debug("-ep93xx_mac_reset"); | |
163 | } | |
164 | ||
165 | /* Eth device open */ | |
166 | static int ep93xx_eth_open(struct eth_device *dev, bd_t *bd) | |
167 | { | |
168 | struct ep93xx_priv *priv = GET_PRIV(dev); | |
169 | struct mac_regs *mac = GET_REGS(dev); | |
170 | uchar *mac_addr = dev->enetaddr; | |
171 | int i; | |
172 | ||
173 | debug("+ep93xx_eth_open"); | |
174 | ||
175 | /* Reset the MAC */ | |
176 | ep93xx_mac_reset(dev); | |
177 | ||
178 | /* Reset the descriptor queues' current and end address values */ | |
179 | priv->tx_dq.current = priv->tx_dq.base; | |
180 | priv->tx_dq.end = (priv->tx_dq.base + NUMTXDESC); | |
181 | ||
182 | priv->tx_sq.current = priv->tx_sq.base; | |
183 | priv->tx_sq.end = (priv->tx_sq.base + NUMTXDESC); | |
184 | ||
185 | priv->rx_dq.current = priv->rx_dq.base; | |
186 | priv->rx_dq.end = (priv->rx_dq.base + NUMRXDESC); | |
187 | ||
188 | priv->rx_sq.current = priv->rx_sq.base; | |
189 | priv->rx_sq.end = (priv->rx_sq.base + NUMRXDESC); | |
190 | ||
191 | /* | |
192 | * Set the transmit descriptor and status queues' base address, | |
193 | * current address, and length registers. Set the maximum frame | |
194 | * length and threshold. Enable the transmit descriptor processor. | |
195 | */ | |
196 | writel((uint32_t)priv->tx_dq.base, &mac->txdq.badd); | |
197 | writel((uint32_t)priv->tx_dq.base, &mac->txdq.curadd); | |
198 | writel(sizeof(struct tx_descriptor) * NUMTXDESC, &mac->txdq.blen); | |
199 | ||
200 | writel((uint32_t)priv->tx_sq.base, &mac->txstsq.badd); | |
201 | writel((uint32_t)priv->tx_sq.base, &mac->txstsq.curadd); | |
202 | writel(sizeof(struct tx_status) * NUMTXDESC, &mac->txstsq.blen); | |
203 | ||
204 | writel(0x00040000, &mac->txdthrshld); | |
205 | writel(0x00040000, &mac->txststhrshld); | |
206 | ||
207 | writel((TXSTARTMAX << 0) | (PKTSIZE_ALIGN << 16), &mac->maxfrmlen); | |
208 | writel(BMCTL_TXEN, &mac->bmctl); | |
209 | ||
210 | /* | |
211 | * Set the receive descriptor and status queues' base address, | |
212 | * current address, and length registers. Enable the receive | |
213 | * descriptor processor. | |
214 | */ | |
215 | writel((uint32_t)priv->rx_dq.base, &mac->rxdq.badd); | |
216 | writel((uint32_t)priv->rx_dq.base, &mac->rxdq.curadd); | |
217 | writel(sizeof(struct rx_descriptor) * NUMRXDESC, &mac->rxdq.blen); | |
218 | ||
219 | writel((uint32_t)priv->rx_sq.base, &mac->rxstsq.badd); | |
220 | writel((uint32_t)priv->rx_sq.base, &mac->rxstsq.curadd); | |
221 | writel(sizeof(struct rx_status) * NUMRXDESC, &mac->rxstsq.blen); | |
222 | ||
223 | writel(0x00040000, &mac->rxdthrshld); | |
224 | ||
225 | writel(BMCTL_RXEN, &mac->bmctl); | |
226 | ||
227 | writel(0x00040000, &mac->rxststhrshld); | |
228 | ||
229 | /* Wait until the receive descriptor processor is active */ | |
230 | while (!(readl(&mac->bmsts) & BMSTS_RXACT)) | |
231 | ; /* noop */ | |
232 | ||
233 | /* | |
234 | * Initialize the RX descriptor queue. Clear the TX descriptor queue. | |
235 | * Clear the RX and TX status queues. Enqueue the RX descriptor and | |
236 | * status entries to the MAC. | |
237 | */ | |
238 | for (i = 0; i < NUMRXDESC; i++) { | |
239 | /* set buffer address */ | |
1fd92db8 | 240 | (priv->rx_dq.base + i)->word1 = (uint32_t)net_rx_packets[i]; |
594d57d0 MK |
241 | |
242 | /* set buffer length, clear buffer index and NSOF */ | |
243 | (priv->rx_dq.base + i)->word2 = PKTSIZE_ALIGN; | |
244 | } | |
245 | ||
246 | memset(priv->tx_dq.base, 0, | |
247 | (sizeof(struct tx_descriptor) * NUMTXDESC)); | |
248 | memset(priv->rx_sq.base, 0, | |
249 | (sizeof(struct rx_status) * NUMRXDESC)); | |
250 | memset(priv->tx_sq.base, 0, | |
251 | (sizeof(struct tx_status) * NUMTXDESC)); | |
252 | ||
253 | writel(NUMRXDESC, &mac->rxdqenq); | |
254 | writel(NUMRXDESC, &mac->rxstsqenq); | |
255 | ||
256 | /* Set the primary MAC address */ | |
257 | writel(AFP_IAPRIMARY, &mac->afp); | |
258 | writel(mac_addr[0] | (mac_addr[1] << 8) | | |
259 | (mac_addr[2] << 16) | (mac_addr[3] << 24), | |
260 | &mac->indad); | |
261 | writel(mac_addr[4] | (mac_addr[5] << 8), &mac->indad_upper); | |
262 | ||
263 | /* Turn on RX and TX */ | |
264 | writel(RXCTL_IA0 | RXCTL_BA | RXCTL_SRXON | | |
265 | RXCTL_RCRCA | RXCTL_MA, &mac->rxctl); | |
266 | writel(TXCTL_STXON, &mac->txctl); | |
267 | ||
268 | /* Dump data structures if we're debugging */ | |
269 | dump_dev(dev); | |
270 | dump_rx_descriptor_queue(dev); | |
271 | dump_rx_status_queue(dev); | |
272 | dump_tx_descriptor_queue(dev); | |
273 | dump_tx_status_queue(dev); | |
274 | ||
275 | debug("-ep93xx_eth_open"); | |
276 | ||
277 | return 1; | |
278 | } | |
279 | ||
280 | /** | |
281 | * Halt EP93xx MAC transmit and receive by clearing the TxCTL and RxCTL | |
282 | * registers. | |
283 | */ | |
284 | static void ep93xx_eth_close(struct eth_device *dev) | |
285 | { | |
286 | struct mac_regs *mac = GET_REGS(dev); | |
287 | ||
288 | debug("+ep93xx_eth_close"); | |
289 | ||
290 | writel(0x00000000, &mac->rxctl); | |
291 | writel(0x00000000, &mac->txctl); | |
292 | ||
293 | debug("-ep93xx_eth_close"); | |
294 | } | |
295 | ||
296 | /** | |
297 | * Copy a frame of data from the MAC into the protocol layer for further | |
298 | * processing. | |
299 | */ | |
300 | static int ep93xx_eth_rcv_packet(struct eth_device *dev) | |
301 | { | |
302 | struct mac_regs *mac = GET_REGS(dev); | |
303 | struct ep93xx_priv *priv = GET_PRIV(dev); | |
304 | int len = -1; | |
305 | ||
306 | debug("+ep93xx_eth_rcv_packet"); | |
307 | ||
308 | if (RX_STATUS_RFP(priv->rx_sq.current)) { | |
309 | if (RX_STATUS_RWE(priv->rx_sq.current)) { | |
310 | /* | |
311 | * We have a good frame. Extract the frame's length | |
312 | * from the current rx_status_queue entry, and copy | |
1fd92db8 | 313 | * the frame's data into net_rx_packets[] of the |
594d57d0 MK |
314 | * protocol stack. We track the total number of |
315 | * bytes in the frame (nbytes_frame) which will be | |
316 | * used when we pass the data off to the protocol | |
1fd92db8 | 317 | * layer via net_process_received_packet(). |
594d57d0 MK |
318 | */ |
319 | len = RX_STATUS_FRAME_LEN(priv->rx_sq.current); | |
320 | ||
1fd92db8 JH |
321 | net_process_received_packet( |
322 | (uchar *)priv->rx_dq.current->word1, len); | |
594d57d0 MK |
323 | |
324 | debug("reporting %d bytes...\n", len); | |
325 | } else { | |
326 | /* Do we have an erroneous packet? */ | |
9b643e31 | 327 | pr_err("packet rx error, status %08X %08X", |
594d57d0 MK |
328 | priv->rx_sq.current->word1, |
329 | priv->rx_sq.current->word2); | |
330 | dump_rx_descriptor_queue(dev); | |
331 | dump_rx_status_queue(dev); | |
332 | } | |
333 | ||
334 | /* | |
335 | * Clear the associated status queue entry, and | |
336 | * increment our current pointers to the next RX | |
337 | * descriptor and status queue entries (making sure | |
338 | * we wrap properly). | |
339 | */ | |
340 | memset((void *)priv->rx_sq.current, 0, | |
341 | sizeof(struct rx_status)); | |
342 | ||
343 | priv->rx_sq.current++; | |
344 | if (priv->rx_sq.current >= priv->rx_sq.end) | |
345 | priv->rx_sq.current = priv->rx_sq.base; | |
346 | ||
347 | priv->rx_dq.current++; | |
348 | if (priv->rx_dq.current >= priv->rx_dq.end) | |
349 | priv->rx_dq.current = priv->rx_dq.base; | |
350 | ||
351 | /* | |
352 | * Finally, return the RX descriptor and status entries | |
353 | * back to the MAC engine, and loop again, checking for | |
354 | * more descriptors to process. | |
355 | */ | |
356 | writel(1, &mac->rxdqenq); | |
357 | writel(1, &mac->rxstsqenq); | |
358 | } else { | |
359 | len = 0; | |
360 | } | |
361 | ||
362 | debug("-ep93xx_eth_rcv_packet %d", len); | |
363 | return len; | |
364 | } | |
365 | ||
366 | /** | |
367 | * Send a block of data via ethernet. | |
368 | */ | |
369 | static int ep93xx_eth_send_packet(struct eth_device *dev, | |
10cbe3b6 | 370 | void * const packet, int const length) |
594d57d0 MK |
371 | { |
372 | struct mac_regs *mac = GET_REGS(dev); | |
373 | struct ep93xx_priv *priv = GET_PRIV(dev); | |
374 | int ret = -1; | |
375 | ||
376 | debug("+ep93xx_eth_send_packet"); | |
377 | ||
378 | /* Parameter check */ | |
379 | BUG_ON(packet == NULL); | |
380 | ||
381 | /* | |
382 | * Initialize the TX descriptor queue with the new packet's info. | |
383 | * Clear the associated status queue entry. Enqueue the packet | |
384 | * to the MAC for transmission. | |
385 | */ | |
386 | ||
387 | /* set buffer address */ | |
388 | priv->tx_dq.current->word1 = (uint32_t)packet; | |
389 | ||
390 | /* set buffer length and EOF bit */ | |
391 | priv->tx_dq.current->word2 = length | TX_DESC_EOF; | |
392 | ||
393 | /* clear tx status */ | |
394 | priv->tx_sq.current->word1 = 0; | |
395 | ||
396 | /* enqueue the TX descriptor */ | |
397 | writel(1, &mac->txdqenq); | |
398 | ||
399 | /* wait for the frame to become processed */ | |
400 | while (!TX_STATUS_TXFP(priv->tx_sq.current)) | |
401 | ; /* noop */ | |
402 | ||
403 | if (!TX_STATUS_TXWE(priv->tx_sq.current)) { | |
9b643e31 | 404 | pr_err("packet tx error, status %08X", |
594d57d0 MK |
405 | priv->tx_sq.current->word1); |
406 | dump_tx_descriptor_queue(dev); | |
407 | dump_tx_status_queue(dev); | |
408 | ||
409 | /* TODO: Add better error handling? */ | |
410 | goto eth_send_out; | |
411 | } | |
412 | ||
413 | ret = 0; | |
414 | /* Fall through */ | |
415 | ||
416 | eth_send_out: | |
417 | debug("-ep93xx_eth_send_packet %d", ret); | |
418 | return ret; | |
419 | } | |
420 | ||
421 | #if defined(CONFIG_MII) | |
422 | int ep93xx_miiphy_initialize(bd_t * const bd) | |
423 | { | |
5a49f174 JH |
424 | int retval; |
425 | struct mii_dev *mdiodev = mdio_alloc(); | |
426 | if (!mdiodev) | |
427 | return -ENOMEM; | |
428 | strncpy(mdiodev->name, "ep93xx_eth0", MDIO_NAME_LEN); | |
429 | mdiodev->read = ep93xx_miiphy_read; | |
430 | mdiodev->write = ep93xx_miiphy_write; | |
431 | ||
432 | retval = mdio_register(mdiodev); | |
433 | if (retval < 0) | |
434 | return retval; | |
594d57d0 MK |
435 | return 0; |
436 | } | |
437 | #endif | |
438 | ||
439 | /** | |
440 | * Initialize the EP93xx MAC. The MAC hardware is reset. Buffers are | |
441 | * allocated, if necessary, for the TX and RX descriptor and status queues, | |
442 | * as well as for received packets. The EP93XX MAC hardware is initialized. | |
443 | * Transmit and receive operations are enabled. | |
444 | */ | |
445 | int ep93xx_eth_initialize(u8 dev_num, int base_addr) | |
446 | { | |
447 | int ret = -1; | |
448 | struct eth_device *dev; | |
449 | struct ep93xx_priv *priv; | |
450 | ||
451 | debug("+ep93xx_eth_initialize"); | |
452 | ||
453 | priv = malloc(sizeof(*priv)); | |
454 | if (!priv) { | |
9b643e31 | 455 | pr_err("malloc() failed"); |
594d57d0 MK |
456 | goto eth_init_failed_0; |
457 | } | |
458 | memset(priv, 0, sizeof(*priv)); | |
459 | ||
460 | priv->regs = (struct mac_regs *)base_addr; | |
461 | ||
462 | priv->tx_dq.base = calloc(NUMTXDESC, | |
463 | sizeof(struct tx_descriptor)); | |
464 | if (priv->tx_dq.base == NULL) { | |
9b643e31 | 465 | pr_err("calloc() failed"); |
594d57d0 MK |
466 | goto eth_init_failed_1; |
467 | } | |
468 | ||
469 | priv->tx_sq.base = calloc(NUMTXDESC, | |
470 | sizeof(struct tx_status)); | |
471 | if (priv->tx_sq.base == NULL) { | |
9b643e31 | 472 | pr_err("calloc() failed"); |
594d57d0 MK |
473 | goto eth_init_failed_2; |
474 | } | |
475 | ||
476 | priv->rx_dq.base = calloc(NUMRXDESC, | |
477 | sizeof(struct rx_descriptor)); | |
478 | if (priv->rx_dq.base == NULL) { | |
9b643e31 | 479 | pr_err("calloc() failed"); |
594d57d0 MK |
480 | goto eth_init_failed_3; |
481 | } | |
482 | ||
483 | priv->rx_sq.base = calloc(NUMRXDESC, | |
484 | sizeof(struct rx_status)); | |
485 | if (priv->rx_sq.base == NULL) { | |
9b643e31 | 486 | pr_err("calloc() failed"); |
594d57d0 MK |
487 | goto eth_init_failed_4; |
488 | } | |
489 | ||
490 | dev = malloc(sizeof *dev); | |
491 | if (dev == NULL) { | |
9b643e31 | 492 | pr_err("malloc() failed"); |
594d57d0 MK |
493 | goto eth_init_failed_5; |
494 | } | |
495 | memset(dev, 0, sizeof *dev); | |
496 | ||
497 | dev->iobase = base_addr; | |
498 | dev->priv = priv; | |
499 | dev->init = ep93xx_eth_open; | |
500 | dev->halt = ep93xx_eth_close; | |
501 | dev->send = ep93xx_eth_send_packet; | |
502 | dev->recv = ep93xx_eth_rcv_packet; | |
503 | ||
504 | sprintf(dev->name, "ep93xx_eth-%hu", dev_num); | |
505 | ||
506 | eth_register(dev); | |
507 | ||
508 | /* Done! */ | |
509 | ret = 1; | |
510 | goto eth_init_done; | |
511 | ||
512 | eth_init_failed_5: | |
513 | free(priv->rx_sq.base); | |
514 | /* Fall through */ | |
515 | ||
516 | eth_init_failed_4: | |
517 | free(priv->rx_dq.base); | |
518 | /* Fall through */ | |
519 | ||
520 | eth_init_failed_3: | |
521 | free(priv->tx_sq.base); | |
522 | /* Fall through */ | |
523 | ||
524 | eth_init_failed_2: | |
525 | free(priv->tx_dq.base); | |
526 | /* Fall through */ | |
527 | ||
528 | eth_init_failed_1: | |
529 | free(priv); | |
530 | /* Fall through */ | |
531 | ||
532 | eth_init_failed_0: | |
533 | /* Fall through */ | |
534 | ||
535 | eth_init_done: | |
536 | debug("-ep93xx_eth_initialize %d", ret); | |
537 | return ret; | |
538 | } | |
539 | ||
540 | #if defined(CONFIG_MII) | |
541 | ||
542 | /** | |
543 | * Maximum MII address we support | |
544 | */ | |
545 | #define MII_ADDRESS_MAX 31 | |
546 | ||
547 | /** | |
548 | * Maximum MII register address we support | |
549 | */ | |
550 | #define MII_REGISTER_MAX 31 | |
551 | ||
552 | /** | |
553 | * Read a 16-bit value from an MII register. | |
554 | */ | |
5a49f174 JH |
555 | static int ep93xx_miiphy_read(struct mii_dev *bus, int addr, int devad, |
556 | int reg) | |
594d57d0 | 557 | { |
5a49f174 | 558 | unsigned short value = 0; |
594d57d0 MK |
559 | struct mac_regs *mac = (struct mac_regs *)MAC_BASE; |
560 | int ret = -1; | |
561 | uint32_t self_ctl; | |
562 | ||
563 | debug("+ep93xx_miiphy_read"); | |
564 | ||
565 | /* Parameter checks */ | |
5a49f174 | 566 | BUG_ON(bus->name == NULL); |
594d57d0 MK |
567 | BUG_ON(addr > MII_ADDRESS_MAX); |
568 | BUG_ON(reg > MII_REGISTER_MAX); | |
594d57d0 MK |
569 | |
570 | /* | |
571 | * Save the current SelfCTL register value. Set MAC to suppress | |
572 | * preamble bits. Wait for any previous MII command to complete | |
573 | * before issuing the new command. | |
574 | */ | |
575 | self_ctl = readl(&mac->selfctl); | |
576 | #if defined(CONFIG_MII_SUPPRESS_PREAMBLE) | |
577 | writel(self_ctl & ~(1 << 8), &mac->selfctl); | |
578 | #endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ | |
579 | ||
580 | while (readl(&mac->miists) & MIISTS_BUSY) | |
581 | ; /* noop */ | |
582 | ||
583 | /* | |
584 | * Issue the MII 'read' command. Wait for the command to complete. | |
585 | * Read the MII data value. | |
586 | */ | |
587 | writel(MIICMD_OPCODE_READ | ((uint32_t)addr << 5) | (uint32_t)reg, | |
588 | &mac->miicmd); | |
589 | while (readl(&mac->miists) & MIISTS_BUSY) | |
590 | ; /* noop */ | |
591 | ||
5a49f174 | 592 | value = (unsigned short)readl(&mac->miidata); |
594d57d0 MK |
593 | |
594 | /* Restore the saved SelfCTL value and return. */ | |
595 | writel(self_ctl, &mac->selfctl); | |
596 | ||
597 | ret = 0; | |
598 | /* Fall through */ | |
599 | ||
600 | debug("-ep93xx_miiphy_read"); | |
5a49f174 JH |
601 | if (ret < 0) |
602 | return ret; | |
603 | return value; | |
594d57d0 MK |
604 | } |
605 | ||
606 | /** | |
607 | * Write a 16-bit value to an MII register. | |
608 | */ | |
5a49f174 JH |
609 | static int ep93xx_miiphy_write(struct mii_dev *bus, int addr, int devad, |
610 | int reg, u16 value) | |
594d57d0 MK |
611 | { |
612 | struct mac_regs *mac = (struct mac_regs *)MAC_BASE; | |
613 | int ret = -1; | |
614 | uint32_t self_ctl; | |
615 | ||
616 | debug("+ep93xx_miiphy_write"); | |
617 | ||
618 | /* Parameter checks */ | |
5a49f174 | 619 | BUG_ON(bus->name == NULL); |
594d57d0 MK |
620 | BUG_ON(addr > MII_ADDRESS_MAX); |
621 | BUG_ON(reg > MII_REGISTER_MAX); | |
622 | ||
623 | /* | |
624 | * Save the current SelfCTL register value. Set MAC to suppress | |
625 | * preamble bits. Wait for any previous MII command to complete | |
626 | * before issuing the new command. | |
627 | */ | |
628 | self_ctl = readl(&mac->selfctl); | |
629 | #if defined(CONFIG_MII_SUPPRESS_PREAMBLE) | |
630 | writel(self_ctl & ~(1 << 8), &mac->selfctl); | |
631 | #endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ | |
632 | ||
633 | while (readl(&mac->miists) & MIISTS_BUSY) | |
634 | ; /* noop */ | |
635 | ||
636 | /* Issue the MII 'write' command. Wait for the command to complete. */ | |
637 | writel((uint32_t)value, &mac->miidata); | |
638 | writel(MIICMD_OPCODE_WRITE | ((uint32_t)addr << 5) | (uint32_t)reg, | |
639 | &mac->miicmd); | |
640 | while (readl(&mac->miists) & MIISTS_BUSY) | |
641 | ; /* noop */ | |
642 | ||
643 | /* Restore the saved SelfCTL value and return. */ | |
644 | writel(self_ctl, &mac->selfctl); | |
645 | ||
646 | ret = 0; | |
647 | /* Fall through */ | |
648 | ||
649 | debug("-ep93xx_miiphy_write"); | |
650 | return ret; | |
651 | } | |
652 | #endif /* defined(CONFIG_MII) */ |