]>
Commit | Line | Data |
---|---|---|
594d57d0 MK |
1 | /* |
2 | * Cirrus Logic EP93xx ethernet MAC / MII driver. | |
3 | * | |
4 | * Copyright (C) 2010, 2009 | |
5 | * Matthias Kaehlcke <matthias@kaehlcke.net> | |
6 | * | |
7 | * Copyright (C) 2004, 2005 | |
8 | * Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.com> | |
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 | * <bezanson@netaudiotech.com> | |
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 */ | |
5700bb63 | 33 | static int ep93xx_miiphy_read(const char * const dev, unsigned char const addr, |
594d57d0 | 34 | unsigned char const reg, unsigned short * const value); |
5700bb63 | 35 | static int ep93xx_miiphy_write(const char * const dev, unsigned char const addr, |
594d57d0 MK |
36 | unsigned char const reg, unsigned short const value); |
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++) | |
56 | printf(" rx_buffer[%2.d] %p\n", i, NetRxPackets[i]); | |
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 */ | |
240 | (priv->rx_dq.base + i)->word1 = (uint32_t)NetRxPackets[i]; | |
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 | |
313 | * the frame's data into NetRxPackets[] of the | |
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 | |
317 | * layer via NetReceive(). | |
318 | */ | |
319 | len = RX_STATUS_FRAME_LEN(priv->rx_sq.current); | |
320 | ||
321 | NetReceive((uchar *)priv->rx_dq.current->word1, len); | |
322 | ||
323 | debug("reporting %d bytes...\n", len); | |
324 | } else { | |
325 | /* Do we have an erroneous packet? */ | |
326 | error("packet rx error, status %08X %08X", | |
327 | priv->rx_sq.current->word1, | |
328 | priv->rx_sq.current->word2); | |
329 | dump_rx_descriptor_queue(dev); | |
330 | dump_rx_status_queue(dev); | |
331 | } | |
332 | ||
333 | /* | |
334 | * Clear the associated status queue entry, and | |
335 | * increment our current pointers to the next RX | |
336 | * descriptor and status queue entries (making sure | |
337 | * we wrap properly). | |
338 | */ | |
339 | memset((void *)priv->rx_sq.current, 0, | |
340 | sizeof(struct rx_status)); | |
341 | ||
342 | priv->rx_sq.current++; | |
343 | if (priv->rx_sq.current >= priv->rx_sq.end) | |
344 | priv->rx_sq.current = priv->rx_sq.base; | |
345 | ||
346 | priv->rx_dq.current++; | |
347 | if (priv->rx_dq.current >= priv->rx_dq.end) | |
348 | priv->rx_dq.current = priv->rx_dq.base; | |
349 | ||
350 | /* | |
351 | * Finally, return the RX descriptor and status entries | |
352 | * back to the MAC engine, and loop again, checking for | |
353 | * more descriptors to process. | |
354 | */ | |
355 | writel(1, &mac->rxdqenq); | |
356 | writel(1, &mac->rxstsqenq); | |
357 | } else { | |
358 | len = 0; | |
359 | } | |
360 | ||
361 | debug("-ep93xx_eth_rcv_packet %d", len); | |
362 | return len; | |
363 | } | |
364 | ||
365 | /** | |
366 | * Send a block of data via ethernet. | |
367 | */ | |
368 | static int ep93xx_eth_send_packet(struct eth_device *dev, | |
10cbe3b6 | 369 | void * const packet, int const length) |
594d57d0 MK |
370 | { |
371 | struct mac_regs *mac = GET_REGS(dev); | |
372 | struct ep93xx_priv *priv = GET_PRIV(dev); | |
373 | int ret = -1; | |
374 | ||
375 | debug("+ep93xx_eth_send_packet"); | |
376 | ||
377 | /* Parameter check */ | |
378 | BUG_ON(packet == NULL); | |
379 | ||
380 | /* | |
381 | * Initialize the TX descriptor queue with the new packet's info. | |
382 | * Clear the associated status queue entry. Enqueue the packet | |
383 | * to the MAC for transmission. | |
384 | */ | |
385 | ||
386 | /* set buffer address */ | |
387 | priv->tx_dq.current->word1 = (uint32_t)packet; | |
388 | ||
389 | /* set buffer length and EOF bit */ | |
390 | priv->tx_dq.current->word2 = length | TX_DESC_EOF; | |
391 | ||
392 | /* clear tx status */ | |
393 | priv->tx_sq.current->word1 = 0; | |
394 | ||
395 | /* enqueue the TX descriptor */ | |
396 | writel(1, &mac->txdqenq); | |
397 | ||
398 | /* wait for the frame to become processed */ | |
399 | while (!TX_STATUS_TXFP(priv->tx_sq.current)) | |
400 | ; /* noop */ | |
401 | ||
402 | if (!TX_STATUS_TXWE(priv->tx_sq.current)) { | |
403 | error("packet tx error, status %08X", | |
404 | priv->tx_sq.current->word1); | |
405 | dump_tx_descriptor_queue(dev); | |
406 | dump_tx_status_queue(dev); | |
407 | ||
408 | /* TODO: Add better error handling? */ | |
409 | goto eth_send_out; | |
410 | } | |
411 | ||
412 | ret = 0; | |
413 | /* Fall through */ | |
414 | ||
415 | eth_send_out: | |
416 | debug("-ep93xx_eth_send_packet %d", ret); | |
417 | return ret; | |
418 | } | |
419 | ||
420 | #if defined(CONFIG_MII) | |
421 | int ep93xx_miiphy_initialize(bd_t * const bd) | |
422 | { | |
423 | miiphy_register("ep93xx_eth0", ep93xx_miiphy_read, ep93xx_miiphy_write); | |
424 | return 0; | |
425 | } | |
426 | #endif | |
427 | ||
428 | /** | |
429 | * Initialize the EP93xx MAC. The MAC hardware is reset. Buffers are | |
430 | * allocated, if necessary, for the TX and RX descriptor and status queues, | |
431 | * as well as for received packets. The EP93XX MAC hardware is initialized. | |
432 | * Transmit and receive operations are enabled. | |
433 | */ | |
434 | int ep93xx_eth_initialize(u8 dev_num, int base_addr) | |
435 | { | |
436 | int ret = -1; | |
437 | struct eth_device *dev; | |
438 | struct ep93xx_priv *priv; | |
439 | ||
440 | debug("+ep93xx_eth_initialize"); | |
441 | ||
442 | priv = malloc(sizeof(*priv)); | |
443 | if (!priv) { | |
444 | error("malloc() failed"); | |
445 | goto eth_init_failed_0; | |
446 | } | |
447 | memset(priv, 0, sizeof(*priv)); | |
448 | ||
449 | priv->regs = (struct mac_regs *)base_addr; | |
450 | ||
451 | priv->tx_dq.base = calloc(NUMTXDESC, | |
452 | sizeof(struct tx_descriptor)); | |
453 | if (priv->tx_dq.base == NULL) { | |
454 | error("calloc() failed"); | |
455 | goto eth_init_failed_1; | |
456 | } | |
457 | ||
458 | priv->tx_sq.base = calloc(NUMTXDESC, | |
459 | sizeof(struct tx_status)); | |
460 | if (priv->tx_sq.base == NULL) { | |
461 | error("calloc() failed"); | |
462 | goto eth_init_failed_2; | |
463 | } | |
464 | ||
465 | priv->rx_dq.base = calloc(NUMRXDESC, | |
466 | sizeof(struct rx_descriptor)); | |
467 | if (priv->rx_dq.base == NULL) { | |
468 | error("calloc() failed"); | |
469 | goto eth_init_failed_3; | |
470 | } | |
471 | ||
472 | priv->rx_sq.base = calloc(NUMRXDESC, | |
473 | sizeof(struct rx_status)); | |
474 | if (priv->rx_sq.base == NULL) { | |
475 | error("calloc() failed"); | |
476 | goto eth_init_failed_4; | |
477 | } | |
478 | ||
479 | dev = malloc(sizeof *dev); | |
480 | if (dev == NULL) { | |
481 | error("malloc() failed"); | |
482 | goto eth_init_failed_5; | |
483 | } | |
484 | memset(dev, 0, sizeof *dev); | |
485 | ||
486 | dev->iobase = base_addr; | |
487 | dev->priv = priv; | |
488 | dev->init = ep93xx_eth_open; | |
489 | dev->halt = ep93xx_eth_close; | |
490 | dev->send = ep93xx_eth_send_packet; | |
491 | dev->recv = ep93xx_eth_rcv_packet; | |
492 | ||
493 | sprintf(dev->name, "ep93xx_eth-%hu", dev_num); | |
494 | ||
495 | eth_register(dev); | |
496 | ||
497 | /* Done! */ | |
498 | ret = 1; | |
499 | goto eth_init_done; | |
500 | ||
501 | eth_init_failed_5: | |
502 | free(priv->rx_sq.base); | |
503 | /* Fall through */ | |
504 | ||
505 | eth_init_failed_4: | |
506 | free(priv->rx_dq.base); | |
507 | /* Fall through */ | |
508 | ||
509 | eth_init_failed_3: | |
510 | free(priv->tx_sq.base); | |
511 | /* Fall through */ | |
512 | ||
513 | eth_init_failed_2: | |
514 | free(priv->tx_dq.base); | |
515 | /* Fall through */ | |
516 | ||
517 | eth_init_failed_1: | |
518 | free(priv); | |
519 | /* Fall through */ | |
520 | ||
521 | eth_init_failed_0: | |
522 | /* Fall through */ | |
523 | ||
524 | eth_init_done: | |
525 | debug("-ep93xx_eth_initialize %d", ret); | |
526 | return ret; | |
527 | } | |
528 | ||
529 | #if defined(CONFIG_MII) | |
530 | ||
531 | /** | |
532 | * Maximum MII address we support | |
533 | */ | |
534 | #define MII_ADDRESS_MAX 31 | |
535 | ||
536 | /** | |
537 | * Maximum MII register address we support | |
538 | */ | |
539 | #define MII_REGISTER_MAX 31 | |
540 | ||
541 | /** | |
542 | * Read a 16-bit value from an MII register. | |
543 | */ | |
5700bb63 | 544 | static int ep93xx_miiphy_read(const char * const dev, unsigned char const addr, |
594d57d0 MK |
545 | unsigned char const reg, unsigned short * const value) |
546 | { | |
547 | struct mac_regs *mac = (struct mac_regs *)MAC_BASE; | |
548 | int ret = -1; | |
549 | uint32_t self_ctl; | |
550 | ||
551 | debug("+ep93xx_miiphy_read"); | |
552 | ||
553 | /* Parameter checks */ | |
554 | BUG_ON(dev == NULL); | |
555 | BUG_ON(addr > MII_ADDRESS_MAX); | |
556 | BUG_ON(reg > MII_REGISTER_MAX); | |
557 | BUG_ON(value == NULL); | |
558 | ||
559 | /* | |
560 | * Save the current SelfCTL register value. Set MAC to suppress | |
561 | * preamble bits. Wait for any previous MII command to complete | |
562 | * before issuing the new command. | |
563 | */ | |
564 | self_ctl = readl(&mac->selfctl); | |
565 | #if defined(CONFIG_MII_SUPPRESS_PREAMBLE) | |
566 | writel(self_ctl & ~(1 << 8), &mac->selfctl); | |
567 | #endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ | |
568 | ||
569 | while (readl(&mac->miists) & MIISTS_BUSY) | |
570 | ; /* noop */ | |
571 | ||
572 | /* | |
573 | * Issue the MII 'read' command. Wait for the command to complete. | |
574 | * Read the MII data value. | |
575 | */ | |
576 | writel(MIICMD_OPCODE_READ | ((uint32_t)addr << 5) | (uint32_t)reg, | |
577 | &mac->miicmd); | |
578 | while (readl(&mac->miists) & MIISTS_BUSY) | |
579 | ; /* noop */ | |
580 | ||
581 | *value = (unsigned short)readl(&mac->miidata); | |
582 | ||
583 | /* Restore the saved SelfCTL value and return. */ | |
584 | writel(self_ctl, &mac->selfctl); | |
585 | ||
586 | ret = 0; | |
587 | /* Fall through */ | |
588 | ||
589 | debug("-ep93xx_miiphy_read"); | |
590 | return ret; | |
591 | } | |
592 | ||
593 | /** | |
594 | * Write a 16-bit value to an MII register. | |
595 | */ | |
5700bb63 | 596 | static int ep93xx_miiphy_write(const char * const dev, unsigned char const addr, |
594d57d0 MK |
597 | unsigned char const reg, unsigned short const value) |
598 | { | |
599 | struct mac_regs *mac = (struct mac_regs *)MAC_BASE; | |
600 | int ret = -1; | |
601 | uint32_t self_ctl; | |
602 | ||
603 | debug("+ep93xx_miiphy_write"); | |
604 | ||
605 | /* Parameter checks */ | |
606 | BUG_ON(dev == NULL); | |
607 | BUG_ON(addr > MII_ADDRESS_MAX); | |
608 | BUG_ON(reg > MII_REGISTER_MAX); | |
609 | ||
610 | /* | |
611 | * Save the current SelfCTL register value. Set MAC to suppress | |
612 | * preamble bits. Wait for any previous MII command to complete | |
613 | * before issuing the new command. | |
614 | */ | |
615 | self_ctl = readl(&mac->selfctl); | |
616 | #if defined(CONFIG_MII_SUPPRESS_PREAMBLE) | |
617 | writel(self_ctl & ~(1 << 8), &mac->selfctl); | |
618 | #endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ | |
619 | ||
620 | while (readl(&mac->miists) & MIISTS_BUSY) | |
621 | ; /* noop */ | |
622 | ||
623 | /* Issue the MII 'write' command. Wait for the command to complete. */ | |
624 | writel((uint32_t)value, &mac->miidata); | |
625 | writel(MIICMD_OPCODE_WRITE | ((uint32_t)addr << 5) | (uint32_t)reg, | |
626 | &mac->miicmd); | |
627 | while (readl(&mac->miists) & MIISTS_BUSY) | |
628 | ; /* noop */ | |
629 | ||
630 | /* Restore the saved SelfCTL value and return. */ | |
631 | writel(self_ctl, &mac->selfctl); | |
632 | ||
633 | ret = 0; | |
634 | /* Fall through */ | |
635 | ||
636 | debug("-ep93xx_miiphy_write"); | |
637 | return ret; | |
638 | } | |
639 | #endif /* defined(CONFIG_MII) */ |