📄 rtl_3c905cx_drv.c
字号:
if (fn_st_addr) { rt_3c905c_vp.cb_fn_base = ioremap(fn_st_addr, 128); retval = -ENOMEM; if (!rt_3c905c_vp.cb_fn_base) goto free_ring; } EL3WINDOW(2); n = inw(ioaddr + Wn2_ResetOptions) & ~0x4010; if (rt_3c905c_vp.drv_flags & INVERT_LED_PWR) n |= 0x10; if (rt_3c905c_vp.drv_flags & INVERT_MII_PWR) n |= 0x4000; outw(n, ioaddr + Wn2_ResetOptions); } /* Extract our information from the EEPROM data. */ rt_3c905c_vp.info1 = eeprom[13]; rt_3c905c_vp.info2 = eeprom[15]; rt_3c905c_vp.capabilities = eeprom[16]; if (rt_3c905c_vp.info1 & 0x8000){ rt_3c905c_vp.full_duplex = 1; printk(KERN_INFO "Full duplex capable\n"); } { unsigned int config; EL3WINDOW(3); /* This reads the MediaOptions register which shows what physical media */ /* connections are available in the NIC */ rt_3c905c_vp.available_media = inw(ioaddr + Wn3_Options); //Wn3_Options = 8 vp.available_media = 0xa if ((rt_3c905c_vp.available_media & 0xff) == 0) /* Broken 3c916 */ rt_3c905c_vp.available_media = 0x40; /* This reads the InternalConfig register which provides a way to set */ /* NIC-specific, non-host-related configuration settings */ config = inl(ioaddr + Wn3_Config); //Wn3_Config = 0 rt_3c905c_vp.default_media = XCVR(config); if (rt_3c905c_vp.default_media == XCVR_NWAY) rt_3c905c_vp.has_nway = 1; rt_3c905c_vp.autoselect = AUTOSELECT(config); } if (rt_3c905c_vp.media_override != 7) { rt_3c905c_vp.if_port = rt_3c905c_vp.media_override; } else rt_3c905c_vp.if_port = rt_3c905c_vp.default_media; if (rt_3c905c_vp.if_port == XCVR_MII || rt_3c905c_vp.if_port == XCVR_NWAY) { int phy, phy_idx = 0; EL3WINDOW(4); mii_preamble_required++; mii_preamble_required++; rtl_3COM905C_mdio_read(24, 1); for (phy = 0; phy < 32 && phy_idx < 1; phy++) { int mii_status, phyx; /* * For the 3c905CX we look at index 24 first, because it bogusly * reports an external PHY at all indices */ if (phy == 0) phyx = 24; else if (phy <= 24) phyx = phy - 1; else phyx = phy; mii_status = rtl_3COM905C_mdio_read(phyx, 1); if (mii_status && mii_status != 0xffff) { rt_3c905c_vp.phys[phy_idx++] = phyx; if ((mii_status & 0x0040) == 0) mii_preamble_required++; } } mii_preamble_required--; if (phy_idx == 0) { rt_3c905c_vp.phys[0] = 24; } else { rt_3c905c_vp.advertising = rtl_3COM905C_mdio_read(rt_3c905c_vp.phys[0], 4); if (rt_3c905c_vp.full_duplex) { /* Only advertise the FD media types. */ rt_3c905c_vp.advertising &= ~0x02A0; rtl_3COM905C_mdio_write(rt_3c905c_vp.phys[0], 4, rt_3c905c_vp.advertising); } } } if (rt_3c905c_vp.capabilities & CapBusMaster) { rt_3c905c_vp.full_bus_master_tx = 1; rt_3c905c_vp.full_bus_master_rx = (rt_3c905c_vp.info2 & 1) ? 1 : 2; rt_3c905c_vp.bus_master = 0; /* AKPM: vortex only */ } if (rt_3c905c_vp.pdev && rt_3c905c_vp.enable_wol) { rt_3c905c_vp.pm_state_valid = 1; rt_pci_save_state(dev, rt_3c905c_vp.power_state); rtl_3COM905C_acpi_set_WOL(); } vortex_open(dev); }// if(dev) return 0; free_ring: pci_free_consistent(dev, sizeof(struct boom_rx_desc) * RX_RING_SIZE, rt_3c905c_vp.rx_ring, rt_3c905c_vp.rx_ring_dma); pci_free_consistent(dev, sizeof(struct boom_tx_desc) * TX_RING_SIZE, rt_3c905c_vp.tx_ring, rt_3c905c_vp.tx_ring_dma); return -1; free_region: if (rt_3c905c_vp.must_free_region){ release_region(ioaddr, vci->io_size); rt_3c905c_vp.must_free_region = 0; } printk("vortex_probe1 fails. Returns %d\n", retval); return -1;}/***************************************************************************************//* ACPI: Advanced Configuration and Power Interface. *//* Set Wake-On-LAN mode and put the board into D3 (power-down) state. *//***************************************************************************************/static void rtl_3COM905C_acpi_set_WOL(void){ long ioaddr = rt_3c905c_vp.ioaddr; /* Power up on: 1==Downloaded Filter, 2==Magic Packets, 4==Link Status. */ EL3WINDOW(7); outw(2, ioaddr + 0x0c); /* The RxFilter must accept the WOL frames. */ outw(SetRxFilter|RxStation|RxBroadcast, ioaddr + EL3_CMD); //RxMulticast outw(RxEnable, ioaddr + EL3_CMD); /* Change the power state to D3; RxEnable doesn't take effect. */ rt_pci_enable_wake(rt_3c905c_vp.pdev, 0, 1); rt_pci_set_power_state(rt_3c905c_vp.pdev, 3);}/***************************************************************************************//* This function registers the interrupt handler and reserves memory for buffers which *//* only will be accessed by the card. *//***************************************************************************************/static int vortex_open(struct pci_dev *dev){ int i; int retval; /* Use the now-standard shared IRQ implementation. */ if ((retval = rtl_request_irq(dev->irq, &boomerang_interrupt))) { printk(KERN_ERR "%s: Could not reserve IRQ %d\n", dev->name, dev->irq); goto out; }else rt_3c905c_vp.must_free_irq = 1; if (rt_3c905c_vp.full_bus_master_rx) { /* Boomerang bus master. */ /* RX RING INITIALIZATION */ for (i = 0; i < RX_RING_SIZE; i++) { unsigned char *skb; rt_3c905c_vp.rx_ring[i].next = cpu_to_le32(rt_3c905c_vp.rx_ring_dma + sizeof(struct boom_rx_desc) * (i+1)); rt_3c905c_vp.rx_ring[i].status = 0; /* Clear complete bit. */ rt_3c905c_vp.rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LAST_FRAG); skb = rtl_malloc(PKT_BUF_SZ); rt_3c905c_vp.rx_skbuff[i] = skb; if (skb == NULL) break; /* Bad news! */ rt_3c905c_vp.rx_ring[i].addr = cpu_to_le32(pci_map_single(rt_3c905c_vp.pdev, skb, PKT_BUF_SZ, PCI_DMA_FROMDEVICE)); } if (i != RX_RING_SIZE) { int j; printk(KERN_EMERG "%s: no memory for rx ring\n", dev->name); for (j = 0; j < i; j++) { if (rt_3c905c_vp.rx_skbuff[j]) { rtl_free(rt_3c905c_vp.rx_skbuff[j]); rt_3c905c_vp.rx_skbuff[j] = 0; } } retval = -ENOMEM; goto out_free_irq; } /* Wrap the ring. */ rt_3c905c_vp.rx_ring[i-1].next = cpu_to_le32(rt_3c905c_vp.rx_ring_dma); /* TX RING INITIALIZATION */ for (i = 0; i < TX_RING_SIZE; i++) { unsigned char *skb; rt_3c905c_vp.tx_ring[i].next = 0; rt_3c905c_vp.tx_ring[i].status = 0; /* Clear complete bit. */ rt_3c905c_vp.tx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LAST_FRAG); skb = rtl_malloc(PKT_BUF_SZ); rt_3c905c_vp.tx_skbuff[i] = skb; if (skb == NULL) break; /* Bad news! */ rt_3c905c_vp.tx_ring[i].addr = cpu_to_le32(pci_map_single(rt_3c905c_vp.pdev, skb, PKT_BUF_SZ, PCI_DMA_FROMDEVICE)); } if (i != TX_RING_SIZE) { int j; printk(KERN_EMERG "%s: no memory for tx ring\n", dev->name); for (j = 0; j < i; j++) { if (rt_3c905c_vp.tx_skbuff[j]) { rtl_free(rt_3c905c_vp.tx_skbuff[j]); rt_3c905c_vp.tx_skbuff[j] = 0; } } retval = -ENOMEM; goto out_free_irq; } /* Wrap the ring. */ rt_3c905c_vp.tx_ring[i-1].next = cpu_to_le32(rt_3c905c_vp.tx_ring_dma); } vortex_up(dev); return 0; out_free_irq: rtl_free_irq(dev->irq); out: return retval;}/***************************************************************************************//* This function is also used to initialise the card. In this function we indicate *//* which interrupts do we want the card to generate. *//***************************************************************************************/static void vortex_up(struct pci_dev *dev){ long ioaddr = rt_3c905c_vp.ioaddr; unsigned int config; int i; rt_3c905c_vp.rx_packets = 0; rt_3c905c_vp.rx_frames_for_us = 0; if (rt_3c905c_vp.pdev && rt_3c905c_vp.enable_wol) { rt_pci_set_power_state(dev, 0); /* Go active */ rt_pci_restore_state(dev, rt_3c905c_vp.power_state); } /* Before initializing select the active media port. */ EL3WINDOW(3); config = inl(ioaddr + Wn3_Config); if (rt_3c905c_vp.media_override != 7) { rt_3c905c_vp.if_port = rt_3c905c_vp.media_override; } else if (rt_3c905c_vp.autoselect) { if (rt_3c905c_vp.has_nway) { } else { /* Find first available media type, starting with 100baseTx. */ rt_3c905c_vp.if_port = XCVR_100baseTx; while (! (rt_3c905c_vp.available_media & media_tbl[rt_3c905c_vp.if_port].mask)) rt_3c905c_vp.if_port = media_tbl[rt_3c905c_vp.if_port].next; } } else { rt_3c905c_vp.if_port = rt_3c905c_vp.default_media; } rt_3c905c_vp.full_duplex = rt_3c905c_vp.force_fd; config = BFINS(config, rt_3c905c_vp.if_port, 20, 4); outl(config, ioaddr + Wn3_Config); if (rt_3c905c_vp.if_port == XCVR_MII || rt_3c905c_vp.if_port == XCVR_NWAY) { int mii_reg1, mii_reg5; EL3WINDOW(4); /* Read BMSR (reg1) only to clear old status. */ mii_reg1 = rtl_3COM905C_mdio_read(rt_3c905c_vp.phys[0], 1); mii_reg5 = rtl_3COM905C_mdio_read(rt_3c905c_vp.phys[0], 5); if (mii_reg5 == 0xffff || mii_reg5 == 0x0000) ; /* No MII device or no link partner report */ else if ((mii_reg5 & 0x0100) != 0 /* 100baseTx-FD */ || (mii_reg5 & 0x00C0) == 0x0040) /* 10T-FD, but not 100-HD */ rt_3c905c_vp.full_duplex = 1; rt_3c905c_vp.partner_flow_ctrl = ((mii_reg5 & 0x0400) != 0); EL3WINDOW(3); } rt_3c905c_vp.full_duplex = 1; /* Set the full-duplex bit. */ outw( ((rt_3c905c_vp.info1 & 0x8000) || rt_3c905c_vp.full_duplex ? 0x20 : 0) | (rt_3c905c_vp.mtu > 1500 ? 0x40 : 0) | ((rt_3c905c_vp.full_duplex && rt_3c905c_vp.flow_ctrl && rt_3c905c_vp.partner_flow_ctrl) ? 0x100 : 0), ioaddr + Wn3_MAC_Ctrl); rtl_3COM905C_issue_and_wait(TxReset); /* * Don't reset the PHY - that upsets autonegotiation during DHCP operations. */ rtl_3COM905C_issue_and_wait(RxReset|0x04); outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); EL3WINDOW(4); /* Set the station address and mask in window 2 each time opened. */ EL3WINDOW(2); for (i = 0; i < 6; i++) outb(rt_3c905c_vp.dev_addr[i], ioaddr + i); for (; i < 12; i+=2) outw(0, ioaddr + i); if (rt_3c905c_vp.cb_fn_base) { unsigned short n = inw(ioaddr + Wn2_ResetOptions) & ~0x4010; if (rt_3c905c_vp.drv_flags & INVERT_LED_PWR) n |= 0x10; if (rt_3c905c_vp.drv_flags & INVERT_MII_PWR) n |= 0x4000; outw(n, ioaddr + Wn2_ResetOptions); } if (rt_3c905c_vp.if_port == XCVR_10base2) /* Start the thinnet transceiver. We should really wait 50ms...*/ outw(StartCoax, ioaddr + EL3_CMD); if (rt_3c905c_vp.if_port != XCVR_NWAY) { EL3WINDOW(4); outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP|Media_SQE)) | media_tbl[rt_3c905c_vp.if_port].media_bits, ioaddr + Wn4_Media); } /* RTOS and statistic??? Let's disable stats. */ outw(StatsDisable, ioaddr + EL3_CMD); EL3WINDOW(6); for (i = 0; i < 10; i++) inb(ioaddr + i); inw(ioaddr + 10); inw(ioaddr + 12); /* New: On the Vortex we must also clear the BadSSD counter. */ EL3WINDOW(4); inb(ioaddr + 12); /* Switch to register set 7 for normal use. */ EL3WINDOW(7); if (rt_3c905c_vp.full_bus_master_rx) { /* Boomerang bus master. */ rt_3c905c_vp.cur_rx = rt_3c905c_vp.dirty_rx = rt_3c905c_vp.cur_tx = 0; outl(rt_3c905c_vp.rx_ring_dma, ioaddr + UpListPtr); } if (rt_3c905c_vp.full_bus_master_tx) { /* Boomerang bus master Tx. */ rt_3c905c_vp.cur_tx = rt_3c905c_vp.dirty_tx = 0; if (rt_3c905c_vp.drv_flags & IS_BOOMERANG) outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */ /* Clear the Rx, Tx rings. */ for (i = 0; i < RX_RING_SIZE; i++) /* AKPM: this is done in vortex_open, too */ rt_3c905c_vp.rx_ring[i].status = 0; outl(0, ioaddr + DownListPtr); } /* Set receiver mode: presumably accept b-case and phys addr only. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -