Mercurial > ecos-v3_0-branch
comparison packages/devs/eth/arm/edb7xxx/current/src/if_edb7xxx.c @ 82:6736c52df507 ecos-sw-2000-04-14
Merge from eCos master repository on 2000-04-14-13:35:46-BST
| author | jlarmour |
|---|---|
| date | Tue, 18 Apr 2000 21:51:55 +0000 |
| parents | |
| children | 6ed91473a1cd |
comparison
equal
deleted
inserted
replaced
| 81:89fef2181d7d | 82:6736c52df507 |
|---|---|
| 1 //========================================================================== | |
| 2 // | |
| 3 // dev/if_edb7xxx.c | |
| 4 // | |
| 5 // Ethernet device driver for Cirrus Logic EDB7xxx using CS8900 | |
| 6 // | |
| 7 //========================================================================== | |
| 8 //####COPYRIGHTBEGIN#### | |
| 9 // | |
| 10 // ------------------------------------------- | |
| 11 // The contents of this file are subject to the Red Hat eCos Public License | |
| 12 // Version 1.1 (the "License"); you may not use this file except in | |
| 13 // compliance with the License. You may obtain a copy of the License at | |
| 14 // http://www.redhat.com/ | |
| 15 // | |
| 16 // Software distributed under the License is distributed on an "AS IS" | |
| 17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the | |
| 18 // License for the specific language governing rights and limitations under | |
| 19 // the License. | |
| 20 // | |
| 21 // The Original Code is eCos - Embedded Configurable Operating System, | |
| 22 // released September 30, 1998. | |
| 23 // | |
| 24 // The Initial Developer of the Original Code is Red Hat. | |
| 25 // Portions created by Red Hat are | |
| 26 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc. | |
| 27 // All Rights Reserved. | |
| 28 // ------------------------------------------- | |
| 29 // | |
| 30 //####COPYRIGHTEND#### | |
| 31 //####BSDCOPYRIGHTBEGIN#### | |
| 32 // | |
| 33 // ------------------------------------------- | |
| 34 // | |
| 35 // Portions of this software may have been derived from OpenBSD or other sources, | |
| 36 // and are covered by the appropriate copyright disclaimers included herein. | |
| 37 // | |
| 38 // ------------------------------------------- | |
| 39 // | |
| 40 //####BSDCOPYRIGHTEND#### | |
| 41 //========================================================================== | |
| 42 //#####DESCRIPTIONBEGIN#### | |
| 43 // | |
| 44 // Author(s): gthomas | |
| 45 // Contributors: gthomas | |
| 46 // Date: 2000-01-10 | |
| 47 // Purpose: | |
| 48 // Description: hardware driver for CS8900 ethernet | |
| 49 // | |
| 50 // | |
| 51 //####DESCRIPTIONEND#### | |
| 52 // | |
| 53 //========================================================================== | |
| 54 | |
| 55 // Ethernet device driver for Cirrus Logic EDB7xxx | |
| 56 // Based on CS8900A | |
| 57 | |
| 58 #include <pkgconf/net.h> | |
| 59 #include <cyg/infra/cyg_type.h> | |
| 60 #include <cyg/hal/hal_arch.h> | |
| 61 #include <cyg/infra/diag.h> | |
| 62 #include <cyg/hal/drv_api.h> | |
| 63 #include <netdev.h> | |
| 64 #include <eth_drv.h> | |
| 65 | |
| 66 #define INTS_DONT_WORK | |
| 67 #undef INTS_DONT_WORK | |
| 68 | |
| 69 #ifdef INTS_DONT_WORK | |
| 70 #define STACK_SIZE CYGNUM_HAL_STACK_SIZE_MINIMUM | |
| 71 static char cs8900_fake_int_stack[STACK_SIZE]; | |
| 72 static cyg_thread cs8900_fake_int_thread_data; | |
| 73 static cyg_handle_t cs8900_fake_int_thread_handle; | |
| 74 static void cs8900_fake_int(cyg_addrword_t); | |
| 75 #endif | |
| 76 | |
| 77 #define CS8900_BASE 0x20000000 | |
| 78 #include "cs8900.h" | |
| 79 #define ETHER_ADDR_LEN 6 | |
| 80 | |
| 81 extern int net_debug; // FIXME | |
| 82 | |
| 83 struct cs8900_priv_data { | |
| 84 int txbusy; // A packet has been sent | |
| 85 unsigned long txkey; // Used to ack when packet sent | |
| 86 } _cs8900_priv_data; | |
| 87 | |
| 88 ETH_DRV_SC(edb7xxx_sc, | |
| 89 &_cs8900_priv_data, // Driver specific data | |
| 90 "eth0", // Name for this interface | |
| 91 cs8900_start, | |
| 92 cs8900_stop, | |
| 93 cs8900_control, | |
| 94 cs8900_can_send, | |
| 95 cs8900_send, | |
| 96 cs8900_recv); | |
| 97 | |
| 98 NETDEVTAB_ENTRY(edb7xxx_netdev, | |
| 99 "edb7xxx", | |
| 100 edb7xxx_cs8900_init, | |
| 101 &edb7xxx_sc); | |
| 102 | |
| 103 // FIXME | |
| 104 static unsigned char enaddr[] = { 0x08, 0x88, 0x12, 0x34, 0x56, 0x78}; | |
| 105 | |
| 106 static void cs8900_int(struct eth_drv_sc *sc); | |
| 107 static cyg_interrupt cs8900_interrupt; | |
| 108 static cyg_handle_t cs8900_interrupt_handle; | |
| 109 | |
| 110 // This ISR is called when the ethernet interrupt occurs | |
| 111 static int | |
| 112 cs8900_isr(cyg_vector_t vector, cyg_addrword_t data, HAL_SavedRegisters *regs) | |
| 113 { | |
| 114 cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_EINT3); | |
| 115 return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR); // Run the DSR | |
| 116 } | |
| 117 | |
| 118 // This DSR handles the ethernet [logical] processing | |
| 119 static void | |
| 120 cs8900_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) | |
| 121 { | |
| 122 cs8900_int((struct eth_drv_sc *)data); | |
| 123 // Allow interrupts to happen again | |
| 124 cyg_drv_interrupt_acknowledge(CYGNUM_HAL_INTERRUPT_EINT3); | |
| 125 cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_EINT3); | |
| 126 } | |
| 127 | |
| 128 static bool | |
| 129 edb7xxx_cs8900_init(struct cyg_netdevtab_entry *tab) | |
| 130 { | |
| 131 struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance; | |
| 132 unsigned short chip_type, chip_rev, chip_status; | |
| 133 int i; | |
| 134 | |
| 135 diag_printf("cs8900 init\n"); | |
| 136 | |
| 137 // Initialize environment, setup interrupt handler | |
| 138 cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_EINT3, | |
| 139 99, // Priority - what goes here? | |
| 140 (cyg_addrword_t)sc, // Data item passed to interrupt handler | |
| 141 (cyg_ISR_t *)cs8900_isr, | |
| 142 (cyg_DSR_t *)cs8900_dsr, | |
| 143 &cs8900_interrupt_handle, | |
| 144 &cs8900_interrupt); | |
| 145 cyg_drv_interrupt_attach(cs8900_interrupt_handle); | |
| 146 cyg_drv_interrupt_acknowledge(CYGNUM_HAL_INTERRUPT_EINT3); | |
| 147 cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_EINT3); | |
| 148 | |
| 149 #ifdef INTS_DONT_WORK | |
| 150 cyg_thread_create(1, // Priority | |
| 151 cs8900_fake_int, // entry | |
| 152 0, // entry parameter | |
| 153 "CS8900 int", // Name | |
| 154 &cs8900_fake_int_stack[0], // Stack | |
| 155 STACK_SIZE, // Size | |
| 156 &cs8900_fake_int_thread_handle, // Handle | |
| 157 &cs8900_fake_int_thread_data // Thread data structure | |
| 158 ); | |
| 159 cyg_thread_resume(cs8900_fake_int_thread_handle); // Start it | |
| 160 #endif | |
| 161 | |
| 162 chip_type = get_reg(PP_ChipID); | |
| 163 chip_rev = get_reg(PP_ChipRev); | |
| 164 | |
| 165 diag_printf("CS8900 - type: %x, rev: %x\n", chip_type, chip_rev); | |
| 166 | |
| 167 put_reg(PP_SelfCtl, PP_SelfCtl_Reset); // Reset chip | |
| 168 while ((get_reg(PP_SelfStat) & PP_SelfStat_InitD) == 0) ; | |
| 169 | |
| 170 chip_status = get_reg(PP_SelfStat); | |
| 171 diag_printf("CS8900 - status: %x (%sEEPROM present)\n", chip_status, | |
| 172 chip_status&PP_SelfStat_EEPROM ? "" : "no "); | |
| 173 | |
| 174 // Set up hardware address - FIXME | |
| 175 for (i = 0; i < ETHER_ADDR_LEN; i += 2) { | |
| 176 put_reg(PP_LAF+i, 0xFFFF); | |
| 177 put_reg(PP_IA+i, enaddr[i] | (enaddr[i+1] << 8)); | |
| 178 } | |
| 179 | |
| 180 // Initialize upper level driver | |
| 181 eth_drv_init(sc, enaddr); | |
| 182 | |
| 183 return true; | |
| 184 } | |
| 185 | |
| 186 static void | |
| 187 cs8900_stop(struct eth_drv_sc *sc) | |
| 188 { | |
| 189 put_reg(PP_SelfCtl, PP_SelfCtl_Reset); // Reset chip | |
| 190 while ((get_reg(PP_SelfStat) & PP_SelfStat_InitD) == 0) ; | |
| 191 } | |
| 192 | |
| 193 // | |
| 194 // This function is called to "start up" the interface. It may be called | |
| 195 // multiple times, even when the hardware is already running. It will be | |
| 196 // called whenever something "hardware oriented" changes and should leave | |
| 197 // the hardware ready to send/receive packets. | |
| 198 // | |
| 199 static void | |
| 200 cs8900_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags) | |
| 201 { | |
| 202 unsigned short stat; | |
| 203 put_reg(PP_BusCtl, PP_BusCtl_MemoryE); // Disable interrupts, memory mode | |
| 204 put_reg(PP_IntReg, PP_IntReg_IRQ0); // Only possibility | |
| 205 put_reg(PP_RxCFG, PP_RxCFG_RxOK | PP_RxCFG_CRC | | |
| 206 PP_RxCFG_RUNT | PP_RxCFG_EXTRA); | |
| 207 put_reg(PP_RxCTL, PP_RxCTL_RxOK | PP_RxCTL_Broadcast | | |
| 208 PP_RxCTL_IA); | |
| 209 put_reg(PP_TxCFG, PP_TxCFG_TxOK | PP_TxCFG_Collision | | |
| 210 PP_TxCFG_CRS | PP_TxCFG_SQE | PP_TxCFG_Late | | |
| 211 PP_TxCFG_Jabber | PP_TxCFG_16Collisions); | |
| 212 put_reg(PP_BufCFG, PP_BufCFG_TxRDY | PP_BufCFG_TxUE | PP_BufCFG_RxMiss | | |
| 213 PP_BufCFG_TxCol | PP_BufCFG_Miss | PP_BufCFG_SWI); | |
| 214 put_reg(PP_IntReg, PP_IntReg_IRQ0); // Only possibility | |
| 215 put_reg(PP_LineCTL, PP_LineCTL_Rx | PP_LineCTL_Tx); | |
| 216 // Clear Interrupt Status Queue before enabling interrupts | |
| 217 while ((stat = CS8900_ISQ) != 0) ; | |
| 218 put_reg(PP_BusCtl, PP_BusCtl_EnableIRQ); | |
| 219 } | |
| 220 | |
| 221 // | |
| 222 // This routine is called to perform special "control" opertions | |
| 223 // | |
| 224 static int | |
| 225 cs8900_control(struct eth_drv_sc *sc, unsigned long key, | |
| 226 void *data, int data_length) | |
| 227 { | |
| 228 switch (key) { | |
| 229 case ETH_DRV_SET_MAC_ADDRESS: | |
| 230 return 0; | |
| 231 break; | |
| 232 default: | |
| 233 return 1; | |
| 234 break; | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 // | |
| 239 // This routine is called to see if it is possible to send another packet. | |
| 240 // It will return non-zero if a transmit is possible, zero otherwise. | |
| 241 // | |
| 242 static int | |
| 243 cs8900_can_send(struct eth_drv_sc *sc) | |
| 244 { | |
| 245 struct cs8900_priv_data *cpd = (struct cs8900_priv_data *)sc->driver_private; | |
| 246 return (cpd->txbusy == 0); | |
| 247 } | |
| 248 | |
| 249 // | |
| 250 // This routine is called to send data to the hardware. | |
| 251 static void | |
| 252 cs8900_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, | |
| 253 int total_len, unsigned long key) | |
| 254 { | |
| 255 struct cs8900_priv_data *cpd = (struct cs8900_priv_data *)sc->driver_private; | |
| 256 int i; | |
| 257 int len; | |
| 258 unsigned char *data; | |
| 259 unsigned short saved_data = 0, *sdata; | |
| 260 unsigned short stat; | |
| 261 bool odd_byte = false; | |
| 262 | |
| 263 // Mark xmitter busy | |
| 264 cpd->txbusy = 1; | |
| 265 cpd->txkey = key; | |
| 266 // Start the xmit sequence | |
| 267 // Note: this can go back once the 'dump' is removed | |
| 268 // CS8900_TxCMD = PP_TxCmd_TxStart_5; // Start more-or-less immediately | |
| 269 CS8900_TxCMD = PP_TxCmd_TxStart_Full; // Start only when all data sent to chip | |
| 270 CS8900_TxLEN = total_len; | |
| 271 stat = get_reg(PP_BusStat); // This actually starts the xmit | |
| 272 | |
| 273 // Put data into buffer | |
| 274 for (i = 0; i < sg_len; i++) { | |
| 275 data = (unsigned char *)sg_list[i].buf; | |
| 276 len = sg_list[i].len; | |
| 277 if (len > 0) { | |
| 278 /* Finish the last word. */ | |
| 279 if (odd_byte) { | |
| 280 saved_data |= (*data++ << 8); | |
| 281 CS8900_RTDATA = saved_data; | |
| 282 len--; | |
| 283 odd_byte = false; | |
| 284 } | |
| 285 /* Output contiguous words. */ | |
| 286 sdata = (unsigned short *)data; | |
| 287 while (len > 1) { | |
| 288 CS8900_RTDATA = *sdata++; | |
| 289 len -= sizeof(unsigned short); | |
| 290 } | |
| 291 /* Save last byte, if necessary. */ | |
| 292 if (len == 1) { | |
| 293 data = (unsigned char *)sdata; | |
| 294 saved_data = *data; | |
| 295 odd_byte = true; | |
| 296 } | |
| 297 } | |
| 298 } | |
| 299 if (odd_byte) { | |
| 300 CS8900_RTDATA = saved_data; | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 // | |
| 305 // This function is called when a packet has been received. It's job is | |
| 306 // to prepare to unload the packet from the hardware. Once the length of | |
| 307 // the packet is known, the upper layer of the driver can be told. When | |
| 308 // the upper layer is ready to unload the packet, the internal function | |
| 309 // 'cs8900_recv' will be called to actually fetch it from the hardware. | |
| 310 // | |
| 311 static void | |
| 312 cs8900_RxEvent(struct eth_drv_sc *sc) | |
| 313 { | |
| 314 unsigned short stat, len; | |
| 315 | |
| 316 stat = CS8900_RTDATA; | |
| 317 len = CS8900_RTDATA; | |
| 318 if (net_debug) { | |
| 319 diag_printf("RxEvent - stat: %x, len: %d\n", stat, len); | |
| 320 } | |
| 321 eth_drv_recv(sc, len); | |
| 322 } | |
| 323 | |
| 324 // | |
| 325 // This function is called as a result of the "eth_drv_recv()" call above. | |
| 326 // It's job is to actually fetch data for a packet from the hardware once | |
| 327 // memory buffers have been allocated for the packet. Note that the buffers | |
| 328 // may come in pieces, using a scatter-gather list. This allows for more | |
| 329 // efficient processing in the upper layers of the stack. | |
| 330 // | |
| 331 static void | |
| 332 cs8900_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len) | |
| 333 { | |
| 334 int i, mlen; | |
| 335 unsigned short *data, val; | |
| 336 unsigned char *cp, cval; | |
| 337 | |
| 338 for (i = 0; i < sg_len; i++) { | |
| 339 data = (unsigned short *)sg_list[i].buf; | |
| 340 mlen = sg_list[i].len; | |
| 341 while (mlen >= sizeof(*data)) { | |
| 342 val = CS8900_RTDATA; | |
| 343 if (data) { | |
| 344 *data++ = val; | |
| 345 } | |
| 346 mlen -= sizeof(*data); | |
| 347 } | |
| 348 if (mlen) { | |
| 349 // Fetch last odd byte | |
| 350 cval = CS8900_RTDATA & 0xFF; | |
| 351 if (cp = (unsigned char *)data) { | |
| 352 *cp = cval; | |
| 353 } | |
| 354 } | |
| 355 } | |
| 356 } | |
| 357 | |
| 358 static void | |
| 359 cs8900_TxEvent(struct eth_drv_sc *sc, int stat) | |
| 360 { | |
| 361 struct cs8900_priv_data *cpd = (struct cs8900_priv_data *)sc->driver_private; | |
| 362 stat = get_reg(PP_TER); | |
| 363 if (net_debug) { | |
| 364 diag_printf("Tx event: %x\n", stat); | |
| 365 } | |
| 366 cpd->txbusy = 0; | |
| 367 eth_drv_tx_done(sc, cpd->txkey, 0); | |
| 368 } | |
| 369 | |
| 370 static void | |
| 371 cs8900_BufEvent(struct eth_drv_sc *sc, int stat) | |
| 372 { | |
| 373 if (stat & PP_BufCFG_RxMiss) { | |
| 374 } | |
| 375 if (stat & PP_BufCFG_TxUE) { | |
| 376 } | |
| 377 } | |
| 378 | |
| 379 static void | |
| 380 cs8900_int(struct eth_drv_sc *sc) | |
| 381 { | |
| 382 unsigned short event; | |
| 383 while ((event = CS8900_ISQ) != 0) { | |
| 384 switch (event & ISQ_EventMask) { | |
| 385 case ISQ_RxEvent: | |
| 386 cs8900_RxEvent(sc); | |
| 387 break; | |
| 388 case ISQ_TxEvent: | |
| 389 cs8900_TxEvent(sc, event); | |
| 390 break; | |
| 391 case ISQ_BufEvent: | |
| 392 cs8900_BufEvent(sc, event); | |
| 393 break; | |
| 394 case ISQ_RxMissEvent: | |
| 395 // Receive miss counter has overflowed | |
| 396 break; | |
| 397 case ISQ_TxColEvent: | |
| 398 // Transmit collision counter has overflowed | |
| 399 break; | |
| 400 default: | |
| 401 diag_printf("%s: Unknown event: %x\n", __FUNCTION__, event); | |
| 402 break; | |
| 403 } | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 #ifdef INTS_DONT_WORK | |
| 408 void | |
| 409 cs8900_fake_int(cyg_addrword_t param) | |
| 410 { | |
| 411 int s; | |
| 412 while (true) { | |
| 413 cyg_thread_delay(5); | |
| 414 s = splnet(); | |
| 415 cs8900_int(&edb7xxx_sc); | |
| 416 splx(s); | |
| 417 } | |
| 418 } | |
| 419 #endif | |
| 420 |
