blob: 07e86fc5fcf097b0191bfcba336875f6406c17fb [file] [log] [blame]
wdenk1adff3d2003-03-26 11:42:53 +00001/*
2 * INCA-IP internal switch ethernet driver.
3 *
4 * (C) Copyright 2003
5 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6 *
7 * See file CREDITS for list of people who contributed to this
8 * project.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of
13 * the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 * MA 02111-1307 USA
24 */
25
26
27#include <common.h>
28
29#if (CONFIG_COMMANDS & CFG_CMD_NET) && defined(CONFIG_NET_MULTI) \
30 && defined(CONFIG_INCA_IP_SWITCH)
31
32#include <malloc.h>
33#include <net.h>
34#include <asm/inca-ip.h>
35#include <asm/addrspace.h>
36
37
38#define NUM_RX_DESC PKTBUFSRX
39#define NUM_TX_DESC 3
40#define TOUT_LOOP 1000000
41
42
43#define DELAY udelay(10000)
44
45#define DMA_WRITE_REG(reg, value) *((volatile u32 *)reg) = (u32)value;
46#define DMA_READ_REG(reg, value) value = (u32)*((volatile u32*)reg)
47#define SW_WRITE_REG(reg, value) \
wdenk57b2d802003-06-27 21:31:46 +000048 *((volatile u32*)reg) = (u32)value;\
49 DELAY;\
50 *((volatile u32*)reg) = (u32)value;
wdenk1adff3d2003-03-26 11:42:53 +000051
52#define SW_READ_REG(reg, value) \
wdenk57b2d802003-06-27 21:31:46 +000053 value = (u32)*((volatile u32*)reg);\
54 DELAY;\
55 value = (u32)*((volatile u32*)reg);
wdenk1adff3d2003-03-26 11:42:53 +000056
57#define INCA_DMA_TX_POLLING_TIME 0x07
58#define INCA_DMA_RX_POLLING_TIME 0x07
59
60#define INCA_DMA_TX_HOLD 0x80000000
61#define INCA_DMA_TX_EOP 0x40000000
62#define INCA_DMA_TX_SOP 0x20000000
63#define INCA_DMA_TX_ICPT 0x10000000
64#define INCA_DMA_TX_IEOP 0x08000000
65
66#define INCA_DMA_RX_C 0x80000000
67#define INCA_DMA_RX_SOP 0x40000000
68#define INCA_DMA_RX_EOP 0x20000000
69
70
71typedef struct
72{
73 union
74 {
75 struct
76 {
77 volatile u32 HOLD :1;
78 volatile u32 ICpt :1;
79 volatile u32 IEop :1;
80 volatile u32 offset :3;
81 volatile u32 reserved0 :4;
82 volatile u32 NFB :22;
83 }field;
84
85 volatile u32 word;
86 }params;
87
88 volatile u32 nextRxDescPtr;
89
90 volatile u32 RxDataPtr;
91
92 union
93 {
94 struct
95 {
96 volatile u32 C :1;
97 volatile u32 Sop :1;
98 volatile u32 Eop :1;
99 volatile u32 reserved3 :12;
100 volatile u32 NBT :17;
101 }field;
102
103 volatile u32 word;
104 }status;
105
106} inca_rx_descriptor_t;
107
108
109typedef struct
110{
111 union
112 {
113 struct
114 {
115 volatile u32 HOLD :1;
116 volatile u32 Eop :1;
117 volatile u32 Sop :1;
118 volatile u32 ICpt :1;
119 volatile u32 IEop :1;
120 volatile u32 reserved0 :5;
121 volatile u32 NBA :22;
122 }field;
123
124 volatile u32 word;
125 }params;
126
127 volatile u32 nextTxDescPtr;
128
129 volatile u32 TxDataPtr;
130
131 volatile u32 C :1;
132 volatile u32 reserved3 :31;
133
134} inca_tx_descriptor_t;
135
136
137static inca_rx_descriptor_t rx_ring[NUM_RX_DESC] __attribute__ ((aligned(16)));
138static inca_tx_descriptor_t tx_ring[NUM_TX_DESC] __attribute__ ((aligned(16)));
139
140static int tx_new, rx_new, tx_hold, rx_hold;
141static int tx_old_hold = -1;
142static int initialized = 0;
143
144
145static int inca_switch_init(struct eth_device *dev, bd_t * bis);
146static int inca_switch_send(struct eth_device *dev, volatile void *packet,
147 int length);
148static int inca_switch_recv(struct eth_device *dev);
149static void inca_switch_halt(struct eth_device *dev);
150static void inca_init_switch_chip(void);
151static void inca_dma_init(void);
152
153
wdenk1adff3d2003-03-26 11:42:53 +0000154int inca_switch_initialize(bd_t * bis)
155{
156 struct eth_device *dev;
157
158#if 0
159 printf("Entered inca_switch_initialize()\n");
160#endif
161
162 if (!(dev = (struct eth_device *) malloc (sizeof *dev)))
163 {
164 printf("Failed to allocate memory\n");
165 return 0;
166 }
167 memset(dev, 0, sizeof(*dev));
168
169 inca_dma_init();
170
171 inca_init_switch_chip();
172
173 sprintf(dev->name, "INCA-IP Switch");
174 dev->init = inca_switch_init;
175 dev->halt = inca_switch_halt;
176 dev->send = inca_switch_send;
177 dev->recv = inca_switch_recv;
178
179 eth_register(dev);
180
181#if 0
182 printf("Leaving inca_switch_initialize()\n");
183#endif
184
185 return 1;
186}
187
188
189static int inca_switch_init(struct eth_device *dev, bd_t * bis)
190{
191 int i;
192 u32 v, regValue;
193 u16 wTmp;
194
195#if 0
196 printf("Entering inca_switch_init()\n");
197#endif
198
199 /* Set MAC address.
200 */
201 wTmp = (u16)dev->enetaddr[0];
202 regValue = (wTmp << 8) | dev->enetaddr[1];
203
204 SW_WRITE_REG(INCA_IP_Switch_PMAC_SA1, regValue);
205
206 wTmp = (u16)dev->enetaddr[2];
207 regValue = (wTmp << 8) | dev->enetaddr[3];
208 regValue = regValue << 16;
209 wTmp = (u16)dev->enetaddr[4];
210 regValue |= (wTmp<<8) | dev->enetaddr[5];
211
212 SW_WRITE_REG(INCA_IP_Switch_PMAC_SA2, regValue);
213
214 /* Initialize the descriptor rings.
215 */
216 for (i = 0; i < NUM_RX_DESC; i++)
217 {
218 inca_rx_descriptor_t * rx_desc = KSEG1ADDR(&rx_ring[i]);
219 memset(rx_desc, 0, sizeof(rx_ring[i]));
220
221 /* Set maximum size of receive buffer.
222 */
223 rx_desc->params.field.NFB = PKTSIZE_ALIGN;
224
225 /* Set the offset of the receive buffer. Zero means
226 * that the offset mechanism is not used.
227 */
228 rx_desc->params.field.offset = 0;
229
230 /* Check if it is the last descriptor.
231 */
232 if (i == (NUM_RX_DESC - 1))
233 {
234 /* Let the last descriptor point to the first
235 * one.
236 */
237 rx_desc->nextRxDescPtr = KSEG1ADDR((u32)rx_ring);
238 }
239 else
240 {
241 /* Set the address of the next descriptor.
242 */
243 rx_desc->nextRxDescPtr = (u32)KSEG1ADDR(&rx_ring[i+1]);
244 }
245
246 rx_desc->RxDataPtr = (u32)KSEG1ADDR(NetRxPackets[i]);
247 }
248
249#if 0
250 printf("rx_ring = 0x%08X 0x%08X\n", (u32)rx_ring, (u32)&rx_ring[0]);
251 printf("tx_ring = 0x%08X 0x%08X\n", (u32)tx_ring, (u32)&tx_ring[0]);
252#endif
253
254 for (i = 0; i < NUM_TX_DESC; i++)
255 {
256 inca_tx_descriptor_t * tx_desc = KSEG1ADDR(&tx_ring[i]);
257
258 memset(tx_desc, 0, sizeof(tx_ring[i]));
259
260 tx_desc->params.word = 0;
261 tx_desc->params.field.HOLD = 1;
262 tx_desc->C = 1;
263
264 /* Check if it is the last descriptor.
265 */
266 if (i == (NUM_TX_DESC - 1))
267 {
268 /* Let the last descriptor point to the
269 * first one.
270 */
271 tx_desc->nextTxDescPtr = KSEG1ADDR((u32)tx_ring);
272 }
273 else
274 {
275 /* Set the address of the next descriptor.
276 */
277 tx_desc->nextTxDescPtr = (u32)KSEG1ADDR(&tx_ring[i+1]);
278 }
279 }
280
281 /* Initialize RxDMA.
282 */
283 DMA_READ_REG(INCA_IP_DMA_DMA_RXISR, v);
284#if 0
285 printf("RX status = 0x%08X\n", v);
286#endif
287
288 /* Writing to the FRDA of CHANNEL.
289 */
290 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXFRDA0, (u32)rx_ring);
291
292 /* Writing to the COMMAND REG.
293 */
294 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0,
wdenk57b2d802003-06-27 21:31:46 +0000295 INCA_IP_DMA_DMA_RXCCR0_INIT);
wdenk1adff3d2003-03-26 11:42:53 +0000296
297 /* Initialize TxDMA.
298 */
299 DMA_READ_REG(INCA_IP_DMA_DMA_TXISR, v);
300#if 0
301 printf("TX status = 0x%08X\n", v);
302#endif
303
304 /* Writing to the FRDA of CHANNEL.
305 */
306 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXFRDA0, (u32)tx_ring);
307
308 tx_new = rx_new = 0;
309
310 tx_hold = NUM_TX_DESC - 1;
311 rx_hold = NUM_RX_DESC - 1;
312
313#if 0
314 rx_ring[rx_hold].params.field.HOLD = 1;
315#endif
316 /* enable spanning tree forwarding, enable the CPU port */
317 /* ST_PT:
wdenk57b2d802003-06-27 21:31:46 +0000318 CPS (CPU port status) 0x3 (forwarding)
319 LPS (LAN port status) 0x3 (forwarding)
320 PPS (PC port status) 0x3 (forwarding)
wdenk1adff3d2003-03-26 11:42:53 +0000321 */
322 SW_WRITE_REG(INCA_IP_Switch_ST_PT,0x3f);
323
324#if 0
325 printf("Leaving inca_switch_init()\n");
326#endif
327
328 return 0;
329}
330
331
332static int inca_switch_send(struct eth_device *dev, volatile void *packet,
333 int length)
334{
335 int i;
336 int res = -1;
337 u32 command;
338 u32 regValue;
339 inca_tx_descriptor_t * tx_desc = KSEG1ADDR(&tx_ring[tx_new]);
340
341#if 0
342 printf("Entered inca_switch_send()\n");
343#endif
344
345 if (length <= 0)
346 {
347 printf ("%s: bad packet size: %d\n", dev->name, length);
348 goto Done;
349 }
wdenk57b2d802003-06-27 21:31:46 +0000350
wdenk1adff3d2003-03-26 11:42:53 +0000351 for(i = 0; tx_desc->C == 0; i++)
352 {
353 if (i >= TOUT_LOOP)
354 {
355 printf("%s: tx error buffer not ready\n", dev->name);
356 goto Done;
357 }
358 }
359
360 if (tx_old_hold >= 0)
361 {
362 KSEG1ADDR(&tx_ring[tx_old_hold])->params.field.HOLD = 1;
363 }
364 tx_old_hold = tx_hold;
365
366 tx_desc->params.word =
wdenk57b2d802003-06-27 21:31:46 +0000367 (INCA_DMA_TX_SOP | INCA_DMA_TX_EOP | INCA_DMA_TX_HOLD);
wdenk1adff3d2003-03-26 11:42:53 +0000368
369 tx_desc->C = 0;
370 tx_desc->TxDataPtr = (u32)packet;
371 tx_desc->params.field.NBA = length;
372
373 KSEG1ADDR(&tx_ring[tx_hold])->params.field.HOLD = 0;
374
375 tx_hold = tx_new;
376 tx_new = (tx_new + 1) % NUM_TX_DESC;
377
378
379 if (! initialized)
380 {
381 command = INCA_IP_DMA_DMA_TXCCR0_INIT;
382 initialized = 1;
383 }
384 else
385 {
386 command = INCA_IP_DMA_DMA_TXCCR0_HR;
387 }
wdenk57b2d802003-06-27 21:31:46 +0000388
wdenk1adff3d2003-03-26 11:42:53 +0000389 DMA_READ_REG(INCA_IP_DMA_DMA_TXCCR0, regValue);
390 regValue |= command;
391#if 0
392 printf("regValue = 0x%x\n", regValue);
393#endif
394 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, regValue);
395
396#if 1
397 for(i = 0; KSEG1ADDR(&tx_ring[tx_hold])->C == 0; i++)
398 {
399 if (i >= TOUT_LOOP)
400 {
401 printf("%s: tx buffer not ready\n", dev->name);
402 goto Done;
403 }
404 }
405#endif
406 res = length;
407Done:
408#if 0
409 printf("Leaving inca_switch_send()\n");
410#endif
411 return res;
412}
413
414
415static int inca_switch_recv(struct eth_device *dev)
416{
417 int length = 0;
418 inca_rx_descriptor_t * rx_desc;
419
420#if 0
421 printf("Entered inca_switch_recv()\n");
422#endif
423
424 for (;;)
425 {
426 rx_desc = KSEG1ADDR(&rx_ring[rx_new]);
427
428 if (rx_desc->status.field.C == 0)
429 {
430 break;
431 }
432
433#if 0
434 rx_ring[rx_new].params.field.HOLD = 1;
435#endif
436
437 if (! rx_desc->status.field.Eop)
438 {
439 printf("Partly received packet!!!\n");
440 break;
441 }
442
443 length = rx_desc->status.field.NBT;
444 rx_desc->status.word &=
wdenk57b2d802003-06-27 21:31:46 +0000445 ~(INCA_DMA_RX_EOP | INCA_DMA_RX_SOP | INCA_DMA_RX_C);
wdenk1adff3d2003-03-26 11:42:53 +0000446#if 0
447{
448 int i;
449 for (i=0;i<length - 4;i++) {
450 if (i % 16 == 0) printf("\n%04x: ", i);
451 printf("%02X ", NetRxPackets[rx_new][i]);
452 }
453 printf("\n");
454}
455#endif
456
457 if (length)
458 {
459#if 0
460 printf("Received %d bytes\n", length);
461#endif
462 NetReceive((void*)KSEG1ADDR(NetRxPackets[rx_new]),
wdenk57b2d802003-06-27 21:31:46 +0000463 length - 4);
wdenk1adff3d2003-03-26 11:42:53 +0000464 }
465 else
466 {
467#if 1
468 printf("Zero length!!!\n");
469#endif
470 }
471
472
473 KSEG1ADDR(&rx_ring[rx_hold])->params.field.HOLD = 0;
474
475 rx_hold = rx_new;
476
477 rx_new = (rx_new + 1) % NUM_RX_DESC;
478 }
479
480#if 0
481 printf("Leaving inca_switch_recv()\n");
482#endif
483
484 return length;
485}
486
487
488static void inca_switch_halt(struct eth_device *dev)
489{
490#if 0
491 printf("Entered inca_switch_halt()\n");
492#endif
493
494#if 1
495 initialized = 0;
496#endif
497#if 1
498 /* Disable forwarding to the CPU port.
499 */
500 SW_WRITE_REG(INCA_IP_Switch_ST_PT,0xf);
501
502 /* Close RxDMA channel.
503 */
504 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF);
505
506 /* Close TxDMA channel.
507 */
508 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, INCA_IP_DMA_DMA_TXCCR0_OFF);
509
510
511#endif
512#if 0
513 printf("Leaving inca_switch_halt()\n");
514#endif
515}
516
517
518static void inca_init_switch_chip(void)
519{
520 u32 regValue;
521
522 /* To workaround a problem with collision counter
523 * (see Errata sheet).
524 */
525 SW_WRITE_REG(INCA_IP_Switch_PC_TX_CTL, 0x00000001);
526 SW_WRITE_REG(INCA_IP_Switch_LAN_TX_CTL, 0x00000001);
527
528#if 1
wdenk57b2d802003-06-27 21:31:46 +0000529 /* init MDIO configuration:
530 MDS (Poll speed): 0x01 (4ms)
531 PHY_LAN_ADDR: 0x06
532 PHY_PC_ADDR: 0x05
533 UEP (Use External PHY): 0x00 (Internal PHY is used)
534 PS (Port Select): 0x00 (PT/UMM for LAN)
535 PT (PHY Test): 0x00 (no test mode)
536 UMM (Use MDIO Mode): 0x00 (state machine is disabled)
wdenk1adff3d2003-03-26 11:42:53 +0000537 */
538 SW_WRITE_REG(INCA_IP_Switch_MDIO_CFG, 0x4c50);
539
wdenk57b2d802003-06-27 21:31:46 +0000540 /* init PHY:
541 SL (Auto Neg. Speed for LAN)
542 SP (Auto Neg. Speed for PC)
543 LL (Link Status for LAN)
544 LP (Link Status for PC)
545 DL (Duplex Status for LAN)
546 DP (Duplex Status for PC)
547 PL (Auto Neg. Pause Status for LAN)
548 PP (Auto Neg. Pause Status for PC)
wdenk1adff3d2003-03-26 11:42:53 +0000549 */
550 SW_WRITE_REG (INCA_IP_Switch_EPHY, 0xff);
551
552 /* MDIO_ACC:
wdenk57b2d802003-06-27 21:31:46 +0000553 RA (Request/Ack) 0x01 (Request)
554 RW (Read/Write) 0x01 (Write)
555 PHY_ADDR 0x05 (PC)
556 REG_ADDR 0x00 (PHY_BCR: basic control register)
557 PHY_DATA 0x8000
558 Reset - software reset
559 LB (loop back) - normal
560 SS (speed select) - 10 Mbit/s
561 ANE (auto neg. enable) - disable
562 PD (power down) - normal
563 ISO (isolate) - normal
564 RAN (restart auto neg.) - normal
565 DM (duplex mode) - half duplex
566 CT (collision test) - enable
wdenk1adff3d2003-03-26 11:42:53 +0000567 */
568 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0a08000);
569
570 /* MDIO_ACC:
wdenk57b2d802003-06-27 21:31:46 +0000571 RA (Request/Ack) 0x01 (Request)
572 RW (Read/Write) 0x01 (Write)
573 PHY_ADDR 0x06 (LAN)
574 REG_ADDR 0x00 (PHY_BCR: basic control register)
575 PHY_DATA 0x8000
576 Reset - software reset
577 LB (loop back) - normal
578 SS (speed select) - 10 Mbit/s
579 ANE (auto neg. enable) - disable
580 PD (power down) - normal
581 ISO (isolate) - normal
582 RAN (restart auto neg.) - normal
583 DM (duplex mode) - half duplex
584 CT (collision test) - enable
wdenk1adff3d2003-03-26 11:42:53 +0000585 */
586 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0c08000);
587#endif
588
589 /* Make sure the CPU port is disabled for now. We
590 * don't want packets to get stacked for us until
591 * we enable DMA and are prepared to receive them.
592 */
593 SW_WRITE_REG(INCA_IP_Switch_ST_PT,0xf);
594
595 SW_READ_REG(INCA_IP_Switch_ARL_CTL, regValue);
596
597 /* CRC GEN is enabled.
598 */
599 regValue |= 0x00000200;
600 SW_WRITE_REG(INCA_IP_Switch_ARL_CTL, regValue);
601
602 /* ADD TAG is disabled.
603 */
604 SW_READ_REG(INCA_IP_Switch_PMAC_HD_CTL, regValue);
605 regValue &= ~0x00000002;
606 SW_WRITE_REG(INCA_IP_Switch_PMAC_HD_CTL, regValue);
607}
608
609
610static void inca_dma_init(void)
611{
612 /* Switch off all DMA channels.
613 */
614 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF);
615 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR1, INCA_IP_DMA_DMA_RXCCR1_OFF);
616
617 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF);
618 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR1, INCA_IP_DMA_DMA_TXCCR1_OFF);
619 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR2, INCA_IP_DMA_DMA_TXCCR2_OFF);
620
621 /* Setup TX channel polling time.
622 */
623 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXPOLL, INCA_DMA_TX_POLLING_TIME);
624
625 /* Setup RX channel polling time.
626 */
627 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXPOLL, INCA_DMA_RX_POLLING_TIME);
628
629 /* ERRATA: write reset value into the DMA RX IMR register.
630 */
631 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXIMR, 0xFFFFFFFF);
632
633 /* Just in case: disable all transmit interrupts also.
634 */
635 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXIMR, 0xFFFFFFFF);
636
637 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXISR, 0xFFFFFFFF);
638 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXISR, 0xFFFFFFFF);
639}
640
641#endif