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