📄 pcnet_cs.c
字号:
printk(" %s xcvr,", if_names[dev->if_port]); printk(" hw_addr "); for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); return;cs_failed: cs_error(link->handle, last_fn, last_ret);failed: pcnet_release(link); link->state &= ~DEV_CONFIG_PENDING; return;} /* pcnet_config *//*====================================================================== After a card is removed, pcnet_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 pcnet_release(dev_link_t *link){ pcnet_dev_t *info = PRIV(link->priv); DEBUG(0, "pcnet_release(0x%p)\n", link); if (info->flags & USE_SHMEM) { iounmap(info->base); pcmcia_release_window(link->win); } pcmcia_release_configuration(link->handle); pcmcia_release_io(link->handle, &link->io); pcmcia_release_irq(link->handle, &link->irq); link->state &= ~DEV_CONFIG;}/*====================================================================== 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 pcnet_event(event_t event, int priority, event_callback_args_t *args){ dev_link_t *link = args->client_data; struct net_device *dev = link->priv; DEBUG(2, "pcnet_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); break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; pcnet_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); pcmcia_release_configuration(link->handle); } break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: if (link->state & DEV_CONFIG) { pcmcia_request_configuration(link->handle, &link->conf); if (link->open) { pcnet_reset_8390(dev); NS8390_init(dev, 1); netif_device_attach(dev); } } break; } return 0;} /* pcnet_event *//*====================================================================== MII interface support for DL10019 and DL10022 based cards On the DL10019, the MII IO direction bit is 0x10; on the DL10022 it is 0x20. Setting both bits seems to work on both card types.======================================================================*/#define DLINK_GPIO 0x1c#define DLINK_DIAG 0x1d#define DLINK_EEPROM 0x1e#define MDIO_SHIFT_CLK 0x80#define MDIO_DATA_OUT 0x40#define MDIO_DIR_WRITE 0x30#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)#define MDIO_DATA_READ 0x10#define MDIO_MASK 0x0fstatic void mdio_sync(kio_addr_t addr){ int bits, mask = inb(addr) & MDIO_MASK; for (bits = 0; bits < 32; bits++) { outb(mask | MDIO_DATA_WRITE1, addr); outb(mask | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr); }}static int mdio_read(kio_addr_t addr, int phy_id, int loc){ u_int cmd = (0x06<<10)|(phy_id<<5)|loc; int i, retval = 0, mask = inb(addr) & MDIO_MASK; mdio_sync(addr); for (i = 13; i >= 0; i--) { int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; outb(mask | dat, addr); outb(mask | dat | MDIO_SHIFT_CLK, addr); } for (i = 19; i > 0; i--) { outb(mask, addr); retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0); outb(mask | MDIO_SHIFT_CLK, addr); } return (retval>>1) & 0xffff;}static void mdio_write(kio_addr_t addr, int phy_id, int loc, int value){ u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value; int i, mask = inb(addr) & MDIO_MASK; mdio_sync(addr); for (i = 31; i >= 0; i--) { int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; outb(mask | dat, addr); outb(mask | dat | MDIO_SHIFT_CLK, addr); } for (i = 1; i >= 0; i--) { outb(mask, addr); outb(mask | MDIO_SHIFT_CLK, addr); }}static void mdio_reset(kio_addr_t addr, int phy_id){ outb_p(0x08, addr); outb_p(0x0c, addr); outb_p(0x08, addr); outb_p(0x0c, addr); outb_p(0x00, addr);}/*====================================================================== EEPROM access routines for DL10019 and DL10022 based cards======================================================================*/#define EE_EEP 0x40#define EE_ASIC 0x10#define EE_CS 0x08#define EE_CK 0x04#define EE_DO 0x02#define EE_DI 0x01#define EE_ADOT 0x01 /* DataOut for ASIC */#define EE_READ_CMD 0x06#define DL19FDUPLX 0x0400 /* DL10019 Full duplex mode */static int read_eeprom(kio_addr_t ioaddr, int location){ int i, retval = 0; kio_addr_t ee_addr = ioaddr + DLINK_EEPROM; int read_cmd = location | (EE_READ_CMD << 8); outb(0, ee_addr); outb(EE_EEP|EE_CS, ee_addr); /* Shift the read command bits out. */ for (i = 10; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_DO : 0; outb_p(EE_EEP|EE_CS|dataval, ee_addr); outb_p(EE_EEP|EE_CS|dataval|EE_CK, ee_addr); } outb(EE_EEP|EE_CS, ee_addr); for (i = 16; i > 0; i--) { outb_p(EE_EEP|EE_CS | EE_CK, ee_addr); retval = (retval << 1) | ((inb(ee_addr) & EE_DI) ? 1 : 0); outb_p(EE_EEP|EE_CS, ee_addr); } /* Terminate the EEPROM access. */ outb(0, ee_addr); return retval;}/* The internal ASIC registers can be changed by EEPROM READ access with EE_ASIC bit set. In ASIC mode, EE_ADOT is used to output the data to the ASIC.*/static void write_asic(kio_addr_t ioaddr, int location, short asic_data){ int i; kio_addr_t ee_addr = ioaddr + DLINK_EEPROM; short dataval; int read_cmd = location | (EE_READ_CMD << 8); asic_data |= read_eeprom(ioaddr, location); outb(0, ee_addr); outb(EE_ASIC|EE_CS|EE_DI, ee_addr); read_cmd = read_cmd >> 1; /* Shift the read command bits out. */ for (i = 9; i >= 0; i--) { dataval = (read_cmd & (1 << i)) ? EE_DO : 0; outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr); outb_p(EE_ASIC|EE_CS|EE_DI|dataval|EE_CK, ee_addr); outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr); } // sync outb(EE_ASIC|EE_CS, ee_addr); outb(EE_ASIC|EE_CS|EE_CK, ee_addr); outb(EE_ASIC|EE_CS, ee_addr); for (i = 15; i >= 0; i--) { dataval = (asic_data & (1 << i)) ? EE_ADOT : 0; outb_p(EE_ASIC|EE_CS|dataval, ee_addr); outb_p(EE_ASIC|EE_CS|dataval|EE_CK, ee_addr); outb_p(EE_ASIC|EE_CS|dataval, ee_addr); } /* Terminate the ASIC access. */ outb(EE_ASIC|EE_DI, ee_addr); outb(EE_ASIC|EE_DI| EE_CK, ee_addr); outb(EE_ASIC|EE_DI, ee_addr); outb(0, ee_addr);}/*====================================================================*/static void set_misc_reg(struct net_device *dev){ kio_addr_t nic_base = dev->base_addr; pcnet_dev_t *info = PRIV(dev); u_char tmp; if (info->flags & HAS_MISC_REG) { tmp = inb_p(nic_base + PCNET_MISC) & ~3; if (dev->if_port == 2) tmp |= 1; if (info->flags & USE_BIG_BUF) tmp |= 2; if (info->flags & HAS_IBM_MISC) tmp |= 8; outb_p(tmp, nic_base + PCNET_MISC); } if (info->flags & IS_DL10022) { if (info->flags & HAS_MII) { mdio_reset(nic_base + DLINK_GPIO, info->eth_phy); /* Restart MII autonegotiation */ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000); mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200); info->mii_reset = jiffies; } else { outb(full_duplex ? 4 : 0, nic_base + DLINK_DIAG); } } else if (info->flags & IS_DL10019) { /* Advertise 100F, 100H, 10F, 10H */ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1); /* Restart MII autonegotiation */ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000); mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200); }}/*====================================================================*/static void mii_phy_probe(struct net_device *dev){ pcnet_dev_t *info = PRIV(dev); kio_addr_t mii_addr = dev->base_addr + DLINK_GPIO; int i; u_int tmp, phyid; for (i = 31; i >= 0; i--) { tmp = mdio_read(mii_addr, i, 1); if ((tmp == 0) || (tmp == 0xffff)) continue; tmp = mdio_read(mii_addr, i, MII_PHYID_REG1); phyid = tmp << 16; phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2); phyid &= MII_PHYID_REV_MASK; DEBUG(0, "%s: MII at %d is 0x%08x\n", dev->name, i, phyid); if (phyid == AM79C9XX_HOME_PHY) { info->pna_phy = i; } else if (phyid != AM79C9XX_ETH_PHY) { info->eth_phy = i; } }}static int pcnet_open(struct net_device *dev){ pcnet_dev_t *info = PRIV(dev); dev_link_t *link = &info->link; DEBUG(2, "pcnet_open('%s')\n", dev->name); if (!DEV_OK(link)) return -ENODEV; link->open++; set_misc_reg(dev); request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev); info->phy_id = info->eth_phy; info->link_status = 0x00; init_timer(&info->watchdog); info->watchdog.function = &ei_watchdog; info->watchdog.data = (u_long)dev; info->watchdog.expires = jiffies + HZ; add_timer(&info->watchdog); return ei_open(dev);} /* pcnet_open *//*====================================================================*/static int pcnet_close(struct net_device *dev){ pcnet_dev_t *info = PRIV(dev); dev_link_t *link = &info->link; DEBUG(2, "pcnet_close('%s')\n", dev->name); ei_close(dev); free_irq(dev->irq, dev); link->open--; netif_stop_queue(dev); del_timer_sync(&info->watchdog); return 0;} /* pcnet_close */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -