1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
|
/*
* cs8900.c
*
* Cirrus Logic Crystal CS89X0 Ethernet driver for barebox
* Copyright (C) 2009 Ivo Clarysse <ivo.clarysse@gmail.com>
*
* Based on cs8900.c from barebox mainline,
* (C) 2003 Wolfgang Denk, wd@denx.de
* (C) Copyright 2002 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
* Copyright (C) 1999 Ben Williamson <benw@pobox.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <common.h>
#include <driver.h>
#include <init.h>
#include <net.h>
#include <malloc.h>
#include <clock.h>
#include <asm/io.h>
#include <errno.h>
/* I/O ports for I/O Space operation */
#define CS8900_RTDATA0 0x0000 /* Receive/Transmit Data (Port 0) */
#define CS8900_RTDATA1 0x0002 /* Receive/Transmit Data (Port 1) */
#define CS8900_TXCMD 0x0004 /* Transmit Command */
#define CS8900_TXLEN 0x0006 /* Transmit Length */
#define CS8900_ISQ 0x0008 /* Interrupt Status Queue */
#define CS8900_PP_PTR 0x000a /* PacketPage Pointer */
#define CS8900_PP_DATA0 0x000c /* PacketPage Data (Port 0) */
#define CS8900_PP_DATA1 0x000e /* PacketPage Data (Port 1) */
/* PacketPage registers */
/* Bus Interface registers */
#define PP_BI_VID 0x0000 /* Vendor (EISA registration #) */
#define PP_BI_PID 0x0002 /* Product ID */
#define PP_BI_IOBASE 0x0020 /* I/O Base Address */
#define PP_BI_IRQ 0x0022 /* Interrupt Number */
#define PP_BI_DMACH 0x0024 /* DMA Channel Number */
#define PP_BI_DMASOF 0x0026 /* DMA Start of Frame */
#define PP_BI_DMAFC 0x0028 /* DMA Frame Count (12bit) */
#define PP_BI_RXDMAC 0x002A /* RxDMA Byte Count */
#define PP_BI_MBAR 0x002C /* Memory Base Address Register (20bit) */
#define PP_BI_BPBA 0x0030 /* Boot PROM Base Address (32bit) */
#define PP_BI_BPAM 0x0034 /* Boot PROM Address Mark (32bit) */
#define PP_BI_EE_CMD 0x0040 /* EEPROM Command */
#define PP_BI_EE_DAT 0x0042 /* EEPROM Data */
#define PP_BI_RFBC 0x0050 /* Received Frame Byte Counter */
/* Status and Control registers */
#define PP_REG_ISQ 0x0120 /* Register 0: ISQ */
#define PP_REG_01 0x0100 /* Register 1: -reserved- */
#define PP_REG_02 0x0122 /* Register 2: -reserved- */
#define PP_REG_RXCFG 0x0102 /* Register 3: RxCFG */
#define PP_REG_RXEVENT 0x0124 /* Register 4: RxEvent/RxEventalt */
#define PP_REG_RXCTL 0x0104 /* Register 5: RxCTL */
#define PP_REG_06 0x0126 /* Register 6: -reserved- */
#define PP_REG_TXCFG 0x0106 /* Register 7: TxCFG */
#define PP_REG_TXEVENT 0x0128 /* Register 8: TxEvent */
#define PP_REG_TXCMD 0x0108 /* Register 9: TxCmd (Transmit Command Status) */
#define PP_REG_0A 0x012A /* Register A: -reserved- */
#define PP_REG_BUFCFG 0x010A /* Register B: BufCFG */
#define PP_REG_BUFEVENT 0x012C /* Register C: BufEvent */
#define PP_REG_0C 0x010C /* Register D: -reserved- */
#define PP_REG_0E 0x012E /* Register E: -reserved- */
#define PP_REG_0F 0x010E /* Register F: -reserved- */
#define PP_REG_RXMISS 0x0130 /* Register 10: RxMISS */
#define PP_REG_11 0x0110 /* Register 11: -reserved- */
#define PP_REG_TXCOL 0x0132 /* Register 12: TxCOL */
#define PP_REG_LINECTL 0x0112 /* Register 13: LineCTL */
#define PP_REG_LINEST 0x0134 /* Register 14: LineST */
#define PP_REG_SELFCTL 0x0114 /* Register 15: SelfCTL */
#define PP_REG_SELFST 0x0136 /* Register 16: SelfST */
#define PP_REG_BUSCTL 0x0116 /* Register 17: BusCTL */
#define PP_REG_BUSST 0x0138 /* Register 18: BusST */
#define PP_REG_TESTCTL 0x0118 /* Register 19: TestCTL */
#define PP_REG_1A 0x013A /* Register 1A: -reserved- */
#define PP_REG_1B 0x011A /* Register 1B: -reserved- */
#define PP_REG_TDR 0x013C /* Register 1C: TDR */
#define PP_REG_1D 0x011C /* Register 1D: -reserved- */
#define PP_REG_1E 0x013E /* Register 1E: -reserved- */
#define PP_REG_1F 0x011E /* Register 1F: -reserved- */
/* Initiate Transmit registers */
#define PP_IT_TXCMD 0x0144 /* Transmit Command */
#define PP_IT_TXLEN 0x0146 /* Transmit Length */
/* Address Filter registers */
#define PP_AF_LAF 0x0150 /* Logical Address Filter */
#define PP_AF_IA 0x0158 /* Individual address (IEEE address) */
/* Frame Location */
#define PP_FL_RXSTATUS 0x0400 /* RXStatus */
#define PP_FL_RXLENGTH 0x0402 /* RxLength */
#define PP_FL_RXLOC 0x0404 /* Receive Frame Location */
#define PP_FL_TXLOC 0x0A00 /* Transmit Frame Location */
/* Register bit masks */
#define RXEVENT_RXOK 0x0100
#define RXCTL_RXOKA 0x0100
#define RXCTL_MULTICASTA 0x0200
#define RXCTL_INDIVIDUALA 0x0400
#define RXCTL_BROADCASTA 0x0800
#define TXCMD_TXSTART_5 0x0000 /* Start Tx after 5 bytes */
#define TXCMD_TXSTART_381 0x0040 /* Start Tx after 381 bytes */
#define TXCMD_TXSTART_1021 0x0080 /* Start Tx after 1021 bytes */
#define TXCMD_TXSTART_FULL 0x00C0 /* Start Tx after all bytes loaded */
#define LINECTL_SERRXON 0x0040
#define LINECTL_SERTXON 0x0080
#define LINEST_LINKOK 0x0080
#define LINEST_AUI 0x0100
#define LINEST_10BT 0x0200
#define LINEST_POLARITYOK 0x1000
#define LINEST_CRS 0x4000
#define SELFCTL_RESET 0x0040
#define SELFST_33VACTIVE 0x0040
#define SELFST_INITD 0x0080
#define SELFST_SIBUSY 0x0100
#define SELFST_EEPROMP 0x0200
#define SELFST_EEPROMOK 0x0400
#define SELFST_ELPRESENT 0x0800
#define SELFST_EESIZE 0x1000
#define BUSST_TXBIDERR 0x0080
#define BUSST_RDY4TXNOW 0x0100
#define CRYSTAL_SEMI_EISA_ID 0x630E
/* Origin: CS8900A datasheet */
#define CS8X0_PID_CS8900A_B 0x0700
#define CS8X0_PID_CS8900A_C 0x0800
#define CS8X0_PID_CS8900A_D 0x0900
#define CS8X0_PID_CS8900A_F 0x0A00
/* Origin: CS8920A datasheet */
#define CS8X0_PID_CS8920_B 0x6100
#define CS8X0_PID_CS8920_C 0x6200
#define CS8X0_PID_CS8920_D 0x6300
#define CS8X0_PID_CS8920A_AB 0x6400
#define CS8X0_PID_CS8920A_C 0x6500
typedef enum {
CS8900,
CS8900A,
CS8920,
CS8920A,
} cs89x0_chip_type_t;
struct cs89x0_chip {
cs89x0_chip_type_t chip_type;
const char *name;
};
struct cs89x0_product {
u16 product_id;
cs89x0_chip_type_t chip_type;
const char *rev_name;
};
static struct cs89x0_chip cs89x0_chips[] = {
{CS8900A, "CS8900A"},
{CS8920A, "CS8920A"}, /* EOL: March 30, 2003 */
{CS8900, "CS8900"}, /* EOL: September 31, 1999 */
{CS8920, "CS8920"}, /* EOL: December 9, 1998 */
};
static struct cs89x0_product cs89x0_product_ids[] = {
{CS8X0_PID_CS8900A_F, CS8900A, "F"},
{CS8X0_PID_CS8900A_D, CS8900A, "D"},
{CS8X0_PID_CS8900A_C, CS8900A, "C"},
{CS8X0_PID_CS8900A_B, CS8900A, "B"},
{CS8X0_PID_CS8920A_C, CS8920A, "C"},
{CS8X0_PID_CS8920A_AB, CS8920A, "A/B"},
{CS8X0_PID_CS8920_D, CS8920, "D"},
{CS8X0_PID_CS8920_C, CS8920, "C"},
{CS8X0_PID_CS8920_B, CS8920, "B"},
};
struct cs8900_priv {
void *regs;
struct cs89x0_product *product;
struct cs89x0_chip *chip;
};
/* Read a 16-bit value from PacketPage Memory using I/O Space operation */
static u16 cs8900_ior(struct cs8900_priv *priv, int reg)
{
u16 result;
writew(reg, priv->regs + CS8900_PP_PTR);
result = readw(priv->regs + CS8900_PP_DATA0);
return result;
}
/* Write a 16-bit value to PacketPage Memory using I/O Space operation */
static void cs8900_iow(struct cs8900_priv *priv, int reg, u16 value)
{
writew(reg, priv->regs + CS8900_PP_PTR);
writew(value, priv->regs + CS8900_PP_DATA0);
}
static void cs8900_reginit(struct cs8900_priv *priv)
{
/* receive only error free packets addressed to this card */
cs8900_iow(priv, PP_REG_RXCTL,
RXCTL_BROADCASTA | RXCTL_INDIVIDUALA | RXCTL_RXOKA);
/* do not generate any interrupts on receive operations */
cs8900_iow(priv, PP_REG_RXCFG, 0);
/* do not generate any interrupts on transmit operations */
cs8900_iow(priv, PP_REG_TXCFG, 0);
/* do not generate any interrupts on buffer operations */
cs8900_iow(priv, PP_REG_BUFCFG, 0);
/* enable transmitter/receiver mode */
cs8900_iow(priv, PP_REG_LINECTL,
LINECTL_SERRXON | LINECTL_SERTXON);
}
static void cs8900_reset(struct cs8900_priv *priv)
{
u16 v;
uint64_t tmo;
v = cs8900_ior(priv, PP_REG_SELFCTL);
v |= SELFCTL_RESET;
cs8900_iow(priv, PP_REG_SELFCTL, v);
/* wait 200ms */
udelay(200000);
tmo = get_time_ns();
while ((cs8900_ior(priv, PP_REG_SELFST) & SELFST_INITD) == 0) {
if (is_timeout(tmo, 1 * SECOND)) {
printf("%s: timeout\n", __func__);
break;
}
}
}
static int cs8900_dev_init(struct eth_device *dev)
{
return 0;
}
static int cs8900_open(struct eth_device *dev)
{
struct cs8900_priv *priv = (struct cs8900_priv *)dev->priv;
cs8900_reset(priv);
cs8900_reginit(priv);
return 0;
}
static int cs8900_send(struct eth_device *dev, void *eth_data,
int data_length)
{
struct cs8900_priv *priv = (struct cs8900_priv *)dev->priv;
u16 *addr;
u16 v;
writew(TXCMD_TXSTART_FULL, priv->regs + CS8900_TXCMD);
writew(data_length, priv->regs + CS8900_TXLEN);
v = cs8900_ior(priv, PP_REG_BUSST);
if (v & BUSST_TXBIDERR) {
printf("%s: frame error\n", __func__);
return -1;
}
if ((v & BUSST_RDY4TXNOW) == 0) {
printf("%s: busy\n", __func__);
return -1;
}
for (addr = eth_data; data_length > 0; data_length -= 2) {
writew(*addr++, priv->regs + CS8900_RTDATA0);
}
return 0;
}
static int cs8900_recv(struct eth_device *dev)
{
struct cs8900_priv *priv = (struct cs8900_priv *)dev->priv;
int len = 0;
u16 status;
u16 *addr;
int i;
status = cs8900_ior(priv, PP_REG_RXEVENT);
if ((status & RXEVENT_RXOK) == 0) {
/* No packet received. */
return 0;
}
status = readw(priv->regs + CS8900_RTDATA0);
len = readw(priv->regs + CS8900_RTDATA0);
for (addr = (u16 *) NetRxPackets[0], i = len >> 1; i > 0; i--) {
*addr++ = readw(priv->regs + CS8900_RTDATA0);
}
if (len & 1) {
*addr++ = readw(priv->regs + CS8900_RTDATA0);
}
net_receive(NetRxPackets[0], len);
return len;
}
static void cs8900_halt(struct eth_device *dev)
{
struct cs8900_priv *priv = (struct cs8900_priv *)dev->priv;
cs8900_iow(priv, PP_REG_BUSCTL, 0);
cs8900_iow(priv, PP_REG_TESTCTL, 0);
cs8900_iow(priv, PP_REG_SELFCTL, 0);
cs8900_iow(priv, PP_REG_LINECTL, 0);
cs8900_iow(priv, PP_REG_BUFCFG, 0);
cs8900_iow(priv, PP_REG_TXCFG, 0);
cs8900_iow(priv, PP_REG_RXCTL, 0);
cs8900_iow(priv, PP_REG_RXCFG, 0);
}
static int cs8900_get_ethaddr(struct eth_device *dev, unsigned char *mac)
{
struct cs8900_priv *priv = (struct cs8900_priv *)dev->priv;
int i;
for (i = 0; i < 6 / 2; i++) {
u16 v;
v = cs8900_ior(priv, PP_AF_IA + i * 2);
mac[i * 2] = v & 0xFF;
mac[i * 2 + 1] = v >> 8;
}
return 0;
}
static int cs8900_set_ethaddr(struct eth_device *dev, unsigned char *mac)
{
struct cs8900_priv *priv = (struct cs8900_priv *)dev->priv;
int i;
for (i = 0; i < 6 / 2; i++) {
u16 v;
v = (mac[i * 2 + 1] << 8) | mac[i * 2];
cs8900_iow(priv, PP_AF_IA + i * 2, v);
}
return 0;
}
static const char *yesno_str(int v)
{
return v ? "yes" : "no";
}
static void cs8900_info(struct device_d *dev)
{
struct eth_device *edev = (struct eth_device *)dev->type_data;
struct cs8900_priv *priv = (struct cs8900_priv *)edev->priv;
u16 v;
printf("%s Rev. %s (PID: 0x%04x) at 0x%08x\n", priv->chip->name,
priv->product->rev_name, priv->product->product_id,
priv->regs);
v = cs8900_ior(priv, PP_REG_LINEST);
printf("Register 14: LineST 0x%04x\n", v);
printf(" Link OK: %s\n", yesno_str(v & LINEST_LINKOK));
printf(" Using AUI: %s\n", yesno_str(v & LINEST_AUI));
printf(" Using 10Base-T: %s\n", yesno_str(v & LINEST_10BT));
printf(" Polarity OK: %s\n", yesno_str(v & LINEST_POLARITYOK));
printf(" Incoming frame: %s\n", yesno_str(v & LINEST_CRS));
v = cs8900_ior(priv, PP_REG_SELFST);
printf("Register 16: SelfST 0x%04x\n", v);
printf(" Voltage: %sV\n", (v & SELFST_33VACTIVE) ? "3.3" : "5");
printf(" Initialization complete: %s\n",
yesno_str(v & SELFST_INITD));
printf(" EEPROM busy: %s\n", yesno_str(v & SELFST_SIBUSY));
printf(" EEPROM present: %s\n", yesno_str(v & SELFST_EEPROMP));
printf(" EEPROM checksum OK: %s\n",
yesno_str(v & SELFST_EEPROMOK));
printf(" EL present: %s\n", yesno_str(v & SELFST_ELPRESENT));
printf(" EEPROM size: %s words\n",
(v & SELFST_EESIZE) ? "64" : "128 or 256");
}
static int cs8900_check_id(struct cs8900_priv *priv)
{
int result = -1;
struct cs89x0_product *product = NULL;
struct cs89x0_chip *chip = NULL;
int i;
u16 v;
v = cs8900_ior(priv, PP_BI_VID);
if (v != CRYSTAL_SEMI_EISA_ID) {
printf("No CS89x0 variant found at 0x%08x vid: 0x%04x\n",
priv->regs, v);
return -1;
}
v = cs8900_ior(priv, PP_BI_PID);
for (i = 0; i < ARRAY_SIZE(cs89x0_product_ids); i++) {
if (cs89x0_product_ids[i].product_id == v) {
product = &(cs89x0_product_ids[i]);
break;
}
}
if (product == NULL) {
printf("Unable to identify CS89x0 variant 0x%04x\n", v);
goto out;
}
for (i = 0; i < ARRAY_SIZE(cs89x0_chips); i++) {
if (cs89x0_chips[i].chip_type == product->chip_type) {
chip = &(cs89x0_chips[i]);
break;
}
}
if (chip == NULL) {
printf("Invalid chip type %d\n", product->chip_type);
goto out;
}
printf("%s Rev. %s (PID: 0x%04x) found at 0x%08x\n", chip->name,
product->rev_name, v, priv->regs);
priv->chip = chip;
priv->product = product;
result = 0;
out:
return result;
}
static int cs8900_probe(struct device_d *dev)
{
struct eth_device *edev;
struct cs8900_priv *priv;
debug("cs8900_init()\n");
priv = (struct cs8900_priv *)xmalloc(sizeof(*priv));
priv->regs = (u16 *)dev->map_base;
if (cs8900_check_id(priv)) {
free(priv);
return -1;
}
edev = (struct eth_device *)xmalloc(sizeof(struct eth_device));
dev->type_data = edev;
edev->priv = priv;
edev->init = cs8900_dev_init;
edev->open = cs8900_open;
edev->send = cs8900_send;
edev->recv = cs8900_recv;
edev->halt = cs8900_halt;
edev->get_ethaddr = cs8900_get_ethaddr;
edev->set_ethaddr = cs8900_set_ethaddr;
eth_register(edev);
return 0;
}
static struct driver_d cs8900_driver = {
.name = "cs8900",
.probe = cs8900_probe,
.info = cs8900_info,
};
static int cs8900_init(void)
{
register_driver(&cs8900_driver);
return 0;
}
device_initcall(cs8900_init);
|