]> git.karo-electronics.de Git - karo-tx-uboot.git/blob - drivers/inca-ip_sw.c
* Make Ethernet autonegotiation on INCA-IP work for all clock rates;
[karo-tx-uboot.git] / drivers / inca-ip_sw.c
1 /*
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)   \
48          *((volatile u32*)reg) = (u32)value;\
49          DELAY;\
50          *((volatile u32*)reg) = (u32)value;
51
52 #define SW_READ_REG(reg, value)    \
53          value = (u32)*((volatile u32*)reg);\
54          DELAY;\
55          value = (u32)*((volatile u32*)reg);
56
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
71 typedef struct
72 {
73         union {
74                 struct {
75                         volatile u32 HOLD                :1;
76                         volatile u32 ICpt                :1;
77                         volatile u32 IEop                :1;
78                         volatile u32 offset              :3;
79                         volatile u32 reserved0           :4;
80                         volatile u32 NFB                 :22;
81                 }field;
82
83                 volatile u32 word;
84         }params;
85
86         volatile u32 nextRxDescPtr;
87
88         volatile u32 RxDataPtr;
89
90         union {
91                 struct {
92                         volatile u32 C                   :1;
93                         volatile u32 Sop                 :1;
94                         volatile u32 Eop                 :1;
95                         volatile u32 reserved3           :12;
96                         volatile u32 NBT                 :17;
97                 }field;
98
99                 volatile u32 word;
100         }status;
101
102 } inca_rx_descriptor_t;
103
104
105 typedef struct
106 {
107         union {
108                 struct {
109                         volatile u32 HOLD                :1;
110                         volatile u32 Eop                 :1;
111                         volatile u32 Sop                 :1;
112                         volatile u32 ICpt                :1;
113                         volatile u32 IEop                :1;
114                         volatile u32 reserved0           :5;
115                         volatile u32 NBA                 :22;
116                 }field;
117
118                 volatile u32 word;
119         }params;
120
121         volatile u32 nextTxDescPtr;
122
123         volatile u32 TxDataPtr;
124
125         volatile u32 C                   :1;
126         volatile u32 reserved3           :31;
127
128 } inca_tx_descriptor_t;
129
130
131 static inca_rx_descriptor_t rx_ring[NUM_RX_DESC] __attribute__ ((aligned(16)));
132 static inca_tx_descriptor_t tx_ring[NUM_TX_DESC] __attribute__ ((aligned(16)));
133
134 static int tx_new, rx_new, tx_hold, rx_hold;
135 static int tx_old_hold = -1;
136 static int initialized  = 0;
137
138
139 static int inca_switch_init(struct eth_device *dev, bd_t * bis);
140 static int inca_switch_send(struct eth_device *dev, volatile void *packet,
141                                                   int length);
142 static int inca_switch_recv(struct eth_device *dev);
143 static void inca_switch_halt(struct eth_device *dev);
144 static void inca_init_switch_chip(void);
145 static void inca_dma_init(void);
146
147
148 int inca_switch_initialize(bd_t * bis)
149 {
150         struct eth_device *dev;
151
152 #if 0
153         printf("Entered inca_switch_initialize()\n");
154 #endif
155
156         if (!(dev = (struct eth_device *) malloc (sizeof *dev))) {
157                 printf("Failed to allocate memory\n");
158                 return 0;
159         }
160         memset(dev, 0, sizeof(*dev));
161
162         inca_dma_init();
163
164         inca_init_switch_chip();
165
166         sprintf(dev->name, "INCA-IP Switch");
167         dev->init = inca_switch_init;
168         dev->halt = inca_switch_halt;
169         dev->send = inca_switch_send;
170         dev->recv = inca_switch_recv;
171
172         eth_register(dev);
173
174 #if 0
175         printf("Leaving inca_switch_initialize()\n");
176 #endif
177
178         return 1;
179 }
180
181
182 static int inca_switch_init(struct eth_device *dev, bd_t * bis)
183 {
184         int i;
185         u32 v, regValue;
186         u16 wTmp;
187
188 #if 0
189         printf("Entering inca_switch_init()\n");
190 #endif
191
192         /* Set MAC address.
193          */
194         wTmp = (u16)dev->enetaddr[0];
195         regValue = (wTmp << 8) | dev->enetaddr[1];
196
197         SW_WRITE_REG(INCA_IP_Switch_PMAC_SA1, regValue);
198
199         wTmp = (u16)dev->enetaddr[2];
200         regValue = (wTmp << 8) | dev->enetaddr[3];
201         regValue = regValue << 16;
202         wTmp = (u16)dev->enetaddr[4];
203         regValue |= (wTmp<<8) | dev->enetaddr[5];
204
205         SW_WRITE_REG(INCA_IP_Switch_PMAC_SA2, regValue);
206
207         /* Initialize the descriptor rings.
208          */
209         for (i = 0; i < NUM_RX_DESC; i++)
210         {
211                 inca_rx_descriptor_t * rx_desc = KSEG1ADDR(&rx_ring[i]);
212                 memset(rx_desc, 0, sizeof(rx_ring[i]));
213
214                 /* Set maximum size of receive buffer.
215                  */
216                 rx_desc->params.field.NFB = PKTSIZE_ALIGN;
217
218                 /* Set the offset of the receive buffer. Zero means
219                  * that the offset mechanism is not used.
220                  */
221                 rx_desc->params.field.offset = 0;
222
223                 /* Check if it is the last descriptor.
224                  */
225                 if (i == (NUM_RX_DESC - 1)) {
226                         /* Let the last descriptor point to the first
227                          * one.
228                          */
229                         rx_desc->nextRxDescPtr = KSEG1ADDR((u32)rx_ring);
230                 } else {
231                         /* Set the address of the next descriptor.
232                          */
233                         rx_desc->nextRxDescPtr = (u32)KSEG1ADDR(&rx_ring[i+1]);
234                 }
235
236                 rx_desc->RxDataPtr = (u32)KSEG1ADDR(NetRxPackets[i]);
237         }
238
239 #if 0
240         printf("rx_ring = 0x%08X 0x%08X\n", (u32)rx_ring, (u32)&rx_ring[0]);
241         printf("tx_ring = 0x%08X 0x%08X\n", (u32)tx_ring, (u32)&tx_ring[0]);
242 #endif
243
244         for (i = 0; i < NUM_TX_DESC; i++) {
245                 inca_tx_descriptor_t * tx_desc = KSEG1ADDR(&tx_ring[i]);
246
247                 memset(tx_desc, 0, sizeof(tx_ring[i]));
248
249                 tx_desc->params.word       = 0;
250                 tx_desc->params.field.HOLD = 1;
251                 tx_desc->C                 = 1;
252
253                         /* Check if it is the last descriptor.
254                          */
255                 if (i == (NUM_TX_DESC - 1)) {
256                                 /* Let the last descriptor point to the
257                                  * first one.
258                                  */
259                         tx_desc->nextTxDescPtr = KSEG1ADDR((u32)tx_ring);
260                 } else {
261                                 /* Set the address of the next descriptor.
262                                  */
263                         tx_desc->nextTxDescPtr = (u32)KSEG1ADDR(&tx_ring[i+1]);
264                 }
265         }
266
267         /* Initialize RxDMA.
268          */
269         DMA_READ_REG(INCA_IP_DMA_DMA_RXISR, v);
270 #if 0
271         printf("RX status = 0x%08X\n", v);
272 #endif
273
274         /* Writing to the FRDA of CHANNEL.
275          */
276         DMA_WRITE_REG(INCA_IP_DMA_DMA_RXFRDA0, (u32)rx_ring);
277
278         /* Writing to the COMMAND REG.
279          */
280         DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0,
281                       INCA_IP_DMA_DMA_RXCCR0_INIT);
282
283         /* Initialize TxDMA.
284          */
285         DMA_READ_REG(INCA_IP_DMA_DMA_TXISR, v);
286 #if 0
287         printf("TX status = 0x%08X\n", v);
288 #endif
289
290         /* Writing to the FRDA of CHANNEL.
291          */
292         DMA_WRITE_REG(INCA_IP_DMA_DMA_TXFRDA0, (u32)tx_ring);
293
294         tx_new = rx_new = 0;
295
296         tx_hold = NUM_TX_DESC - 1;
297         rx_hold = NUM_RX_DESC - 1;
298
299 #if 0
300         rx_ring[rx_hold].params.field.HOLD = 1;
301 #endif
302         /* enable spanning tree forwarding, enable the CPU port */
303         /* ST_PT:
304          *      CPS (CPU port status)   0x3 (forwarding)
305          *      LPS (LAN port status)   0x3 (forwarding)
306          *      PPS (PC port status)    0x3 (forwarding)
307          */
308         SW_WRITE_REG(INCA_IP_Switch_ST_PT,0x3f);
309
310 #if 0
311         printf("Leaving inca_switch_init()\n");
312 #endif
313
314         return 0;
315 }
316
317
318 static int inca_switch_send(struct eth_device *dev, volatile void *packet,
319                                                   int length)
320 {
321         int                    i;
322         int                    res         = -1;
323         u32                    command;
324         u32                    regValue;
325         inca_tx_descriptor_t * tx_desc     = KSEG1ADDR(&tx_ring[tx_new]);
326
327 #if 0
328         printf("Entered inca_switch_send()\n");
329 #endif
330
331         if (length <= 0) {
332                 printf ("%s: bad packet size: %d\n", dev->name, length);
333                 goto Done;
334         }
335
336         for(i = 0; tx_desc->C == 0; i++) {
337                 if (i >= TOUT_LOOP) {
338                         printf("%s: tx error buffer not ready\n", dev->name);
339                         goto Done;
340                 }
341         }
342
343         if (tx_old_hold >= 0) {
344                 KSEG1ADDR(&tx_ring[tx_old_hold])->params.field.HOLD = 1;
345         }
346         tx_old_hold = tx_hold;
347
348         tx_desc->params.word =
349                         (INCA_DMA_TX_SOP | INCA_DMA_TX_EOP | INCA_DMA_TX_HOLD);
350
351         tx_desc->C = 0;
352         tx_desc->TxDataPtr = (u32)packet;
353         tx_desc->params.field.NBA = length;
354
355         KSEG1ADDR(&tx_ring[tx_hold])->params.field.HOLD = 0;
356
357         tx_hold = tx_new;
358         tx_new  = (tx_new + 1) % NUM_TX_DESC;
359
360
361         if (! initialized) {
362                 command = INCA_IP_DMA_DMA_TXCCR0_INIT;
363                 initialized = 1;
364         } else {
365                 command = INCA_IP_DMA_DMA_TXCCR0_HR;
366         }
367
368         DMA_READ_REG(INCA_IP_DMA_DMA_TXCCR0, regValue);
369         regValue |= command;
370 #if 0
371         printf("regValue = 0x%x\n", regValue);
372 #endif
373         DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, regValue);
374
375 #if 1
376         for(i = 0; KSEG1ADDR(&tx_ring[tx_hold])->C == 0; i++) {
377                 if (i >= TOUT_LOOP) {
378                         printf("%s: tx buffer not ready\n", dev->name);
379                         goto Done;
380                 }
381         }
382 #endif
383         res = length;
384 Done:
385 #if 0
386         printf("Leaving inca_switch_send()\n");
387 #endif
388         return res;
389 }
390
391
392 static int inca_switch_recv(struct eth_device *dev)
393 {
394         int                    length  = 0;
395         inca_rx_descriptor_t * rx_desc;
396
397 #if 0
398         printf("Entered inca_switch_recv()\n");
399 #endif
400
401         for (;;) {
402                 rx_desc = KSEG1ADDR(&rx_ring[rx_new]);
403
404                 if (rx_desc->status.field.C == 0) {
405                         break;
406                 }
407
408 #if 0
409                 rx_ring[rx_new].params.field.HOLD = 1;
410 #endif
411
412                 if (! rx_desc->status.field.Eop) {
413                         printf("Partly received packet!!!\n");
414                         break;
415                 }
416
417                 length = rx_desc->status.field.NBT;
418                 rx_desc->status.word &=
419                          ~(INCA_DMA_RX_EOP | INCA_DMA_RX_SOP | INCA_DMA_RX_C);
420 #if 0
421 {
422   int i;
423   for (i=0;i<length - 4;i++) {
424     if (i % 16 == 0) printf("\n%04x: ", i);
425     printf("%02X ", NetRxPackets[rx_new][i]);
426   }
427   printf("\n");
428 }
429 #endif
430
431                 if (length) {
432 #if 0
433                         printf("Received %d bytes\n", length);
434 #endif
435                         NetReceive((void*)KSEG1ADDR(NetRxPackets[rx_new]),
436                                     length - 4);
437                 } else {
438 #if 1
439                         printf("Zero length!!!\n");
440 #endif
441                 }
442
443
444                 KSEG1ADDR(&rx_ring[rx_hold])->params.field.HOLD = 0;
445
446                 rx_hold = rx_new;
447
448                 rx_new = (rx_new + 1) % NUM_RX_DESC;
449         }
450
451 #if 0
452         printf("Leaving inca_switch_recv()\n");
453 #endif
454
455         return length;
456 }
457
458
459 static void inca_switch_halt(struct eth_device *dev)
460 {
461 #if 0
462         printf("Entered inca_switch_halt()\n");
463 #endif
464
465 #if 1
466         initialized = 0;
467 #endif
468 #if 1
469         /* Disable forwarding to the CPU port.
470          */
471         SW_WRITE_REG(INCA_IP_Switch_ST_PT,0xf);
472
473         /* Close RxDMA channel.
474          */
475         DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF);
476
477         /* Close TxDMA channel.
478          */
479         DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, INCA_IP_DMA_DMA_TXCCR0_OFF);
480
481
482 #endif
483 #if 0
484         printf("Leaving inca_switch_halt()\n");
485 #endif
486 }
487
488
489 static void inca_init_switch_chip(void)
490 {
491         u32 regValue;
492
493         /* To workaround a problem with collision counter
494          * (see Errata sheet).
495          */
496         SW_WRITE_REG(INCA_IP_Switch_PC_TX_CTL, 0x00000001);
497         SW_WRITE_REG(INCA_IP_Switch_LAN_TX_CTL, 0x00000001);
498
499 #if 1
500         /* init MDIO configuration:
501          *      MDS (Poll speed):       0x01 (4ms)
502          *      PHY_LAN_ADDR:           0x06
503          *      PHY_PC_ADDR:            0x05
504          *      UEP (Use External PHY): 0x00 (Internal PHY is used)
505          *      PS (Port Select):       0x00 (PT/UMM for LAN)
506          *      PT (PHY Test):          0x00 (no test mode)
507          *      UMM (Use MDIO Mode):    0x00 (state machine is disabled)
508          */
509         SW_WRITE_REG(INCA_IP_Switch_MDIO_CFG, 0x4c50);
510
511         /* init PHY:
512          *      SL (Auto Neg. Speed for LAN)
513          *      SP (Auto Neg. Speed for PC)
514          *      LL (Link Status for LAN)
515          *      LP (Link Status for PC)
516          *      DL (Duplex Status for LAN)
517          *      DP (Duplex Status for PC)
518          *      PL (Auto Neg. Pause Status for LAN)
519          *      PP (Auto Neg. Pause Status for PC)
520          */
521         SW_WRITE_REG (INCA_IP_Switch_EPHY, 0xff);
522
523         /* MDIO_ACC:
524          *      RA (Request/Ack)  0x01 (Request)
525          *      RW (Read/Write)   0x01 (Write)
526          *      PHY_ADDR          0x05 (PC)
527          *      REG_ADDR          0x00 (PHY_BCR: basic control register)
528          *      PHY_DATA          0x8000
529          *                    Reset                   - software reset
530          *                    LB (loop back)          - normal
531          *                    SS (speed select)       - 10 Mbit/s
532          *                    ANE (auto neg. enable)  - enable
533          *                    PD (power down)         - normal
534          *                    ISO (isolate)           - normal
535          *                    RAN (restart auto neg.) - normal
536          *                    DM (duplex mode)        - half duplex
537          *                    CT (collision test)     - enable
538          */
539         SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0a09000);
540
541         /* MDIO_ACC:
542          *      RA (Request/Ack)  0x01 (Request)
543          *      RW (Read/Write)   0x01 (Write)
544          *      PHY_ADDR          0x06 (LAN)
545          *      REG_ADDR          0x00 (PHY_BCR: basic control register)
546          *      PHY_DATA          0x8000
547          *                    Reset                   - software reset
548          *                    LB (loop back)          - normal
549          *                    SS (speed select)       - 10 Mbit/s
550          *                    ANE (auto neg. enable)  - enable
551          *                    PD (power down)         - normal
552          *                    ISO (isolate)           - normal
553          *                    RAN (restart auto neg.) - normal
554          *                    DM (duplex mode)        - half duplex
555          *                    CT (collision test)     - enable
556          */
557         SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0c09000);
558
559 #endif
560
561         /* Make sure the CPU port is disabled for now. We
562          * don't want packets to get stacked for us until
563          * we enable DMA and are prepared to receive them.
564          */
565         SW_WRITE_REG(INCA_IP_Switch_ST_PT,0xf);
566
567         SW_READ_REG(INCA_IP_Switch_ARL_CTL, regValue);
568
569         /* CRC GEN is enabled.
570          */
571         regValue |= 0x00000200;
572         SW_WRITE_REG(INCA_IP_Switch_ARL_CTL, regValue);
573
574         /* ADD TAG is disabled.
575          */
576         SW_READ_REG(INCA_IP_Switch_PMAC_HD_CTL, regValue);
577         regValue &= ~0x00000002;
578         SW_WRITE_REG(INCA_IP_Switch_PMAC_HD_CTL, regValue);
579 }
580
581
582 static void inca_dma_init(void)
583 {
584         /* Switch off all DMA channels.
585          */
586         DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF);
587         DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR1, INCA_IP_DMA_DMA_RXCCR1_OFF);
588
589         DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF);
590         DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR1, INCA_IP_DMA_DMA_TXCCR1_OFF);
591         DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR2, INCA_IP_DMA_DMA_TXCCR2_OFF);
592
593         /* Setup TX channel polling time.
594          */
595         DMA_WRITE_REG(INCA_IP_DMA_DMA_TXPOLL, INCA_DMA_TX_POLLING_TIME);
596
597         /* Setup RX channel polling time.
598          */
599         DMA_WRITE_REG(INCA_IP_DMA_DMA_RXPOLL, INCA_DMA_RX_POLLING_TIME);
600
601         /* ERRATA: write reset value into the DMA RX IMR register.
602          */
603         DMA_WRITE_REG(INCA_IP_DMA_DMA_RXIMR, 0xFFFFFFFF);
604
605         /* Just in case: disable all transmit interrupts also.
606          */
607         DMA_WRITE_REG(INCA_IP_DMA_DMA_TXIMR, 0xFFFFFFFF);
608
609         DMA_WRITE_REG(INCA_IP_DMA_DMA_TXISR, 0xFFFFFFFF);
610         DMA_WRITE_REG(INCA_IP_DMA_DMA_RXISR, 0xFFFFFFFF);
611 }
612
613 #endif