blob: d852a150fa913a4dfb8c478f94ce500a6a0f386e [file] [log] [blame]
wdenk1adff3d2003-03-26 11:42:53 +00001/*
2 * INCA-IP internal switch ethernet driver.
3 *
wdenk0e2874cb2004-03-02 14:05:39 +00004 * (C) Copyright 2003-2004
wdenk1adff3d2003-03-26 11:42:53 +00005 * 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
wdenk0a12b752004-03-11 22:46:36 +000017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
wdenk1adff3d2003-03-26 11:42:53 +000018 * 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
wdenk1adff3d2003-03-26 11:42:53 +000029#include <malloc.h>
30#include <net.h>
31#include <asm/inca-ip.h>
32#include <asm/addrspace.h>
33
34
35#define NUM_RX_DESC PKTBUFSRX
36#define NUM_TX_DESC 3
37#define TOUT_LOOP 1000000
38
39
40#define DELAY udelay(10000)
wdenk7f1447f2004-06-09 00:10:59 +000041 /* Sometimes the store word instruction hangs while writing to one
42 * of the Switch registers. Moving the instruction into a separate
43 * function somehow makes the problem go away.
44 */
45static void SWORD(volatile u32 * reg, u32 value)
46{
47 *reg = value;
48}
wdenk1adff3d2003-03-26 11:42:53 +000049
50#define DMA_WRITE_REG(reg, value) *((volatile u32 *)reg) = (u32)value;
51#define DMA_READ_REG(reg, value) value = (u32)*((volatile u32*)reg)
52#define SW_WRITE_REG(reg, value) \
wdenk7f1447f2004-06-09 00:10:59 +000053 SWORD(reg, value);\
wdenk0a12b752004-03-11 22:46:36 +000054 DELAY;\
wdenk7f1447f2004-06-09 00:10:59 +000055 SWORD(reg, value);
wdenk1adff3d2003-03-26 11:42:53 +000056
wdenk0a12b752004-03-11 22:46:36 +000057#define SW_READ_REG(reg, value) \
58 value = (u32)*((volatile u32*)reg);\
59 DELAY;\
60 value = (u32)*((volatile u32*)reg);
wdenk1adff3d2003-03-26 11:42:53 +000061
wdenk0a12b752004-03-11 22:46:36 +000062#define INCA_DMA_TX_POLLING_TIME 0x07
63#define INCA_DMA_RX_POLLING_TIME 0x07
wdenk1adff3d2003-03-26 11:42:53 +000064
wdenk0a12b752004-03-11 22:46:36 +000065#define INCA_DMA_TX_HOLD 0x80000000
66#define INCA_DMA_TX_EOP 0x40000000
67#define INCA_DMA_TX_SOP 0x20000000
68#define INCA_DMA_TX_ICPT 0x10000000
69#define INCA_DMA_TX_IEOP 0x08000000
wdenk1adff3d2003-03-26 11:42:53 +000070
wdenk0a12b752004-03-11 22:46:36 +000071#define INCA_DMA_RX_C 0x80000000
72#define INCA_DMA_RX_SOP 0x40000000
73#define INCA_DMA_RX_EOP 0x20000000
wdenk1adff3d2003-03-26 11:42:53 +000074
wdenk0e2874cb2004-03-02 14:05:39 +000075#define INCA_SWITCH_PHY_SPEED_10H 0x1
76#define INCA_SWITCH_PHY_SPEED_10F 0x5
77#define INCA_SWITCH_PHY_SPEED_100H 0x2
78#define INCA_SWITCH_PHY_SPEED_100F 0x6
79
wdenk4dd56e52004-02-20 22:02:48 +000080/************************ Auto MDIX settings ************************/
wdenk0a12b752004-03-11 22:46:36 +000081#define INCA_IP_AUTO_MDIX_LAN_PORTS_DIR INCA_IP_Ports_P1_DIR
82#define INCA_IP_AUTO_MDIX_LAN_PORTS_ALTSEL INCA_IP_Ports_P1_ALTSEL
83#define INCA_IP_AUTO_MDIX_LAN_PORTS_OUT INCA_IP_Ports_P1_OUT
84#define INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX 16
wdenk4dd56e52004-02-20 22:02:48 +000085
wdenk0a12b752004-03-11 22:46:36 +000086#define WAIT_SIGNAL_RETRIES 100
87#define WAIT_LINK_RETRIES 100
88#define LINK_RETRY_DELAY 2000 /* ms */
wdenk4dd56e52004-02-20 22:02:48 +000089/********************************************************************/
wdenk1adff3d2003-03-26 11:42:53 +000090
91typedef struct
92{
wdenk5d841732003-08-17 18:55:18 +000093 union {
94 struct {
wdenk0a12b752004-03-11 22:46:36 +000095 volatile u32 HOLD :1;
96 volatile u32 ICpt :1;
97 volatile u32 IEop :1;
98 volatile u32 offset :3;
99 volatile u32 reserved0 :4;
100 volatile u32 NFB :22;
wdenk1adff3d2003-03-26 11:42:53 +0000101 }field;
102
103 volatile u32 word;
104 }params;
105
106 volatile u32 nextRxDescPtr;
107
108 volatile u32 RxDataPtr;
109
wdenk5d841732003-08-17 18:55:18 +0000110 union {
111 struct {
wdenk0a12b752004-03-11 22:46:36 +0000112 volatile u32 C :1;
113 volatile u32 Sop :1;
114 volatile u32 Eop :1;
115 volatile u32 reserved3 :12;
116 volatile u32 NBT :17;
wdenk1adff3d2003-03-26 11:42:53 +0000117 }field;
118
119 volatile u32 word;
120 }status;
121
122} inca_rx_descriptor_t;
123
124
125typedef struct
126{
wdenk5d841732003-08-17 18:55:18 +0000127 union {
128 struct {
wdenk0a12b752004-03-11 22:46:36 +0000129 volatile u32 HOLD :1;
130 volatile u32 Eop :1;
131 volatile u32 Sop :1;
132 volatile u32 ICpt :1;
133 volatile u32 IEop :1;
134 volatile u32 reserved0 :5;
135 volatile u32 NBA :22;
wdenk1adff3d2003-03-26 11:42:53 +0000136 }field;
137
138 volatile u32 word;
139 }params;
140
141 volatile u32 nextTxDescPtr;
142
143 volatile u32 TxDataPtr;
144
wdenk0a12b752004-03-11 22:46:36 +0000145 volatile u32 C :1;
146 volatile u32 reserved3 :31;
wdenk1adff3d2003-03-26 11:42:53 +0000147
148} inca_tx_descriptor_t;
149
150
151static inca_rx_descriptor_t rx_ring[NUM_RX_DESC] __attribute__ ((aligned(16)));
152static inca_tx_descriptor_t tx_ring[NUM_TX_DESC] __attribute__ ((aligned(16)));
153
154static int tx_new, rx_new, tx_hold, rx_hold;
155static int tx_old_hold = -1;
wdenk0a12b752004-03-11 22:46:36 +0000156static int initialized = 0;
wdenk1adff3d2003-03-26 11:42:53 +0000157
158
159static int inca_switch_init(struct eth_device *dev, bd_t * bis);
wdenk0a12b752004-03-11 22:46:36 +0000160static int inca_switch_send(struct eth_device *dev, volatile void *packet, int length);
wdenk1adff3d2003-03-26 11:42:53 +0000161static int inca_switch_recv(struct eth_device *dev);
162static void inca_switch_halt(struct eth_device *dev);
163static void inca_init_switch_chip(void);
164static void inca_dma_init(void);
wdenk4dd56e52004-02-20 22:02:48 +0000165static int inca_amdix(void);
wdenk1adff3d2003-03-26 11:42:53 +0000166
167
wdenk1adff3d2003-03-26 11:42:53 +0000168int inca_switch_initialize(bd_t * bis)
169{
170 struct eth_device *dev;
171
172#if 0
173 printf("Entered inca_switch_initialize()\n");
174#endif
175
wdenk5d841732003-08-17 18:55:18 +0000176 if (!(dev = (struct eth_device *) malloc (sizeof *dev))) {
wdenk1adff3d2003-03-26 11:42:53 +0000177 printf("Failed to allocate memory\n");
178 return 0;
179 }
180 memset(dev, 0, sizeof(*dev));
181
182 inca_dma_init();
183
184 inca_init_switch_chip();
wdenk56ed43e2004-02-22 23:46:08 +0000185
wdenkdb82c8e2004-02-26 23:01:04 +0000186#if defined(CONFIG_INCA_IP_SWITCH_AMDIX)
wdenk4dd56e52004-02-20 22:02:48 +0000187 inca_amdix();
wdenkdb82c8e2004-02-26 23:01:04 +0000188#endif
wdenk1adff3d2003-03-26 11:42:53 +0000189
190 sprintf(dev->name, "INCA-IP Switch");
191 dev->init = inca_switch_init;
192 dev->halt = inca_switch_halt;
193 dev->send = inca_switch_send;
194 dev->recv = inca_switch_recv;
195
196 eth_register(dev);
197
198#if 0
199 printf("Leaving inca_switch_initialize()\n");
200#endif
201
202 return 1;
203}
204
205
206static int inca_switch_init(struct eth_device *dev, bd_t * bis)
207{
208 int i;
209 u32 v, regValue;
210 u16 wTmp;
211
212#if 0
213 printf("Entering inca_switch_init()\n");
214#endif
215
wdenk5d841732003-08-17 18:55:18 +0000216 /* Set MAC address.
217 */
wdenk1adff3d2003-03-26 11:42:53 +0000218 wTmp = (u16)dev->enetaddr[0];
219 regValue = (wTmp << 8) | dev->enetaddr[1];
220
221 SW_WRITE_REG(INCA_IP_Switch_PMAC_SA1, regValue);
222
223 wTmp = (u16)dev->enetaddr[2];
224 regValue = (wTmp << 8) | dev->enetaddr[3];
225 regValue = regValue << 16;
226 wTmp = (u16)dev->enetaddr[4];
227 regValue |= (wTmp<<8) | dev->enetaddr[5];
228
229 SW_WRITE_REG(INCA_IP_Switch_PMAC_SA2, regValue);
230
wdenk5d841732003-08-17 18:55:18 +0000231 /* Initialize the descriptor rings.
232 */
wdenk0e2874cb2004-03-02 14:05:39 +0000233 for (i = 0; i < NUM_RX_DESC; i++) {
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900234 inca_rx_descriptor_t * rx_desc = (inca_rx_descriptor_t *)CKSEG1ADDR(&rx_ring[i]);
wdenk1adff3d2003-03-26 11:42:53 +0000235 memset(rx_desc, 0, sizeof(rx_ring[i]));
236
wdenk5d841732003-08-17 18:55:18 +0000237 /* Set maximum size of receive buffer.
238 */
wdenk1adff3d2003-03-26 11:42:53 +0000239 rx_desc->params.field.NFB = PKTSIZE_ALIGN;
240
wdenk5d841732003-08-17 18:55:18 +0000241 /* Set the offset of the receive buffer. Zero means
242 * that the offset mechanism is not used.
243 */
wdenk1adff3d2003-03-26 11:42:53 +0000244 rx_desc->params.field.offset = 0;
245
246 /* Check if it is the last descriptor.
247 */
wdenk5d841732003-08-17 18:55:18 +0000248 if (i == (NUM_RX_DESC - 1)) {
249 /* Let the last descriptor point to the first
250 * one.
251 */
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900252 rx_desc->nextRxDescPtr = (u32)CKSEG1ADDR(rx_ring);
wdenk5d841732003-08-17 18:55:18 +0000253 } else {
254 /* Set the address of the next descriptor.
255 */
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900256 rx_desc->nextRxDescPtr = (u32)CKSEG1ADDR(&rx_ring[i+1]);
wdenk1adff3d2003-03-26 11:42:53 +0000257 }
258
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900259 rx_desc->RxDataPtr = (u32)CKSEG1ADDR(NetRxPackets[i]);
wdenk1adff3d2003-03-26 11:42:53 +0000260 }
261
262#if 0
263 printf("rx_ring = 0x%08X 0x%08X\n", (u32)rx_ring, (u32)&rx_ring[0]);
264 printf("tx_ring = 0x%08X 0x%08X\n", (u32)tx_ring, (u32)&tx_ring[0]);
265#endif
266
wdenk5d841732003-08-17 18:55:18 +0000267 for (i = 0; i < NUM_TX_DESC; i++) {
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900268 inca_tx_descriptor_t * tx_desc = (inca_tx_descriptor_t *)CKSEG1ADDR(&tx_ring[i]);
wdenk1adff3d2003-03-26 11:42:53 +0000269
270 memset(tx_desc, 0, sizeof(tx_ring[i]));
271
wdenk0a12b752004-03-11 22:46:36 +0000272 tx_desc->params.word = 0;
wdenk1adff3d2003-03-26 11:42:53 +0000273 tx_desc->params.field.HOLD = 1;
wdenk0a12b752004-03-11 22:46:36 +0000274 tx_desc->C = 1;
wdenk1adff3d2003-03-26 11:42:53 +0000275
276 /* Check if it is the last descriptor.
277 */
wdenk5d841732003-08-17 18:55:18 +0000278 if (i == (NUM_TX_DESC - 1)) {
wdenk1adff3d2003-03-26 11:42:53 +0000279 /* Let the last descriptor point to the
280 * first one.
281 */
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900282 tx_desc->nextTxDescPtr = (u32)CKSEG1ADDR(tx_ring);
wdenk5d841732003-08-17 18:55:18 +0000283 } else {
wdenk1adff3d2003-03-26 11:42:53 +0000284 /* Set the address of the next descriptor.
285 */
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900286 tx_desc->nextTxDescPtr = (u32)CKSEG1ADDR(&tx_ring[i+1]);
wdenk1adff3d2003-03-26 11:42:53 +0000287 }
288 }
289
wdenk5d841732003-08-17 18:55:18 +0000290 /* Initialize RxDMA.
291 */
wdenk1adff3d2003-03-26 11:42:53 +0000292 DMA_READ_REG(INCA_IP_DMA_DMA_RXISR, v);
293#if 0
294 printf("RX status = 0x%08X\n", v);
295#endif
296
wdenk5d841732003-08-17 18:55:18 +0000297 /* Writing to the FRDA of CHANNEL.
298 */
wdenk1adff3d2003-03-26 11:42:53 +0000299 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXFRDA0, (u32)rx_ring);
300
wdenk5d841732003-08-17 18:55:18 +0000301 /* Writing to the COMMAND REG.
302 */
wdenk0a12b752004-03-11 22:46:36 +0000303 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_INIT);
wdenk1adff3d2003-03-26 11:42:53 +0000304
wdenk5d841732003-08-17 18:55:18 +0000305 /* Initialize TxDMA.
306 */
wdenk1adff3d2003-03-26 11:42:53 +0000307 DMA_READ_REG(INCA_IP_DMA_DMA_TXISR, v);
308#if 0
309 printf("TX status = 0x%08X\n", v);
310#endif
311
wdenk5d841732003-08-17 18:55:18 +0000312 /* Writing to the FRDA of CHANNEL.
313 */
wdenk1adff3d2003-03-26 11:42:53 +0000314 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXFRDA0, (u32)tx_ring);
315
316 tx_new = rx_new = 0;
317
318 tx_hold = NUM_TX_DESC - 1;
319 rx_hold = NUM_RX_DESC - 1;
320
321#if 0
322 rx_ring[rx_hold].params.field.HOLD = 1;
323#endif
wdenk5d841732003-08-17 18:55:18 +0000324 /* enable spanning tree forwarding, enable the CPU port */
325 /* ST_PT:
wdenk0a12b752004-03-11 22:46:36 +0000326 * CPS (CPU port status) 0x3 (forwarding)
327 * LPS (LAN port status) 0x3 (forwarding)
328 * PPS (PC port status) 0x3 (forwarding)
wdenk5d841732003-08-17 18:55:18 +0000329 */
wdenk1adff3d2003-03-26 11:42:53 +0000330 SW_WRITE_REG(INCA_IP_Switch_ST_PT,0x3f);
331
332#if 0
333 printf("Leaving inca_switch_init()\n");
334#endif
335
336 return 0;
337}
338
339
wdenk0e2874cb2004-03-02 14:05:39 +0000340static int inca_switch_send(struct eth_device *dev, volatile void *packet, int length)
wdenk1adff3d2003-03-26 11:42:53 +0000341{
wdenk0a12b752004-03-11 22:46:36 +0000342 int i;
343 int res = -1;
344 u32 command;
345 u32 regValue;
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900346 inca_tx_descriptor_t * tx_desc = (inca_tx_descriptor_t *)CKSEG1ADDR(&tx_ring[tx_new]);
wdenk1adff3d2003-03-26 11:42:53 +0000347
348#if 0
349 printf("Entered inca_switch_send()\n");
350#endif
351
wdenk5d841732003-08-17 18:55:18 +0000352 if (length <= 0) {
wdenk1adff3d2003-03-26 11:42:53 +0000353 printf ("%s: bad packet size: %d\n", dev->name, length);
354 goto Done;
355 }
wdenk57b2d802003-06-27 21:31:46 +0000356
wdenk5d841732003-08-17 18:55:18 +0000357 for(i = 0; tx_desc->C == 0; i++) {
358 if (i >= TOUT_LOOP) {
wdenk1adff3d2003-03-26 11:42:53 +0000359 printf("%s: tx error buffer not ready\n", dev->name);
360 goto Done;
361 }
362 }
363
wdenk5d841732003-08-17 18:55:18 +0000364 if (tx_old_hold >= 0) {
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900365 ((inca_tx_descriptor_t *)CKSEG1ADDR(&tx_ring[tx_old_hold]))->params.field.HOLD = 1;
wdenk1adff3d2003-03-26 11:42:53 +0000366 }
367 tx_old_hold = tx_hold;
368
369 tx_desc->params.word =
wdenk57b2d802003-06-27 21:31:46 +0000370 (INCA_DMA_TX_SOP | INCA_DMA_TX_EOP | INCA_DMA_TX_HOLD);
wdenk1adff3d2003-03-26 11:42:53 +0000371
372 tx_desc->C = 0;
373 tx_desc->TxDataPtr = (u32)packet;
374 tx_desc->params.field.NBA = length;
375
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900376 ((inca_tx_descriptor_t *)CKSEG1ADDR(&tx_ring[tx_hold]))->params.field.HOLD = 0;
wdenk1adff3d2003-03-26 11:42:53 +0000377
378 tx_hold = tx_new;
wdenk0a12b752004-03-11 22:46:36 +0000379 tx_new = (tx_new + 1) % NUM_TX_DESC;
wdenk1adff3d2003-03-26 11:42:53 +0000380
381
wdenk5d841732003-08-17 18:55:18 +0000382 if (! initialized) {
wdenk1adff3d2003-03-26 11:42:53 +0000383 command = INCA_IP_DMA_DMA_TXCCR0_INIT;
384 initialized = 1;
wdenk5d841732003-08-17 18:55:18 +0000385 } else {
wdenk1adff3d2003-03-26 11:42:53 +0000386 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
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900397 for(i = 0; ((inca_tx_descriptor_t *)CKSEG1ADDR(&tx_ring[tx_hold]))->C == 0; i++) {
wdenk5d841732003-08-17 18:55:18 +0000398 if (i >= TOUT_LOOP) {
wdenk1adff3d2003-03-26 11:42:53 +0000399 printf("%s: tx buffer not ready\n", dev->name);
400 goto Done;
401 }
402 }
403#endif
404 res = length;
405Done:
406#if 0
407 printf("Leaving inca_switch_send()\n");
408#endif
409 return res;
410}
411
412
413static int inca_switch_recv(struct eth_device *dev)
414{
wdenk0a12b752004-03-11 22:46:36 +0000415 int length = 0;
wdenk1adff3d2003-03-26 11:42:53 +0000416 inca_rx_descriptor_t * rx_desc;
417
418#if 0
419 printf("Entered inca_switch_recv()\n");
420#endif
421
wdenk5d841732003-08-17 18:55:18 +0000422 for (;;) {
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900423 rx_desc = (inca_rx_descriptor_t *)CKSEG1ADDR(&rx_ring[rx_new]);
wdenk1adff3d2003-03-26 11:42:53 +0000424
wdenk5d841732003-08-17 18:55:18 +0000425 if (rx_desc->status.field.C == 0) {
wdenk1adff3d2003-03-26 11:42:53 +0000426 break;
427 }
428
429#if 0
430 rx_ring[rx_new].params.field.HOLD = 1;
431#endif
432
wdenk5d841732003-08-17 18:55:18 +0000433 if (! rx_desc->status.field.Eop) {
wdenk1adff3d2003-03-26 11:42:53 +0000434 printf("Partly received packet!!!\n");
435 break;
436 }
437
438 length = rx_desc->status.field.NBT;
439 rx_desc->status.word &=
wdenk57b2d802003-06-27 21:31:46 +0000440 ~(INCA_DMA_RX_EOP | INCA_DMA_RX_SOP | INCA_DMA_RX_C);
wdenk1adff3d2003-03-26 11:42:53 +0000441#if 0
442{
443 int i;
444 for (i=0;i<length - 4;i++) {
445 if (i % 16 == 0) printf("\n%04x: ", i);
446 printf("%02X ", NetRxPackets[rx_new][i]);
447 }
448 printf("\n");
449}
450#endif
451
wdenk5d841732003-08-17 18:55:18 +0000452 if (length) {
wdenk1adff3d2003-03-26 11:42:53 +0000453#if 0
454 printf("Received %d bytes\n", length);
455#endif
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900456 NetReceive((void*)CKSEG1ADDR(NetRxPackets[rx_new]), length - 4);
wdenk5d841732003-08-17 18:55:18 +0000457 } else {
wdenk1adff3d2003-03-26 11:42:53 +0000458#if 1
459 printf("Zero length!!!\n");
460#endif
461 }
462
463
Shinya Kuribayashi6c6b2612008-06-05 22:29:00 +0900464 ((inca_rx_descriptor_t *)CKSEG1ADDR(&rx_ring[rx_hold]))->params.field.HOLD = 0;
wdenk1adff3d2003-03-26 11:42:53 +0000465
466 rx_hold = rx_new;
467
468 rx_new = (rx_new + 1) % NUM_RX_DESC;
469 }
470
471#if 0
472 printf("Leaving inca_switch_recv()\n");
473#endif
474
475 return length;
476}
477
478
479static void inca_switch_halt(struct eth_device *dev)
480{
481#if 0
482 printf("Entered inca_switch_halt()\n");
483#endif
484
485#if 1
486 initialized = 0;
487#endif
488#if 1
wdenk5d841732003-08-17 18:55:18 +0000489 /* Disable forwarding to the CPU port.
490 */
wdenk1adff3d2003-03-26 11:42:53 +0000491 SW_WRITE_REG(INCA_IP_Switch_ST_PT,0xf);
492
wdenk5d841732003-08-17 18:55:18 +0000493 /* Close RxDMA channel.
494 */
wdenk1adff3d2003-03-26 11:42:53 +0000495 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF);
496
wdenk5d841732003-08-17 18:55:18 +0000497 /* Close TxDMA channel.
498 */
wdenk1adff3d2003-03-26 11:42:53 +0000499 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, INCA_IP_DMA_DMA_TXCCR0_OFF);
500
501
502#endif
503#if 0
504 printf("Leaving inca_switch_halt()\n");
505#endif
506}
507
508
509static void inca_init_switch_chip(void)
510{
511 u32 regValue;
512
wdenk5d841732003-08-17 18:55:18 +0000513 /* To workaround a problem with collision counter
514 * (see Errata sheet).
515 */
wdenk1adff3d2003-03-26 11:42:53 +0000516 SW_WRITE_REG(INCA_IP_Switch_PC_TX_CTL, 0x00000001);
517 SW_WRITE_REG(INCA_IP_Switch_LAN_TX_CTL, 0x00000001);
518
519#if 1
wdenk5d841732003-08-17 18:55:18 +0000520 /* init MDIO configuration:
wdenk0a12b752004-03-11 22:46:36 +0000521 * MDS (Poll speed): 0x01 (4ms)
522 * PHY_LAN_ADDR: 0x06
523 * PHY_PC_ADDR: 0x05
wdenk5d841732003-08-17 18:55:18 +0000524 * UEP (Use External PHY): 0x00 (Internal PHY is used)
wdenk0a12b752004-03-11 22:46:36 +0000525 * PS (Port Select): 0x00 (PT/UMM for LAN)
526 * PT (PHY Test): 0x00 (no test mode)
527 * UMM (Use MDIO Mode): 0x00 (state machine is disabled)
wdenk5d841732003-08-17 18:55:18 +0000528 */
wdenk1adff3d2003-03-26 11:42:53 +0000529 SW_WRITE_REG(INCA_IP_Switch_MDIO_CFG, 0x4c50);
530
wdenk5d841732003-08-17 18:55:18 +0000531 /* init PHY:
532 * SL (Auto Neg. Speed for LAN)
533 * SP (Auto Neg. Speed for PC)
534 * LL (Link Status for LAN)
535 * LP (Link Status for PC)
536 * DL (Duplex Status for LAN)
537 * DP (Duplex Status for PC)
538 * PL (Auto Neg. Pause Status for LAN)
539 * PP (Auto Neg. Pause Status for PC)
540 */
wdenk1adff3d2003-03-26 11:42:53 +0000541 SW_WRITE_REG (INCA_IP_Switch_EPHY, 0xff);
542
wdenk5d841732003-08-17 18:55:18 +0000543 /* MDIO_ACC:
544 * RA (Request/Ack) 0x01 (Request)
wdenk0a12b752004-03-11 22:46:36 +0000545 * RW (Read/Write) 0x01 (Write)
546 * PHY_ADDR 0x05 (PC)
547 * REG_ADDR 0x00 (PHY_BCR: basic control register)
548 * PHY_DATA 0x8000
549 * Reset - software reset
550 * LB (loop back) - normal
551 * SS (speed select) - 10 Mbit/s
wdenk5d841732003-08-17 18:55:18 +0000552 * ANE (auto neg. enable) - enable
wdenk0a12b752004-03-11 22:46:36 +0000553 * PD (power down) - normal
554 * ISO (isolate) - normal
wdenk5d841732003-08-17 18:55:18 +0000555 * RAN (restart auto neg.) - normal
wdenk0a12b752004-03-11 22:46:36 +0000556 * DM (duplex mode) - half duplex
wdenk5d841732003-08-17 18:55:18 +0000557 * CT (collision test) - enable
558 */
559 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0a09000);
560
561 /* MDIO_ACC:
562 * RA (Request/Ack) 0x01 (Request)
wdenk0a12b752004-03-11 22:46:36 +0000563 * RW (Read/Write) 0x01 (Write)
564 * PHY_ADDR 0x06 (LAN)
565 * REG_ADDR 0x00 (PHY_BCR: basic control register)
566 * PHY_DATA 0x8000
567 * Reset - software reset
568 * LB (loop back) - normal
569 * SS (speed select) - 10 Mbit/s
wdenk5d841732003-08-17 18:55:18 +0000570 * ANE (auto neg. enable) - enable
wdenk0a12b752004-03-11 22:46:36 +0000571 * PD (power down) - normal
572 * ISO (isolate) - normal
wdenk5d841732003-08-17 18:55:18 +0000573 * RAN (restart auto neg.) - normal
wdenk0a12b752004-03-11 22:46:36 +0000574 * DM (duplex mode) - half duplex
wdenk5d841732003-08-17 18:55:18 +0000575 * CT (collision test) - enable
576 */
wdenk0a12b752004-03-11 22:46:36 +0000577 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0c09000);
wdenk1adff3d2003-03-26 11:42:53 +0000578
wdenk1adff3d2003-03-26 11:42:53 +0000579#endif
580
wdenk5d841732003-08-17 18:55:18 +0000581 /* Make sure the CPU port is disabled for now. We
582 * don't want packets to get stacked for us until
583 * we enable DMA and are prepared to receive them.
584 */
wdenk1adff3d2003-03-26 11:42:53 +0000585 SW_WRITE_REG(INCA_IP_Switch_ST_PT,0xf);
586
587 SW_READ_REG(INCA_IP_Switch_ARL_CTL, regValue);
588
wdenk5d841732003-08-17 18:55:18 +0000589 /* CRC GEN is enabled.
590 */
wdenk1adff3d2003-03-26 11:42:53 +0000591 regValue |= 0x00000200;
592 SW_WRITE_REG(INCA_IP_Switch_ARL_CTL, regValue);
593
wdenk5d841732003-08-17 18:55:18 +0000594 /* ADD TAG is disabled.
595 */
wdenk1adff3d2003-03-26 11:42:53 +0000596 SW_READ_REG(INCA_IP_Switch_PMAC_HD_CTL, regValue);
597 regValue &= ~0x00000002;
598 SW_WRITE_REG(INCA_IP_Switch_PMAC_HD_CTL, regValue);
599}
600
601
602static void inca_dma_init(void)
603{
wdenk5d841732003-08-17 18:55:18 +0000604 /* Switch off all DMA channels.
605 */
wdenk1adff3d2003-03-26 11:42:53 +0000606 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF);
607 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR1, INCA_IP_DMA_DMA_RXCCR1_OFF);
608
609 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF);
610 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR1, INCA_IP_DMA_DMA_TXCCR1_OFF);
611 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR2, INCA_IP_DMA_DMA_TXCCR2_OFF);
612
wdenk5d841732003-08-17 18:55:18 +0000613 /* Setup TX channel polling time.
614 */
wdenk1adff3d2003-03-26 11:42:53 +0000615 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXPOLL, INCA_DMA_TX_POLLING_TIME);
616
wdenk5d841732003-08-17 18:55:18 +0000617 /* Setup RX channel polling time.
618 */
wdenk1adff3d2003-03-26 11:42:53 +0000619 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXPOLL, INCA_DMA_RX_POLLING_TIME);
620
wdenk5d841732003-08-17 18:55:18 +0000621 /* ERRATA: write reset value into the DMA RX IMR register.
622 */
wdenk1adff3d2003-03-26 11:42:53 +0000623 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXIMR, 0xFFFFFFFF);
624
wdenk5d841732003-08-17 18:55:18 +0000625 /* Just in case: disable all transmit interrupts also.
626 */
wdenk1adff3d2003-03-26 11:42:53 +0000627 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXIMR, 0xFFFFFFFF);
628
629 DMA_WRITE_REG(INCA_IP_DMA_DMA_TXISR, 0xFFFFFFFF);
630 DMA_WRITE_REG(INCA_IP_DMA_DMA_RXISR, 0xFFFFFFFF);
631}
632
wdenkdb82c8e2004-02-26 23:01:04 +0000633#if defined(CONFIG_INCA_IP_SWITCH_AMDIX)
wdenk4dd56e52004-02-20 22:02:48 +0000634static int inca_amdix(void)
635{
wdenk0e2874cb2004-03-02 14:05:39 +0000636 u32 phyReg1 = 0;
637 u32 phyReg4 = 0;
638 u32 phyReg5 = 0;
639 u32 phyReg6 = 0;
640 u32 phyReg31 = 0;
641 u32 regEphy = 0;
wdenk4dd56e52004-02-20 22:02:48 +0000642 int mdi_flag;
643 int retries;
644
645 /* Setup GPIO pins.
646 */
647 *INCA_IP_AUTO_MDIX_LAN_PORTS_DIR |= (1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX);
648 *INCA_IP_AUTO_MDIX_LAN_PORTS_ALTSEL |= (1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX);
649
wdenk0e2874cb2004-03-02 14:05:39 +0000650#if 0
wdenk4dd56e52004-02-20 22:02:48 +0000651 /* Wait for signal.
652 */
653 retries = WAIT_SIGNAL_RETRIES;
wdenk0e2874cb2004-03-02 14:05:39 +0000654 while (--retries) {
wdenk4dd56e52004-02-20 22:02:48 +0000655 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC,
656 (0x1 << 31) | /* RA */
657 (0x0 << 30) | /* Read */
658 (0x6 << 21) | /* LAN */
659 (17 << 16)); /* PHY_MCSR */
wdenk0e2874cb2004-03-02 14:05:39 +0000660 do {
661 SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg1);
662 } while (phyReg1 & (1 << 31));
wdenk4dd56e52004-02-20 22:02:48 +0000663
wdenk0e2874cb2004-03-02 14:05:39 +0000664 if (phyReg1 & (1 << 1)) {
wdenk4dd56e52004-02-20 22:02:48 +0000665 /* Signal detected */
666 break;
667 }
668 }
669
670 if (!retries)
wdenk0e2874cb2004-03-02 14:05:39 +0000671 goto Fail;
672#endif
wdenk4dd56e52004-02-20 22:02:48 +0000673
674 /* Set MDI mode.
675 */
676 *INCA_IP_AUTO_MDIX_LAN_PORTS_OUT &= ~(1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX);
677 mdi_flag = 1;
678
679 /* Wait for link.
680 */
681 retries = WAIT_LINK_RETRIES;
wdenk0e2874cb2004-03-02 14:05:39 +0000682 while (--retries) {
wdenk4dd56e52004-02-20 22:02:48 +0000683 udelay(LINK_RETRY_DELAY * 1000);
684 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC,
685 (0x1 << 31) | /* RA */
686 (0x0 << 30) | /* Read */
687 (0x6 << 21) | /* LAN */
688 (1 << 16)); /* PHY_BSR */
wdenk0e2874cb2004-03-02 14:05:39 +0000689 do {
690 SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg1);
691 } while (phyReg1 & (1 << 31));
wdenk4dd56e52004-02-20 22:02:48 +0000692
wdenk0e2874cb2004-03-02 14:05:39 +0000693 if (phyReg1 & (1 << 2)) {
wdenk4dd56e52004-02-20 22:02:48 +0000694 /* Link is up */
695 break;
wdenk0e2874cb2004-03-02 14:05:39 +0000696 } else if (mdi_flag) {
wdenk4dd56e52004-02-20 22:02:48 +0000697 /* Set MDIX mode */
698 *INCA_IP_AUTO_MDIX_LAN_PORTS_OUT |= (1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX);
699 mdi_flag = 0;
wdenk0e2874cb2004-03-02 14:05:39 +0000700 } else {
wdenk4dd56e52004-02-20 22:02:48 +0000701 /* Set MDI mode */
702 *INCA_IP_AUTO_MDIX_LAN_PORTS_OUT &= ~(1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX);
703 mdi_flag = 1;
704 }
705 }
706
wdenk0e2874cb2004-03-02 14:05:39 +0000707 if (!retries) {
708 goto Fail;
709 } else {
710 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC,
711 (0x1 << 31) | /* RA */
712 (0x0 << 30) | /* Read */
713 (0x6 << 21) | /* LAN */
714 (1 << 16)); /* PHY_BSR */
715 do {
716 SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg1);
717 } while (phyReg1 & (1 << 31));
718
719 /* Auto-negotiation / Parallel detection complete
720 */
721 if (phyReg1 & (1 << 5)) {
722 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC,
723 (0x1 << 31) | /* RA */
724 (0x0 << 30) | /* Read */
725 (0x6 << 21) | /* LAN */
726 (31 << 16)); /* PHY_SCSR */
727 do {
wdenk0a12b752004-03-11 22:46:36 +0000728 SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg31);
wdenk0e2874cb2004-03-02 14:05:39 +0000729 } while (phyReg31 & (1 << 31));
730
731 switch ((phyReg31 >> 2) & 0x7) {
732 case INCA_SWITCH_PHY_SPEED_10H:
733 /* 10Base-T Half-duplex */
734 regEphy = 0;
735 break;
736 case INCA_SWITCH_PHY_SPEED_10F:
737 /* 10Base-T Full-duplex */
738 regEphy = INCA_IP_Switch_EPHY_DL;
739 break;
740 case INCA_SWITCH_PHY_SPEED_100H:
741 /* 100Base-TX Half-duplex */
742 regEphy = INCA_IP_Switch_EPHY_SL;
743 break;
744 case INCA_SWITCH_PHY_SPEED_100F:
745 /* 100Base-TX Full-duplex */
746 regEphy = INCA_IP_Switch_EPHY_SL | INCA_IP_Switch_EPHY_DL;
747 break;
748 }
749
750 /* In case of Auto-negotiation,
751 * update the negotiated PAUSE support status
752 */
753 if (phyReg1 & (1 << 3)) {
754 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC,
755 (0x1 << 31) | /* RA */
756 (0x0 << 30) | /* Read */
757 (0x6 << 21) | /* LAN */
758 (6 << 16)); /* PHY_ANER */
759 do {
wdenk0a12b752004-03-11 22:46:36 +0000760 SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg6);
wdenk0e2874cb2004-03-02 14:05:39 +0000761 } while (phyReg6 & (1 << 31));
762
763 /* We are Autoneg-able.
764 * Is Link partner also able to autoneg?
765 */
766 if (phyReg6 & (1 << 0)) {
767 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC,
768 (0x1 << 31) | /* RA */
769 (0x0 << 30) | /* Read */
770 (0x6 << 21) | /* LAN */
771 (4 << 16)); /* PHY_ANAR */
772 do {
wdenk0a12b752004-03-11 22:46:36 +0000773 SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg4);
wdenk0e2874cb2004-03-02 14:05:39 +0000774 } while (phyReg4 & (1 << 31));
775
776 /* We advertise PAUSE capab.
777 * Does link partner also advertise it?
778 */
779 if (phyReg4 & (1 << 10)) {
780 SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC,
781 (0x1 << 31) | /* RA */
782 (0x0 << 30) | /* Read */
783 (0x6 << 21) | /* LAN */
784 (5 << 16)); /* PHY_ANLPAR */
785 do {
wdenk0a12b752004-03-11 22:46:36 +0000786 SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg5);
wdenk0e2874cb2004-03-02 14:05:39 +0000787 } while (phyReg5 & (1 << 31));
788
789 /* Link partner is PAUSE capab.
790 */
791 if (phyReg5 & (1 << 10)) {
792 regEphy |= INCA_IP_Switch_EPHY_PL;
793 }
794 }
795 }
796
797 }
798
799 /* Link is up */
800 regEphy |= INCA_IP_Switch_EPHY_LL;
801
802 SW_WRITE_REG(INCA_IP_Switch_EPHY, regEphy);
803 }
804 }
wdenk4dd56e52004-02-20 22:02:48 +0000805
806 return 0;
wdenk0e2874cb2004-03-02 14:05:39 +0000807
808Fail:
809 printf("No Link on LAN port\n");
810 return -1;
wdenk4dd56e52004-02-20 22:02:48 +0000811}
wdenkdb82c8e2004-02-26 23:01:04 +0000812#endif /* CONFIG_INCA_IP_SWITCH_AMDIX */