| /* Advanced Micro Devices Inc. AMD8111E Linux Network Driver | |
| * Copyright (C) 2004 Advanced Micro Devices | |
| * Copyright (C) 2005 Liu Tao <liutao1980@gmail.com> [etherboot port] | |
| * | |
| * Copyright 2001,2002 Jeff Garzik <jgarzik@mandrakesoft.com> [ 8139cp.c,tg3.c ] | |
| * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)[ tg3.c] | |
| * Copyright 1996-1999 Thomas Bogendoerfer [ pcnet32.c ] | |
| * Derived from the lance driver written 1993,1994,1995 by Donald Becker. | |
| * Copyright 1993 United States Government as represented by the | |
| * Director, National Security Agency.[ pcnet32.c ] | |
| * Carsten Langgaard, carstenl@mips.com [ pcnet32.c ] | |
| * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. | |
| * | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 2 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| * 02110-1301, USA. | |
| * USA | |
| */ | |
| FILE_LICENCE ( GPL2_OR_LATER ); | |
| #include "etherboot.h" | |
| #include "nic.h" | |
| #include "mii.h" | |
| #include <ipxe/pci.h> | |
| #include <ipxe/ethernet.h> | |
| #include "string.h" | |
| #include "stdint.h" | |
| #include "amd8111e.h" | |
| /* driver definitions */ | |
| #define NUM_TX_SLOTS 2 | |
| #define NUM_RX_SLOTS 4 | |
| #define TX_SLOTS_MASK 1 | |
| #define RX_SLOTS_MASK 3 | |
| #define TX_BUF_LEN 1536 | |
| #define RX_BUF_LEN 1536 | |
| #define TX_PKT_LEN_MAX (ETH_FRAME_LEN - ETH_HLEN) | |
| #define RX_PKT_LEN_MIN 60 | |
| #define RX_PKT_LEN_MAX ETH_FRAME_LEN | |
| #define TX_TIMEOUT 3000 | |
| #define TX_PROCESS_TIME 10 | |
| #define TX_RETRY (TX_TIMEOUT / TX_PROCESS_TIME) | |
| #define PHY_RW_RETRY 10 | |
| struct amd8111e_tx_desc { | |
| u16 buf_len; | |
| u16 tx_flags; | |
| u16 tag_ctrl_info; | |
| u16 tag_ctrl_cmd; | |
| u32 buf_phy_addr; | |
| u32 reserved; | |
| }; | |
| struct amd8111e_rx_desc { | |
| u32 reserved; | |
| u16 msg_len; | |
| u16 tag_ctrl_info; | |
| u16 buf_len; | |
| u16 rx_flags; | |
| u32 buf_phy_addr; | |
| }; | |
| struct eth_frame { | |
| u8 dst_addr[ETH_ALEN]; | |
| u8 src_addr[ETH_ALEN]; | |
| u16 type; | |
| u8 data[ETH_FRAME_LEN - ETH_HLEN]; | |
| } __attribute__((packed)); | |
| struct amd8111e_priv { | |
| struct amd8111e_tx_desc tx_ring[NUM_TX_SLOTS]; | |
| struct amd8111e_rx_desc rx_ring[NUM_RX_SLOTS]; | |
| unsigned char tx_buf[NUM_TX_SLOTS][TX_BUF_LEN]; | |
| unsigned char rx_buf[NUM_RX_SLOTS][RX_BUF_LEN]; | |
| unsigned long tx_idx, rx_idx; | |
| int tx_consistent; | |
| char opened; | |
| char link; | |
| char speed; | |
| char duplex; | |
| int ext_phy_addr; | |
| u32 ext_phy_id; | |
| struct pci_device *pdev; | |
| struct nic *nic; | |
| void *mmio; | |
| }; | |
| #define amd8111e NIC_FAKE_BSS ( struct amd8111e_priv ) | |
| /******************************************************** | |
| * locale functions * | |
| ********************************************************/ | |
| static void amd8111e_init_hw_default(struct amd8111e_priv *lp); | |
| static int amd8111e_start(struct amd8111e_priv *lp); | |
| static int amd8111e_read_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 *val); | |
| #if 0 | |
| static int amd8111e_write_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 val); | |
| #endif | |
| static void amd8111e_probe_ext_phy(struct amd8111e_priv *lp); | |
| static void amd8111e_disable_interrupt(struct amd8111e_priv *lp); | |
| static void amd8111e_enable_interrupt(struct amd8111e_priv *lp); | |
| static void amd8111e_force_interrupt(struct amd8111e_priv *lp); | |
| static int amd8111e_get_mac_address(struct amd8111e_priv *lp); | |
| static int amd8111e_init_rx_ring(struct amd8111e_priv *lp); | |
| static int amd8111e_init_tx_ring(struct amd8111e_priv *lp); | |
| static int amd8111e_wait_tx_ring(struct amd8111e_priv *lp, unsigned int index); | |
| static void amd8111e_wait_link(struct amd8111e_priv *lp); | |
| static void amd8111e_poll_link(struct amd8111e_priv *lp); | |
| static void amd8111e_restart(struct amd8111e_priv *lp); | |
| /* | |
| * This function clears necessary the device registers. | |
| */ | |
| static void amd8111e_init_hw_default(struct amd8111e_priv *lp) | |
| { | |
| unsigned int reg_val; | |
| void *mmio = lp->mmio; | |
| /* stop the chip */ | |
| writel(RUN, mmio + CMD0); | |
| /* Clear RCV_RING_BASE_ADDR */ | |
| writel(0, mmio + RCV_RING_BASE_ADDR0); | |
| /* Clear XMT_RING_BASE_ADDR */ | |
| writel(0, mmio + XMT_RING_BASE_ADDR0); | |
| writel(0, mmio + XMT_RING_BASE_ADDR1); | |
| writel(0, mmio + XMT_RING_BASE_ADDR2); | |
| writel(0, mmio + XMT_RING_BASE_ADDR3); | |
| /* Clear CMD0 */ | |
| writel(CMD0_CLEAR, mmio + CMD0); | |
| /* Clear CMD2 */ | |
| writel(CMD2_CLEAR, mmio + CMD2); | |
| /* Clear CMD7 */ | |
| writel(CMD7_CLEAR, mmio + CMD7); | |
| /* Clear DLY_INT_A and DLY_INT_B */ | |
| writel(0x0, mmio + DLY_INT_A); | |
| writel(0x0, mmio + DLY_INT_B); | |
| /* Clear FLOW_CONTROL */ | |
| writel(0x0, mmio + FLOW_CONTROL); | |
| /* Clear INT0 write 1 to clear register */ | |
| reg_val = readl(mmio + INT0); | |
| writel(reg_val, mmio + INT0); | |
| /* Clear STVAL */ | |
| writel(0x0, mmio + STVAL); | |
| /* Clear INTEN0 */ | |
| writel(INTEN0_CLEAR, mmio + INTEN0); | |
| /* Clear LADRF */ | |
| writel(0x0, mmio + LADRF); | |
| /* Set SRAM_SIZE & SRAM_BOUNDARY registers */ | |
| writel(0x80010, mmio + SRAM_SIZE); | |
| /* Clear RCV_RING0_LEN */ | |
| writel(0x0, mmio + RCV_RING_LEN0); | |
| /* Clear XMT_RING0/1/2/3_LEN */ | |
| writel(0x0, mmio + XMT_RING_LEN0); | |
| writel(0x0, mmio + XMT_RING_LEN1); | |
| writel(0x0, mmio + XMT_RING_LEN2); | |
| writel(0x0, mmio + XMT_RING_LEN3); | |
| /* Clear XMT_RING_LIMIT */ | |
| writel(0x0, mmio + XMT_RING_LIMIT); | |
| /* Clear MIB */ | |
| writew(MIB_CLEAR, mmio + MIB_ADDR); | |
| /* Clear LARF */ | |
| writel( 0, mmio + LADRF); | |
| writel( 0, mmio + LADRF + 4); | |
| /* SRAM_SIZE register */ | |
| reg_val = readl(mmio + SRAM_SIZE); | |
| /* Set default value to CTRL1 Register */ | |
| writel(CTRL1_DEFAULT, mmio + CTRL1); | |
| /* To avoid PCI posting bug */ | |
| readl(mmio + CMD2); | |
| } | |
| /* | |
| * This function initializes the device registers and starts the device. | |
| */ | |
| static int amd8111e_start(struct amd8111e_priv *lp) | |
| { | |
| struct nic *nic = lp->nic; | |
| void *mmio = lp->mmio; | |
| int i, reg_val; | |
| /* stop the chip */ | |
| writel(RUN, mmio + CMD0); | |
| /* AUTOPOLL0 Register *//*TBD default value is 8100 in FPS */ | |
| writew(0x8100 | lp->ext_phy_addr, mmio + AUTOPOLL0); | |
| /* enable the port manager and set auto negotiation always */ | |
| writel(VAL1 | EN_PMGR, mmio + CMD3 ); | |
| writel(XPHYANE | XPHYRST, mmio + CTRL2); | |
| /* set control registers */ | |
| reg_val = readl(mmio + CTRL1); | |
| reg_val &= ~XMTSP_MASK; | |
| writel(reg_val | XMTSP_128 | CACHE_ALIGN, mmio + CTRL1); | |
| /* initialize tx and rx ring base addresses */ | |
| amd8111e_init_tx_ring(lp); | |
| amd8111e_init_rx_ring(lp); | |
| writel(virt_to_bus(lp->tx_ring), mmio + XMT_RING_BASE_ADDR0); | |
| writel(virt_to_bus(lp->rx_ring), mmio + RCV_RING_BASE_ADDR0); | |
| writew(NUM_TX_SLOTS, mmio + XMT_RING_LEN0); | |
| writew(NUM_RX_SLOTS, mmio + RCV_RING_LEN0); | |
| /* set default IPG to 96 */ | |
| writew(DEFAULT_IPG, mmio + IPG); | |
| writew(DEFAULT_IPG - IFS1_DELTA, mmio + IFS1); | |
| /* AutoPAD transmit, Retransmit on Underflow */ | |
| writel(VAL0 | APAD_XMT | REX_RTRY | REX_UFLO, mmio + CMD2); | |
| /* JUMBO disabled */ | |
| writel(JUMBO, mmio + CMD3); | |
| /* Setting the MAC address to the device */ | |
| for(i = 0; i < ETH_ALEN; i++) | |
| writeb(nic->node_addr[i], mmio + PADR + i); | |
| /* set RUN bit to start the chip, interrupt not enabled */ | |
| writel(VAL2 | RDMD0 | VAL0 | RUN, mmio + CMD0); | |
| /* To avoid PCI posting bug */ | |
| readl(mmio + CMD0); | |
| return 0; | |
| } | |
| /* | |
| This function will read the PHY registers. | |
| */ | |
| static int amd8111e_read_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 *val) | |
| { | |
| void *mmio = lp->mmio; | |
| unsigned int reg_val; | |
| unsigned int retry = PHY_RW_RETRY; | |
| reg_val = readl(mmio + PHY_ACCESS); | |
| while (reg_val & PHY_CMD_ACTIVE) | |
| reg_val = readl(mmio + PHY_ACCESS); | |
| writel(PHY_RD_CMD | ((phy_addr & 0x1f) << 21) | ((reg & 0x1f) << 16), | |
| mmio + PHY_ACCESS); | |
| do { | |
| reg_val = readl(mmio + PHY_ACCESS); | |
| udelay(30); /* It takes 30 us to read/write data */ | |
| } while (--retry && (reg_val & PHY_CMD_ACTIVE)); | |
| if (reg_val & PHY_RD_ERR) { | |
| *val = 0; | |
| return -1; | |
| } | |
| *val = reg_val & 0xffff; | |
| return 0; | |
| } | |
| /* | |
| This function will write into PHY registers. | |
| */ | |
| #if 0 | |
| static int amd8111e_write_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 val) | |
| { | |
| void *mmio = lp->mmio; | |
| unsigned int reg_val; | |
| unsigned int retry = PHY_RW_RETRY; | |
| reg_val = readl(mmio + PHY_ACCESS); | |
| while (reg_val & PHY_CMD_ACTIVE) | |
| reg_val = readl(mmio + PHY_ACCESS); | |
| writel(PHY_WR_CMD | ((phy_addr & 0x1f) << 21) | ((reg & 0x1f) << 16) | val, | |
| mmio + PHY_ACCESS); | |
| do { | |
| reg_val = readl(mmio + PHY_ACCESS); | |
| udelay(30); /* It takes 30 us to read/write the data */ | |
| } while (--retry && (reg_val & PHY_CMD_ACTIVE)); | |
| if(reg_val & PHY_RD_ERR) | |
| return -1; | |
| return 0; | |
| } | |
| #endif | |
| static void amd8111e_probe_ext_phy(struct amd8111e_priv *lp) | |
| { | |
| int i; | |
| lp->ext_phy_id = 0; | |
| lp->ext_phy_addr = 1; | |
| for (i = 0x1e; i >= 0; i--) { | |
| u32 id1, id2; | |
| if (amd8111e_read_phy(lp, i, MII_PHYSID1, &id1)) | |
| continue; | |
| if (amd8111e_read_phy(lp, i, MII_PHYSID2, &id2)) | |
| continue; | |
| lp->ext_phy_id = (id1 << 16) | id2; | |
| lp->ext_phy_addr = i; | |
| break; | |
| } | |
| if (lp->ext_phy_id) | |
| printf("Found MII PHY ID 0x%08x at address 0x%02x\n", | |
| (unsigned int) lp->ext_phy_id, lp->ext_phy_addr); | |
| else | |
| printf("Couldn't detect MII PHY, assuming address 0x01\n"); | |
| } | |
| static void amd8111e_disable_interrupt(struct amd8111e_priv *lp) | |
| { | |
| void *mmio = lp->mmio; | |
| unsigned int int0; | |
| writel(INTREN, mmio + CMD0); | |
| writel(INTEN0_CLEAR, mmio + INTEN0); | |
| int0 = readl(mmio + INT0); | |
| writel(int0, mmio + INT0); | |
| readl(mmio + INT0); | |
| } | |
| static void amd8111e_enable_interrupt(struct amd8111e_priv *lp) | |
| { | |
| void *mmio = lp->mmio; | |
| writel(VAL3 | LCINTEN | VAL1 | TINTEN0 | VAL0 | RINTEN0, mmio + INTEN0); | |
| writel(VAL0 | INTREN, mmio + CMD0); | |
| readl(mmio + CMD0); | |
| } | |
| static void amd8111e_force_interrupt(struct amd8111e_priv *lp) | |
| { | |
| void *mmio = lp->mmio; | |
| writel(VAL0 | UINTCMD, mmio + CMD0); | |
| readl(mmio + CMD0); | |
| } | |
| static int amd8111e_get_mac_address(struct amd8111e_priv *lp) | |
| { | |
| struct nic *nic = lp->nic; | |
| void *mmio = lp->mmio; | |
| int i; | |
| /* BIOS should have set mac address to PADR register, | |
| * so we read PADR to get it. | |
| */ | |
| for (i = 0; i < ETH_ALEN; i++) | |
| nic->node_addr[i] = readb(mmio + PADR + i); | |
| DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) ); | |
| return 0; | |
| } | |
| static int amd8111e_init_rx_ring(struct amd8111e_priv *lp) | |
| { | |
| int i; | |
| lp->rx_idx = 0; | |
| /* Initilaizing receive descriptors */ | |
| for (i = 0; i < NUM_RX_SLOTS; i++) { | |
| lp->rx_ring[i].buf_phy_addr = cpu_to_le32(virt_to_bus(lp->rx_buf[i])); | |
| lp->rx_ring[i].buf_len = cpu_to_le16(RX_BUF_LEN); | |
| wmb(); | |
| lp->rx_ring[i].rx_flags = cpu_to_le16(OWN_BIT); | |
| } | |
| return 0; | |
| } | |
| static int amd8111e_init_tx_ring(struct amd8111e_priv *lp) | |
| { | |
| int i; | |
| lp->tx_idx = 0; | |
| lp->tx_consistent = 1; | |
| /* Initializing transmit descriptors */ | |
| for (i = 0; i < NUM_TX_SLOTS; i++) { | |
| lp->tx_ring[i].tx_flags = 0; | |
| lp->tx_ring[i].buf_phy_addr = 0; | |
| lp->tx_ring[i].buf_len = 0; | |
| } | |
| return 0; | |
| } | |
| static int amd8111e_wait_tx_ring(struct amd8111e_priv *lp, unsigned int index) | |
| { | |
| volatile u16 status; | |
| int retry = TX_RETRY; | |
| status = le16_to_cpu(lp->tx_ring[index].tx_flags); | |
| while (--retry && (status & OWN_BIT)) { | |
| mdelay(TX_PROCESS_TIME); | |
| status = le16_to_cpu(lp->tx_ring[index].tx_flags); | |
| } | |
| if (status & OWN_BIT) { | |
| printf("Error: tx slot %d timeout, stat = 0x%x\n", index, status); | |
| amd8111e_restart(lp); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| static void amd8111e_wait_link(struct amd8111e_priv *lp) | |
| { | |
| unsigned int status; | |
| u32 reg_val; | |
| do { | |
| /* read phy to update STAT0 register */ | |
| amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMCR, ®_val); | |
| amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMSR, ®_val); | |
| amd8111e_read_phy(lp, lp->ext_phy_addr, MII_ADVERTISE, ®_val); | |
| amd8111e_read_phy(lp, lp->ext_phy_addr, MII_LPA, ®_val); | |
| status = readl(lp->mmio + STAT0); | |
| } while (!(status & AUTONEG_COMPLETE) || !(status & LINK_STATS)); | |
| } | |
| static void amd8111e_poll_link(struct amd8111e_priv *lp) | |
| { | |
| unsigned int status, speed; | |
| u32 reg_val; | |
| if (!lp->link) { | |
| /* read phy to update STAT0 register */ | |
| amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMCR, ®_val); | |
| amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMSR, ®_val); | |
| amd8111e_read_phy(lp, lp->ext_phy_addr, MII_ADVERTISE, ®_val); | |
| amd8111e_read_phy(lp, lp->ext_phy_addr, MII_LPA, ®_val); | |
| status = readl(lp->mmio + STAT0); | |
| if (status & LINK_STATS) { | |
| lp->link = 1; | |
| speed = (status & SPEED_MASK) >> 7; | |
| if (speed == PHY_SPEED_100) | |
| lp->speed = 1; | |
| else | |
| lp->speed = 0; | |
| if (status & FULL_DPLX) | |
| lp->duplex = 1; | |
| else | |
| lp->duplex = 0; | |
| printf("Link is up: %s Mbps %s duplex\n", | |
| lp->speed ? "100" : "10", lp->duplex ? "full" : "half"); | |
| } | |
| } else { | |
| status = readl(lp->mmio + STAT0); | |
| if (!(status & LINK_STATS)) { | |
| lp->link = 0; | |
| printf("Link is down\n"); | |
| } | |
| } | |
| } | |
| static void amd8111e_restart(struct amd8111e_priv *lp) | |
| { | |
| printf("\nStarting nic...\n"); | |
| amd8111e_disable_interrupt(lp); | |
| amd8111e_init_hw_default(lp); | |
| amd8111e_probe_ext_phy(lp); | |
| amd8111e_get_mac_address(lp); | |
| amd8111e_start(lp); | |
| printf("Waiting link up...\n"); | |
| lp->link = 0; | |
| amd8111e_wait_link(lp); | |
| amd8111e_poll_link(lp); | |
| } | |
| /******************************************************** | |
| * Interface Functions * | |
| ********************************************************/ | |
| static void amd8111e_transmit(struct nic *nic, const char *dst_addr, | |
| unsigned int type, unsigned int size, const char *packet) | |
| { | |
| struct amd8111e_priv *lp = nic->priv_data; | |
| struct eth_frame *frame; | |
| unsigned int index; | |
| /* check packet size */ | |
| if (size > TX_PKT_LEN_MAX) { | |
| printf("amd8111e_transmit(): too large packet, drop\n"); | |
| return; | |
| } | |
| /* get tx slot */ | |
| index = lp->tx_idx; | |
| if (amd8111e_wait_tx_ring(lp, index)) | |
| return; | |
| /* fill frame */ | |
| frame = (struct eth_frame *)lp->tx_buf[index]; | |
| memset(frame->data, 0, TX_PKT_LEN_MAX); | |
| memcpy(frame->dst_addr, dst_addr, ETH_ALEN); | |
| memcpy(frame->src_addr, nic->node_addr, ETH_ALEN); | |
| frame->type = htons(type); | |
| memcpy(frame->data, packet, size); | |
| /* start xmit */ | |
| lp->tx_ring[index].buf_len = cpu_to_le16(ETH_HLEN + size); | |
| lp->tx_ring[index].buf_phy_addr = cpu_to_le32(virt_to_bus(frame)); | |
| wmb(); | |
| lp->tx_ring[index].tx_flags = | |
| cpu_to_le16(OWN_BIT | STP_BIT | ENP_BIT | ADD_FCS_BIT | LTINT_BIT); | |
| writel(VAL1 | TDMD0, lp->mmio + CMD0); | |
| readl(lp->mmio + CMD0); | |
| /* update slot pointer */ | |
| lp->tx_idx = (lp->tx_idx + 1) & TX_SLOTS_MASK; | |
| } | |
| static int amd8111e_poll(struct nic *nic, int retrieve) | |
| { | |
| /* return true if there's an ethernet packet ready to read */ | |
| /* nic->packet should contain data on return */ | |
| /* nic->packetlen should contain length of data */ | |
| struct amd8111e_priv *lp = nic->priv_data; | |
| u16 status, pkt_len; | |
| unsigned int index, pkt_ok; | |
| amd8111e_poll_link(lp); | |
| index = lp->rx_idx; | |
| status = le16_to_cpu(lp->rx_ring[index].rx_flags); | |
| pkt_len = le16_to_cpu(lp->rx_ring[index].msg_len) - 4; /* remove 4bytes FCS */ | |
| if (status & OWN_BIT) | |
| return 0; | |
| if (status & ERR_BIT) | |
| pkt_ok = 0; | |
| else if (!(status & STP_BIT)) | |
| pkt_ok = 0; | |
| else if (!(status & ENP_BIT)) | |
| pkt_ok = 0; | |
| else if (pkt_len < RX_PKT_LEN_MIN) | |
| pkt_ok = 0; | |
| else if (pkt_len > RX_PKT_LEN_MAX) | |
| pkt_ok = 0; | |
| else | |
| pkt_ok = 1; | |
| if (pkt_ok) { | |
| if (!retrieve) | |
| return 1; | |
| nic->packetlen = pkt_len; | |
| memcpy(nic->packet, lp->rx_buf[index], nic->packetlen); | |
| } | |
| lp->rx_ring[index].buf_phy_addr = cpu_to_le32(virt_to_bus(lp->rx_buf[index])); | |
| lp->rx_ring[index].buf_len = cpu_to_le16(RX_BUF_LEN); | |
| wmb(); | |
| lp->rx_ring[index].rx_flags = cpu_to_le16(OWN_BIT); | |
| writel(VAL2 | RDMD0, lp->mmio + CMD0); | |
| readl(lp->mmio + CMD0); | |
| lp->rx_idx = (lp->rx_idx + 1) & RX_SLOTS_MASK; | |
| return pkt_ok; | |
| } | |
| static void amd8111e_disable(struct nic *nic, void *hwdev __unused) | |
| { | |
| struct amd8111e_priv *lp = nic->priv_data; | |
| /* disable interrupt */ | |
| amd8111e_disable_interrupt(lp); | |
| /* stop chip */ | |
| amd8111e_init_hw_default(lp); | |
| /* unmap mmio */ | |
| iounmap(lp->mmio); | |
| /* update status */ | |
| lp->opened = 0; | |
| } | |
| static void amd8111e_irq(struct nic *nic, irq_action_t action) | |
| { | |
| struct amd8111e_priv *lp = nic->priv_data; | |
| switch (action) { | |
| case DISABLE: | |
| amd8111e_disable_interrupt(lp); | |
| break; | |
| case ENABLE: | |
| amd8111e_enable_interrupt(lp); | |
| break; | |
| case FORCE: | |
| amd8111e_force_interrupt(lp); | |
| break; | |
| } | |
| } | |
| static struct nic_operations amd8111e_operations = { | |
| .connect = dummy_connect, | |
| .poll = amd8111e_poll, | |
| .transmit = amd8111e_transmit, | |
| .irq = amd8111e_irq, | |
| }; | |
| static int amd8111e_probe(struct nic *nic, struct pci_device *pdev) | |
| { | |
| struct amd8111e_priv *lp = &amd8111e; | |
| unsigned long mmio_start, mmio_len; | |
| nic->ioaddr = pdev->ioaddr; | |
| nic->irqno = pdev->irq; | |
| mmio_start = pci_bar_start(pdev, PCI_BASE_ADDRESS_0); | |
| mmio_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_0); | |
| memset(lp, 0, sizeof(*lp)); | |
| lp->pdev = pdev; | |
| lp->nic = nic; | |
| lp->mmio = pci_ioremap(pdev, mmio_start, mmio_len); | |
| lp->opened = 1; | |
| adjust_pci_device(pdev); | |
| nic->priv_data = lp; | |
| amd8111e_restart(lp); | |
| nic->nic_op = &amd8111e_operations; | |
| return 1; | |
| } | |
| static struct pci_device_id amd8111e_nics[] = { | |
| PCI_ROM(0x1022, 0x7462, "amd8111e", "AMD8111E", 0), | |
| }; | |
| PCI_DRIVER ( amd8111e_driver, amd8111e_nics, PCI_NO_CLASS ); | |
| DRIVER ( "AMD8111E", nic_driver, pci_driver, amd8111e_driver, | |
| amd8111e_probe, amd8111e_disable, amd8111e ); | |
| /* | |
| * Local variables: | |
| * c-basic-offset: 8 | |
| * c-indent-level: 8 | |
| * tab-width: 8 | |
| * End: | |
| */ |