* Code cleanup:
[oweals/u-boot.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 int 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
189 static 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,
295                       INCA_IP_DMA_DMA_RXCCR0_INIT);
296
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:
318                  CPS (CPU port status)   0x3 (forwarding)
319                  LPS (LAN port status)   0x3 (forwarding)
320                  PPS (PC port status)    0x3 (forwarding)
321            */
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
332 static 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         }
350
351         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 =
367                         (INCA_DMA_TX_SOP | INCA_DMA_TX_EOP | INCA_DMA_TX_HOLD);
368
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         }
388
389         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;
407 Done:
408 #if 0
409         printf("Leaving inca_switch_send()\n");
410 #endif
411         return res;
412 }
413
414
415 static 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 &=
445                          ~(INCA_DMA_RX_EOP | INCA_DMA_RX_SOP | INCA_DMA_RX_C);
446 #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]),
463                                     length - 4);
464                 }
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
488 static 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
518 static 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
529            /* 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)
537            */
538         SW_WRITE_REG(INCA_IP_Switch_MDIO_CFG, 0x4c50);
539
540            /* 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)
549            */
550         SW_WRITE_REG (INCA_IP_Switch_EPHY, 0xff);
551
552            /* MDIO_ACC:
553                  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
567            */
568         SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0a08000);
569
570            /* MDIO_ACC:
571                  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
585            */
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
610 static 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