📄 rtl_3c905cx_drv.c
字号:
rtl_3COM905C_set_rx_mode(); outw(StatsDisable, ioaddr + EL3_CMD); /* Turn off statistics ASAP. */ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ /* Allow status bits to be seen. */ rt_3c905c_vp.status_enable = SetStatusEnb | (rt_3c905c_vp.full_bus_master_rx ? UpComplete : RxComplete) | DownComplete; rt_3c905c_vp.intr_enable = SetIntrEnb | UpComplete | DownComplete; outw(rt_3c905c_vp.status_enable, ioaddr + EL3_CMD); /* Ack all pending events, and set active indicator mask. */ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); outw(rt_3c905c_vp.intr_enable, ioaddr + EL3_CMD); if (rt_3c905c_vp.cb_fn_base) writel(0x8000, rt_3c905c_vp.cb_fn_base + 4);}/***************************************************************************************//* This function is used to issue commands to the card and wait till the command has *//* been successfuly carried out. *//***************************************************************************************/static voidrtl_3COM905C_issue_and_wait(int cmd){ int i; outw(cmd, rt_3c905c_vp.ioaddr + EL3_CMD); for (i = 0; i < 2000; i++) { if (!(inw(rt_3c905c_vp.ioaddr + EL3_STATUS) & CmdInProgress)) return; } /* OK, that didn't work. Do it the slow way. One second */ for (i = 0; i < 100000; i++) { if (!(inw(rt_3c905c_vp.ioaddr + EL3_STATUS) & CmdInProgress)) { return; } usleep(10); } printk(KERN_ERR ": command 0x%04x did not complete! Status=0x%x\n", cmd, inw(rt_3c905c_vp.ioaddr + EL3_STATUS));}/***************************************************************************************//* This functions sets the receive mode of the card. *//* Pre-Cyclone chips have no documented multicast filter, so the only *//* multicast setting is to receive all multicast frames. At least *//* the chip has a very clean way to set the mode, unlike many others. *//***************************************************************************************/static void rtl_3COM905C_set_rx_mode(void){ long ioaddr = rt_3c905c_vp.ioaddr; int new_mode; new_mode = SetRxFilter|RxStation|RxBroadcast;//RxMulticast|RxBroadcast; outw(new_mode, ioaddr + EL3_CMD);}/***************************************************************************************//* This function is used to release the card. It is called from the close call. *//***************************************************************************************/static void vortex_down(struct pci_dev *dev){ long ioaddr = rt_3c905c_vp.ioaddr; /* Turn off statistics ASAP. We update rt_3c905c_vp->stats below. */ outw(StatsDisable, ioaddr + EL3_CMD); /* Disable the receiver and transmitter. */ outw(RxDisable, ioaddr + EL3_CMD); outw(TxDisable, ioaddr + EL3_CMD); if (rt_3c905c_vp.if_port == XCVR_10base2) /* Turn off thinnet power. Green! */ outw(StopCoax, ioaddr + EL3_CMD); outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); if (rt_3c905c_vp.full_bus_master_rx) outl(0, ioaddr + UpListPtr); if (rt_3c905c_vp.full_bus_master_tx) outl(0, ioaddr + DownListPtr); if (rt_3c905c_vp.pdev && rt_3c905c_vp.enable_wol) { rt_pci_save_state(rt_3c905c_vp.pdev, rt_3c905c_vp.power_state); rtl_3COM905C_acpi_set_WOL(); }}/***************************************************************************************//* This function is used to release the card. *//***************************************************************************************/static int vortex_close(struct pci_dev *dev){ int i; vortex_down(dev); if(rt_3c905c_vp.must_free_irq){ rtl_free_irq(dev->irq); rt_3c905c_vp.must_free_irq = 0; } if (rt_3c905c_vp.full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ for (i = 0; i < RX_RING_SIZE; i++) if (rt_3c905c_vp.rx_skbuff[i]) { rtl_free(rt_3c905c_vp.rx_skbuff[i]); } } if (rt_3c905c_vp.full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ for (i = 0; i < TX_RING_SIZE; i++) if (rt_3c905c_vp.tx_skbuff[i]) { rtl_free(rt_3c905c_vp.tx_skbuff[i]); } } return 0;}/***************************************************************************************//* This function is never called, but it is intended to solve errors in the card. *//* Handle uncommon interrupt sources. This is a separate routine to minimize */ /* the cache impact. *//***************************************************************************************/ static void vortex_error(struct pci_dev *dev, int status){ long ioaddr = rt_3c905c_vp.ioaddr; int do_tx_reset = 0, reset_mask = 0; if (status & RxEarly) { /* Rx early is unused. */ outw(AckIntr | RxEarly, ioaddr + EL3_CMD); } if (status & StatsFull) { /* Empty statistics. */ static int DoneDidThat; printk(KERN_DEBUG "%s: Updating stats.\n", dev->name); /* HACK: Disable statistics as an interrupt source. */ /* This occurs when we have the wrong media type! */ if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) { printk(KERN_WARNING "%s: Updating statistics failed, disabling " "stats as an interrupt source.\n", dev->name); EL3WINDOW(5); outw(SetIntrEnb | (inw(ioaddr + 10) & ~StatsFull), ioaddr + EL3_CMD); rt_3c905c_vp.intr_enable &= ~StatsFull; EL3WINDOW(7); DoneDidThat++; } } if (status & IntReq) { /* Restore all interrupt sources. */ outw(rt_3c905c_vp.status_enable, ioaddr + EL3_CMD); outw(rt_3c905c_vp.intr_enable, ioaddr + EL3_CMD); } if (status & HostError) { u16 fifo_diag; EL3WINDOW(4); fifo_diag = inw(ioaddr + Wn4_FIFODiag); printk(KERN_ERR "%s: Host error, FIFO diagnostic register %4.4x.\n", dev->name, fifo_diag); /* Adapter failure requires Tx/Rx reset and reinit. */ if (rt_3c905c_vp.full_bus_master_tx) { int bus_status = inl(ioaddr + PktStatus); /* 0x80000000 PCI master abort. */ /* 0x40000000 PCI target abort. */ printk(KERN_ERR "%s: PCI bus error, bus status %8.8x\n", dev->name, bus_status); /* In this case, blow the card away */ vortex_down(dev); rtl_3COM905C_issue_and_wait(TotalReset | 0xff); vortex_up(dev); /* AKPM: bug. vortex_up() assumes that the rx ring is full. It may not be. */ } else if (fifo_diag & 0x0400) do_tx_reset = 1; if (fifo_diag & 0x3000) { /* Reset Rx fifo and upload logic */ rtl_3COM905C_issue_and_wait(RxReset|0x07); /* Set the Rx filter to the current state. */ rtl_3COM905C_set_rx_mode(); outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ outw(AckIntr | HostError, ioaddr + EL3_CMD); } } if (do_tx_reset) { rtl_3COM905C_issue_and_wait(TxReset|reset_mask); outw(TxEnable, ioaddr + EL3_CMD); }}/***************************************************************************************//* This function is called during the release phase. It is mainly used to update pci *//* stuff. *//***************************************************************************************/static void vortex_remove_one (struct pci_dev *pdev){ outw(TotalReset|0x14, rt_3c905c_vp.ioaddr + EL3_CMD); if (rt_3c905c_vp.pdev && rt_3c905c_vp.enable_wol) { rt_pci_set_power_state(rt_3c905c_vp.pdev, 0); /* Go active */ if (rt_3c905c_vp.pm_state_valid) pci_restore_state(rt_3c905c_vp.pdev, rt_3c905c_vp.power_state); } pci_free_consistent(pdev, sizeof(struct boom_rx_desc) * RX_RING_SIZE, rt_3c905c_vp.rx_ring, rt_3c905c_vp.rx_ring_dma); pci_free_consistent(pdev, sizeof(struct boom_tx_desc) * TX_RING_SIZE, rt_3c905c_vp.tx_ring, rt_3c905c_vp.tx_ring_dma); if (rt_3c905c_vp.must_free_region){ release_region(rt_3c905c_vp.ioaddr, rt_3c905c_vp.io_size); rt_3c905c_vp.must_free_region = 0; }}/***************************************************************************************//* This function is used to send a packet. It writes into the internal buffers of the *//* card. *//***************************************************************************************/static int rt_3c905c_send_packet(const char *buffer, size_t size){ long ioaddr = rt_3c905c_vp.ioaddr; unsigned char *buff; int entry = rt_3c905c_vp.cur_tx % TX_RING_SIZE; int previous = (rt_3c905c_vp.cur_tx + TX_RING_SIZE - 1) % TX_RING_SIZE; struct boom_tx_desc *actual = &rt_3c905c_vp.tx_ring[entry]; rt_3c905c_writting = 1; /* Wait for the stall to complete. */ rtl_3COM905C_issue_and_wait(DownStall); buff = rt_3c905c_vp.tx_skbuff[entry]; if(buff) memcpy(buff, buffer, size); actual->length = cpu_to_le32(size | LAST_FRAG); if (inl(ioaddr + DownListPtr) == 0) { outl(rt_3c905c_vp.tx_ring_dma + entry * sizeof(struct boom_tx_desc), ioaddr + DownListPtr); { int tmp = previous; while((rt_3c905c_vp.tx_ring[tmp].next != 0) && (tmp != entry)){ rt_3c905c_vp.tx_ring[tmp].next = 0; tmp = (tmp + TX_RING_SIZE - 1) % TX_RING_SIZE; } } rt_3c905c_vp.queued_packet++; }else rt_3c905c_vp.tx_ring[previous].next = cpu_to_le32(rt_3c905c_vp.tx_ring_dma + sizeof(struct boom_tx_desc) * entry); outw(DownUnstall, ioaddr + EL3_CMD); rt_3c905c_vp.cur_tx++; rt_3c905c_writting = 0; return size;}/* 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 maximum data clock rate is 2.5 Mhz. The minimum timing is usually met by back-to-back PCI I/O cycles, but we insert a delay to avoid "overclocking" issues. */#define rtl_3COM905C_mdio_delay() inl(mdio_addr)#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 rtl_3COM905C_mdio_sync(long ioaddr, int bits){ long mdio_addr = ioaddr + Wn4_PhysicalMgmt; /* Establish sync by sending at least 32 logic ones. */ while (-- bits >= 0) { outw(MDIO_DATA_WRITE1, mdio_addr); rtl_3COM905C_mdio_delay(); outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); rtl_3COM905C_mdio_delay(); }}/***************************************************************************************/static int rtl_3COM905C_mdio_read(int phy_id, int location){ int i; long ioaddr = rt_3c905c_vp.ioaddr; int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; unsigned int retval = 0; long mdio_addr = ioaddr + Wn4_PhysicalMgmt; if (mii_preamble_required) rtl_3COM905C_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); rtl_3COM905C_mdio_delay(); outw(dataval | MDIO_SHIFT_CLK, mdio_addr); rtl_3COM905C_mdio_delay(); } /* Read the two transition, 16 data, and wire-idle bits. */ for (i = 19; i > 0; i--) { outw(MDIO_ENB_IN, mdio_addr); rtl_3COM905C_mdio_delay(); retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); rtl_3COM905C_mdio_delay(); } return retval & 0x20000 ? 0xffff : retval>>1 & 0xffff;}/***************************************************************************************/static void rtl_3COM905C_mdio_write(int phy_id, int location, int value){ long ioaddr = rt_3c905c_vp.ioaddr; int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; long mdio_addr = ioaddr + Wn4_PhysicalMgmt; int i; if (mii_preamble_required) rtl_3COM905C_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); rtl_3COM905C_mdio_delay(); outw(dataval | MDIO_SHIFT_CLK, mdio_addr); rtl_3COM905C_mdio_delay(); } /* Leave the interface idle. */ for (i = 1; i >= 0; i--) { outw(MDIO_ENB_IN, mdio_addr); rtl_3COM905C_mdio_delay(); outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); rtl_3COM905C_mdio_delay(); } return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -