📄 3c574_cs.c
字号:
i = CardServices(RequestIO, link->handle, &link->io); if (i == CS_SUCCESS) break; } if (i != CS_SUCCESS) { cs_error(link->handle, RequestIO, i); goto failed; } CS_CHECK(RequestIRQ, link->handle, &link->irq); CS_CHECK(RequestConfiguration, link->handle, &link->conf); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; if (register_netdev(dev) != 0) { printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n"); goto failed; } ioaddr = dev->base_addr; strcpy(lp->node.dev_name, dev->name); link->dev = &lp->node; link->state &= ~DEV_CONFIG_PENDING; /* The 3c574 normally uses an EEPROM for configuration info, including the hardware address. The future products may include a modem chip and put the address in the CIS. */ tuple.DesiredTuple = 0x88; if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) { CardServices(GetTupleData, handle, &tuple); for (i = 0; i < 3; i++) phys_addr[i] = htons(buf[i]); } else { EL3WINDOW(0); for (i = 0; i < 3; i++) phys_addr[i] = htons(read_eeprom(ioaddr, i + 10)); if (phys_addr[0] == 0x6060) { printk(KERN_NOTICE "3c574_cs: IO port conflict at 0x%03lx" "-0x%03lx\n", dev->base_addr, dev->base_addr+15); goto failed; } } tuple.DesiredTuple = CISTPL_VERS_1; if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS && CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS && CardServices(ParseTuple, handle, &tuple, &parse) == CS_SUCCESS) { cardname = parse.version_1.str + parse.version_1.ofs[1]; } else cardname = "3Com 3c574"; printk(KERN_INFO "%s: %s at io %#3lx, irq %d, hw_addr ", dev->name, cardname, dev->base_addr, dev->irq); for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : ".\n")); { u_char mcr, *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; union wn3_config config; outw(2<<11, ioaddr + RunnerRdCtrl); mcr = inb(ioaddr + 2); outw(0<<11, ioaddr + RunnerRdCtrl); printk(KERN_INFO " ASIC rev %d,", mcr>>3); EL3WINDOW(3); config.i = inl(ioaddr + Wn3_Config); printk(" %dK FIFO split %s Rx:Tx, %sMII interface.\n", 8 << config.u.ram_size, ram_split[config.u.ram_split], config.u.autoselect ? "autoselect " : ""); lp->default_media = config.u.xcvr; lp->autoselect = config.u.autoselect; } { int phy; /* Roadrunner only: Turn on the MII transceiver */ outw(0x8040, ioaddr + Wn3_Options); mdelay(1); outw(0xc040, ioaddr + Wn3_Options); tc574_wait_for_completion(dev, TxReset); tc574_wait_for_completion(dev, RxReset); mdelay(1); outw(0x8040, ioaddr + Wn3_Options); EL3WINDOW(4); for (phy = 1; phy <= 32; phy++) { int mii_status; mdio_sync(ioaddr, 32); mii_status = mdio_read(ioaddr, phy & 0x1f, 1); if (mii_status != 0xffff) { lp->phys = phy & 0x1f; DEBUG(0, " MII transceiver at index %d, status %x.\n", phy, mii_status); if ((mii_status & 0x0040) == 0) mii_preamble_required = 1; break; } } if (phy > 32) { printk(KERN_NOTICE " No MII transceivers found!\n"); goto failed; } i = mdio_read(ioaddr, lp->phys, 16) | 0x40; mdio_write(ioaddr, lp->phys, 16, i); lp->advertising = mdio_read(ioaddr, lp->phys, 4); if (full_duplex) { /* Only advertise the FD media types. */ lp->advertising &= ~0x02a0; mdio_write(ioaddr, lp->phys, 4, lp->advertising); } } return;cs_failed: cs_error(link->handle, last_fn, last_ret);failed: tc574_release((u_long)link); return;} /* tc574_config *//* After a card is removed, tc574_release() will unregister the net device, and release the PCMCIA configuration. If the device is still open, this will be postponed until it is closed.*/static void tc574_release(u_long arg){ dev_link_t *link = (dev_link_t *)arg; DEBUG(0, "3c574_release(0x%p)\n", link); if (link->open) { DEBUG(1, "3c574_cs: release postponed, '%s' still open\n", link->dev->dev_name); link->state |= DEV_STALE_CONFIG; return; } CardServices(ReleaseConfiguration, link->handle); CardServices(ReleaseIO, link->handle, &link->io); CardServices(ReleaseIRQ, link->handle, &link->irq); link->state &= ~DEV_CONFIG;} /* tc574_release *//* The card status event handler. Mostly, this schedules other stuff to run after an event is received. A CARD_REMOVAL event also sets some flags to discourage the net drivers from trying to talk to the card any more.*/static int tc574_event(event_t event, int priority, event_callback_args_t *args){ dev_link_t *link = args->client_data; struct el3_private *lp = link->priv; struct net_device *dev = &lp->dev; DEBUG(1, "3c574_event(0x%06x)\n", event); switch (event) { case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { netif_device_detach(dev); mod_timer(&link->release, jiffies + HZ/20); } break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; tc574_config(link); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: if (link->state & DEV_CONFIG) { if (link->open) netif_device_detach(dev); CardServices(ReleaseConfiguration, link->handle); } break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: if (link->state & DEV_CONFIG) { CardServices(RequestConfiguration, link->handle, &link->conf); if (link->open) { tc574_reset(dev); netif_device_attach(dev); } } break; } return 0;} /* tc574_event */static void dump_status(struct net_device *dev){ ioaddr_t ioaddr = dev->base_addr; EL3WINDOW(1); printk(KERN_INFO " irq status %04x, rx status %04x, tx status " "%02x, tx free %04x\n", inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus), inb(ioaddr+TxStatus), inw(ioaddr+TxFree)); EL3WINDOW(4); printk(KERN_INFO " diagnostics: fifo %04x net %04x ethernet %04x" " media %04x\n", inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08), inw(ioaddr+0x0a)); EL3WINDOW(1);}/* Use this for commands that may take time to finish*/static void tc574_wait_for_completion(struct net_device *dev, int cmd){ int i = 1500; outw(cmd, dev->base_addr + EL3_CMD); while (--i > 0) if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; if (i == 0) printk(KERN_NOTICE "%s: command 0x%04x did not complete!\n", dev->name, cmd);}/* Read a word from the EEPROM using the regular EEPROM access register. Assume that we are in register window zero. */static u_short read_eeprom(ioaddr_t ioaddr, int index){ int timer; outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd); /* Pause for at least 162 usec for the read to take place. */ for (timer = 1620; timer >= 0; timer--) { if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) break; } return inw(ioaddr + Wn0EepromData);}/* MII transceiver control section. Read and write the MII registers using software-generated serial MDIO protocol. See the MII specifications or DP83840A data sheet for details. The maxium data clock rate is 2.5 Mhz. The timing is easily met by the slow PC card interface. */#define MDIO_SHIFT_CLK 0x01#define MDIO_DIR_WRITE 0x04#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)#define MDIO_DATA_READ 0x02#define MDIO_ENB_IN 0x00/* Generate the preamble required for initial synchronization and a few older transceivers. */static void mdio_sync(ioaddr_t ioaddr, int bits){ int mdio_addr = ioaddr + Wn4_PhysicalMgmt; /* Establish sync by sending at least 32 logic ones. */ while (-- bits >= 0) { outw(MDIO_DATA_WRITE1, mdio_addr); outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); }}static int mdio_read(ioaddr_t ioaddr, int phy_id, int location){ int i; int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; unsigned int retval = 0; int mdio_addr = ioaddr + Wn4_PhysicalMgmt; if (mii_preamble_required) mdio_sync(ioaddr, 32); /* Shift the read command bits out. */ for (i = 14; i >= 0; i--) { int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; outw(dataval, mdio_addr); outw(dataval | MDIO_SHIFT_CLK, mdio_addr); } /* Read the two transition, 16 data, and wire-idle bits. */ for (i = 19; i > 0; i--) { outw(MDIO_ENB_IN, mdio_addr); retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); } return (retval>>1) & 0xffff;}static void mdio_write(ioaddr_t ioaddr, int phy_id, int location, int value){ int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; int mdio_addr = ioaddr + Wn4_PhysicalMgmt; int i; if (mii_preamble_required) mdio_sync(ioaddr, 32); /* Shift the command bits out. */ for (i = 31; i >= 0; i--) { int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; outw(dataval, mdio_addr); outw(dataval | MDIO_SHIFT_CLK, mdio_addr); } /* Leave the interface idle. */ for (i = 1; i >= 0; i--) { outw(MDIO_ENB_IN, mdio_addr); outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); } return;}/* Reset and restore all of the 3c574 registers. */static void tc574_reset(struct net_device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; int i, ioaddr = dev->base_addr; tc574_wait_for_completion(dev, TotalReset|0x10); /* Clear any transactions in progress. */ outw(0, ioaddr + RunnerWrCtrl); outw(0, ioaddr + RunnerRdCtrl); /* Set the station address and mask. */ EL3WINDOW(2); for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + i); for (; i < 12; i+=2) outw(0, ioaddr + i); /* Reset config options */ EL3WINDOW(3); outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b, ioaddr + Wn3_Config); /* Roadrunner only: Turn on the MII transceiver. */ outw(0x8040, ioaddr + Wn3_Options); mdelay(1); outw(0xc040, ioaddr + Wn3_Options); tc574_wait_for_completion(dev, TxReset); tc574_wait_for_completion(dev, RxReset); mdelay(1); outw(0x8040, ioaddr + Wn3_Options); /* Switch to the stats window, and clear all stats by reading. */ outw(StatsDisable, ioaddr + EL3_CMD); EL3WINDOW(6); for (i = 0; i < 10; i++) inb(ioaddr + i); inw(ioaddr + 10); inw(ioaddr + 12); EL3WINDOW(4); inb(ioaddr + 12); inb(ioaddr + 13); /* .. enable any extra statistics bits.. */ outw(0x0040, ioaddr + Wn4_NetDiag); /* .. re-sync MII and re-fill what NWay is advertising. */ mdio_sync(ioaddr, 32); mdio_write(ioaddr, lp->phys, 4, lp->advertising); if (!auto_polarity) { /* works for TDK 78Q2120 series MII's */ int i = mdio_read(ioaddr, lp->phys, 16) | 0x20; mdio_write(ioaddr, lp->phys, 16, i); } /* Switch to register set 1 for normal use, just for TxFree. */ EL3WINDOW(1); set_rx_mode(dev); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ /* Allow status bits to be seen. */ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); /* Ack all pending events, and set active indicator mask. */ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull | AdapterFailure | RxEarly, ioaddr + EL3_CMD);}static int el3_open(struct net_device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; dev_link_t *link = &lp->link; if (!DEV_OK(link)) return -ENODEV; link->open++; MOD_INC_USE_COUNT; netif_start_queue(dev); tc574_reset(dev); lp->media.function = &media_check; lp->media.data = (u_long)lp; lp->media.expires = jiffies + HZ; add_timer(&lp->media); DEBUG(2, "%s: opened, status %4.4x.\n", dev->name, inw(dev->base_addr + EL3_STATUS)); return 0;}static void el3_tx_timeout(struct net_device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; ioaddr_t ioaddr = dev->base_addr; printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name); dump_status(dev); lp->stats.tx_errors++; dev->trans_start = jiffies; /* Issue TX_RESET and TX_START commands. */ tc574_wait_for_completion(dev, TxReset); outw(TxEnable, ioaddr + EL3_CMD); netif_wake_queue(dev);}static void pop_tx_status(struct net_device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; ioaddr_t ioaddr = dev->base_addr; int i; /* Clear the Tx status stack. */ for (i = 32; i > 0; i--) { u_char tx_status = inb(ioaddr + TxStatus); if (!(tx_status & 0x84)) break; /* reset transmitter on jabber error or underrun */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -