| /* 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; |
| }; |
| |
| static struct amd8111e_priv amd8111e; |
| |
| |
| /******************************************************** |
| * 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) |
| { |
| 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 ); |
| |
| /* |
| * Local variables: |
| * c-basic-offset: 8 |
| * c-indent-level: 8 |
| * tab-width: 8 |
| * End: |
| */ |