]>
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++) | |
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? */ | |
327 | error("packet rx error, status %08X %08X", | |
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)) { | |
404 | error("packet tx error, status %08X", | |
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 | { | |
424 | miiphy_register("ep93xx_eth0", ep93xx_miiphy_read, ep93xx_miiphy_write); | |
425 | return 0; | |
426 | } | |
427 | #endif | |
428 | ||
429 | /** | |
430 | * Initialize the EP93xx MAC. The MAC hardware is reset. Buffers are | |
431 | * allocated, if necessary, for the TX and RX descriptor and status queues, | |
432 | * as well as for received packets. The EP93XX MAC hardware is initialized. | |
433 | * Transmit and receive operations are enabled. | |
434 | */ | |
435 | int ep93xx_eth_initialize(u8 dev_num, int base_addr) | |
436 | { | |
437 | int ret = -1; | |
438 | struct eth_device *dev; | |
439 | struct ep93xx_priv *priv; | |
440 | ||
441 | debug("+ep93xx_eth_initialize"); | |
442 | ||
443 | priv = malloc(sizeof(*priv)); | |
444 | if (!priv) { | |
445 | error("malloc() failed"); | |
446 | goto eth_init_failed_0; | |
447 | } | |
448 | memset(priv, 0, sizeof(*priv)); | |
449 | ||
450 | priv->regs = (struct mac_regs *)base_addr; | |
451 | ||
452 | priv->tx_dq.base = calloc(NUMTXDESC, | |
453 | sizeof(struct tx_descriptor)); | |
454 | if (priv->tx_dq.base == NULL) { | |
455 | error("calloc() failed"); | |
456 | goto eth_init_failed_1; | |
457 | } | |
458 | ||
459 | priv->tx_sq.base = calloc(NUMTXDESC, | |
460 | sizeof(struct tx_status)); | |
461 | if (priv->tx_sq.base == NULL) { | |
462 | error("calloc() failed"); | |
463 | goto eth_init_failed_2; | |
464 | } | |
465 | ||
466 | priv->rx_dq.base = calloc(NUMRXDESC, | |
467 | sizeof(struct rx_descriptor)); | |
468 | if (priv->rx_dq.base == NULL) { | |
469 | error("calloc() failed"); | |
470 | goto eth_init_failed_3; | |
471 | } | |
472 | ||
473 | priv->rx_sq.base = calloc(NUMRXDESC, | |
474 | sizeof(struct rx_status)); | |
475 | if (priv->rx_sq.base == NULL) { | |
476 | error("calloc() failed"); | |
477 | goto eth_init_failed_4; | |
478 | } | |
479 | ||
480 | dev = malloc(sizeof *dev); | |
481 | if (dev == NULL) { | |
482 | error("malloc() failed"); | |
483 | goto eth_init_failed_5; | |
484 | } | |
485 | memset(dev, 0, sizeof *dev); | |
486 | ||
487 | dev->iobase = base_addr; | |
488 | dev->priv = priv; | |
489 | dev->init = ep93xx_eth_open; | |
490 | dev->halt = ep93xx_eth_close; | |
491 | dev->send = ep93xx_eth_send_packet; | |
492 | dev->recv = ep93xx_eth_rcv_packet; | |
493 | ||
494 | sprintf(dev->name, "ep93xx_eth-%hu", dev_num); | |
495 | ||
496 | eth_register(dev); | |
497 | ||
498 | /* Done! */ | |
499 | ret = 1; | |
500 | goto eth_init_done; | |
501 | ||
502 | eth_init_failed_5: | |
503 | free(priv->rx_sq.base); | |
504 | /* Fall through */ | |
505 | ||
506 | eth_init_failed_4: | |
507 | free(priv->rx_dq.base); | |
508 | /* Fall through */ | |
509 | ||
510 | eth_init_failed_3: | |
511 | free(priv->tx_sq.base); | |
512 | /* Fall through */ | |
513 | ||
514 | eth_init_failed_2: | |
515 | free(priv->tx_dq.base); | |
516 | /* Fall through */ | |
517 | ||
518 | eth_init_failed_1: | |
519 | free(priv); | |
520 | /* Fall through */ | |
521 | ||
522 | eth_init_failed_0: | |
523 | /* Fall through */ | |
524 | ||
525 | eth_init_done: | |
526 | debug("-ep93xx_eth_initialize %d", ret); | |
527 | return ret; | |
528 | } | |
529 | ||
530 | #if defined(CONFIG_MII) | |
531 | ||
532 | /** | |
533 | * Maximum MII address we support | |
534 | */ | |
535 | #define MII_ADDRESS_MAX 31 | |
536 | ||
537 | /** | |
538 | * Maximum MII register address we support | |
539 | */ | |
540 | #define MII_REGISTER_MAX 31 | |
541 | ||
542 | /** | |
543 | * Read a 16-bit value from an MII register. | |
544 | */ | |
5700bb63 | 545 | static int ep93xx_miiphy_read(const char * const dev, unsigned char const addr, |
594d57d0 MK |
546 | unsigned char const reg, unsigned short * const value) |
547 | { | |
548 | struct mac_regs *mac = (struct mac_regs *)MAC_BASE; | |
549 | int ret = -1; | |
550 | uint32_t self_ctl; | |
551 | ||
552 | debug("+ep93xx_miiphy_read"); | |
553 | ||
554 | /* Parameter checks */ | |
555 | BUG_ON(dev == NULL); | |
556 | BUG_ON(addr > MII_ADDRESS_MAX); | |
557 | BUG_ON(reg > MII_REGISTER_MAX); | |
558 | BUG_ON(value == NULL); | |
559 | ||
560 | /* | |
561 | * Save the current SelfCTL register value. Set MAC to suppress | |
562 | * preamble bits. Wait for any previous MII command to complete | |
563 | * before issuing the new command. | |
564 | */ | |
565 | self_ctl = readl(&mac->selfctl); | |
566 | #if defined(CONFIG_MII_SUPPRESS_PREAMBLE) | |
567 | writel(self_ctl & ~(1 << 8), &mac->selfctl); | |
568 | #endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ | |
569 | ||
570 | while (readl(&mac->miists) & MIISTS_BUSY) | |
571 | ; /* noop */ | |
572 | ||
573 | /* | |
574 | * Issue the MII 'read' command. Wait for the command to complete. | |
575 | * Read the MII data value. | |
576 | */ | |
577 | writel(MIICMD_OPCODE_READ | ((uint32_t)addr << 5) | (uint32_t)reg, | |
578 | &mac->miicmd); | |
579 | while (readl(&mac->miists) & MIISTS_BUSY) | |
580 | ; /* noop */ | |
581 | ||
582 | *value = (unsigned short)readl(&mac->miidata); | |
583 | ||
584 | /* Restore the saved SelfCTL value and return. */ | |
585 | writel(self_ctl, &mac->selfctl); | |
586 | ||
587 | ret = 0; | |
588 | /* Fall through */ | |
589 | ||
590 | debug("-ep93xx_miiphy_read"); | |
591 | return ret; | |
592 | } | |
593 | ||
594 | /** | |
595 | * Write a 16-bit value to an MII register. | |
596 | */ | |
5700bb63 | 597 | static int ep93xx_miiphy_write(const char * const dev, unsigned char const addr, |
594d57d0 MK |
598 | unsigned char const reg, unsigned short const value) |
599 | { | |
600 | struct mac_regs *mac = (struct mac_regs *)MAC_BASE; | |
601 | int ret = -1; | |
602 | uint32_t self_ctl; | |
603 | ||
604 | debug("+ep93xx_miiphy_write"); | |
605 | ||
606 | /* Parameter checks */ | |
607 | BUG_ON(dev == NULL); | |
608 | BUG_ON(addr > MII_ADDRESS_MAX); | |
609 | BUG_ON(reg > MII_REGISTER_MAX); | |
610 | ||
611 | /* | |
612 | * Save the current SelfCTL register value. Set MAC to suppress | |
613 | * preamble bits. Wait for any previous MII command to complete | |
614 | * before issuing the new command. | |
615 | */ | |
616 | self_ctl = readl(&mac->selfctl); | |
617 | #if defined(CONFIG_MII_SUPPRESS_PREAMBLE) | |
618 | writel(self_ctl & ~(1 << 8), &mac->selfctl); | |
619 | #endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ | |
620 | ||
621 | while (readl(&mac->miists) & MIISTS_BUSY) | |
622 | ; /* noop */ | |
623 | ||
624 | /* Issue the MII 'write' command. Wait for the command to complete. */ | |
625 | writel((uint32_t)value, &mac->miidata); | |
626 | writel(MIICMD_OPCODE_WRITE | ((uint32_t)addr << 5) | (uint32_t)reg, | |
627 | &mac->miicmd); | |
628 | while (readl(&mac->miists) & MIISTS_BUSY) | |
629 | ; /* noop */ | |
630 | ||
631 | /* Restore the saved SelfCTL value and return. */ | |
632 | writel(self_ctl, &mac->selfctl); | |
633 | ||
634 | ret = 0; | |
635 | /* Fall through */ | |
636 | ||
637 | debug("-ep93xx_miiphy_write"); | |
638 | return ret; | |
639 | } | |
640 | #endif /* defined(CONFIG_MII) */ |