From 8df158ac36fa0937f51c372f0c2d0ad1b86ebe4c Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Sat, 30 Jul 2011 00:36:02 -0700 Subject: toshiba: Move the Toshiba drivers Move the Toshiba ethernet drivers into drivers/net/ethernet/toshiba and make the necessary Kconfig and Makefile changes. CC: Geoff Levand CC: Jens Osterkamp CC: Ishizaki Kou Signed-off-by: Jeff Kirsher --- drivers/net/Kconfig | 37 - drivers/net/Makefile | 6 - drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/toshiba/Kconfig | 56 + drivers/net/ethernet/toshiba/Makefile | 10 + drivers/net/ethernet/toshiba/ps3_gelic_net.c | 1896 +++++++++++++++ drivers/net/ethernet/toshiba/ps3_gelic_net.h | 380 +++ drivers/net/ethernet/toshiba/ps3_gelic_wireless.c | 2681 +++++++++++++++++++++ drivers/net/ethernet/toshiba/ps3_gelic_wireless.h | 326 +++ drivers/net/ethernet/toshiba/spider_net.c | 2603 ++++++++++++++++++++ drivers/net/ethernet/toshiba/spider_net.h | 489 ++++ drivers/net/ethernet/toshiba/spider_net_ethtool.c | 179 ++ drivers/net/ethernet/toshiba/tc35815.c | 2228 +++++++++++++++++ drivers/net/ps3_gelic_net.c | 1896 --------------- drivers/net/ps3_gelic_net.h | 380 --- drivers/net/ps3_gelic_wireless.c | 2681 --------------------- drivers/net/ps3_gelic_wireless.h | 326 --- drivers/net/spider_net.c | 2603 -------------------- drivers/net/spider_net.h | 489 ---- drivers/net/spider_net_ethtool.c | 179 -- drivers/net/tc35815.c | 2228 ----------------- 22 files changed, 10850 insertions(+), 10825 deletions(-) create mode 100644 drivers/net/ethernet/toshiba/Kconfig create mode 100644 drivers/net/ethernet/toshiba/Makefile create mode 100644 drivers/net/ethernet/toshiba/ps3_gelic_net.c create mode 100644 drivers/net/ethernet/toshiba/ps3_gelic_net.h create mode 100644 drivers/net/ethernet/toshiba/ps3_gelic_wireless.c create mode 100644 drivers/net/ethernet/toshiba/ps3_gelic_wireless.h create mode 100644 drivers/net/ethernet/toshiba/spider_net.c create mode 100644 drivers/net/ethernet/toshiba/spider_net.h create mode 100644 drivers/net/ethernet/toshiba/spider_net_ethtool.c create mode 100644 drivers/net/ethernet/toshiba/tc35815.c delete mode 100644 drivers/net/ps3_gelic_net.c delete mode 100644 drivers/net/ps3_gelic_net.h delete mode 100644 drivers/net/ps3_gelic_wireless.c delete mode 100644 drivers/net/ps3_gelic_wireless.h delete mode 100644 drivers/net/spider_net.c delete mode 100644 drivers/net/spider_net.h delete mode 100644 drivers/net/spider_net_ethtool.c delete mode 100644 drivers/net/tc35815.c (limited to 'drivers/net') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index a6edd3546fe9..1e1df3d79850 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -478,11 +478,6 @@ config FORCEDETH To compile this driver as a module, choose M here. The module will be called forcedeth. -config TC35815 - tristate "TOSHIBA TC35815 Ethernet support" - depends on NET_PCI && PCI && MIPS - select PHYLIB - config FEALNX tristate "Myson MTD-8xx PCI Ethernet support" depends on NET_PCI && PCI @@ -658,15 +653,6 @@ config SIS190 To compile this driver as a module, choose M here: the module will be called sis190. This is recommended. -config SPIDER_NET - tristate "Spider Gigabit Ethernet driver" - depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB) - select FW_LOADER - select SUNGEM_PHY - help - This driver supports the Gigabit Ethernet chips present on the - Cell Processor-Based Blades from IBM. - config TSI108_ETH tristate "Tundra TSI108 gigabit Ethernet support" depends on TSI108_BRIDGE @@ -675,29 +661,6 @@ config TSI108_ETH To compile this driver as a module, choose M here: the module will be called tsi108_eth. -config GELIC_NET - tristate "PS3 Gigabit Ethernet driver" - depends on PPC_PS3 - select PS3_SYS_MANAGER - help - This driver supports the network device on the PS3 game - console. This driver has built-in support for Ethernet. - - To compile this driver as a module, choose M here: the - module will be called ps3_gelic. - -config GELIC_WIRELESS - bool "PS3 Wireless support" - depends on WLAN - depends on GELIC_NET - select WIRELESS_EXT - help - This option adds the support for the wireless feature of PS3. - If you have the wireless-less model of PS3 or have no plan to - use wireless feature, disabling this option saves memory. As - the driver automatically distinguishes the models, you can - safely enable this option even if you have a wireless-less model. - config XILINX_LL_TEMAC tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver" depends on PPC || MICROBLAZE diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e448e6ed5918..275ed4a548ae 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -29,12 +29,6 @@ obj-$(CONFIG_SIS900) += sis900.o obj-$(CONFIG_R6040) += r6040.o obj-$(CONFIG_YELLOWFIN) += yellowfin.o obj-$(CONFIG_FEALNX) += fealnx.o -spidernet-y += spider_net.o spider_net_ethtool.o -obj-$(CONFIG_SPIDER_NET) += spidernet.o -obj-$(CONFIG_GELIC_NET) += ps3_gelic.o -gelic_wireless-$(CONFIG_GELIC_WIRELESS) += ps3_gelic_wireless.o -ps3_gelic-objs += ps3_gelic_net.o $(gelic_wireless-y) -obj-$(CONFIG_TC35815) += tc35815.o obj-$(CONFIG_SKFP) += skfp/ obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o obj-$(CONFIG_RIONET) += rionet.o diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index d90f47f3b782..97542479c01f 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -48,6 +48,7 @@ source "drivers/net/ethernet/smsc/Kconfig" source "drivers/net/ethernet/stmicro/Kconfig" source "drivers/net/ethernet/sun/Kconfig" source "drivers/net/ethernet/tehuti/Kconfig" +source "drivers/net/ethernet/toshiba/Kconfig" source "drivers/net/ethernet/via/Kconfig" endif # ETHERNET diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index cf27ae0eb3ec..7e7a319e7187 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -39,4 +39,5 @@ obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/ obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/ obj-$(CONFIG_NET_VENDOR_SUN) += sun/ obj-$(CONFIG_NET_VENDOR_TEHUTI) += tehuti/ +obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/ obj-$(CONFIG_NET_VENDOR_VIA) += via/ diff --git a/drivers/net/ethernet/toshiba/Kconfig b/drivers/net/ethernet/toshiba/Kconfig new file mode 100644 index 000000000000..6ef2ce2c0ea7 --- /dev/null +++ b/drivers/net/ethernet/toshiba/Kconfig @@ -0,0 +1,56 @@ +# +# Toshiba network device configuration +# + +config NET_VENDOR_TOSHIBA + bool "Toshiba devices" + depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB) || PPC_PS3 + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available from + . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Toshiba cards. If you say Y, you will be asked + for your specific card in the following questions. + +if NET_VENDOR_TOSHIBA + +config GELIC_NET + tristate "PS3 Gigabit Ethernet driver" + depends on PPC_PS3 + select PS3_SYS_MANAGER + ---help--- + This driver supports the network device on the PS3 game + console. This driver has built-in support for Ethernet. + + To compile this driver as a module, choose M here: the + module will be called ps3_gelic. + +config GELIC_WIRELESS + bool "PS3 Wireless support" + depends on GELIC_NET && WLAN + select WIRELESS_EXT + ---help--- + This option adds the support for the wireless feature of PS3. + If you have the wireless-less model of PS3 or have no plan to + use wireless feature, disabling this option saves memory. As + the driver automatically distinguishes the models, you can + safely enable this option even if you have a wireless-less model. + +config SPIDER_NET + tristate "Spider Gigabit Ethernet driver" + depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB) + select FW_LOADER + select SUNGEM_PHY + ---help--- + This driver supports the Gigabit Ethernet chips present on the + Cell Processor-Based Blades from IBM. + +config TC35815 + tristate "TOSHIBA TC35815 Ethernet support" + depends on PCI && MIPS + select PHYLIB + +endif # NET_VENDOR_TOSHIBA diff --git a/drivers/net/ethernet/toshiba/Makefile b/drivers/net/ethernet/toshiba/Makefile new file mode 100644 index 000000000000..71d861f55add --- /dev/null +++ b/drivers/net/ethernet/toshiba/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the Toshiba network device drivers. +# + +obj-$(CONFIG_GELIC_NET) += ps3_gelic.o +gelic_wireless-$(CONFIG_GELIC_WIRELESS) += ps3_gelic_wireless.o +ps3_gelic-objs += ps3_gelic_net.o $(gelic_wireless-y) +spidernet-y += spider_net.o spider_net_ethtool.o +obj-$(CONFIG_SPIDER_NET) += spidernet.o ethernet/sun/sungem_phy.o +obj-$(CONFIG_TC35815) += tc35815.o diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c new file mode 100644 index 000000000000..d82a82d9870c --- /dev/null +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c @@ -0,0 +1,1896 @@ +/* + * PS3 gelic network driver. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation + * + * This file is based on: spider_net.c + * + * (C) Copyright IBM Corp. 2005 + * + * Authors : Utz Bacher + * Jens Osterkamp + * + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#undef DEBUG + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ps3_gelic_net.h" +#include "ps3_gelic_wireless.h" + +#define DRV_NAME "Gelic Network Driver" +#define DRV_VERSION "2.0" + +MODULE_AUTHOR("SCE Inc."); +MODULE_DESCRIPTION("Gelic Network driver"); +MODULE_LICENSE("GPL"); + + +static inline void gelic_card_enable_rxdmac(struct gelic_card *card); +static inline void gelic_card_disable_rxdmac(struct gelic_card *card); +static inline void gelic_card_disable_txdmac(struct gelic_card *card); +static inline void gelic_card_reset_chain(struct gelic_card *card, + struct gelic_descr_chain *chain, + struct gelic_descr *start_descr); + +/* set irq_mask */ +int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask) +{ + int status; + + status = lv1_net_set_interrupt_mask(bus_id(card), dev_id(card), + mask, 0); + if (status) + dev_info(ctodev(card), + "%s failed %d\n", __func__, status); + return status; +} + +static inline void gelic_card_rx_irq_on(struct gelic_card *card) +{ + card->irq_mask |= GELIC_CARD_RXINT; + gelic_card_set_irq_mask(card, card->irq_mask); +} +static inline void gelic_card_rx_irq_off(struct gelic_card *card) +{ + card->irq_mask &= ~GELIC_CARD_RXINT; + gelic_card_set_irq_mask(card, card->irq_mask); +} + +static void gelic_card_get_ether_port_status(struct gelic_card *card, + int inform) +{ + u64 v2; + struct net_device *ether_netdev; + + lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_GET_ETH_PORT_STATUS, + GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0, + &card->ether_port_status, &v2); + + if (inform) { + ether_netdev = card->netdev[GELIC_PORT_ETHERNET_0]; + if (card->ether_port_status & GELIC_LV1_ETHER_LINK_UP) + netif_carrier_on(ether_netdev); + else + netif_carrier_off(ether_netdev); + } +} + +static int gelic_card_set_link_mode(struct gelic_card *card, int mode) +{ + int status; + u64 v1, v2; + + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_SET_NEGOTIATION_MODE, + GELIC_LV1_PHY_ETHERNET_0, mode, 0, &v1, &v2); + if (status) { + pr_info("%s: failed setting negotiation mode %d\n", __func__, + status); + return -EBUSY; + } + + card->link_mode = mode; + return 0; +} + +void gelic_card_up(struct gelic_card *card) +{ + pr_debug("%s: called\n", __func__); + mutex_lock(&card->updown_lock); + if (atomic_inc_return(&card->users) == 1) { + pr_debug("%s: real do\n", __func__); + /* enable irq */ + gelic_card_set_irq_mask(card, card->irq_mask); + /* start rx */ + gelic_card_enable_rxdmac(card); + + napi_enable(&card->napi); + } + mutex_unlock(&card->updown_lock); + pr_debug("%s: done\n", __func__); +} + +void gelic_card_down(struct gelic_card *card) +{ + u64 mask; + pr_debug("%s: called\n", __func__); + mutex_lock(&card->updown_lock); + if (atomic_dec_if_positive(&card->users) == 0) { + pr_debug("%s: real do\n", __func__); + napi_disable(&card->napi); + /* + * Disable irq. Wireless interrupts will + * be disabled later if any + */ + mask = card->irq_mask & (GELIC_CARD_WLAN_EVENT_RECEIVED | + GELIC_CARD_WLAN_COMMAND_COMPLETED); + gelic_card_set_irq_mask(card, mask); + /* stop rx */ + gelic_card_disable_rxdmac(card); + gelic_card_reset_chain(card, &card->rx_chain, + card->descr + GELIC_NET_TX_DESCRIPTORS); + /* stop tx */ + gelic_card_disable_txdmac(card); + } + mutex_unlock(&card->updown_lock); + pr_debug("%s: done\n", __func__); +} + +/** + * gelic_descr_get_status -- returns the status of a descriptor + * @descr: descriptor to look at + * + * returns the status as in the dmac_cmd_status field of the descriptor + */ +static enum gelic_descr_dma_status +gelic_descr_get_status(struct gelic_descr *descr) +{ + return be32_to_cpu(descr->dmac_cmd_status) & GELIC_DESCR_DMA_STAT_MASK; +} + +/** + * gelic_descr_set_status -- sets the status of a descriptor + * @descr: descriptor to change + * @status: status to set in the descriptor + * + * changes the status to the specified value. Doesn't change other bits + * in the status + */ +static void gelic_descr_set_status(struct gelic_descr *descr, + enum gelic_descr_dma_status status) +{ + descr->dmac_cmd_status = cpu_to_be32(status | + (be32_to_cpu(descr->dmac_cmd_status) & + ~GELIC_DESCR_DMA_STAT_MASK)); + /* + * dma_cmd_status field is used to indicate whether the descriptor + * is valid or not. + * Usually caller of this function wants to inform that to the + * hardware, so we assure here the hardware sees the change. + */ + wmb(); +} + +/** + * gelic_card_free_chain - free descriptor chain + * @card: card structure + * @descr_in: address of desc + */ +static void gelic_card_free_chain(struct gelic_card *card, + struct gelic_descr *descr_in) +{ + struct gelic_descr *descr; + + for (descr = descr_in; descr && descr->bus_addr; descr = descr->next) { + dma_unmap_single(ctodev(card), descr->bus_addr, + GELIC_DESCR_SIZE, DMA_BIDIRECTIONAL); + descr->bus_addr = 0; + } +} + +/** + * gelic_card_init_chain - links descriptor chain + * @card: card structure + * @chain: address of chain + * @start_descr: address of descriptor array + * @no: number of descriptors + * + * we manage a circular list that mirrors the hardware structure, + * except that the hardware uses bus addresses. + * + * returns 0 on success, <0 on failure + */ +static int __devinit gelic_card_init_chain(struct gelic_card *card, + struct gelic_descr_chain *chain, + struct gelic_descr *start_descr, + int no) +{ + int i; + struct gelic_descr *descr; + + descr = start_descr; + memset(descr, 0, sizeof(*descr) * no); + + /* set up the hardware pointers in each descriptor */ + for (i = 0; i < no; i++, descr++) { + gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE); + descr->bus_addr = + dma_map_single(ctodev(card), descr, + GELIC_DESCR_SIZE, + DMA_BIDIRECTIONAL); + + if (!descr->bus_addr) + goto iommu_error; + + descr->next = descr + 1; + descr->prev = descr - 1; + } + /* make them as ring */ + (descr - 1)->next = start_descr; + start_descr->prev = (descr - 1); + + /* chain bus addr of hw descriptor */ + descr = start_descr; + for (i = 0; i < no; i++, descr++) { + descr->next_descr_addr = cpu_to_be32(descr->next->bus_addr); + } + + chain->head = start_descr; + chain->tail = start_descr; + + /* do not chain last hw descriptor */ + (descr - 1)->next_descr_addr = 0; + + return 0; + +iommu_error: + for (i--, descr--; 0 <= i; i--, descr--) + if (descr->bus_addr) + dma_unmap_single(ctodev(card), descr->bus_addr, + GELIC_DESCR_SIZE, + DMA_BIDIRECTIONAL); + return -ENOMEM; +} + +/** + * gelic_card_reset_chain - reset status of a descriptor chain + * @card: card structure + * @chain: address of chain + * @start_descr: address of descriptor array + * + * Reset the status of dma descriptors to ready state + * and re-initialize the hardware chain for later use + */ +static void gelic_card_reset_chain(struct gelic_card *card, + struct gelic_descr_chain *chain, + struct gelic_descr *start_descr) +{ + struct gelic_descr *descr; + + for (descr = start_descr; start_descr != descr->next; descr++) { + gelic_descr_set_status(descr, GELIC_DESCR_DMA_CARDOWNED); + descr->next_descr_addr = cpu_to_be32(descr->next->bus_addr); + } + + chain->head = start_descr; + chain->tail = (descr - 1); + + (descr - 1)->next_descr_addr = 0; +} +/** + * gelic_descr_prepare_rx - reinitializes a rx descriptor + * @card: card structure + * @descr: descriptor to re-init + * + * return 0 on success, <0 on failure + * + * allocates a new rx skb, iommu-maps it and attaches it to the descriptor. + * Activate the descriptor state-wise + */ +static int gelic_descr_prepare_rx(struct gelic_card *card, + struct gelic_descr *descr) +{ + int offset; + unsigned int bufsize; + + if (gelic_descr_get_status(descr) != GELIC_DESCR_DMA_NOT_IN_USE) + dev_info(ctodev(card), "%s: ERROR status\n", __func__); + /* we need to round up the buffer size to a multiple of 128 */ + bufsize = ALIGN(GELIC_NET_MAX_MTU, GELIC_NET_RXBUF_ALIGN); + + /* and we need to have it 128 byte aligned, therefore we allocate a + * bit more */ + descr->skb = dev_alloc_skb(bufsize + GELIC_NET_RXBUF_ALIGN - 1); + if (!descr->skb) { + descr->buf_addr = 0; /* tell DMAC don't touch memory */ + dev_info(ctodev(card), + "%s:allocate skb failed !!\n", __func__); + return -ENOMEM; + } + descr->buf_size = cpu_to_be32(bufsize); + descr->dmac_cmd_status = 0; + descr->result_size = 0; + descr->valid_size = 0; + descr->data_error = 0; + + offset = ((unsigned long)descr->skb->data) & + (GELIC_NET_RXBUF_ALIGN - 1); + if (offset) + skb_reserve(descr->skb, GELIC_NET_RXBUF_ALIGN - offset); + /* io-mmu-map the skb */ + descr->buf_addr = cpu_to_be32(dma_map_single(ctodev(card), + descr->skb->data, + GELIC_NET_MAX_MTU, + DMA_FROM_DEVICE)); + if (!descr->buf_addr) { + dev_kfree_skb_any(descr->skb); + descr->skb = NULL; + dev_info(ctodev(card), + "%s:Could not iommu-map rx buffer\n", __func__); + gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE); + return -ENOMEM; + } else { + gelic_descr_set_status(descr, GELIC_DESCR_DMA_CARDOWNED); + return 0; + } +} + +/** + * gelic_card_release_rx_chain - free all skb of rx descr + * @card: card structure + * + */ +static void gelic_card_release_rx_chain(struct gelic_card *card) +{ + struct gelic_descr *descr = card->rx_chain.head; + + do { + if (descr->skb) { + dma_unmap_single(ctodev(card), + be32_to_cpu(descr->buf_addr), + descr->skb->len, + DMA_FROM_DEVICE); + descr->buf_addr = 0; + dev_kfree_skb_any(descr->skb); + descr->skb = NULL; + gelic_descr_set_status(descr, + GELIC_DESCR_DMA_NOT_IN_USE); + } + descr = descr->next; + } while (descr != card->rx_chain.head); +} + +/** + * gelic_card_fill_rx_chain - fills descriptors/skbs in the rx chains + * @card: card structure + * + * fills all descriptors in the rx chain: allocates skbs + * and iommu-maps them. + * returns 0 on success, < 0 on failure + */ +static int gelic_card_fill_rx_chain(struct gelic_card *card) +{ + struct gelic_descr *descr = card->rx_chain.head; + int ret; + + do { + if (!descr->skb) { + ret = gelic_descr_prepare_rx(card, descr); + if (ret) + goto rewind; + } + descr = descr->next; + } while (descr != card->rx_chain.head); + + return 0; +rewind: + gelic_card_release_rx_chain(card); + return ret; +} + +/** + * gelic_card_alloc_rx_skbs - allocates rx skbs in rx descriptor chains + * @card: card structure + * + * returns 0 on success, < 0 on failure + */ +static int __devinit gelic_card_alloc_rx_skbs(struct gelic_card *card) +{ + struct gelic_descr_chain *chain; + int ret; + chain = &card->rx_chain; + ret = gelic_card_fill_rx_chain(card); + chain->tail = card->rx_top->prev; /* point to the last */ + return ret; +} + +/** + * gelic_descr_release_tx - processes a used tx descriptor + * @card: card structure + * @descr: descriptor to release + * + * releases a used tx descriptor (unmapping, freeing of skb) + */ +static void gelic_descr_release_tx(struct gelic_card *card, + struct gelic_descr *descr) +{ + struct sk_buff *skb = descr->skb; + + BUG_ON(!(be32_to_cpu(descr->data_status) & GELIC_DESCR_TX_TAIL)); + + dma_unmap_single(ctodev(card), be32_to_cpu(descr->buf_addr), skb->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + + descr->buf_addr = 0; + descr->buf_size = 0; + descr->next_descr_addr = 0; + descr->result_size = 0; + descr->valid_size = 0; + descr->data_status = 0; + descr->data_error = 0; + descr->skb = NULL; + + /* set descr status */ + gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE); +} + +static void gelic_card_stop_queues(struct gelic_card *card) +{ + netif_stop_queue(card->netdev[GELIC_PORT_ETHERNET_0]); + + if (card->netdev[GELIC_PORT_WIRELESS]) + netif_stop_queue(card->netdev[GELIC_PORT_WIRELESS]); +} +static void gelic_card_wake_queues(struct gelic_card *card) +{ + netif_wake_queue(card->netdev[GELIC_PORT_ETHERNET_0]); + + if (card->netdev[GELIC_PORT_WIRELESS]) + netif_wake_queue(card->netdev[GELIC_PORT_WIRELESS]); +} +/** + * gelic_card_release_tx_chain - processes sent tx descriptors + * @card: adapter structure + * @stop: net_stop sequence + * + * releases the tx descriptors that gelic has finished with + */ +static void gelic_card_release_tx_chain(struct gelic_card *card, int stop) +{ + struct gelic_descr_chain *tx_chain; + enum gelic_descr_dma_status status; + struct net_device *netdev; + int release = 0; + + for (tx_chain = &card->tx_chain; + tx_chain->head != tx_chain->tail && tx_chain->tail; + tx_chain->tail = tx_chain->tail->next) { + status = gelic_descr_get_status(tx_chain->tail); + netdev = tx_chain->tail->skb->dev; + switch (status) { + case GELIC_DESCR_DMA_RESPONSE_ERROR: + case GELIC_DESCR_DMA_PROTECTION_ERROR: + case GELIC_DESCR_DMA_FORCE_END: + if (printk_ratelimit()) + dev_info(ctodev(card), + "%s: forcing end of tx descriptor " \ + "with status %x\n", + __func__, status); + netdev->stats.tx_dropped++; + break; + + case GELIC_DESCR_DMA_COMPLETE: + if (tx_chain->tail->skb) { + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += + tx_chain->tail->skb->len; + } + break; + + case GELIC_DESCR_DMA_CARDOWNED: + /* pending tx request */ + default: + /* any other value (== GELIC_DESCR_DMA_NOT_IN_USE) */ + if (!stop) + goto out; + } + gelic_descr_release_tx(card, tx_chain->tail); + release ++; + } +out: + if (!stop && release) + gelic_card_wake_queues(card); +} + +/** + * gelic_net_set_multi - sets multicast addresses and promisc flags + * @netdev: interface device structure + * + * gelic_net_set_multi configures multicast addresses as needed for the + * netdev interface. It also sets up multicast, allmulti and promisc + * flags appropriately + */ +void gelic_net_set_multi(struct net_device *netdev) +{ + struct gelic_card *card = netdev_card(netdev); + struct netdev_hw_addr *ha; + unsigned int i; + uint8_t *p; + u64 addr; + int status; + + /* clear all multicast address */ + status = lv1_net_remove_multicast_address(bus_id(card), dev_id(card), + 0, 1); + if (status) + dev_err(ctodev(card), + "lv1_net_remove_multicast_address failed %d\n", + status); + /* set broadcast address */ + status = lv1_net_add_multicast_address(bus_id(card), dev_id(card), + GELIC_NET_BROADCAST_ADDR, 0); + if (status) + dev_err(ctodev(card), + "lv1_net_add_multicast_address failed, %d\n", + status); + + if ((netdev->flags & IFF_ALLMULTI) || + (netdev_mc_count(netdev) > GELIC_NET_MC_COUNT_MAX)) { + status = lv1_net_add_multicast_address(bus_id(card), + dev_id(card), + 0, 1); + if (status) + dev_err(ctodev(card), + "lv1_net_add_multicast_address failed, %d\n", + status); + return; + } + + /* set multicast addresses */ + netdev_for_each_mc_addr(ha, netdev) { + addr = 0; + p = ha->addr; + for (i = 0; i < ETH_ALEN; i++) { + addr <<= 8; + addr |= *p++; + } + status = lv1_net_add_multicast_address(bus_id(card), + dev_id(card), + addr, 0); + if (status) + dev_err(ctodev(card), + "lv1_net_add_multicast_address failed, %d\n", + status); + } +} + +/** + * gelic_card_enable_rxdmac - enables the receive DMA controller + * @card: card structure + * + * gelic_card_enable_rxdmac enables the DMA controller by setting RX_DMA_EN + * in the GDADMACCNTR register + */ +static inline void gelic_card_enable_rxdmac(struct gelic_card *card) +{ + int status; + +#ifdef DEBUG + if (gelic_descr_get_status(card->rx_chain.head) != + GELIC_DESCR_DMA_CARDOWNED) { + printk(KERN_ERR "%s: status=%x\n", __func__, + be32_to_cpu(card->rx_chain.head->dmac_cmd_status)); + printk(KERN_ERR "%s: nextphy=%x\n", __func__, + be32_to_cpu(card->rx_chain.head->next_descr_addr)); + printk(KERN_ERR "%s: head=%p\n", __func__, + card->rx_chain.head); + } +#endif + status = lv1_net_start_rx_dma(bus_id(card), dev_id(card), + card->rx_chain.head->bus_addr, 0); + if (status) + dev_info(ctodev(card), + "lv1_net_start_rx_dma failed, status=%d\n", status); +} + +/** + * gelic_card_disable_rxdmac - disables the receive DMA controller + * @card: card structure + * + * gelic_card_disable_rxdmac terminates processing on the DMA controller by + * turing off DMA and issuing a force end + */ +static inline void gelic_card_disable_rxdmac(struct gelic_card *card) +{ + int status; + + /* this hvc blocks until the DMA in progress really stopped */ + status = lv1_net_stop_rx_dma(bus_id(card), dev_id(card), 0); + if (status) + dev_err(ctodev(card), + "lv1_net_stop_rx_dma failed, %d\n", status); +} + +/** + * gelic_card_disable_txdmac - disables the transmit DMA controller + * @card: card structure + * + * gelic_card_disable_txdmac terminates processing on the DMA controller by + * turing off DMA and issuing a force end + */ +static inline void gelic_card_disable_txdmac(struct gelic_card *card) +{ + int status; + + /* this hvc blocks until the DMA in progress really stopped */ + status = lv1_net_stop_tx_dma(bus_id(card), dev_id(card), 0); + if (status) + dev_err(ctodev(card), + "lv1_net_stop_tx_dma failed, status=%d\n", status); +} + +/** + * gelic_net_stop - called upon ifconfig down + * @netdev: interface device structure + * + * always returns 0 + */ +int gelic_net_stop(struct net_device *netdev) +{ + struct gelic_card *card; + + pr_debug("%s: start\n", __func__); + + netif_stop_queue(netdev); + netif_carrier_off(netdev); + + card = netdev_card(netdev); + gelic_card_down(card); + + pr_debug("%s: done\n", __func__); + return 0; +} + +/** + * gelic_card_get_next_tx_descr - returns the next available tx descriptor + * @card: device structure to get descriptor from + * + * returns the address of the next descriptor, or NULL if not available. + */ +static struct gelic_descr * +gelic_card_get_next_tx_descr(struct gelic_card *card) +{ + if (!card->tx_chain.head) + return NULL; + /* see if the next descriptor is free */ + if (card->tx_chain.tail != card->tx_chain.head->next && + gelic_descr_get_status(card->tx_chain.head) == + GELIC_DESCR_DMA_NOT_IN_USE) + return card->tx_chain.head; + else + return NULL; + +} + +/** + * gelic_net_set_txdescr_cmdstat - sets the tx descriptor command field + * @descr: descriptor structure to fill out + * @skb: packet to consider + * + * fills out the command and status field of the descriptor structure, + * depending on hardware checksum settings. This function assumes a wmb() + * has executed before. + */ +static void gelic_descr_set_tx_cmdstat(struct gelic_descr *descr, + struct sk_buff *skb) +{ + if (skb->ip_summed != CHECKSUM_PARTIAL) + descr->dmac_cmd_status = + cpu_to_be32(GELIC_DESCR_DMA_CMD_NO_CHKSUM | + GELIC_DESCR_TX_DMA_FRAME_TAIL); + else { + /* is packet ip? + * if yes: tcp? udp? */ + if (skb->protocol == htons(ETH_P_IP)) { + if (ip_hdr(skb)->protocol == IPPROTO_TCP) + descr->dmac_cmd_status = + cpu_to_be32(GELIC_DESCR_DMA_CMD_TCP_CHKSUM | + GELIC_DESCR_TX_DMA_FRAME_TAIL); + + else if (ip_hdr(skb)->protocol == IPPROTO_UDP) + descr->dmac_cmd_status = + cpu_to_be32(GELIC_DESCR_DMA_CMD_UDP_CHKSUM | + GELIC_DESCR_TX_DMA_FRAME_TAIL); + else /* + * the stack should checksum non-tcp and non-udp + * packets on his own: NETIF_F_IP_CSUM + */ + descr->dmac_cmd_status = + cpu_to_be32(GELIC_DESCR_DMA_CMD_NO_CHKSUM | + GELIC_DESCR_TX_DMA_FRAME_TAIL); + } + } +} + +static inline struct sk_buff *gelic_put_vlan_tag(struct sk_buff *skb, + unsigned short tag) +{ + struct vlan_ethhdr *veth; + static unsigned int c; + + if (skb_headroom(skb) < VLAN_HLEN) { + struct sk_buff *sk_tmp = skb; + pr_debug("%s: hd=%d c=%ud\n", __func__, skb_headroom(skb), c); + skb = skb_realloc_headroom(sk_tmp, VLAN_HLEN); + if (!skb) + return NULL; + dev_kfree_skb_any(sk_tmp); + } + veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN); + + /* Move the mac addresses to the top of buffer */ + memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN); + + veth->h_vlan_proto = cpu_to_be16(ETH_P_8021Q); + veth->h_vlan_TCI = htons(tag); + + return skb; +} + +/** + * gelic_descr_prepare_tx - setup a descriptor for sending packets + * @card: card structure + * @descr: descriptor structure + * @skb: packet to use + * + * returns 0 on success, <0 on failure. + * + */ +static int gelic_descr_prepare_tx(struct gelic_card *card, + struct gelic_descr *descr, + struct sk_buff *skb) +{ + dma_addr_t buf; + + if (card->vlan_required) { + struct sk_buff *skb_tmp; + enum gelic_port_type type; + + type = netdev_port(skb->dev)->type; + skb_tmp = gelic_put_vlan_tag(skb, + card->vlan[type].tx); + if (!skb_tmp) + return -ENOMEM; + skb = skb_tmp; + } + + buf = dma_map_single(ctodev(card), skb->data, skb->len, DMA_TO_DEVICE); + + if (!buf) { + dev_err(ctodev(card), + "dma map 2 failed (%p, %i). Dropping packet\n", + skb->data, skb->len); + return -ENOMEM; + } + + descr->buf_addr = cpu_to_be32(buf); + descr->buf_size = cpu_to_be32(skb->len); + descr->skb = skb; + descr->data_status = 0; + descr->next_descr_addr = 0; /* terminate hw descr */ + gelic_descr_set_tx_cmdstat(descr, skb); + + /* bump free descriptor pointer */ + card->tx_chain.head = descr->next; + return 0; +} + +/** + * gelic_card_kick_txdma - enables TX DMA processing + * @card: card structure + * @descr: descriptor address to enable TX processing at + * + */ +static int gelic_card_kick_txdma(struct gelic_card *card, + struct gelic_descr *descr) +{ + int status = 0; + + if (card->tx_dma_progress) + return 0; + + if (gelic_descr_get_status(descr) == GELIC_DESCR_DMA_CARDOWNED) { + card->tx_dma_progress = 1; + status = lv1_net_start_tx_dma(bus_id(card), dev_id(card), + descr->bus_addr, 0); + if (status) { + card->tx_dma_progress = 0; + dev_info(ctodev(card), "lv1_net_start_txdma failed," \ + "status=%d\n", status); + } + } + return status; +} + +/** + * gelic_net_xmit - transmits a frame over the device + * @skb: packet to send out + * @netdev: interface device structure + * + * returns 0 on success, <0 on failure + */ +int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct gelic_card *card = netdev_card(netdev); + struct gelic_descr *descr; + int result; + unsigned long flags; + + spin_lock_irqsave(&card->tx_lock, flags); + + gelic_card_release_tx_chain(card, 0); + + descr = gelic_card_get_next_tx_descr(card); + if (!descr) { + /* + * no more descriptors free + */ + gelic_card_stop_queues(card); + spin_unlock_irqrestore(&card->tx_lock, flags); + return NETDEV_TX_BUSY; + } + + result = gelic_descr_prepare_tx(card, descr, skb); + if (result) { + /* + * DMA map failed. As chances are that failure + * would continue, just release skb and return + */ + netdev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + spin_unlock_irqrestore(&card->tx_lock, flags); + return NETDEV_TX_OK; + } + /* + * link this prepared descriptor to previous one + * to achieve high performance + */ + descr->prev->next_descr_addr = cpu_to_be32(descr->bus_addr); + /* + * as hardware descriptor is modified in the above lines, + * ensure that the hardware sees it + */ + wmb(); + if (gelic_card_kick_txdma(card, descr)) { + /* + * kick failed. + * release descriptor which was just prepared + */ + netdev->stats.tx_dropped++; + /* don't trigger BUG_ON() in gelic_descr_release_tx */ + descr->data_status = cpu_to_be32(GELIC_DESCR_TX_TAIL); + gelic_descr_release_tx(card, descr); + /* reset head */ + card->tx_chain.head = descr; + /* reset hw termination */ + descr->prev->next_descr_addr = 0; + dev_info(ctodev(card), "%s: kick failure\n", __func__); + } + + spin_unlock_irqrestore(&card->tx_lock, flags); + return NETDEV_TX_OK; +} + +/** + * gelic_net_pass_skb_up - takes an skb from a descriptor and passes it on + * @descr: descriptor to process + * @card: card structure + * @netdev: net_device structure to be passed packet + * + * iommu-unmaps the skb, fills out skb structure and passes the data to the + * stack. The descriptor state is not changed. + */ +static void gelic_net_pass_skb_up(struct gelic_descr *descr, + struct gelic_card *card, + struct net_device *netdev) + +{ + struct sk_buff *skb = descr->skb; + u32 data_status, data_error; + + data_status = be32_to_cpu(descr->data_status); + data_error = be32_to_cpu(descr->data_error); + /* unmap skb buffer */ + dma_unmap_single(ctodev(card), be32_to_cpu(descr->buf_addr), + GELIC_NET_MAX_MTU, + DMA_FROM_DEVICE); + + skb_put(skb, be32_to_cpu(descr->valid_size)? + be32_to_cpu(descr->valid_size) : + be32_to_cpu(descr->result_size)); + if (!descr->valid_size) + dev_info(ctodev(card), "buffer full %x %x %x\n", + be32_to_cpu(descr->result_size), + be32_to_cpu(descr->buf_size), + be32_to_cpu(descr->dmac_cmd_status)); + + descr->skb = NULL; + /* + * the card put 2 bytes vlan tag in front + * of the ethernet frame + */ + skb_pull(skb, 2); + skb->protocol = eth_type_trans(skb, netdev); + + /* checksum offload */ + if (netdev->features & NETIF_F_RXCSUM) { + if ((data_status & GELIC_DESCR_DATA_STATUS_CHK_MASK) && + (!(data_error & GELIC_DESCR_DATA_ERROR_CHK_MASK))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb_checksum_none_assert(skb); + } else + skb_checksum_none_assert(skb); + + /* update netdevice statistics */ + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += skb->len; + + /* pass skb up to stack */ + netif_receive_skb(skb); +} + +/** + * gelic_card_decode_one_descr - processes an rx descriptor + * @card: card structure + * + * returns 1 if a packet has been sent to the stack, otherwise 0 + * + * processes an rx descriptor by iommu-unmapping the data buffer and passing + * the packet up to the stack + */ +static int gelic_card_decode_one_descr(struct gelic_card *card) +{ + enum gelic_descr_dma_status status; + struct gelic_descr_chain *chain = &card->rx_chain; + struct gelic_descr *descr = chain->head; + struct net_device *netdev = NULL; + int dmac_chain_ended; + + status = gelic_descr_get_status(descr); + + if (status == GELIC_DESCR_DMA_CARDOWNED) + return 0; + + if (status == GELIC_DESCR_DMA_NOT_IN_USE) { + dev_dbg(ctodev(card), "dormant descr? %p\n", descr); + return 0; + } + + /* netdevice select */ + if (card->vlan_required) { + unsigned int i; + u16 vid; + vid = *(u16 *)(descr->skb->data) & VLAN_VID_MASK; + for (i = 0; i < GELIC_PORT_MAX; i++) { + if (card->vlan[i].rx == vid) { + netdev = card->netdev[i]; + break; + } + } + if (GELIC_PORT_MAX <= i) { + pr_info("%s: unknown packet vid=%x\n", __func__, vid); + goto refill; + } + } else + netdev = card->netdev[GELIC_PORT_ETHERNET_0]; + + if ((status == GELIC_DESCR_DMA_RESPONSE_ERROR) || + (status == GELIC_DESCR_DMA_PROTECTION_ERROR) || + (status == GELIC_DESCR_DMA_FORCE_END)) { + dev_info(ctodev(card), "dropping RX descriptor with state %x\n", + status); + netdev->stats.rx_dropped++; + goto refill; + } + + if (status == GELIC_DESCR_DMA_BUFFER_FULL) { + /* + * Buffer full would occur if and only if + * the frame length was longer than the size of this + * descriptor's buffer. If the frame length was equal + * to or shorter than buffer'size, FRAME_END condition + * would occur. + * Anyway this frame was longer than the MTU, + * just drop it. + */ + dev_info(ctodev(card), "overlength frame\n"); + goto refill; + } + /* + * descriptors any other than FRAME_END here should + * be treated as error. + */ + if (status != GELIC_DESCR_DMA_FRAME_END) { + dev_dbg(ctodev(card), "RX descriptor with state %x\n", + status); + goto refill; + } + + /* ok, we've got a packet in descr */ + gelic_net_pass_skb_up(descr, card, netdev); +refill: + + /* is the current descriptor terminated with next_descr == NULL? */ + dmac_chain_ended = + be32_to_cpu(descr->dmac_cmd_status) & + GELIC_DESCR_RX_DMA_CHAIN_END; + /* + * So that always DMAC can see the end + * of the descriptor chain to avoid + * from unwanted DMAC overrun. + */ + descr->next_descr_addr = 0; + + /* change the descriptor state: */ + gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE); + + /* + * this call can fail, but for now, just leave this + * decriptor without skb + */ + gelic_descr_prepare_rx(card, descr); + + chain->tail = descr; + chain->head = descr->next; + + /* + * Set this descriptor the end of the chain. + */ + descr->prev->next_descr_addr = cpu_to_be32(descr->bus_addr); + + /* + * If dmac chain was met, DMAC stopped. + * thus re-enable it + */ + + if (dmac_chain_ended) + gelic_card_enable_rxdmac(card); + + return 1; +} + +/** + * gelic_net_poll - NAPI poll function called by the stack to return packets + * @napi: napi structure + * @budget: number of packets we can pass to the stack at most + * + * returns the number of the processed packets + * + */ +static int gelic_net_poll(struct napi_struct *napi, int budget) +{ + struct gelic_card *card = container_of(napi, struct gelic_card, napi); + int packets_done = 0; + + while (packets_done < budget) { + if (!gelic_card_decode_one_descr(card)) + break; + + packets_done++; + } + + if (packets_done < budget) { + napi_complete(napi); + gelic_card_rx_irq_on(card); + } + return packets_done; +} +/** + * gelic_net_change_mtu - changes the MTU of an interface + * @netdev: interface device structure + * @new_mtu: new MTU value + * + * returns 0 on success, <0 on failure + */ +int gelic_net_change_mtu(struct net_device *netdev, int new_mtu) +{ + /* no need to re-alloc skbs or so -- the max mtu is about 2.3k + * and mtu is outbound only anyway */ + if ((new_mtu < GELIC_NET_MIN_MTU) || + (new_mtu > GELIC_NET_MAX_MTU)) { + return -EINVAL; + } + netdev->mtu = new_mtu; + return 0; +} + +/** + * gelic_card_interrupt - event handler for gelic_net + */ +static irqreturn_t gelic_card_interrupt(int irq, void *ptr) +{ + unsigned long flags; + struct gelic_card *card = ptr; + u64 status; + + status = card->irq_status; + + if (!status) + return IRQ_NONE; + + status &= card->irq_mask; + + if (status & GELIC_CARD_RXINT) { + gelic_card_rx_irq_off(card); + napi_schedule(&card->napi); + } + + if (status & GELIC_CARD_TXINT) { + spin_lock_irqsave(&card->tx_lock, flags); + card->tx_dma_progress = 0; + gelic_card_release_tx_chain(card, 0); + /* kick outstanding tx descriptor if any */ + gelic_card_kick_txdma(card, card->tx_chain.tail); + spin_unlock_irqrestore(&card->tx_lock, flags); + } + + /* ether port status changed */ + if (status & GELIC_CARD_PORT_STATUS_CHANGED) + gelic_card_get_ether_port_status(card, 1); + +#ifdef CONFIG_GELIC_WIRELESS + if (status & (GELIC_CARD_WLAN_EVENT_RECEIVED | + GELIC_CARD_WLAN_COMMAND_COMPLETED)) + gelic_wl_interrupt(card->netdev[GELIC_PORT_WIRELESS], status); +#endif + + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/** + * gelic_net_poll_controller - artificial interrupt for netconsole etc. + * @netdev: interface device structure + * + * see Documentation/networking/netconsole.txt + */ +void gelic_net_poll_controller(struct net_device *netdev) +{ + struct gelic_card *card = netdev_card(netdev); + + gelic_card_set_irq_mask(card, 0); + gelic_card_interrupt(netdev->irq, netdev); + gelic_card_set_irq_mask(card, card->irq_mask); +} +#endif /* CONFIG_NET_POLL_CONTROLLER */ + +/** + * gelic_net_open - called upon ifconfig up + * @netdev: interface device structure + * + * returns 0 on success, <0 on failure + * + * gelic_net_open allocates all the descriptors and memory needed for + * operation, sets up multicast list and enables interrupts + */ +int gelic_net_open(struct net_device *netdev) +{ + struct gelic_card *card = netdev_card(netdev); + + dev_dbg(ctodev(card), " -> %s %p\n", __func__, netdev); + + gelic_card_up(card); + + netif_start_queue(netdev); + gelic_card_get_ether_port_status(card, 1); + + dev_dbg(ctodev(card), " <- %s\n", __func__); + return 0; +} + +void gelic_net_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1); + strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1); +} + +static int gelic_ether_get_settings(struct net_device *netdev, + struct ethtool_cmd *cmd) +{ + struct gelic_card *card = netdev_card(netdev); + + gelic_card_get_ether_port_status(card, 0); + + if (card->ether_port_status & GELIC_LV1_ETHER_FULL_DUPLEX) + cmd->duplex = DUPLEX_FULL; + else + cmd->duplex = DUPLEX_HALF; + + switch (card->ether_port_status & GELIC_LV1_ETHER_SPEED_MASK) { + case GELIC_LV1_ETHER_SPEED_10: + ethtool_cmd_speed_set(cmd, SPEED_10); + break; + case GELIC_LV1_ETHER_SPEED_100: + ethtool_cmd_speed_set(cmd, SPEED_100); + break; + case GELIC_LV1_ETHER_SPEED_1000: + ethtool_cmd_speed_set(cmd, SPEED_1000); + break; + default: + pr_info("%s: speed unknown\n", __func__); + ethtool_cmd_speed_set(cmd, SPEED_10); + break; + } + + cmd->supported = SUPPORTED_TP | SUPPORTED_Autoneg | + SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full; + cmd->advertising = cmd->supported; + if (card->link_mode & GELIC_LV1_ETHER_AUTO_NEG) { + cmd->autoneg = AUTONEG_ENABLE; + } else { + cmd->autoneg = AUTONEG_DISABLE; + cmd->advertising &= ~ADVERTISED_Autoneg; + } + cmd->port = PORT_TP; + + return 0; +} + +static int gelic_ether_set_settings(struct net_device *netdev, + struct ethtool_cmd *cmd) +{ + struct gelic_card *card = netdev_card(netdev); + u64 mode; + int ret; + + if (cmd->autoneg == AUTONEG_ENABLE) { + mode = GELIC_LV1_ETHER_AUTO_NEG; + } else { + switch (cmd->speed) { + case SPEED_10: + mode = GELIC_LV1_ETHER_SPEED_10; + break; + case SPEED_100: + mode = GELIC_LV1_ETHER_SPEED_100; + break; + case SPEED_1000: + mode = GELIC_LV1_ETHER_SPEED_1000; + break; + default: + return -EINVAL; + } + if (cmd->duplex == DUPLEX_FULL) + mode |= GELIC_LV1_ETHER_FULL_DUPLEX; + else if (cmd->speed == SPEED_1000) { + pr_info("1000 half duplex is not supported.\n"); + return -EINVAL; + } + } + + ret = gelic_card_set_link_mode(card, mode); + + if (ret) + return ret; + + return 0; +} + +static void gelic_net_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + if (0 <= ps3_compare_firmware_version(2, 2, 0)) + wol->supported = WAKE_MAGIC; + else + wol->supported = 0; + + wol->wolopts = ps3_sys_manager_get_wol() ? wol->supported : 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} +static int gelic_net_set_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + int status; + struct gelic_card *card; + u64 v1, v2; + + if (ps3_compare_firmware_version(2, 2, 0) < 0 || + !capable(CAP_NET_ADMIN)) + return -EPERM; + + if (wol->wolopts & ~WAKE_MAGIC) + return -EINVAL; + + card = netdev_card(netdev); + if (wol->wolopts & WAKE_MAGIC) { + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_SET_WOL, + GELIC_LV1_WOL_MAGIC_PACKET, + 0, GELIC_LV1_WOL_MP_ENABLE, + &v1, &v2); + if (status) { + pr_info("%s: enabling WOL failed %d\n", __func__, + status); + status = -EIO; + goto done; + } + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_SET_WOL, + GELIC_LV1_WOL_ADD_MATCH_ADDR, + 0, GELIC_LV1_WOL_MATCH_ALL, + &v1, &v2); + if (!status) + ps3_sys_manager_set_wol(1); + else { + pr_info("%s: enabling WOL filter failed %d\n", + __func__, status); + status = -EIO; + } + } else { + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_SET_WOL, + GELIC_LV1_WOL_MAGIC_PACKET, + 0, GELIC_LV1_WOL_MP_DISABLE, + &v1, &v2); + if (status) { + pr_info("%s: disabling WOL failed %d\n", __func__, + status); + status = -EIO; + goto done; + } + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_SET_WOL, + GELIC_LV1_WOL_DELETE_MATCH_ADDR, + 0, GELIC_LV1_WOL_MATCH_ALL, + &v1, &v2); + if (!status) + ps3_sys_manager_set_wol(0); + else { + pr_info("%s: removing WOL filter failed %d\n", + __func__, status); + status = -EIO; + } + } +done: + return status; +} + +static const struct ethtool_ops gelic_ether_ethtool_ops = { + .get_drvinfo = gelic_net_get_drvinfo, + .get_settings = gelic_ether_get_settings, + .set_settings = gelic_ether_set_settings, + .get_link = ethtool_op_get_link, + .get_wol = gelic_net_get_wol, + .set_wol = gelic_net_set_wol, +}; + +/** + * gelic_net_tx_timeout_task - task scheduled by the watchdog timeout + * function (to be called not under interrupt status) + * @work: work is context of tx timout task + * + * called as task when tx hangs, resets interface (if interface is up) + */ +static void gelic_net_tx_timeout_task(struct work_struct *work) +{ + struct gelic_card *card = + container_of(work, struct gelic_card, tx_timeout_task); + struct net_device *netdev = card->netdev[GELIC_PORT_ETHERNET_0]; + + dev_info(ctodev(card), "%s:Timed out. Restarting...\n", __func__); + + if (!(netdev->flags & IFF_UP)) + goto out; + + netif_device_detach(netdev); + gelic_net_stop(netdev); + + gelic_net_open(netdev); + netif_device_attach(netdev); + +out: + atomic_dec(&card->tx_timeout_task_counter); +} + +/** + * gelic_net_tx_timeout - called when the tx timeout watchdog kicks in. + * @netdev: interface device structure + * + * called, if tx hangs. Schedules a task that resets the interface + */ +void gelic_net_tx_timeout(struct net_device *netdev) +{ + struct gelic_card *card; + + card = netdev_card(netdev); + atomic_inc(&card->tx_timeout_task_counter); + if (netdev->flags & IFF_UP) + schedule_work(&card->tx_timeout_task); + else + atomic_dec(&card->tx_timeout_task_counter); +} + +static const struct net_device_ops gelic_netdevice_ops = { + .ndo_open = gelic_net_open, + .ndo_stop = gelic_net_stop, + .ndo_start_xmit = gelic_net_xmit, + .ndo_set_multicast_list = gelic_net_set_multi, + .ndo_change_mtu = gelic_net_change_mtu, + .ndo_tx_timeout = gelic_net_tx_timeout, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = gelic_net_poll_controller, +#endif +}; + +/** + * gelic_ether_setup_netdev_ops - initialization of net_device operations + * @netdev: net_device structure + * + * fills out function pointers in the net_device structure + */ +static void __devinit gelic_ether_setup_netdev_ops(struct net_device *netdev, + struct napi_struct *napi) +{ + netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT; + /* NAPI */ + netif_napi_add(netdev, napi, + gelic_net_poll, GELIC_NET_NAPI_WEIGHT); + netdev->ethtool_ops = &gelic_ether_ethtool_ops; + netdev->netdev_ops = &gelic_netdevice_ops; +} + +/** + * gelic_ether_setup_netdev - initialization of net_device + * @netdev: net_device structure + * @card: card structure + * + * Returns 0 on success or <0 on failure + * + * gelic_ether_setup_netdev initializes the net_device structure + * and register it. + **/ +int __devinit gelic_net_setup_netdev(struct net_device *netdev, + struct gelic_card *card) +{ + int status; + u64 v1, v2; + + netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM; + + netdev->features = NETIF_F_IP_CSUM; + if (GELIC_CARD_RX_CSUM_DEFAULT) + netdev->features |= NETIF_F_RXCSUM; + + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_GET_MAC_ADDRESS, + 0, 0, 0, &v1, &v2); + v1 <<= 16; + if (status || !is_valid_ether_addr((u8 *)&v1)) { + dev_info(ctodev(card), + "%s:lv1_net_control GET_MAC_ADDR failed %d\n", + __func__, status); + return -EINVAL; + } + memcpy(netdev->dev_addr, &v1, ETH_ALEN); + + if (card->vlan_required) { + netdev->hard_header_len += VLAN_HLEN; + /* + * As vlan is internally used, + * we can not receive vlan packets + */ + netdev->features |= NETIF_F_VLAN_CHALLENGED; + } + + status = register_netdev(netdev); + if (status) { + dev_err(ctodev(card), "%s:Couldn't register %s %d\n", + __func__, netdev->name, status); + return status; + } + dev_info(ctodev(card), "%s: MAC addr %pM\n", + netdev->name, netdev->dev_addr); + + return 0; +} + +/** + * gelic_alloc_card_net - allocates net_device and card structure + * + * returns the card structure or NULL in case of errors + * + * the card and net_device structures are linked to each other + */ +#define GELIC_ALIGN (32) +static struct gelic_card * __devinit gelic_alloc_card_net(struct net_device **netdev) +{ + struct gelic_card *card; + struct gelic_port *port; + void *p; + size_t alloc_size; + /* + * gelic requires dma descriptor is 32 bytes aligned and + * the hypervisor requires irq_status is 8 bytes aligned. + */ + BUILD_BUG_ON(offsetof(struct gelic_card, irq_status) % 8); + BUILD_BUG_ON(offsetof(struct gelic_card, descr) % 32); + alloc_size = + sizeof(struct gelic_card) + + sizeof(struct gelic_descr) * GELIC_NET_RX_DESCRIPTORS + + sizeof(struct gelic_descr) * GELIC_NET_TX_DESCRIPTORS + + GELIC_ALIGN - 1; + + p = kzalloc(alloc_size, GFP_KERNEL); + if (!p) + return NULL; + card = PTR_ALIGN(p, GELIC_ALIGN); + card->unalign = p; + + /* + * alloc netdev + */ + *netdev = alloc_etherdev(sizeof(struct gelic_port)); + if (!netdev) { + kfree(card->unalign); + return NULL; + } + port = netdev_priv(*netdev); + + /* gelic_port */ + port->netdev = *netdev; + port->card = card; + port->type = GELIC_PORT_ETHERNET_0; + + /* gelic_card */ + card->netdev[GELIC_PORT_ETHERNET_0] = *netdev; + + INIT_WORK(&card->tx_timeout_task, gelic_net_tx_timeout_task); + init_waitqueue_head(&card->waitq); + atomic_set(&card->tx_timeout_task_counter, 0); + mutex_init(&card->updown_lock); + atomic_set(&card->users, 0); + + return card; +} + +static void __devinit gelic_card_get_vlan_info(struct gelic_card *card) +{ + u64 v1, v2; + int status; + unsigned int i; + struct { + int tx; + int rx; + } vlan_id_ix[2] = { + [GELIC_PORT_ETHERNET_0] = { + .tx = GELIC_LV1_VLAN_TX_ETHERNET_0, + .rx = GELIC_LV1_VLAN_RX_ETHERNET_0 + }, + [GELIC_PORT_WIRELESS] = { + .tx = GELIC_LV1_VLAN_TX_WIRELESS, + .rx = GELIC_LV1_VLAN_RX_WIRELESS + } + }; + + for (i = 0; i < ARRAY_SIZE(vlan_id_ix); i++) { + /* tx tag */ + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_GET_VLAN_ID, + vlan_id_ix[i].tx, + 0, 0, &v1, &v2); + if (status || !v1) { + if (status != LV1_NO_ENTRY) + dev_dbg(ctodev(card), + "get vlan id for tx(%d) failed(%d)\n", + vlan_id_ix[i].tx, status); + card->vlan[i].tx = 0; + card->vlan[i].rx = 0; + continue; + } + card->vlan[i].tx = (u16)v1; + + /* rx tag */ + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_GET_VLAN_ID, + vlan_id_ix[i].rx, + 0, 0, &v1, &v2); + if (status || !v1) { + if (status != LV1_NO_ENTRY) + dev_info(ctodev(card), + "get vlan id for rx(%d) failed(%d)\n", + vlan_id_ix[i].rx, status); + card->vlan[i].tx = 0; + card->vlan[i].rx = 0; + continue; + } + card->vlan[i].rx = (u16)v1; + + dev_dbg(ctodev(card), "vlan_id[%d] tx=%02x rx=%02x\n", + i, card->vlan[i].tx, card->vlan[i].rx); + } + + if (card->vlan[GELIC_PORT_ETHERNET_0].tx) { + BUG_ON(!card->vlan[GELIC_PORT_WIRELESS].tx); + card->vlan_required = 1; + } else + card->vlan_required = 0; + + /* check wirelss capable firmware */ + if (ps3_compare_firmware_version(1, 6, 0) < 0) { + card->vlan[GELIC_PORT_WIRELESS].tx = 0; + card->vlan[GELIC_PORT_WIRELESS].rx = 0; + } + + dev_info(ctodev(card), "internal vlan %s\n", + card->vlan_required? "enabled" : "disabled"); +} +/** + * ps3_gelic_driver_probe - add a device to the control of this driver + */ +static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev) +{ + struct gelic_card *card; + struct net_device *netdev; + int result; + + pr_debug("%s: called\n", __func__); + result = ps3_open_hv_device(dev); + + if (result) { + dev_dbg(&dev->core, "%s:ps3_open_hv_device failed\n", + __func__); + goto fail_open; + } + + result = ps3_dma_region_create(dev->d_region); + + if (result) { + dev_dbg(&dev->core, "%s:ps3_dma_region_create failed(%d)\n", + __func__, result); + BUG_ON("check region type"); + goto fail_dma_region; + } + + /* alloc card/netdevice */ + card = gelic_alloc_card_net(&netdev); + if (!card) { + dev_info(&dev->core, "%s:gelic_net_alloc_card failed\n", + __func__); + result = -ENOMEM; + goto fail_alloc_card; + } + ps3_system_bus_set_drvdata(dev, card); + card->dev = dev; + + /* get internal vlan info */ + gelic_card_get_vlan_info(card); + + card->link_mode = GELIC_LV1_ETHER_AUTO_NEG; + + /* setup interrupt */ + result = lv1_net_set_interrupt_status_indicator(bus_id(card), + dev_id(card), + ps3_mm_phys_to_lpar(__pa(&card->irq_status)), + 0); + + if (result) { + dev_dbg(&dev->core, + "%s:set_interrupt_status_indicator failed: %s\n", + __func__, ps3_result(result)); + result = -EIO; + goto fail_status_indicator; + } + + result = ps3_sb_event_receive_port_setup(dev, PS3_BINDING_CPU_ANY, + &card->irq); + + if (result) { + dev_info(ctodev(card), + "%s:gelic_net_open_device failed (%d)\n", + __func__, result); + result = -EPERM; + goto fail_alloc_irq; + } + result = request_irq(card->irq, gelic_card_interrupt, + IRQF_DISABLED, netdev->name, card); + + if (result) { + dev_info(ctodev(card), "%s:request_irq failed (%d)\n", + __func__, result); + goto fail_request_irq; + } + + /* setup card structure */ + card->irq_mask = GELIC_CARD_RXINT | GELIC_CARD_TXINT | + GELIC_CARD_PORT_STATUS_CHANGED; + + + if (gelic_card_init_chain(card, &card->tx_chain, + card->descr, GELIC_NET_TX_DESCRIPTORS)) + goto fail_alloc_tx; + if (gelic_card_init_chain(card, &card->rx_chain, + card->descr + GELIC_NET_TX_DESCRIPTORS, + GELIC_NET_RX_DESCRIPTORS)) + goto fail_alloc_rx; + + /* head of chain */ + card->tx_top = card->tx_chain.head; + card->rx_top = card->rx_chain.head; + dev_dbg(ctodev(card), "descr rx %p, tx %p, size %#lx, num %#x\n", + card->rx_top, card->tx_top, sizeof(struct gelic_descr), + GELIC_NET_RX_DESCRIPTORS); + /* allocate rx skbs */ + if (gelic_card_alloc_rx_skbs(card)) + goto fail_alloc_skbs; + + spin_lock_init(&card->tx_lock); + card->tx_dma_progress = 0; + + /* setup net_device structure */ + netdev->irq = card->irq; + SET_NETDEV_DEV(netdev, &card->dev->core); + gelic_ether_setup_netdev_ops(netdev, &card->napi); + result = gelic_net_setup_netdev(netdev, card); + if (result) { + dev_dbg(&dev->core, "%s: setup_netdev failed %d", + __func__, result); + goto fail_setup_netdev; + } + +#ifdef CONFIG_GELIC_WIRELESS + if (gelic_wl_driver_probe(card)) { + dev_dbg(&dev->core, "%s: WL init failed\n", __func__); + goto fail_setup_netdev; + } +#endif + pr_debug("%s: done\n", __func__); + return 0; + +fail_setup_netdev: +fail_alloc_skbs: + gelic_card_free_chain(card, card->rx_chain.head); +fail_alloc_rx: + gelic_card_free_chain(card, card->tx_chain.head); +fail_alloc_tx: + free_irq(card->irq, card); + netdev->irq = NO_IRQ; +fail_request_irq: + ps3_sb_event_receive_port_destroy(dev, card->irq); +fail_alloc_irq: + lv1_net_set_interrupt_status_indicator(bus_id(card), + bus_id(card), + 0, 0); +fail_status_indicator: + ps3_system_bus_set_drvdata(dev, NULL); + kfree(netdev_card(netdev)->unalign); + free_netdev(netdev); +fail_alloc_card: + ps3_dma_region_free(dev->d_region); +fail_dma_region: + ps3_close_hv_device(dev); +fail_open: + return result; +} + +/** + * ps3_gelic_driver_remove - remove a device from the control of this driver + */ + +static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev) +{ + struct gelic_card *card = ps3_system_bus_get_drvdata(dev); + struct net_device *netdev0; + pr_debug("%s: called\n", __func__); + + /* set auto-negotiation */ + gelic_card_set_link_mode(card, GELIC_LV1_ETHER_AUTO_NEG); + +#ifdef CONFIG_GELIC_WIRELESS + gelic_wl_driver_remove(card); +#endif + /* stop interrupt */ + gelic_card_set_irq_mask(card, 0); + + /* turn off DMA, force end */ + gelic_card_disable_rxdmac(card); + gelic_card_disable_txdmac(card); + + /* release chains */ + gelic_card_release_tx_chain(card, 1); + gelic_card_release_rx_chain(card); + + gelic_card_free_chain(card, card->tx_top); + gelic_card_free_chain(card, card->rx_top); + + netdev0 = card->netdev[GELIC_PORT_ETHERNET_0]; + /* disconnect event port */ + free_irq(card->irq, card); + netdev0->irq = NO_IRQ; + ps3_sb_event_receive_port_destroy(card->dev, card->irq); + + wait_event(card->waitq, + atomic_read(&card->tx_timeout_task_counter) == 0); + + lv1_net_set_interrupt_status_indicator(bus_id(card), dev_id(card), + 0 , 0); + + unregister_netdev(netdev0); + kfree(netdev_card(netdev0)->unalign); + free_netdev(netdev0); + + ps3_system_bus_set_drvdata(dev, NULL); + + ps3_dma_region_free(dev->d_region); + + ps3_close_hv_device(dev); + + pr_debug("%s: done\n", __func__); + return 0; +} + +static struct ps3_system_bus_driver ps3_gelic_driver = { + .match_id = PS3_MATCH_ID_GELIC, + .probe = ps3_gelic_driver_probe, + .remove = ps3_gelic_driver_remove, + .shutdown = ps3_gelic_driver_remove, + .core.name = "ps3_gelic_driver", + .core.owner = THIS_MODULE, +}; + +static int __init ps3_gelic_driver_init (void) +{ + return firmware_has_feature(FW_FEATURE_PS3_LV1) + ? ps3_system_bus_driver_register(&ps3_gelic_driver) + : -ENODEV; +} + +static void __exit ps3_gelic_driver_exit (void) +{ + ps3_system_bus_driver_unregister(&ps3_gelic_driver); +} + +module_init(ps3_gelic_driver_init); +module_exit(ps3_gelic_driver_exit); + +MODULE_ALIAS(PS3_MODULE_ALIAS_GELIC); + diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.h b/drivers/net/ethernet/toshiba/ps3_gelic_net.h new file mode 100644 index 000000000000..d3fadfbc3bcc --- /dev/null +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.h @@ -0,0 +1,380 @@ +/* + * PS3 Platfom gelic network driver. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation. + * + * This file is based on: spider_net.h + * + * (C) Copyright IBM Corp. 2005 + * + * Authors : Utz Bacher + * Jens Osterkamp + * + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef _GELIC_NET_H +#define _GELIC_NET_H + +/* descriptors */ +#define GELIC_NET_RX_DESCRIPTORS 128 /* num of descriptors */ +#define GELIC_NET_TX_DESCRIPTORS 128 /* num of descriptors */ + +#define GELIC_NET_MAX_MTU VLAN_ETH_FRAME_LEN +#define GELIC_NET_MIN_MTU VLAN_ETH_ZLEN +#define GELIC_NET_RXBUF_ALIGN 128 +#define GELIC_CARD_RX_CSUM_DEFAULT 1 /* hw chksum */ +#define GELIC_NET_WATCHDOG_TIMEOUT 5*HZ +#define GELIC_NET_NAPI_WEIGHT (GELIC_NET_RX_DESCRIPTORS) +#define GELIC_NET_BROADCAST_ADDR 0xffffffffffffL + +#define GELIC_NET_MC_COUNT_MAX 32 /* multicast address list */ + +/* virtual interrupt status register bits */ + /* INT1 */ +#define GELIC_CARD_TX_RAM_FULL_ERR 0x0000000000000001L +#define GELIC_CARD_RX_RAM_FULL_ERR 0x0000000000000002L +#define GELIC_CARD_TX_SHORT_FRAME_ERR 0x0000000000000004L +#define GELIC_CARD_TX_INVALID_DESCR_ERR 0x0000000000000008L +#define GELIC_CARD_RX_FIFO_FULL_ERR 0x0000000000002000L +#define GELIC_CARD_RX_DESCR_CHAIN_END 0x0000000000004000L +#define GELIC_CARD_RX_INVALID_DESCR_ERR 0x0000000000008000L +#define GELIC_CARD_TX_RESPONCE_ERR 0x0000000000010000L +#define GELIC_CARD_RX_RESPONCE_ERR 0x0000000000100000L +#define GELIC_CARD_TX_PROTECTION_ERR 0x0000000000400000L +#define GELIC_CARD_RX_PROTECTION_ERR 0x0000000004000000L +#define GELIC_CARD_TX_TCP_UDP_CHECKSUM_ERR 0x0000000008000000L +#define GELIC_CARD_PORT_STATUS_CHANGED 0x0000000020000000L +#define GELIC_CARD_WLAN_EVENT_RECEIVED 0x0000000040000000L +#define GELIC_CARD_WLAN_COMMAND_COMPLETED 0x0000000080000000L + /* INT 0 */ +#define GELIC_CARD_TX_FLAGGED_DESCR 0x0004000000000000L +#define GELIC_CARD_RX_FLAGGED_DESCR 0x0040000000000000L +#define GELIC_CARD_TX_TRANSFER_END 0x0080000000000000L +#define GELIC_CARD_TX_DESCR_CHAIN_END 0x0100000000000000L +#define GELIC_CARD_NUMBER_OF_RX_FRAME 0x1000000000000000L +#define GELIC_CARD_ONE_TIME_COUNT_TIMER 0x4000000000000000L +#define GELIC_CARD_FREE_RUN_COUNT_TIMER 0x8000000000000000L + +/* initial interrupt mask */ +#define GELIC_CARD_TXINT GELIC_CARD_TX_DESCR_CHAIN_END + +#define GELIC_CARD_RXINT (GELIC_CARD_RX_DESCR_CHAIN_END | \ + GELIC_CARD_NUMBER_OF_RX_FRAME) + + /* RX descriptor data_status bits */ +enum gelic_descr_rx_status { + GELIC_DESCR_RXDMADU = 0x80000000, /* destination MAC addr unknown */ + GELIC_DESCR_RXLSTFBF = 0x40000000, /* last frame buffer */ + GELIC_DESCR_RXIPCHK = 0x20000000, /* IP checksum performed */ + GELIC_DESCR_RXTCPCHK = 0x10000000, /* TCP/UDP checksup performed */ + GELIC_DESCR_RXWTPKT = 0x00C00000, /* + * wakeup trigger packet + * 01: Magic Packet (TM) + * 10: ARP packet + * 11: Multicast MAC addr + */ + GELIC_DESCR_RXVLNPKT = 0x00200000, /* VLAN packet */ + /* bit 20..16 reserved */ + GELIC_DESCR_RXRRECNUM = 0x0000ff00, /* reception receipt number */ + /* bit 7..0 reserved */ +}; + +#define GELIC_DESCR_DATA_STATUS_CHK_MASK \ + (GELIC_DESCR_RXIPCHK | GELIC_DESCR_RXTCPCHK) + + /* TX descriptor data_status bits */ +enum gelic_descr_tx_status { + GELIC_DESCR_TX_TAIL = 0x00000001, /* gelic treated this + * descriptor was end of + * a tx frame + */ +}; + +/* RX descriptor data error bits */ +enum gelic_descr_rx_error { + /* bit 31 reserved */ + GELIC_DESCR_RXALNERR = 0x40000000, /* alignement error 10/100M */ + GELIC_DESCR_RXOVERERR = 0x20000000, /* oversize error */ + GELIC_DESCR_RXRNTERR = 0x10000000, /* Runt error */ + GELIC_DESCR_RXIPCHKERR = 0x08000000, /* IP checksum error */ + GELIC_DESCR_RXTCPCHKERR = 0x04000000, /* TCP/UDP checksum error */ + GELIC_DESCR_RXDRPPKT = 0x00100000, /* drop packet */ + GELIC_DESCR_RXIPFMTERR = 0x00080000, /* IP packet format error */ + /* bit 18 reserved */ + GELIC_DESCR_RXDATAERR = 0x00020000, /* IP packet format error */ + GELIC_DESCR_RXCALERR = 0x00010000, /* cariier extension length + * error */ + GELIC_DESCR_RXCREXERR = 0x00008000, /* carrier extension error */ + GELIC_DESCR_RXMLTCST = 0x00004000, /* multicast address frame */ + /* bit 13..0 reserved */ +}; +#define GELIC_DESCR_DATA_ERROR_CHK_MASK \ + (GELIC_DESCR_RXIPCHKERR | GELIC_DESCR_RXTCPCHKERR) + +/* DMA command and status (RX and TX)*/ +enum gelic_descr_dma_status { + GELIC_DESCR_DMA_COMPLETE = 0x00000000, /* used in tx */ + GELIC_DESCR_DMA_BUFFER_FULL = 0x00000000, /* used in rx */ + GELIC_DESCR_DMA_RESPONSE_ERROR = 0x10000000, /* used in rx, tx */ + GELIC_DESCR_DMA_PROTECTION_ERROR = 0x20000000, /* used in rx, tx */ + GELIC_DESCR_DMA_FRAME_END = 0x40000000, /* used in rx */ + GELIC_DESCR_DMA_FORCE_END = 0x50000000, /* used in rx, tx */ + GELIC_DESCR_DMA_CARDOWNED = 0xa0000000, /* used in rx, tx */ + GELIC_DESCR_DMA_NOT_IN_USE = 0xb0000000, /* any other value */ +}; + +#define GELIC_DESCR_DMA_STAT_MASK (0xf0000000) + +/* tx descriptor command and status */ +enum gelic_descr_tx_dma_status { + /* [19] */ + GELIC_DESCR_TX_DMA_IKE = 0x00080000, /* IPSEC off */ + /* [18] */ + GELIC_DESCR_TX_DMA_FRAME_TAIL = 0x00040000, /* last descriptor of + * the packet + */ + /* [17..16] */ + GELIC_DESCR_TX_DMA_TCP_CHKSUM = 0x00020000, /* TCP packet */ + GELIC_DESCR_TX_DMA_UDP_CHKSUM = 0x00030000, /* UDP packet */ + GELIC_DESCR_TX_DMA_NO_CHKSUM = 0x00000000, /* no checksum */ + + /* [1] */ + GELIC_DESCR_TX_DMA_CHAIN_END = 0x00000002, /* DMA terminated + * due to chain end + */ +}; + +#define GELIC_DESCR_DMA_CMD_NO_CHKSUM \ + (GELIC_DESCR_DMA_CARDOWNED | GELIC_DESCR_TX_DMA_IKE | \ + GELIC_DESCR_TX_DMA_NO_CHKSUM) + +#define GELIC_DESCR_DMA_CMD_TCP_CHKSUM \ + (GELIC_DESCR_DMA_CARDOWNED | GELIC_DESCR_TX_DMA_IKE | \ + GELIC_DESCR_TX_DMA_TCP_CHKSUM) + +#define GELIC_DESCR_DMA_CMD_UDP_CHKSUM \ + (GELIC_DESCR_DMA_CARDOWNED | GELIC_DESCR_TX_DMA_IKE | \ + GELIC_DESCR_TX_DMA_UDP_CHKSUM) + +enum gelic_descr_rx_dma_status { + /* [ 1 ] */ + GELIC_DESCR_RX_DMA_CHAIN_END = 0x00000002, /* DMA terminated + * due to chain end + */ +}; + +/* for lv1_net_control */ +enum gelic_lv1_net_control_code { + GELIC_LV1_GET_MAC_ADDRESS = 1, + GELIC_LV1_GET_ETH_PORT_STATUS = 2, + GELIC_LV1_SET_NEGOTIATION_MODE = 3, + GELIC_LV1_GET_VLAN_ID = 4, + GELIC_LV1_SET_WOL = 5, + GELIC_LV1_GET_CHANNEL = 6, + GELIC_LV1_POST_WLAN_CMD = 9, + GELIC_LV1_GET_WLAN_CMD_RESULT = 10, + GELIC_LV1_GET_WLAN_EVENT = 11, +}; + +/* for GELIC_LV1_SET_WOL */ +enum gelic_lv1_wol_command { + GELIC_LV1_WOL_MAGIC_PACKET = 1, + GELIC_LV1_WOL_ADD_MATCH_ADDR = 6, + GELIC_LV1_WOL_DELETE_MATCH_ADDR = 7, +}; + +/* for GELIC_LV1_WOL_MAGIC_PACKET */ +enum gelic_lv1_wol_mp_arg { + GELIC_LV1_WOL_MP_DISABLE = 0, + GELIC_LV1_WOL_MP_ENABLE = 1, +}; + +/* for GELIC_LV1_WOL_{ADD,DELETE}_MATCH_ADDR */ +enum gelic_lv1_wol_match_arg { + GELIC_LV1_WOL_MATCH_INDIVIDUAL = 0, + GELIC_LV1_WOL_MATCH_ALL = 1, +}; + +/* status returened from GET_ETH_PORT_STATUS */ +enum gelic_lv1_ether_port_status { + GELIC_LV1_ETHER_LINK_UP = 0x0000000000000001L, + GELIC_LV1_ETHER_FULL_DUPLEX = 0x0000000000000002L, + GELIC_LV1_ETHER_AUTO_NEG = 0x0000000000000004L, + + GELIC_LV1_ETHER_SPEED_10 = 0x0000000000000010L, + GELIC_LV1_ETHER_SPEED_100 = 0x0000000000000020L, + GELIC_LV1_ETHER_SPEED_1000 = 0x0000000000000040L, + GELIC_LV1_ETHER_SPEED_MASK = 0x0000000000000070L, +}; + +enum gelic_lv1_vlan_index { + /* for outgoing packets */ + GELIC_LV1_VLAN_TX_ETHERNET_0 = 0x0000000000000002L, + GELIC_LV1_VLAN_TX_WIRELESS = 0x0000000000000003L, + + /* for incoming packets */ + GELIC_LV1_VLAN_RX_ETHERNET_0 = 0x0000000000000012L, + GELIC_LV1_VLAN_RX_WIRELESS = 0x0000000000000013L, +}; + +enum gelic_lv1_phy { + GELIC_LV1_PHY_ETHERNET_0 = 0x0000000000000002L, +}; + +/* size of hardware part of gelic descriptor */ +#define GELIC_DESCR_SIZE (32) + +enum gelic_port_type { + GELIC_PORT_ETHERNET_0 = 0, + GELIC_PORT_WIRELESS = 1, + GELIC_PORT_MAX +}; + +struct gelic_descr { + /* as defined by the hardware */ + __be32 buf_addr; + __be32 buf_size; + __be32 next_descr_addr; + __be32 dmac_cmd_status; + __be32 result_size; + __be32 valid_size; /* all zeroes for tx */ + __be32 data_status; + __be32 data_error; /* all zeroes for tx */ + + /* used in the driver */ + struct sk_buff *skb; + dma_addr_t bus_addr; + struct gelic_descr *next; + struct gelic_descr *prev; +} __attribute__((aligned(32))); + +struct gelic_descr_chain { + /* we walk from tail to head */ + struct gelic_descr *head; + struct gelic_descr *tail; +}; + +struct gelic_vlan_id { + u16 tx; + u16 rx; +}; + +struct gelic_card { + struct napi_struct napi; + struct net_device *netdev[GELIC_PORT_MAX]; + /* + * hypervisor requires irq_status should be + * 8 bytes aligned, but u64 member is + * always disposed in that manner + */ + u64 irq_status; + u64 irq_mask; + + struct ps3_system_bus_device *dev; + struct gelic_vlan_id vlan[GELIC_PORT_MAX]; + int vlan_required; + + struct gelic_descr_chain tx_chain; + struct gelic_descr_chain rx_chain; + /* + * tx_lock guards tx descriptor list and + * tx_dma_progress. + */ + spinlock_t tx_lock; + int tx_dma_progress; + + struct work_struct tx_timeout_task; + atomic_t tx_timeout_task_counter; + wait_queue_head_t waitq; + + /* only first user should up the card */ + struct mutex updown_lock; + atomic_t users; + + u64 ether_port_status; + int link_mode; + + /* original address returned by kzalloc */ + void *unalign; + + /* + * each netdevice has copy of irq + */ + unsigned int irq; + struct gelic_descr *tx_top, *rx_top; + struct gelic_descr descr[0]; /* must be the last */ +}; + +struct gelic_port { + struct gelic_card *card; + struct net_device *netdev; + enum gelic_port_type type; + long priv[0]; /* long for alignment */ +}; + +static inline struct gelic_card *port_to_card(struct gelic_port *p) +{ + return p->card; +} +static inline struct net_device *port_to_netdev(struct gelic_port *p) +{ + return p->netdev; +} +static inline struct gelic_card *netdev_card(struct net_device *d) +{ + return ((struct gelic_port *)netdev_priv(d))->card; +} +static inline struct gelic_port *netdev_port(struct net_device *d) +{ + return (struct gelic_port *)netdev_priv(d); +} +static inline struct device *ctodev(struct gelic_card *card) +{ + return &card->dev->core; +} +static inline u64 bus_id(struct gelic_card *card) +{ + return card->dev->bus_id; +} +static inline u64 dev_id(struct gelic_card *card) +{ + return card->dev->dev_id; +} + +static inline void *port_priv(struct gelic_port *port) +{ + return port->priv; +} + +extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask); +/* shared netdev ops */ +extern void gelic_card_up(struct gelic_card *card); +extern void gelic_card_down(struct gelic_card *card); +extern int gelic_net_open(struct net_device *netdev); +extern int gelic_net_stop(struct net_device *netdev); +extern int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev); +extern void gelic_net_set_multi(struct net_device *netdev); +extern void gelic_net_tx_timeout(struct net_device *netdev); +extern int gelic_net_change_mtu(struct net_device *netdev, int new_mtu); +extern int gelic_net_setup_netdev(struct net_device *netdev, + struct gelic_card *card); + +/* shared ethtool ops */ +extern void gelic_net_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info); +extern void gelic_net_poll_controller(struct net_device *netdev); + +#endif /* _GELIC_NET_H */ diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c new file mode 100644 index 000000000000..2e62938c0f82 --- /dev/null +++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c @@ -0,0 +1,2681 @@ +/* + * PS3 gelic network driver. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#undef DEBUG + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ps3_gelic_net.h" +#include "ps3_gelic_wireless.h" + + +static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan, + u8 *essid, size_t essid_len); +static int gelic_wl_try_associate(struct net_device *netdev); + +/* + * tables + */ + +/* 802.11b/g channel to freq in MHz */ +static const int channel_freq[] = { + 2412, 2417, 2422, 2427, 2432, + 2437, 2442, 2447, 2452, 2457, + 2462, 2467, 2472, 2484 +}; +#define NUM_CHANNELS ARRAY_SIZE(channel_freq) + +/* in bps */ +static const int bitrate_list[] = { + 1000000, + 2000000, + 5500000, + 11000000, + 6000000, + 9000000, + 12000000, + 18000000, + 24000000, + 36000000, + 48000000, + 54000000 +}; +#define NUM_BITRATES ARRAY_SIZE(bitrate_list) + +/* + * wpa2 support requires the hypervisor version 2.0 or later + */ +static inline int wpa2_capable(void) +{ + return 0 <= ps3_compare_firmware_version(2, 0, 0); +} + +static inline int precise_ie(void) +{ + return 0 <= ps3_compare_firmware_version(2, 2, 0); +} +/* + * post_eurus_cmd helpers + */ +struct eurus_cmd_arg_info { + int pre_arg; /* command requires arg1, arg2 at POST COMMAND */ + int post_arg; /* command requires arg1, arg2 at GET_RESULT */ +}; + +static const struct eurus_cmd_arg_info cmd_info[GELIC_EURUS_CMD_MAX_INDEX] = { + [GELIC_EURUS_CMD_SET_COMMON_CFG] = { .pre_arg = 1}, + [GELIC_EURUS_CMD_SET_WEP_CFG] = { .pre_arg = 1}, + [GELIC_EURUS_CMD_SET_WPA_CFG] = { .pre_arg = 1}, + [GELIC_EURUS_CMD_GET_COMMON_CFG] = { .post_arg = 1}, + [GELIC_EURUS_CMD_GET_WEP_CFG] = { .post_arg = 1}, + [GELIC_EURUS_CMD_GET_WPA_CFG] = { .post_arg = 1}, + [GELIC_EURUS_CMD_GET_RSSI_CFG] = { .post_arg = 1}, + [GELIC_EURUS_CMD_START_SCAN] = { .pre_arg = 1}, + [GELIC_EURUS_CMD_GET_SCAN] = { .post_arg = 1}, +}; + +#ifdef DEBUG +static const char *cmdstr(enum gelic_eurus_command ix) +{ + switch (ix) { + case GELIC_EURUS_CMD_ASSOC: + return "ASSOC"; + case GELIC_EURUS_CMD_DISASSOC: + return "DISASSOC"; + case GELIC_EURUS_CMD_START_SCAN: + return "SCAN"; + case GELIC_EURUS_CMD_GET_SCAN: + return "GET SCAN"; + case GELIC_EURUS_CMD_SET_COMMON_CFG: + return "SET_COMMON_CFG"; + case GELIC_EURUS_CMD_GET_COMMON_CFG: + return "GET_COMMON_CFG"; + case GELIC_EURUS_CMD_SET_WEP_CFG: + return "SET_WEP_CFG"; + case GELIC_EURUS_CMD_GET_WEP_CFG: + return "GET_WEP_CFG"; + case GELIC_EURUS_CMD_SET_WPA_CFG: + return "SET_WPA_CFG"; + case GELIC_EURUS_CMD_GET_WPA_CFG: + return "GET_WPA_CFG"; + case GELIC_EURUS_CMD_GET_RSSI_CFG: + return "GET_RSSI"; + default: + break; + } + return ""; +}; +#else +static inline const char *cmdstr(enum gelic_eurus_command ix) +{ + return ""; +} +#endif + +/* synchronously do eurus commands */ +static void gelic_eurus_sync_cmd_worker(struct work_struct *work) +{ + struct gelic_eurus_cmd *cmd; + struct gelic_card *card; + struct gelic_wl_info *wl; + + u64 arg1, arg2; + + pr_debug("%s: <-\n", __func__); + cmd = container_of(work, struct gelic_eurus_cmd, work); + BUG_ON(cmd_info[cmd->cmd].pre_arg && + cmd_info[cmd->cmd].post_arg); + wl = cmd->wl; + card = port_to_card(wl_port(wl)); + + if (cmd_info[cmd->cmd].pre_arg) { + arg1 = (cmd->buffer) ? + ps3_mm_phys_to_lpar(__pa(cmd->buffer)) : + 0; + arg2 = cmd->buf_size; + } else { + arg1 = 0; + arg2 = 0; + } + init_completion(&wl->cmd_done_intr); + pr_debug("%s: cmd='%s' start\n", __func__, cmdstr(cmd->cmd)); + cmd->status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_POST_WLAN_CMD, + cmd->cmd, arg1, arg2, + &cmd->tag, &cmd->size); + if (cmd->status) { + complete(&cmd->done); + pr_info("%s: cmd issue failed\n", __func__); + return; + } + + wait_for_completion(&wl->cmd_done_intr); + + if (cmd_info[cmd->cmd].post_arg) { + arg1 = ps3_mm_phys_to_lpar(__pa(cmd->buffer)); + arg2 = cmd->buf_size; + } else { + arg1 = 0; + arg2 = 0; + } + + cmd->status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_GET_WLAN_CMD_RESULT, + cmd->tag, arg1, arg2, + &cmd->cmd_status, &cmd->size); +#ifdef DEBUG + if (cmd->status || cmd->cmd_status) { + pr_debug("%s: cmd done tag=%#lx arg1=%#lx, arg2=%#lx\n", __func__, + cmd->tag, arg1, arg2); + pr_debug("%s: cmd done status=%#x cmd_status=%#lx size=%#lx\n", + __func__, cmd->status, cmd->cmd_status, cmd->size); + } +#endif + complete(&cmd->done); + pr_debug("%s: cmd='%s' done\n", __func__, cmdstr(cmd->cmd)); +} + +static struct gelic_eurus_cmd *gelic_eurus_sync_cmd(struct gelic_wl_info *wl, + unsigned int eurus_cmd, + void *buffer, + unsigned int buf_size) +{ + struct gelic_eurus_cmd *cmd; + + /* allocate cmd */ + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return NULL; + + /* initialize members */ + cmd->cmd = eurus_cmd; + cmd->buffer = buffer; + cmd->buf_size = buf_size; + cmd->wl = wl; + INIT_WORK(&cmd->work, gelic_eurus_sync_cmd_worker); + init_completion(&cmd->done); + queue_work(wl->eurus_cmd_queue, &cmd->work); + + /* wait for command completion */ + wait_for_completion(&cmd->done); + + return cmd; +} + +static u32 gelic_wl_get_link(struct net_device *netdev) +{ + struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); + u32 ret; + + pr_debug("%s: <-\n", __func__); + mutex_lock(&wl->assoc_stat_lock); + if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) + ret = 1; + else + ret = 0; + mutex_unlock(&wl->assoc_stat_lock); + pr_debug("%s: ->\n", __func__); + return ret; +} + +static void gelic_wl_send_iwap_event(struct gelic_wl_info *wl, u8 *bssid) +{ + union iwreq_data data; + + memset(&data, 0, sizeof(data)); + if (bssid) + memcpy(data.ap_addr.sa_data, bssid, ETH_ALEN); + data.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWAP, + &data, NULL); +} + +/* + * wireless extension handlers and helpers + */ + +/* SIOGIWNAME */ +static int gelic_wl_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *iwreq, char *extra) +{ + strcpy(iwreq->name, "IEEE 802.11bg"); + return 0; +} + +static void gelic_wl_get_ch_info(struct gelic_wl_info *wl) +{ + struct gelic_card *card = port_to_card(wl_port(wl)); + u64 ch_info_raw, tmp; + int status; + + if (!test_and_set_bit(GELIC_WL_STAT_CH_INFO, &wl->stat)) { + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_LV1_GET_CHANNEL, 0, 0, 0, + &ch_info_raw, + &tmp); + /* some fw versions may return error */ + if (status) { + if (status != LV1_NO_ENTRY) + pr_info("%s: available ch unknown\n", __func__); + wl->ch_info = 0x07ff;/* 11 ch */ + } else + /* 16 bits of MSB has available channels */ + wl->ch_info = ch_info_raw >> 48; + } +} + +/* SIOGIWRANGE */ +static int gelic_wl_get_range(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *iwreq, char *extra) +{ + struct iw_point *point = &iwreq->data; + struct iw_range *range = (struct iw_range *)extra; + struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); + unsigned int i, chs; + + pr_debug("%s: <-\n", __func__); + point->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 22; + + /* available channels and frequencies */ + gelic_wl_get_ch_info(wl); + + for (i = 0, chs = 0; + i < NUM_CHANNELS && chs < IW_MAX_FREQUENCIES; i++) + if (wl->ch_info & (1 << i)) { + range->freq[chs].i = i + 1; + range->freq[chs].m = channel_freq[i]; + range->freq[chs].e = 6; + chs++; + } + range->num_frequency = chs; + range->old_num_frequency = chs; + range->num_channels = chs; + range->old_num_channels = chs; + + /* bitrates */ + for (i = 0; i < NUM_BITRATES; i++) + range->bitrate[i] = bitrate_list[i]; + range->num_bitrates = i; + + /* signal levels */ + range->max_qual.qual = 100; /* relative value */ + range->max_qual.level = 100; + range->avg_qual.qual = 50; + range->avg_qual.level = 50; + range->sensitivity = 0; + + /* Event capability */ + IW_EVENT_CAPA_SET_KERNEL(range->event_capa); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); + + /* encryption capability */ + range->enc_capa = IW_ENC_CAPA_WPA | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP | + IW_ENC_CAPA_4WAY_HANDSHAKE; + if (wpa2_capable()) + range->enc_capa |= IW_ENC_CAPA_WPA2; + range->encoding_size[0] = 5; /* 40bit WEP */ + range->encoding_size[1] = 13; /* 104bit WEP */ + range->encoding_size[2] = 32; /* WPA-PSK */ + range->num_encoding_sizes = 3; + range->max_encoding_tokens = GELIC_WEP_KEYS; + + /* scan capability */ + range->scan_capa = IW_SCAN_CAPA_ESSID; + + pr_debug("%s: ->\n", __func__); + return 0; + +} + +/* SIOC{G,S}IWSCAN */ +static int gelic_wl_set_scan(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + struct iw_scan_req *req; + u8 *essid = NULL; + size_t essid_len = 0; + + if (wrqu->data.length == sizeof(struct iw_scan_req) && + wrqu->data.flags & IW_SCAN_THIS_ESSID) { + req = (struct iw_scan_req*)extra; + essid = req->essid; + essid_len = req->essid_len; + pr_debug("%s: ESSID scan =%s\n", __func__, essid); + } + return gelic_wl_start_scan(wl, 1, essid, essid_len); +} + +#define OUI_LEN 3 +static const u8 rsn_oui[OUI_LEN] = { 0x00, 0x0f, 0xac }; +static const u8 wpa_oui[OUI_LEN] = { 0x00, 0x50, 0xf2 }; + +/* + * synthesize WPA/RSN IE data + * See WiFi WPA specification and IEEE 802.11-2007 7.3.2.25 + * for the format + */ +static size_t gelic_wl_synthesize_ie(u8 *buf, + struct gelic_eurus_scan_info *scan) +{ + + const u8 *oui_header; + u8 *start = buf; + int rsn; + int ccmp; + + pr_debug("%s: <- sec=%16x\n", __func__, scan->security); + switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_MASK) { + case GELIC_EURUS_SCAN_SEC_WPA: + rsn = 0; + break; + case GELIC_EURUS_SCAN_SEC_WPA2: + rsn = 1; + break; + default: + /* WEP or none. No IE returned */ + return 0; + } + + switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_WPA_MASK) { + case GELIC_EURUS_SCAN_SEC_WPA_TKIP: + ccmp = 0; + break; + case GELIC_EURUS_SCAN_SEC_WPA_AES: + ccmp = 1; + break; + default: + if (rsn) { + ccmp = 1; + pr_info("%s: no cipher info. defaulted to CCMP\n", + __func__); + } else { + ccmp = 0; + pr_info("%s: no cipher info. defaulted to TKIP\n", + __func__); + } + } + + if (rsn) + oui_header = rsn_oui; + else + oui_header = wpa_oui; + + /* element id */ + if (rsn) + *buf++ = WLAN_EID_RSN; + else + *buf++ = WLAN_EID_GENERIC; + + /* length filed; set later */ + buf++; + + /* wpa special header */ + if (!rsn) { + memcpy(buf, wpa_oui, OUI_LEN); + buf += OUI_LEN; + *buf++ = 0x01; + } + + /* version */ + *buf++ = 0x01; /* version 1.0 */ + *buf++ = 0x00; + + /* group cipher */ + memcpy(buf, oui_header, OUI_LEN); + buf += OUI_LEN; + + if (ccmp) + *buf++ = 0x04; /* CCMP */ + else + *buf++ = 0x02; /* TKIP */ + + /* pairwise key count always 1 */ + *buf++ = 0x01; + *buf++ = 0x00; + + /* pairwise key suit */ + memcpy(buf, oui_header, OUI_LEN); + buf += OUI_LEN; + if (ccmp) + *buf++ = 0x04; /* CCMP */ + else + *buf++ = 0x02; /* TKIP */ + + /* AKM count is 1 */ + *buf++ = 0x01; + *buf++ = 0x00; + + /* AKM suite is assumed as PSK*/ + memcpy(buf, oui_header, OUI_LEN); + buf += OUI_LEN; + *buf++ = 0x02; /* PSK */ + + /* RSN capabilities is 0 */ + *buf++ = 0x00; + *buf++ = 0x00; + + /* set length field */ + start[1] = (buf - start - 2); + + pr_debug("%s: ->\n", __func__); + return buf - start; +} + +struct ie_item { + u8 *data; + u8 len; +}; + +struct ie_info { + struct ie_item wpa; + struct ie_item rsn; +}; + +static void gelic_wl_parse_ie(u8 *data, size_t len, + struct ie_info *ie_info) +{ + size_t data_left = len; + u8 *pos = data; + u8 item_len; + u8 item_id; + + pr_debug("%s: data=%p len=%ld\n", __func__, + data, len); + memset(ie_info, 0, sizeof(struct ie_info)); + + while (2 <= data_left) { + item_id = *pos++; + item_len = *pos++; + data_left -= 2; + + if (data_left < item_len) + break; + + switch (item_id) { + case WLAN_EID_GENERIC: + if ((OUI_LEN + 1 <= item_len) && + !memcmp(pos, wpa_oui, OUI_LEN) && + pos[OUI_LEN] == 0x01) { + ie_info->wpa.data = pos - 2; + ie_info->wpa.len = item_len + 2; + } + break; + case WLAN_EID_RSN: + ie_info->rsn.data = pos - 2; + /* length includes the header */ + ie_info->rsn.len = item_len + 2; + break; + default: + pr_debug("%s: ignore %#x,%d\n", __func__, + item_id, item_len); + break; + } + pos += item_len; + data_left -= item_len; + } + pr_debug("%s: wpa=%p,%d wpa2=%p,%d\n", __func__, + ie_info->wpa.data, ie_info->wpa.len, + ie_info->rsn.data, ie_info->rsn.len); +} + + +/* + * translate the scan informations from hypervisor to a + * independent format + */ +static char *gelic_wl_translate_scan(struct net_device *netdev, + struct iw_request_info *info, + char *ev, + char *stop, + struct gelic_wl_scan_info *network) +{ + struct iw_event iwe; + struct gelic_eurus_scan_info *scan = network->hwinfo; + char *tmp; + u8 rate; + unsigned int i, j, len; + u8 buf[64]; /* arbitrary size large enough */ + + pr_debug("%s: <-\n", __func__); + + /* first entry should be AP's mac address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, &scan->bssid[2], ETH_ALEN); + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_ADDR_LEN); + + /* ESSID */ + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.u.data.length = strnlen(scan->essid, 32); + ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid); + + /* FREQUENCY */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = be16_to_cpu(scan->channel); + iwe.u.freq.e = 0; /* table value in MHz */ + iwe.u.freq.i = 0; + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_FREQ_LEN); + + /* RATES */ + iwe.cmd = SIOCGIWRATE; + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + /* to stuff multiple values in one event */ + tmp = ev + iwe_stream_lcp_len(info); + /* put them in ascendant order (older is first) */ + i = 0; + j = 0; + pr_debug("%s: rates=%d rate=%d\n", __func__, + network->rate_len, network->rate_ext_len); + while (i < network->rate_len) { + if (j < network->rate_ext_len && + ((scan->ext_rate[j] & 0x7f) < (scan->rate[i] & 0x7f))) + rate = scan->ext_rate[j++] & 0x7f; + else + rate = scan->rate[i++] & 0x7f; + iwe.u.bitrate.value = rate * 500000; /* 500kbps unit */ + tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe, + IW_EV_PARAM_LEN); + } + while (j < network->rate_ext_len) { + iwe.u.bitrate.value = (scan->ext_rate[j++] & 0x7f) * 500000; + tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe, + IW_EV_PARAM_LEN); + } + /* Check if we added any rate */ + if (iwe_stream_lcp_len(info) < (tmp - ev)) + ev = tmp; + + /* ENCODE */ + iwe.cmd = SIOCGIWENCODE; + if (be16_to_cpu(scan->capability) & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid); + + /* MODE */ + iwe.cmd = SIOCGIWMODE; + if (be16_to_cpu(scan->capability) & + (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { + if (be16_to_cpu(scan->capability) & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_UINT_LEN); + } + + /* QUAL */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_ALL_UPDATED | + IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; + iwe.u.qual.level = be16_to_cpu(scan->rssi); + iwe.u.qual.qual = be16_to_cpu(scan->rssi); + iwe.u.qual.noise = 0; + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_QUAL_LEN); + + /* RSN */ + memset(&iwe, 0, sizeof(iwe)); + if (be16_to_cpu(scan->size) <= sizeof(*scan)) { + /* If wpa[2] capable station, synthesize IE and put it */ + len = gelic_wl_synthesize_ie(buf, scan); + if (len) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = len; + ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); + } + } else { + /* this scan info has IE data */ + struct ie_info ie_info; + size_t data_len; + + data_len = be16_to_cpu(scan->size) - sizeof(*scan); + + gelic_wl_parse_ie(scan->elements, data_len, &ie_info); + + if (ie_info.wpa.len && (ie_info.wpa.len <= sizeof(buf))) { + memcpy(buf, ie_info.wpa.data, ie_info.wpa.len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie_info.wpa.len; + ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); + } + + if (ie_info.rsn.len && (ie_info.rsn.len <= sizeof(buf))) { + memset(&iwe, 0, sizeof(iwe)); + memcpy(buf, ie_info.rsn.data, ie_info.rsn.len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie_info.rsn.len; + ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); + } + } + + pr_debug("%s: ->\n", __func__); + return ev; +} + + +static int gelic_wl_get_scan(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + struct gelic_wl_scan_info *scan_info; + char *ev = extra; + char *stop = ev + wrqu->data.length; + int ret = 0; + unsigned long this_time = jiffies; + + pr_debug("%s: <-\n", __func__); + if (mutex_lock_interruptible(&wl->scan_lock)) + return -EAGAIN; + + switch (wl->scan_stat) { + case GELIC_WL_SCAN_STAT_SCANNING: + /* If a scan in progress, caller should call me again */ + ret = -EAGAIN; + goto out; + break; + + case GELIC_WL_SCAN_STAT_INIT: + /* last scan request failed or never issued */ + ret = -ENODEV; + goto out; + break; + case GELIC_WL_SCAN_STAT_GOT_LIST: + /* ok, use current list */ + break; + } + + list_for_each_entry(scan_info, &wl->network_list, list) { + if (wl->scan_age == 0 || + time_after(scan_info->last_scanned + wl->scan_age, + this_time)) + ev = gelic_wl_translate_scan(netdev, info, + ev, stop, + scan_info); + else + pr_debug("%s:entry too old\n", __func__); + + if (stop - ev <= IW_EV_ADDR_LEN) { + ret = -E2BIG; + goto out; + } + } + + wrqu->data.length = ev - extra; + wrqu->data.flags = 0; +out: + mutex_unlock(&wl->scan_lock); + pr_debug("%s: -> %d %d\n", __func__, ret, wrqu->data.length); + return ret; +} + +#ifdef DEBUG +static void scan_list_dump(struct gelic_wl_info *wl) +{ + struct gelic_wl_scan_info *scan_info; + int i; + + i = 0; + list_for_each_entry(scan_info, &wl->network_list, list) { + pr_debug("%s: item %d\n", __func__, i++); + pr_debug("valid=%d eurusindex=%d last=%lx\n", + scan_info->valid, scan_info->eurus_index, + scan_info->last_scanned); + pr_debug("r_len=%d r_ext_len=%d essid_len=%d\n", + scan_info->rate_len, scan_info->rate_ext_len, + scan_info->essid_len); + /* -- */ + pr_debug("bssid=%pM\n", &scan_info->hwinfo->bssid[2]); + pr_debug("essid=%s\n", scan_info->hwinfo->essid); + } +} +#endif + +static int gelic_wl_set_auth(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct iw_param *param = &data->param; + struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); + unsigned long irqflag; + int ret = 0; + + pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX); + spin_lock_irqsave(&wl->lock, irqflag); + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + if (param->value & IW_AUTH_WPA_VERSION_DISABLED) { + pr_debug("%s: NO WPA selected\n", __func__); + wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; + wl->group_cipher_method = GELIC_WL_CIPHER_WEP; + wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP; + } + if (param->value & IW_AUTH_WPA_VERSION_WPA) { + pr_debug("%s: WPA version 1 selected\n", __func__); + wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA; + wl->group_cipher_method = GELIC_WL_CIPHER_TKIP; + wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP; + wl->auth_method = GELIC_EURUS_AUTH_OPEN; + } + if (param->value & IW_AUTH_WPA_VERSION_WPA2) { + /* + * As the hypervisor may not tell the cipher + * information of the AP if it is WPA2, + * you will not decide suitable cipher from + * its beacon. + * You should have knowledge about the AP's + * cipher information in other method prior to + * the association. + */ + if (!precise_ie()) + pr_info("%s: WPA2 may not work\n", __func__); + if (wpa2_capable()) { + wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA2; + wl->group_cipher_method = GELIC_WL_CIPHER_AES; + wl->pairwise_cipher_method = + GELIC_WL_CIPHER_AES; + wl->auth_method = GELIC_EURUS_AUTH_OPEN; + } else + ret = -EINVAL; + } + break; + + case IW_AUTH_CIPHER_PAIRWISE: + if (param->value & + (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) { + pr_debug("%s: WEP selected\n", __func__); + wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP; + } + if (param->value & IW_AUTH_CIPHER_TKIP) { + pr_debug("%s: TKIP selected\n", __func__); + wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP; + } + if (param->value & IW_AUTH_CIPHER_CCMP) { + pr_debug("%s: CCMP selected\n", __func__); + wl->pairwise_cipher_method = GELIC_WL_CIPHER_AES; + } + if (param->value & IW_AUTH_CIPHER_NONE) { + pr_debug("%s: no auth selected\n", __func__); + wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE; + } + break; + case IW_AUTH_CIPHER_GROUP: + if (param->value & + (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) { + pr_debug("%s: WEP selected\n", __func__); + wl->group_cipher_method = GELIC_WL_CIPHER_WEP; + } + if (param->value & IW_AUTH_CIPHER_TKIP) { + pr_debug("%s: TKIP selected\n", __func__); + wl->group_cipher_method = GELIC_WL_CIPHER_TKIP; + } + if (param->value & IW_AUTH_CIPHER_CCMP) { + pr_debug("%s: CCMP selected\n", __func__); + wl->group_cipher_method = GELIC_WL_CIPHER_AES; + } + if (param->value & IW_AUTH_CIPHER_NONE) { + pr_debug("%s: no auth selected\n", __func__); + wl->group_cipher_method = GELIC_WL_CIPHER_NONE; + } + break; + case IW_AUTH_80211_AUTH_ALG: + if (param->value & IW_AUTH_ALG_SHARED_KEY) { + pr_debug("%s: shared key specified\n", __func__); + wl->auth_method = GELIC_EURUS_AUTH_SHARED; + } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { + pr_debug("%s: open system specified\n", __func__); + wl->auth_method = GELIC_EURUS_AUTH_OPEN; + } else + ret = -EINVAL; + break; + + case IW_AUTH_WPA_ENABLED: + if (param->value) { + pr_debug("%s: WPA enabled\n", __func__); + wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA; + } else { + pr_debug("%s: WPA disabled\n", __func__); + wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; + } + break; + + case IW_AUTH_KEY_MGMT: + if (param->value & IW_AUTH_KEY_MGMT_PSK) + break; + /* intentionally fall through */ + default: + ret = -EOPNOTSUPP; + break; + } + + if (!ret) + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); + + spin_unlock_irqrestore(&wl->lock, irqflag); + pr_debug("%s: -> %d\n", __func__, ret); + return ret; +} + +static int gelic_wl_get_auth(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *iwreq, char *extra) +{ + struct iw_param *param = &iwreq->param; + struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); + unsigned long irqflag; + int ret = 0; + + pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX); + spin_lock_irqsave(&wl->lock, irqflag); + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + switch (wl->wpa_level) { + case GELIC_WL_WPA_LEVEL_WPA: + param->value |= IW_AUTH_WPA_VERSION_WPA; + break; + case GELIC_WL_WPA_LEVEL_WPA2: + param->value |= IW_AUTH_WPA_VERSION_WPA2; + break; + default: + param->value |= IW_AUTH_WPA_VERSION_DISABLED; + } + break; + + case IW_AUTH_80211_AUTH_ALG: + if (wl->auth_method == GELIC_EURUS_AUTH_SHARED) + param->value = IW_AUTH_ALG_SHARED_KEY; + else if (wl->auth_method == GELIC_EURUS_AUTH_OPEN) + param->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + + case IW_AUTH_WPA_ENABLED: + switch (wl->wpa_level) { + case GELIC_WL_WPA_LEVEL_WPA: + case GELIC_WL_WPA_LEVEL_WPA2: + param->value = 1; + break; + default: + param->value = 0; + break; + } + break; + default: + ret = -EOPNOTSUPP; + } + + spin_unlock_irqrestore(&wl->lock, irqflag); + pr_debug("%s: -> %d\n", __func__, ret); + return ret; +} + +/* SIOC{S,G}IWESSID */ +static int gelic_wl_set_essid(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + unsigned long irqflag; + + pr_debug("%s: <- l=%d f=%d\n", __func__, + data->essid.length, data->essid.flags); + if (IW_ESSID_MAX_SIZE < data->essid.length) + return -EINVAL; + + spin_lock_irqsave(&wl->lock, irqflag); + if (data->essid.flags) { + wl->essid_len = data->essid.length; + memcpy(wl->essid, extra, wl->essid_len); + pr_debug("%s: essid = '%s'\n", __func__, extra); + set_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat); + } else { + pr_debug("%s: ESSID any\n", __func__); + clear_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat); + } + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); + spin_unlock_irqrestore(&wl->lock, irqflag); + + + gelic_wl_try_associate(netdev); /* FIXME */ + pr_debug("%s: ->\n", __func__); + return 0; +} + +static int gelic_wl_get_essid(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + unsigned long irqflag; + + pr_debug("%s: <-\n", __func__); + mutex_lock(&wl->assoc_stat_lock); + spin_lock_irqsave(&wl->lock, irqflag); + if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat) || + wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) { + memcpy(extra, wl->essid, wl->essid_len); + data->essid.length = wl->essid_len; + data->essid.flags = 1; + } else + data->essid.flags = 0; + + mutex_unlock(&wl->assoc_stat_lock); + spin_unlock_irqrestore(&wl->lock, irqflag); + pr_debug("%s: -> len=%d\n", __func__, data->essid.length); + + return 0; +} + +/* SIO{S,G}IWENCODE */ +static int gelic_wl_set_encode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + struct iw_point *enc = &data->encoding; + __u16 flags; + unsigned long irqflag; + int key_index, index_specified; + int ret = 0; + + pr_debug("%s: <-\n", __func__); + flags = enc->flags & IW_ENCODE_FLAGS; + key_index = enc->flags & IW_ENCODE_INDEX; + + pr_debug("%s: key_index = %d\n", __func__, key_index); + pr_debug("%s: key_len = %d\n", __func__, enc->length); + pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS); + + if (GELIC_WEP_KEYS < key_index) + return -EINVAL; + + spin_lock_irqsave(&wl->lock, irqflag); + if (key_index) { + index_specified = 1; + key_index--; + } else { + index_specified = 0; + key_index = wl->current_key; + } + + if (flags & IW_ENCODE_NOKEY) { + /* if just IW_ENCODE_NOKEY, change current key index */ + if (!flags && index_specified) { + wl->current_key = key_index; + goto done; + } + + if (flags & IW_ENCODE_DISABLED) { + if (!index_specified) { + /* disable encryption */ + wl->group_cipher_method = GELIC_WL_CIPHER_NONE; + wl->pairwise_cipher_method = + GELIC_WL_CIPHER_NONE; + /* invalidate all key */ + wl->key_enabled = 0; + } else + clear_bit(key_index, &wl->key_enabled); + } + + if (flags & IW_ENCODE_OPEN) + wl->auth_method = GELIC_EURUS_AUTH_OPEN; + if (flags & IW_ENCODE_RESTRICTED) { + pr_info("%s: shared key mode enabled\n", __func__); + wl->auth_method = GELIC_EURUS_AUTH_SHARED; + } + } else { + if (IW_ENCODING_TOKEN_MAX < enc->length) { + ret = -EINVAL; + goto done; + } + wl->key_len[key_index] = enc->length; + memcpy(wl->key[key_index], extra, enc->length); + set_bit(key_index, &wl->key_enabled); + wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP; + wl->group_cipher_method = GELIC_WL_CIPHER_WEP; + } + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); +done: + spin_unlock_irqrestore(&wl->lock, irqflag); + pr_debug("%s: ->\n", __func__); + return ret; +} + +static int gelic_wl_get_encode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + struct iw_point *enc = &data->encoding; + unsigned long irqflag; + unsigned int key_index, index_specified; + int ret = 0; + + pr_debug("%s: <-\n", __func__); + key_index = enc->flags & IW_ENCODE_INDEX; + pr_debug("%s: flag=%#x point=%p len=%d extra=%p\n", __func__, + enc->flags, enc->pointer, enc->length, extra); + if (GELIC_WEP_KEYS < key_index) + return -EINVAL; + + spin_lock_irqsave(&wl->lock, irqflag); + if (key_index) { + index_specified = 1; + key_index--; + } else { + index_specified = 0; + key_index = wl->current_key; + } + + if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { + switch (wl->auth_method) { + case GELIC_EURUS_AUTH_OPEN: + enc->flags = IW_ENCODE_OPEN; + break; + case GELIC_EURUS_AUTH_SHARED: + enc->flags = IW_ENCODE_RESTRICTED; + break; + } + } else + enc->flags = IW_ENCODE_DISABLED; + + if (test_bit(key_index, &wl->key_enabled)) { + if (enc->length < wl->key_len[key_index]) { + ret = -EINVAL; + goto done; + } + enc->length = wl->key_len[key_index]; + memcpy(extra, wl->key[key_index], wl->key_len[key_index]); + } else { + enc->length = 0; + enc->flags |= IW_ENCODE_NOKEY; + } + enc->flags |= key_index + 1; + pr_debug("%s: -> flag=%x len=%d\n", __func__, + enc->flags, enc->length); + +done: + spin_unlock_irqrestore(&wl->lock, irqflag); + return ret; +} + +/* SIOC{S,G}IWAP */ +static int gelic_wl_set_ap(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + unsigned long irqflag; + + pr_debug("%s: <-\n", __func__); + if (data->ap_addr.sa_family != ARPHRD_ETHER) + return -EINVAL; + + spin_lock_irqsave(&wl->lock, irqflag); + if (is_valid_ether_addr(data->ap_addr.sa_data)) { + memcpy(wl->bssid, data->ap_addr.sa_data, + ETH_ALEN); + set_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat); + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); + pr_debug("%s: bss=%pM\n", __func__, wl->bssid); + } else { + pr_debug("%s: clear bssid\n", __func__); + clear_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat); + memset(wl->bssid, 0, ETH_ALEN); + } + spin_unlock_irqrestore(&wl->lock, irqflag); + pr_debug("%s: ->\n", __func__); + return 0; +} + +static int gelic_wl_get_ap(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + unsigned long irqflag; + + pr_debug("%s: <-\n", __func__); + mutex_lock(&wl->assoc_stat_lock); + spin_lock_irqsave(&wl->lock, irqflag); + if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) { + data->ap_addr.sa_family = ARPHRD_ETHER; + memcpy(data->ap_addr.sa_data, wl->active_bssid, + ETH_ALEN); + } else + memset(data->ap_addr.sa_data, 0, ETH_ALEN); + + spin_unlock_irqrestore(&wl->lock, irqflag); + mutex_unlock(&wl->assoc_stat_lock); + pr_debug("%s: ->\n", __func__); + return 0; +} + +/* SIOC{S,G}IWENCODEEXT */ +static int gelic_wl_set_encodeext(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + struct iw_point *enc = &data->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + __u16 alg; + __u16 flags; + unsigned long irqflag; + int key_index; + int ret = 0; + + pr_debug("%s: <-\n", __func__); + flags = enc->flags & IW_ENCODE_FLAGS; + alg = ext->alg; + key_index = enc->flags & IW_ENCODE_INDEX; + + pr_debug("%s: key_index = %d\n", __func__, key_index); + pr_debug("%s: key_len = %d\n", __func__, enc->length); + pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS); + pr_debug("%s: ext_flag=%x\n", __func__, ext->ext_flags); + pr_debug("%s: ext_key_len=%x\n", __func__, ext->key_len); + + if (GELIC_WEP_KEYS < key_index) + return -EINVAL; + + spin_lock_irqsave(&wl->lock, irqflag); + if (key_index) + key_index--; + else + key_index = wl->current_key; + + if (!enc->length && (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) { + /* reques to change default key index */ + pr_debug("%s: request to change default key to %d\n", + __func__, key_index); + wl->current_key = key_index; + goto done; + } + + if (alg == IW_ENCODE_ALG_NONE || (flags & IW_ENCODE_DISABLED)) { + pr_debug("%s: alg disabled\n", __func__); + wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; + wl->group_cipher_method = GELIC_WL_CIPHER_NONE; + wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE; + wl->auth_method = GELIC_EURUS_AUTH_OPEN; /* should be open */ + } else if (alg == IW_ENCODE_ALG_WEP) { + pr_debug("%s: WEP requested\n", __func__); + if (flags & IW_ENCODE_OPEN) { + pr_debug("%s: open key mode\n", __func__); + wl->auth_method = GELIC_EURUS_AUTH_OPEN; + } + if (flags & IW_ENCODE_RESTRICTED) { + pr_debug("%s: shared key mode\n", __func__); + wl->auth_method = GELIC_EURUS_AUTH_SHARED; + } + if (IW_ENCODING_TOKEN_MAX < ext->key_len) { + pr_info("%s: key is too long %d\n", __func__, + ext->key_len); + ret = -EINVAL; + goto done; + } + /* OK, update the key */ + wl->key_len[key_index] = ext->key_len; + memset(wl->key[key_index], 0, IW_ENCODING_TOKEN_MAX); + memcpy(wl->key[key_index], ext->key, ext->key_len); + set_bit(key_index, &wl->key_enabled); + /* remember wep info changed */ + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); + } else if (alg == IW_ENCODE_ALG_PMK) { + if (ext->key_len != WPA_PSK_LEN) { + pr_err("%s: PSK length wrong %d\n", __func__, + ext->key_len); + ret = -EINVAL; + goto done; + } + memset(wl->psk, 0, sizeof(wl->psk)); + memcpy(wl->psk, ext->key, ext->key_len); + wl->psk_len = ext->key_len; + wl->psk_type = GELIC_EURUS_WPA_PSK_BIN; + /* remember PSK configured */ + set_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat); + } +done: + spin_unlock_irqrestore(&wl->lock, irqflag); + pr_debug("%s: ->\n", __func__); + return ret; +} + +static int gelic_wl_get_encodeext(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + struct iw_point *enc = &data->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + unsigned long irqflag; + int key_index; + int ret = 0; + int max_key_len; + + pr_debug("%s: <-\n", __func__); + + max_key_len = enc->length - sizeof(struct iw_encode_ext); + if (max_key_len < 0) + return -EINVAL; + key_index = enc->flags & IW_ENCODE_INDEX; + + pr_debug("%s: key_index = %d\n", __func__, key_index); + pr_debug("%s: key_len = %d\n", __func__, enc->length); + pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS); + + if (GELIC_WEP_KEYS < key_index) + return -EINVAL; + + spin_lock_irqsave(&wl->lock, irqflag); + if (key_index) + key_index--; + else + key_index = wl->current_key; + + memset(ext, 0, sizeof(struct iw_encode_ext)); + switch (wl->group_cipher_method) { + case GELIC_WL_CIPHER_WEP: + ext->alg = IW_ENCODE_ALG_WEP; + enc->flags |= IW_ENCODE_ENABLED; + break; + case GELIC_WL_CIPHER_TKIP: + ext->alg = IW_ENCODE_ALG_TKIP; + enc->flags |= IW_ENCODE_ENABLED; + break; + case GELIC_WL_CIPHER_AES: + ext->alg = IW_ENCODE_ALG_CCMP; + enc->flags |= IW_ENCODE_ENABLED; + break; + case GELIC_WL_CIPHER_NONE: + default: + ext->alg = IW_ENCODE_ALG_NONE; + enc->flags |= IW_ENCODE_NOKEY; + break; + } + + if (!(enc->flags & IW_ENCODE_NOKEY)) { + if (max_key_len < wl->key_len[key_index]) { + ret = -E2BIG; + goto out; + } + if (test_bit(key_index, &wl->key_enabled)) + memcpy(ext->key, wl->key[key_index], + wl->key_len[key_index]); + else + pr_debug("%s: disabled key requested ix=%d\n", + __func__, key_index); + } +out: + spin_unlock_irqrestore(&wl->lock, irqflag); + pr_debug("%s: ->\n", __func__); + return ret; +} +/* SIOC{S,G}IWMODE */ +static int gelic_wl_set_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + __u32 mode = data->mode; + int ret; + + pr_debug("%s: <-\n", __func__); + if (mode == IW_MODE_INFRA) + ret = 0; + else + ret = -EOPNOTSUPP; + pr_debug("%s: -> %d\n", __func__, ret); + return ret; +} + +static int gelic_wl_get_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + __u32 *mode = &data->mode; + pr_debug("%s: <-\n", __func__); + *mode = IW_MODE_INFRA; + pr_debug("%s: ->\n", __func__); + return 0; +} + +/* SIOCGIWNICKN */ +static int gelic_wl_get_nick(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + strcpy(extra, "gelic_wl"); + data->data.length = strlen(extra); + data->data.flags = 1; + return 0; +} + + +/* --- */ + +static struct iw_statistics *gelic_wl_get_wireless_stats( + struct net_device *netdev) +{ + + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + struct gelic_eurus_cmd *cmd; + struct iw_statistics *is; + struct gelic_eurus_rssi_info *rssi; + void *buf; + + pr_debug("%s: <-\n", __func__); + + buf = (void *)__get_free_page(GFP_KERNEL); + if (!buf) + return NULL; + + is = &wl->iwstat; + memset(is, 0, sizeof(*is)); + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_RSSI_CFG, + buf, sizeof(*rssi)); + if (cmd && !cmd->status && !cmd->cmd_status) { + rssi = buf; + is->qual.level = be16_to_cpu(rssi->rssi); + is->qual.updated = IW_QUAL_LEVEL_UPDATED | + IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; + } else + /* not associated */ + is->qual.updated = IW_QUAL_ALL_INVALID; + + kfree(cmd); + free_page((unsigned long)buf); + pr_debug("%s: ->\n", __func__); + return is; +} + +/* + * scanning helpers + */ +static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan, + u8 *essid, size_t essid_len) +{ + struct gelic_eurus_cmd *cmd; + int ret = 0; + void *buf = NULL; + size_t len; + + pr_debug("%s: <- always=%d\n", __func__, always_scan); + if (mutex_lock_interruptible(&wl->scan_lock)) + return -ERESTARTSYS; + + /* + * If already a scan in progress, do not trigger more + */ + if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) { + pr_debug("%s: scanning now\n", __func__); + goto out; + } + + init_completion(&wl->scan_done); + /* + * If we have already a bss list, don't try to get new + * unless we are doing an ESSID scan + */ + if ((!essid_len && !always_scan) + && wl->scan_stat == GELIC_WL_SCAN_STAT_GOT_LIST) { + pr_debug("%s: already has the list\n", __func__); + complete(&wl->scan_done); + goto out; + } + + /* ESSID scan ? */ + if (essid_len && essid) { + buf = (void *)__get_free_page(GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; + } + len = IW_ESSID_MAX_SIZE; /* hypervisor always requires 32 */ + memset(buf, 0, len); + memcpy(buf, essid, essid_len); + pr_debug("%s: essid scan='%s'\n", __func__, (char *)buf); + } else + len = 0; + + /* + * issue start scan request + */ + wl->scan_stat = GELIC_WL_SCAN_STAT_SCANNING; + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_START_SCAN, + buf, len); + if (!cmd || cmd->status || cmd->cmd_status) { + wl->scan_stat = GELIC_WL_SCAN_STAT_INIT; + complete(&wl->scan_done); + ret = -ENOMEM; + goto out; + } + kfree(cmd); +out: + free_page((unsigned long)buf); + mutex_unlock(&wl->scan_lock); + pr_debug("%s: ->\n", __func__); + return ret; +} + +/* + * retrieve scan result from the chip (hypervisor) + * this function is invoked by schedule work. + */ +static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl) +{ + struct gelic_eurus_cmd *cmd = NULL; + struct gelic_wl_scan_info *target, *tmp; + struct gelic_wl_scan_info *oldest = NULL; + struct gelic_eurus_scan_info *scan_info; + unsigned int scan_info_size; + union iwreq_data data; + unsigned long this_time = jiffies; + unsigned int data_len, i, found, r; + void *buf; + + pr_debug("%s:start\n", __func__); + mutex_lock(&wl->scan_lock); + + buf = (void *)__get_free_page(GFP_KERNEL); + if (!buf) { + pr_info("%s: scan buffer alloc failed\n", __func__); + goto out; + } + + if (wl->scan_stat != GELIC_WL_SCAN_STAT_SCANNING) { + /* + * stop() may be called while scanning, ignore result + */ + pr_debug("%s: scan complete when stat != scanning(%d)\n", + __func__, wl->scan_stat); + goto out; + } + + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_SCAN, + buf, PAGE_SIZE); + if (!cmd || cmd->status || cmd->cmd_status) { + wl->scan_stat = GELIC_WL_SCAN_STAT_INIT; + pr_info("%s:cmd failed\n", __func__); + kfree(cmd); + goto out; + } + data_len = cmd->size; + pr_debug("%s: data_len = %d\n", __func__, data_len); + kfree(cmd); + + /* OK, bss list retrieved */ + wl->scan_stat = GELIC_WL_SCAN_STAT_GOT_LIST; + + /* mark all entries are old */ + list_for_each_entry_safe(target, tmp, &wl->network_list, list) { + target->valid = 0; + /* expire too old entries */ + if (time_before(target->last_scanned + wl->scan_age, + this_time)) { + kfree(target->hwinfo); + target->hwinfo = NULL; + list_move_tail(&target->list, &wl->network_free_list); + } + } + + /* put them in the network_list */ + for (i = 0, scan_info_size = 0, scan_info = buf; + scan_info_size < data_len; + i++, scan_info_size += be16_to_cpu(scan_info->size), + scan_info = (void *)scan_info + be16_to_cpu(scan_info->size)) { + pr_debug("%s:size=%d bssid=%pM scan_info=%p\n", __func__, + be16_to_cpu(scan_info->size), + &scan_info->bssid[2], scan_info); + + /* + * The wireless firmware may return invalid channel 0 and/or + * invalid rate if the AP emits zero length SSID ie. As this + * scan information is useless, ignore it + */ + if (!be16_to_cpu(scan_info->channel) || !scan_info->rate[0]) { + pr_debug("%s: invalid scan info\n", __func__); + continue; + } + + found = 0; + oldest = NULL; + list_for_each_entry(target, &wl->network_list, list) { + if (!compare_ether_addr(&target->hwinfo->bssid[2], + &scan_info->bssid[2])) { + found = 1; + pr_debug("%s: same BBS found scanned list\n", + __func__); + break; + } + if (!oldest || + (target->last_scanned < oldest->last_scanned)) + oldest = target; + } + + if (!found) { + /* not found in the list */ + if (list_empty(&wl->network_free_list)) { + /* expire oldest */ + target = oldest; + } else { + target = list_entry(wl->network_free_list.next, + struct gelic_wl_scan_info, + list); + } + } + + /* update the item */ + target->last_scanned = this_time; + target->valid = 1; + target->eurus_index = i; + kfree(target->hwinfo); + target->hwinfo = kzalloc(be16_to_cpu(scan_info->size), + GFP_KERNEL); + if (!target->hwinfo) { + pr_info("%s: kzalloc failed\n", __func__); + continue; + } + /* copy hw scan info */ + memcpy(target->hwinfo, scan_info, scan_info->size); + target->essid_len = strnlen(scan_info->essid, + sizeof(scan_info->essid)); + target->rate_len = 0; + for (r = 0; r < 12; r++) + if (scan_info->rate[r]) + target->rate_len++; + if (8 < target->rate_len) + pr_info("%s: AP returns %d rates\n", __func__, + target->rate_len); + target->rate_ext_len = 0; + for (r = 0; r < 16; r++) + if (scan_info->ext_rate[r]) + target->rate_ext_len++; + list_move_tail(&target->list, &wl->network_list); + } + memset(&data, 0, sizeof(data)); + wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWSCAN, &data, + NULL); +out: + free_page((unsigned long)buf); + complete(&wl->scan_done); + mutex_unlock(&wl->scan_lock); + pr_debug("%s:end\n", __func__); +} + +/* + * Select an appropriate bss from current scan list regarding + * current settings from userspace. + * The caller must hold wl->scan_lock, + * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST + */ +static void update_best(struct gelic_wl_scan_info **best, + struct gelic_wl_scan_info *candid, + int *best_weight, + int *weight) +{ + if (*best_weight < ++(*weight)) { + *best_weight = *weight; + *best = candid; + } +} + +static +struct gelic_wl_scan_info *gelic_wl_find_best_bss(struct gelic_wl_info *wl) +{ + struct gelic_wl_scan_info *scan_info; + struct gelic_wl_scan_info *best_bss; + int weight, best_weight; + u16 security; + + pr_debug("%s: <-\n", __func__); + + best_bss = NULL; + best_weight = 0; + + list_for_each_entry(scan_info, &wl->network_list, list) { + pr_debug("%s: station %p\n", __func__, scan_info); + + if (!scan_info->valid) { + pr_debug("%s: station invalid\n", __func__); + continue; + } + + /* If bss specified, check it only */ + if (test_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat)) { + if (!compare_ether_addr(&scan_info->hwinfo->bssid[2], + wl->bssid)) { + best_bss = scan_info; + pr_debug("%s: bssid matched\n", __func__); + break; + } else { + pr_debug("%s: bssid unmached\n", __func__); + continue; + } + } + + weight = 0; + + /* security */ + security = be16_to_cpu(scan_info->hwinfo->security) & + GELIC_EURUS_SCAN_SEC_MASK; + if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) { + if (security == GELIC_EURUS_SCAN_SEC_WPA2) + update_best(&best_bss, scan_info, + &best_weight, &weight); + else + continue; + } else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA) { + if (security == GELIC_EURUS_SCAN_SEC_WPA) + update_best(&best_bss, scan_info, + &best_weight, &weight); + else + continue; + } else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_NONE && + wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { + if (security == GELIC_EURUS_SCAN_SEC_WEP) + update_best(&best_bss, scan_info, + &best_weight, &weight); + else + continue; + } + + /* If ESSID is set, check it */ + if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) { + if ((scan_info->essid_len == wl->essid_len) && + !strncmp(wl->essid, + scan_info->hwinfo->essid, + scan_info->essid_len)) + update_best(&best_bss, scan_info, + &best_weight, &weight); + else + continue; + } + } + +#ifdef DEBUG + pr_debug("%s: -> bss=%p\n", __func__, best_bss); + if (best_bss) { + pr_debug("%s:addr=%pM\n", __func__, + &best_bss->hwinfo->bssid[2]); + } +#endif + return best_bss; +} + +/* + * Setup WEP configuration to the chip + * The caller must hold wl->scan_lock, + * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST + */ +static int gelic_wl_do_wep_setup(struct gelic_wl_info *wl) +{ + unsigned int i; + struct gelic_eurus_wep_cfg *wep; + struct gelic_eurus_cmd *cmd; + int wep104 = 0; + int have_key = 0; + int ret = 0; + + pr_debug("%s: <-\n", __func__); + /* we can assume no one should uses the buffer */ + wep = (struct gelic_eurus_wep_cfg *)__get_free_page(GFP_KERNEL); + if (!wep) + return -ENOMEM; + + memset(wep, 0, sizeof(*wep)); + + if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { + pr_debug("%s: WEP mode\n", __func__); + for (i = 0; i < GELIC_WEP_KEYS; i++) { + if (!test_bit(i, &wl->key_enabled)) + continue; + + pr_debug("%s: key#%d enabled\n", __func__, i); + have_key = 1; + if (wl->key_len[i] == 13) + wep104 = 1; + else if (wl->key_len[i] != 5) { + pr_info("%s: wrong wep key[%d]=%d\n", + __func__, i, wl->key_len[i]); + ret = -EINVAL; + goto out; + } + memcpy(wep->key[i], wl->key[i], wl->key_len[i]); + } + + if (!have_key) { + pr_info("%s: all wep key disabled\n", __func__); + ret = -EINVAL; + goto out; + } + + if (wep104) { + pr_debug("%s: 104bit key\n", __func__); + wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_104BIT); + } else { + pr_debug("%s: 40bit key\n", __func__); + wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_40BIT); + } + } else { + pr_debug("%s: NO encryption\n", __func__); + wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_NONE); + } + + /* issue wep setup */ + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WEP_CFG, + wep, sizeof(*wep)); + if (!cmd) + ret = -ENOMEM; + else if (cmd->status || cmd->cmd_status) + ret = -ENXIO; + + kfree(cmd); +out: + free_page((unsigned long)wep); + pr_debug("%s: ->\n", __func__); + return ret; +} + +#ifdef DEBUG +static const char *wpasecstr(enum gelic_eurus_wpa_security sec) +{ + switch (sec) { + case GELIC_EURUS_WPA_SEC_NONE: + return "NONE"; + break; + case GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP: + return "WPA_TKIP_TKIP"; + break; + case GELIC_EURUS_WPA_SEC_WPA_TKIP_AES: + return "WPA_TKIP_AES"; + break; + case GELIC_EURUS_WPA_SEC_WPA_AES_AES: + return "WPA_AES_AES"; + break; + case GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP: + return "WPA2_TKIP_TKIP"; + break; + case GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES: + return "WPA2_TKIP_AES"; + break; + case GELIC_EURUS_WPA_SEC_WPA2_AES_AES: + return "WPA2_AES_AES"; + break; + } + return ""; +}; +#endif + +static int gelic_wl_do_wpa_setup(struct gelic_wl_info *wl) +{ + struct gelic_eurus_wpa_cfg *wpa; + struct gelic_eurus_cmd *cmd; + u16 security; + int ret = 0; + + pr_debug("%s: <-\n", __func__); + /* we can assume no one should uses the buffer */ + wpa = (struct gelic_eurus_wpa_cfg *)__get_free_page(GFP_KERNEL); + if (!wpa) + return -ENOMEM; + + memset(wpa, 0, sizeof(*wpa)); + + if (!test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat)) + pr_info("%s: PSK not configured yet\n", __func__); + + /* copy key */ + memcpy(wpa->psk, wl->psk, wl->psk_len); + + /* set security level */ + if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) { + if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) { + security = GELIC_EURUS_WPA_SEC_WPA2_AES_AES; + } else { + if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES && + precise_ie()) + security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES; + else + security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP; + } + } else { + if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) { + security = GELIC_EURUS_WPA_SEC_WPA_AES_AES; + } else { + if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES && + precise_ie()) + security = GELIC_EURUS_WPA_SEC_WPA_TKIP_AES; + else + security = GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP; + } + } + wpa->security = cpu_to_be16(security); + + /* PSK type */ + wpa->psk_type = cpu_to_be16(wl->psk_type); +#ifdef DEBUG + pr_debug("%s: sec=%s psktype=%s\n", __func__, + wpasecstr(wpa->security), + (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ? + "BIN" : "passphrase"); +#if 0 + /* + * don't enable here if you plan to submit + * the debug log because this dumps your precious + * passphrase/key. + */ + pr_debug("%s: psk=%s\n", __func__, + (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ? + "N/A" : wpa->psk); +#endif +#endif + /* issue wpa setup */ + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WPA_CFG, + wpa, sizeof(*wpa)); + if (!cmd) + ret = -ENOMEM; + else if (cmd->status || cmd->cmd_status) + ret = -ENXIO; + kfree(cmd); + free_page((unsigned long)wpa); + pr_debug("%s: --> %d\n", __func__, ret); + return ret; +} + +/* + * Start association. caller must hold assoc_stat_lock + */ +static int gelic_wl_associate_bss(struct gelic_wl_info *wl, + struct gelic_wl_scan_info *bss) +{ + struct gelic_eurus_cmd *cmd; + struct gelic_eurus_common_cfg *common; + int ret = 0; + unsigned long rc; + + pr_debug("%s: <-\n", __func__); + + /* do common config */ + common = (struct gelic_eurus_common_cfg *)__get_free_page(GFP_KERNEL); + if (!common) + return -ENOMEM; + + memset(common, 0, sizeof(*common)); + common->bss_type = cpu_to_be16(GELIC_EURUS_BSS_INFRA); + common->op_mode = cpu_to_be16(GELIC_EURUS_OPMODE_11BG); + + common->scan_index = cpu_to_be16(bss->eurus_index); + switch (wl->auth_method) { + case GELIC_EURUS_AUTH_OPEN: + common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_OPEN); + break; + case GELIC_EURUS_AUTH_SHARED: + common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_SHARED); + break; + } + +#ifdef DEBUG + scan_list_dump(wl); +#endif + pr_debug("%s: common cfg index=%d bsstype=%d auth=%d\n", __func__, + be16_to_cpu(common->scan_index), + be16_to_cpu(common->bss_type), + be16_to_cpu(common->auth_method)); + + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_COMMON_CFG, + common, sizeof(*common)); + if (!cmd || cmd->status || cmd->cmd_status) { + ret = -ENOMEM; + kfree(cmd); + goto out; + } + kfree(cmd); + + /* WEP/WPA */ + switch (wl->wpa_level) { + case GELIC_WL_WPA_LEVEL_NONE: + /* If WEP or no security, setup WEP config */ + ret = gelic_wl_do_wep_setup(wl); + break; + case GELIC_WL_WPA_LEVEL_WPA: + case GELIC_WL_WPA_LEVEL_WPA2: + ret = gelic_wl_do_wpa_setup(wl); + break; + } + + if (ret) { + pr_debug("%s: WEP/WPA setup failed %d\n", __func__, + ret); + ret = -EPERM; + gelic_wl_send_iwap_event(wl, NULL); + goto out; + } + + /* start association */ + init_completion(&wl->assoc_done); + wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATING; + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_ASSOC, + NULL, 0); + if (!cmd || cmd->status || cmd->cmd_status) { + pr_debug("%s: assoc request failed\n", __func__); + wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; + kfree(cmd); + ret = -ENOMEM; + gelic_wl_send_iwap_event(wl, NULL); + goto out; + } + kfree(cmd); + + /* wait for connected event */ + rc = wait_for_completion_timeout(&wl->assoc_done, HZ * 4);/*FIXME*/ + + if (!rc) { + /* timeouted. Maybe key or cyrpt mode is wrong */ + pr_info("%s: connect timeout\n", __func__); + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, + NULL, 0); + kfree(cmd); + wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; + gelic_wl_send_iwap_event(wl, NULL); + ret = -ENXIO; + } else { + wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATED; + /* copy bssid */ + memcpy(wl->active_bssid, &bss->hwinfo->bssid[2], ETH_ALEN); + + /* send connect event */ + gelic_wl_send_iwap_event(wl, wl->active_bssid); + pr_info("%s: connected\n", __func__); + } +out: + free_page((unsigned long)common); + pr_debug("%s: ->\n", __func__); + return ret; +} + +/* + * connected event + */ +static void gelic_wl_connected_event(struct gelic_wl_info *wl, + u64 event) +{ + u64 desired_event = 0; + + switch (wl->wpa_level) { + case GELIC_WL_WPA_LEVEL_NONE: + desired_event = GELIC_LV1_WL_EVENT_CONNECTED; + break; + case GELIC_WL_WPA_LEVEL_WPA: + case GELIC_WL_WPA_LEVEL_WPA2: + desired_event = GELIC_LV1_WL_EVENT_WPA_CONNECTED; + break; + } + + if (desired_event == event) { + pr_debug("%s: completed\n", __func__); + complete(&wl->assoc_done); + netif_carrier_on(port_to_netdev(wl_port(wl))); + } else + pr_debug("%s: event %#llx under wpa\n", + __func__, event); +} + +/* + * disconnect event + */ +static void gelic_wl_disconnect_event(struct gelic_wl_info *wl, + u64 event) +{ + struct gelic_eurus_cmd *cmd; + int lock; + + /* + * If we fall here in the middle of association, + * associate_bss() should be waiting for complation of + * wl->assoc_done. + * As it waits with timeout, just leave assoc_done + * uncompleted, then it terminates with timeout + */ + if (!mutex_trylock(&wl->assoc_stat_lock)) { + pr_debug("%s: already locked\n", __func__); + lock = 0; + } else { + pr_debug("%s: obtain lock\n", __func__); + lock = 1; + } + + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0); + kfree(cmd); + + /* send disconnected event to the supplicant */ + if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) + gelic_wl_send_iwap_event(wl, NULL); + + wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; + netif_carrier_off(port_to_netdev(wl_port(wl))); + + if (lock) + mutex_unlock(&wl->assoc_stat_lock); +} +/* + * event worker + */ +#ifdef DEBUG +static const char *eventstr(enum gelic_lv1_wl_event event) +{ + static char buf[32]; + char *ret; + if (event & GELIC_LV1_WL_EVENT_DEVICE_READY) + ret = "EURUS_READY"; + else if (event & GELIC_LV1_WL_EVENT_SCAN_COMPLETED) + ret = "SCAN_COMPLETED"; + else if (event & GELIC_LV1_WL_EVENT_DEAUTH) + ret = "DEAUTH"; + else if (event & GELIC_LV1_WL_EVENT_BEACON_LOST) + ret = "BEACON_LOST"; + else if (event & GELIC_LV1_WL_EVENT_CONNECTED) + ret = "CONNECTED"; + else if (event & GELIC_LV1_WL_EVENT_WPA_CONNECTED) + ret = "WPA_CONNECTED"; + else if (event & GELIC_LV1_WL_EVENT_WPA_ERROR) + ret = "WPA_ERROR"; + else { + sprintf(buf, "Unknown(%#x)", event); + ret = buf; + } + return ret; +} +#else +static const char *eventstr(enum gelic_lv1_wl_event event) +{ + return NULL; +} +#endif +static void gelic_wl_event_worker(struct work_struct *work) +{ + struct gelic_wl_info *wl; + struct gelic_port *port; + u64 event, tmp; + int status; + + pr_debug("%s:start\n", __func__); + wl = container_of(work, struct gelic_wl_info, event_work.work); + port = wl_port(wl); + while (1) { + status = lv1_net_control(bus_id(port->card), dev_id(port->card), + GELIC_LV1_GET_WLAN_EVENT, 0, 0, 0, + &event, &tmp); + if (status) { + if (status != LV1_NO_ENTRY) + pr_debug("%s:wlan event failed %d\n", + __func__, status); + /* got all events */ + pr_debug("%s:end\n", __func__); + return; + } + pr_debug("%s: event=%s\n", __func__, eventstr(event)); + switch (event) { + case GELIC_LV1_WL_EVENT_SCAN_COMPLETED: + gelic_wl_scan_complete_event(wl); + break; + case GELIC_LV1_WL_EVENT_BEACON_LOST: + case GELIC_LV1_WL_EVENT_DEAUTH: + gelic_wl_disconnect_event(wl, event); + break; + case GELIC_LV1_WL_EVENT_CONNECTED: + case GELIC_LV1_WL_EVENT_WPA_CONNECTED: + gelic_wl_connected_event(wl, event); + break; + default: + break; + } + } /* while */ +} +/* + * association worker + */ +static void gelic_wl_assoc_worker(struct work_struct *work) +{ + struct gelic_wl_info *wl; + + struct gelic_wl_scan_info *best_bss; + int ret; + unsigned long irqflag; + u8 *essid; + size_t essid_len; + + wl = container_of(work, struct gelic_wl_info, assoc_work.work); + + mutex_lock(&wl->assoc_stat_lock); + + if (wl->assoc_stat != GELIC_WL_ASSOC_STAT_DISCONN) + goto out; + + spin_lock_irqsave(&wl->lock, irqflag); + if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) { + pr_debug("%s: assoc ESSID configured %s\n", __func__, + wl->essid); + essid = wl->essid; + essid_len = wl->essid_len; + } else { + essid = NULL; + essid_len = 0; + } + spin_unlock_irqrestore(&wl->lock, irqflag); + + ret = gelic_wl_start_scan(wl, 0, essid, essid_len); + if (ret == -ERESTARTSYS) { + pr_debug("%s: scan start failed association\n", __func__); + schedule_delayed_work(&wl->assoc_work, HZ/10); /*FIXME*/ + goto out; + } else if (ret) { + pr_info("%s: scan prerequisite failed\n", __func__); + goto out; + } + + /* + * Wait for bss scan completion + * If we have scan list already, gelic_wl_start_scan() + * returns OK and raises the complete. Thus, + * it's ok to wait unconditionally here + */ + wait_for_completion(&wl->scan_done); + + pr_debug("%s: scan done\n", __func__); + mutex_lock(&wl->scan_lock); + if (wl->scan_stat != GELIC_WL_SCAN_STAT_GOT_LIST) { + gelic_wl_send_iwap_event(wl, NULL); + pr_info("%s: no scan list. association failed\n", __func__); + goto scan_lock_out; + } + + /* find best matching bss */ + best_bss = gelic_wl_find_best_bss(wl); + if (!best_bss) { + gelic_wl_send_iwap_event(wl, NULL); + pr_info("%s: no bss matched. association failed\n", __func__); + goto scan_lock_out; + } + + /* ok, do association */ + ret = gelic_wl_associate_bss(wl, best_bss); + if (ret) + pr_info("%s: association failed %d\n", __func__, ret); +scan_lock_out: + mutex_unlock(&wl->scan_lock); +out: + mutex_unlock(&wl->assoc_stat_lock); +} +/* + * Interrupt handler + * Called from the ethernet interrupt handler + * Processes wireless specific virtual interrupts only + */ +void gelic_wl_interrupt(struct net_device *netdev, u64 status) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + + if (status & GELIC_CARD_WLAN_COMMAND_COMPLETED) { + pr_debug("%s:cmd complete\n", __func__); + complete(&wl->cmd_done_intr); + } + + if (status & GELIC_CARD_WLAN_EVENT_RECEIVED) { + pr_debug("%s:event received\n", __func__); + queue_delayed_work(wl->event_queue, &wl->event_work, 0); + } +} + +/* + * driver helpers + */ +static const iw_handler gelic_wl_wext_handler[] = +{ + IW_HANDLER(SIOCGIWNAME, gelic_wl_get_name), + IW_HANDLER(SIOCGIWRANGE, gelic_wl_get_range), + IW_HANDLER(SIOCSIWSCAN, gelic_wl_set_scan), + IW_HANDLER(SIOCGIWSCAN, gelic_wl_get_scan), + IW_HANDLER(SIOCSIWAUTH, gelic_wl_set_auth), + IW_HANDLER(SIOCGIWAUTH, gelic_wl_get_auth), + IW_HANDLER(SIOCSIWESSID, gelic_wl_set_essid), + IW_HANDLER(SIOCGIWESSID, gelic_wl_get_essid), + IW_HANDLER(SIOCSIWENCODE, gelic_wl_set_encode), + IW_HANDLER(SIOCGIWENCODE, gelic_wl_get_encode), + IW_HANDLER(SIOCSIWAP, gelic_wl_set_ap), + IW_HANDLER(SIOCGIWAP, gelic_wl_get_ap), + IW_HANDLER(SIOCSIWENCODEEXT, gelic_wl_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, gelic_wl_get_encodeext), + IW_HANDLER(SIOCSIWMODE, gelic_wl_set_mode), + IW_HANDLER(SIOCGIWMODE, gelic_wl_get_mode), + IW_HANDLER(SIOCGIWNICKN, gelic_wl_get_nick), +}; + +static const struct iw_handler_def gelic_wl_wext_handler_def = { + .num_standard = ARRAY_SIZE(gelic_wl_wext_handler), + .standard = gelic_wl_wext_handler, + .get_wireless_stats = gelic_wl_get_wireless_stats, +}; + +static struct net_device * __devinit gelic_wl_alloc(struct gelic_card *card) +{ + struct net_device *netdev; + struct gelic_port *port; + struct gelic_wl_info *wl; + unsigned int i; + + pr_debug("%s:start\n", __func__); + netdev = alloc_etherdev(sizeof(struct gelic_port) + + sizeof(struct gelic_wl_info)); + pr_debug("%s: netdev =%p card=%p\n", __func__, netdev, card); + if (!netdev) + return NULL; + + strcpy(netdev->name, "wlan%d"); + + port = netdev_priv(netdev); + port->netdev = netdev; + port->card = card; + port->type = GELIC_PORT_WIRELESS; + + wl = port_wl(port); + pr_debug("%s: wl=%p port=%p\n", __func__, wl, port); + + /* allocate scan list */ + wl->networks = kzalloc(sizeof(struct gelic_wl_scan_info) * + GELIC_WL_BSS_MAX_ENT, GFP_KERNEL); + + if (!wl->networks) + goto fail_bss; + + wl->eurus_cmd_queue = create_singlethread_workqueue("gelic_cmd"); + if (!wl->eurus_cmd_queue) + goto fail_cmd_workqueue; + + wl->event_queue = create_singlethread_workqueue("gelic_event"); + if (!wl->event_queue) + goto fail_event_workqueue; + + INIT_LIST_HEAD(&wl->network_free_list); + INIT_LIST_HEAD(&wl->network_list); + for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++) + list_add_tail(&wl->networks[i].list, + &wl->network_free_list); + init_completion(&wl->cmd_done_intr); + + INIT_DELAYED_WORK(&wl->event_work, gelic_wl_event_worker); + INIT_DELAYED_WORK(&wl->assoc_work, gelic_wl_assoc_worker); + mutex_init(&wl->scan_lock); + mutex_init(&wl->assoc_stat_lock); + + init_completion(&wl->scan_done); + /* for the case that no scan request is issued and stop() is called */ + complete(&wl->scan_done); + + spin_lock_init(&wl->lock); + + wl->scan_age = 5*HZ; /* FIXME */ + + /* buffer for receiving scanned list etc */ + BUILD_BUG_ON(PAGE_SIZE < + sizeof(struct gelic_eurus_scan_info) * + GELIC_EURUS_MAX_SCAN); + pr_debug("%s:end\n", __func__); + return netdev; + +fail_event_workqueue: + destroy_workqueue(wl->eurus_cmd_queue); +fail_cmd_workqueue: + kfree(wl->networks); +fail_bss: + free_netdev(netdev); + pr_debug("%s:end error\n", __func__); + return NULL; + +} + +static void gelic_wl_free(struct gelic_wl_info *wl) +{ + struct gelic_wl_scan_info *scan_info; + unsigned int i; + + pr_debug("%s: <-\n", __func__); + + pr_debug("%s: destroy queues\n", __func__); + destroy_workqueue(wl->eurus_cmd_queue); + destroy_workqueue(wl->event_queue); + + scan_info = wl->networks; + for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++, scan_info++) + kfree(scan_info->hwinfo); + kfree(wl->networks); + + free_netdev(port_to_netdev(wl_port(wl))); + + pr_debug("%s: ->\n", __func__); +} + +static int gelic_wl_try_associate(struct net_device *netdev) +{ + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); + int ret = -1; + unsigned int i; + + pr_debug("%s: <-\n", __func__); + + /* check constraits for start association */ + /* for no access restriction AP */ + if (wl->group_cipher_method == GELIC_WL_CIPHER_NONE) { + if (test_bit(GELIC_WL_STAT_CONFIGURED, + &wl->stat)) + goto do_associate; + else { + pr_debug("%s: no wep, not configured\n", __func__); + return ret; + } + } + + /* for WEP, one of four keys should be set */ + if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { + /* one of keys set */ + for (i = 0; i < GELIC_WEP_KEYS; i++) { + if (test_bit(i, &wl->key_enabled)) + goto do_associate; + } + pr_debug("%s: WEP, but no key specified\n", __func__); + return ret; + } + + /* for WPA[2], psk should be set */ + if ((wl->group_cipher_method == GELIC_WL_CIPHER_TKIP) || + (wl->group_cipher_method == GELIC_WL_CIPHER_AES)) { + if (test_bit(GELIC_WL_STAT_WPA_PSK_SET, + &wl->stat)) + goto do_associate; + else { + pr_debug("%s: AES/TKIP, but PSK not configured\n", + __func__); + return ret; + } + } + +do_associate: + ret = schedule_delayed_work(&wl->assoc_work, 0); + pr_debug("%s: start association work %d\n", __func__, ret); + return ret; +} + +/* + * netdev handlers + */ +static int gelic_wl_open(struct net_device *netdev) +{ + struct gelic_card *card = netdev_card(netdev); + + pr_debug("%s:->%p\n", __func__, netdev); + + gelic_card_up(card); + + /* try to associate */ + gelic_wl_try_associate(netdev); + + netif_start_queue(netdev); + + pr_debug("%s:<-\n", __func__); + return 0; +} + +/* + * reset state machine + */ +static int gelic_wl_reset_state(struct gelic_wl_info *wl) +{ + struct gelic_wl_scan_info *target; + struct gelic_wl_scan_info *tmp; + + /* empty scan list */ + list_for_each_entry_safe(target, tmp, &wl->network_list, list) { + list_move_tail(&target->list, &wl->network_free_list); + } + wl->scan_stat = GELIC_WL_SCAN_STAT_INIT; + + /* clear configuration */ + wl->auth_method = GELIC_EURUS_AUTH_OPEN; + wl->group_cipher_method = GELIC_WL_CIPHER_NONE; + wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE; + wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; + + wl->key_enabled = 0; + wl->current_key = 0; + + wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE; + wl->psk_len = 0; + + wl->essid_len = 0; + memset(wl->essid, 0, sizeof(wl->essid)); + memset(wl->bssid, 0, sizeof(wl->bssid)); + memset(wl->active_bssid, 0, sizeof(wl->active_bssid)); + + wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; + + memset(&wl->iwstat, 0, sizeof(wl->iwstat)); + /* all status bit clear */ + wl->stat = 0; + return 0; +} + +/* + * Tell eurus to terminate association + */ +static void gelic_wl_disconnect(struct net_device *netdev) +{ + struct gelic_port *port = netdev_priv(netdev); + struct gelic_wl_info *wl = port_wl(port); + struct gelic_eurus_cmd *cmd; + + /* + * If scann process is running on chip, + * further requests will be rejected + */ + if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) + wait_for_completion_timeout(&wl->scan_done, HZ); + + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0); + kfree(cmd); + gelic_wl_send_iwap_event(wl, NULL); +}; + +static int gelic_wl_stop(struct net_device *netdev) +{ + struct gelic_port *port = netdev_priv(netdev); + struct gelic_wl_info *wl = port_wl(port); + struct gelic_card *card = netdev_card(netdev); + + pr_debug("%s:<-\n", __func__); + + /* + * Cancel pending association work. + * event work can run after netdev down + */ + cancel_delayed_work(&wl->assoc_work); + + if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) + gelic_wl_disconnect(netdev); + + /* reset our state machine */ + gelic_wl_reset_state(wl); + + netif_stop_queue(netdev); + + gelic_card_down(card); + + pr_debug("%s:->\n", __func__); + return 0; +} + +/* -- */ + +static const struct net_device_ops gelic_wl_netdevice_ops = { + .ndo_open = gelic_wl_open, + .ndo_stop = gelic_wl_stop, + .ndo_start_xmit = gelic_net_xmit, + .ndo_set_multicast_list = gelic_net_set_multi, + .ndo_change_mtu = gelic_net_change_mtu, + .ndo_tx_timeout = gelic_net_tx_timeout, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = gelic_net_poll_controller, +#endif +}; + +static const struct ethtool_ops gelic_wl_ethtool_ops = { + .get_drvinfo = gelic_net_get_drvinfo, + .get_link = gelic_wl_get_link, +}; + +static void __devinit gelic_wl_setup_netdev_ops(struct net_device *netdev) +{ + struct gelic_wl_info *wl; + wl = port_wl(netdev_priv(netdev)); + BUG_ON(!wl); + netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT; + + netdev->ethtool_ops = &gelic_wl_ethtool_ops; + netdev->netdev_ops = &gelic_wl_netdevice_ops; + netdev->wireless_data = &wl->wireless_data; + netdev->wireless_handlers = &gelic_wl_wext_handler_def; +} + +/* + * driver probe/remove + */ +int __devinit gelic_wl_driver_probe(struct gelic_card *card) +{ + int ret; + struct net_device *netdev; + + pr_debug("%s:start\n", __func__); + + if (ps3_compare_firmware_version(1, 6, 0) < 0) + return 0; + if (!card->vlan[GELIC_PORT_WIRELESS].tx) + return 0; + + /* alloc netdevice for wireless */ + netdev = gelic_wl_alloc(card); + if (!netdev) + return -ENOMEM; + + /* setup net_device structure */ + SET_NETDEV_DEV(netdev, &card->dev->core); + gelic_wl_setup_netdev_ops(netdev); + + /* setup some of net_device and register it */ + ret = gelic_net_setup_netdev(netdev, card); + if (ret) + goto fail_setup; + card->netdev[GELIC_PORT_WIRELESS] = netdev; + + /* add enable wireless interrupt */ + card->irq_mask |= GELIC_CARD_WLAN_EVENT_RECEIVED | + GELIC_CARD_WLAN_COMMAND_COMPLETED; + /* to allow wireless commands while both interfaces are down */ + gelic_card_set_irq_mask(card, GELIC_CARD_WLAN_EVENT_RECEIVED | + GELIC_CARD_WLAN_COMMAND_COMPLETED); + pr_debug("%s:end\n", __func__); + return 0; + +fail_setup: + gelic_wl_free(port_wl(netdev_port(netdev))); + + return ret; +} + +int gelic_wl_driver_remove(struct gelic_card *card) +{ + struct gelic_wl_info *wl; + struct net_device *netdev; + + pr_debug("%s:start\n", __func__); + + if (ps3_compare_firmware_version(1, 6, 0) < 0) + return 0; + if (!card->vlan[GELIC_PORT_WIRELESS].tx) + return 0; + + netdev = card->netdev[GELIC_PORT_WIRELESS]; + wl = port_wl(netdev_priv(netdev)); + + /* if the interface was not up, but associated */ + if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) + gelic_wl_disconnect(netdev); + + complete(&wl->cmd_done_intr); + + /* cancel all work queue */ + cancel_delayed_work(&wl->assoc_work); + cancel_delayed_work(&wl->event_work); + flush_workqueue(wl->eurus_cmd_queue); + flush_workqueue(wl->event_queue); + + unregister_netdev(netdev); + + /* disable wireless interrupt */ + pr_debug("%s: disable intr\n", __func__); + card->irq_mask &= ~(GELIC_CARD_WLAN_EVENT_RECEIVED | + GELIC_CARD_WLAN_COMMAND_COMPLETED); + /* free bss list, netdev*/ + gelic_wl_free(wl); + pr_debug("%s:end\n", __func__); + return 0; +} diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.h b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.h new file mode 100644 index 000000000000..f7e51b7d7049 --- /dev/null +++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.h @@ -0,0 +1,326 @@ +/* + * PS3 gelic network driver. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corporation + * + * 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 version 2. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef _GELIC_WIRELESS_H +#define _GELIC_WIRELESS_H + +#include +#include + + +/* return value from GELIC_LV1_GET_WLAN_EVENT netcontrol */ +enum gelic_lv1_wl_event { + GELIC_LV1_WL_EVENT_DEVICE_READY = 0x01, /* Eurus ready */ + GELIC_LV1_WL_EVENT_SCAN_COMPLETED = 0x02, /* Scan has completed */ + GELIC_LV1_WL_EVENT_DEAUTH = 0x04, /* Deauthed by the AP */ + GELIC_LV1_WL_EVENT_BEACON_LOST = 0x08, /* Beacon lost detected */ + GELIC_LV1_WL_EVENT_CONNECTED = 0x10, /* Connected to AP */ + GELIC_LV1_WL_EVENT_WPA_CONNECTED = 0x20, /* WPA connection */ + GELIC_LV1_WL_EVENT_WPA_ERROR = 0x40, /* MIC error */ +}; + +/* arguments for GELIC_LV1_POST_WLAN_COMMAND netcontrol */ +enum gelic_eurus_command { + GELIC_EURUS_CMD_ASSOC = 1, /* association start */ + GELIC_EURUS_CMD_DISASSOC = 2, /* disassociate */ + GELIC_EURUS_CMD_START_SCAN = 3, /* scan start */ + GELIC_EURUS_CMD_GET_SCAN = 4, /* get scan result */ + GELIC_EURUS_CMD_SET_COMMON_CFG = 5, /* set common config */ + GELIC_EURUS_CMD_GET_COMMON_CFG = 6, /* set common config */ + GELIC_EURUS_CMD_SET_WEP_CFG = 7, /* set WEP config */ + GELIC_EURUS_CMD_GET_WEP_CFG = 8, /* get WEP config */ + GELIC_EURUS_CMD_SET_WPA_CFG = 9, /* set WPA config */ + GELIC_EURUS_CMD_GET_WPA_CFG = 10, /* get WPA config */ + GELIC_EURUS_CMD_GET_RSSI_CFG = 11, /* get RSSI info. */ + GELIC_EURUS_CMD_MAX_INDEX +}; + +/* for GELIC_EURUS_CMD_COMMON_CFG */ +enum gelic_eurus_bss_type { + GELIC_EURUS_BSS_INFRA = 0, + GELIC_EURUS_BSS_ADHOC = 1, /* not supported */ +}; + +enum gelic_eurus_auth_method { + GELIC_EURUS_AUTH_OPEN = 0, /* FIXME: WLAN_AUTH_OPEN */ + GELIC_EURUS_AUTH_SHARED = 1, /* not supported */ +}; + +enum gelic_eurus_opmode { + GELIC_EURUS_OPMODE_11BG = 0, /* 802.11b/g */ + GELIC_EURUS_OPMODE_11B = 1, /* 802.11b only */ + GELIC_EURUS_OPMODE_11G = 2, /* 802.11g only */ +}; + +struct gelic_eurus_common_cfg { + /* all fields are big endian */ + u16 scan_index; + u16 bss_type; /* infra or adhoc */ + u16 auth_method; /* shared key or open */ + u16 op_mode; /* B/G */ +} __packed; + + +/* for GELIC_EURUS_CMD_WEP_CFG */ +enum gelic_eurus_wep_security { + GELIC_EURUS_WEP_SEC_NONE = 0, + GELIC_EURUS_WEP_SEC_40BIT = 1, + GELIC_EURUS_WEP_SEC_104BIT = 2, +}; + +struct gelic_eurus_wep_cfg { + /* all fields are big endian */ + u16 security; + u8 key[4][16]; +} __packed; + +/* for GELIC_EURUS_CMD_WPA_CFG */ +enum gelic_eurus_wpa_security { + GELIC_EURUS_WPA_SEC_NONE = 0x0000, + /* group=TKIP, pairwise=TKIP */ + GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP = 0x0001, + /* group=AES, pairwise=AES */ + GELIC_EURUS_WPA_SEC_WPA_AES_AES = 0x0002, + /* group=TKIP, pairwise=TKIP */ + GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP = 0x0004, + /* group=AES, pairwise=AES */ + GELIC_EURUS_WPA_SEC_WPA2_AES_AES = 0x0008, + /* group=TKIP, pairwise=AES */ + GELIC_EURUS_WPA_SEC_WPA_TKIP_AES = 0x0010, + /* group=TKIP, pairwise=AES */ + GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES = 0x0020, +}; + +enum gelic_eurus_wpa_psk_type { + GELIC_EURUS_WPA_PSK_PASSPHRASE = 0, /* passphrase string */ + GELIC_EURUS_WPA_PSK_BIN = 1, /* 32 bytes binary key */ +}; + +#define GELIC_WL_EURUS_PSK_MAX_LEN 64 +#define WPA_PSK_LEN 32 /* WPA spec says 256bit */ + +struct gelic_eurus_wpa_cfg { + /* all fields are big endian */ + u16 security; + u16 psk_type; /* psk key encoding type */ + u8 psk[GELIC_WL_EURUS_PSK_MAX_LEN]; /* psk key; hex or passphrase */ +} __packed; + +/* for GELIC_EURUS_CMD_{START,GET}_SCAN */ +enum gelic_eurus_scan_capability { + GELIC_EURUS_SCAN_CAP_ADHOC = 0x0000, + GELIC_EURUS_SCAN_CAP_INFRA = 0x0001, + GELIC_EURUS_SCAN_CAP_MASK = 0x0001, +}; + +enum gelic_eurus_scan_sec_type { + GELIC_EURUS_SCAN_SEC_NONE = 0x0000, + GELIC_EURUS_SCAN_SEC_WEP = 0x0100, + GELIC_EURUS_SCAN_SEC_WPA = 0x0200, + GELIC_EURUS_SCAN_SEC_WPA2 = 0x0400, + GELIC_EURUS_SCAN_SEC_MASK = 0x0f00, +}; + +enum gelic_eurus_scan_sec_wep_type { + GELIC_EURUS_SCAN_SEC_WEP_UNKNOWN = 0x0000, + GELIC_EURUS_SCAN_SEC_WEP_40 = 0x0001, + GELIC_EURUS_SCAN_SEC_WEP_104 = 0x0002, + GELIC_EURUS_SCAN_SEC_WEP_MASK = 0x0003, +}; + +enum gelic_eurus_scan_sec_wpa_type { + GELIC_EURUS_SCAN_SEC_WPA_UNKNOWN = 0x0000, + GELIC_EURUS_SCAN_SEC_WPA_TKIP = 0x0001, + GELIC_EURUS_SCAN_SEC_WPA_AES = 0x0002, + GELIC_EURUS_SCAN_SEC_WPA_MASK = 0x0003, +}; + +/* + * hw BSS information structure returned from GELIC_EURUS_CMD_GET_SCAN + */ +struct gelic_eurus_scan_info { + /* all fields are big endian */ + __be16 size; + __be16 rssi; /* percentage */ + __be16 channel; /* channel number */ + __be16 beacon_period; /* FIXME: in msec unit */ + __be16 capability; + __be16 security; + u8 bssid[8]; /* last ETH_ALEN are valid. bssid[0],[1] are unused */ + u8 essid[32]; /* IW_ESSID_MAX_SIZE */ + u8 rate[16]; /* first 12 are valid */ + u8 ext_rate[16]; /* first 16 are valid */ + __be32 reserved1; + __be32 reserved2; + __be32 reserved3; + __be32 reserved4; + u8 elements[0]; /* ie */ +} __packed; + +/* the hypervisor returns bbs up to 16 */ +#define GELIC_EURUS_MAX_SCAN (16) +struct gelic_wl_scan_info { + struct list_head list; + struct gelic_eurus_scan_info *hwinfo; + + int valid; /* set 1 if this entry was in latest scanned list + * from Eurus */ + unsigned int eurus_index; /* index in the Eurus list */ + unsigned long last_scanned; /* acquired time */ + + unsigned int rate_len; + unsigned int rate_ext_len; + unsigned int essid_len; +}; + +/* for GELIC_EURUS_CMD_GET_RSSI */ +struct gelic_eurus_rssi_info { + /* big endian */ + __be16 rssi; +} __packed; + + +/* for 'stat' member of gelic_wl_info */ +enum gelic_wl_info_status_bit { + GELIC_WL_STAT_CONFIGURED, + GELIC_WL_STAT_CH_INFO, /* ch info acquired */ + GELIC_WL_STAT_ESSID_SET, /* ESSID specified by userspace */ + GELIC_WL_STAT_BSSID_SET, /* BSSID specified by userspace */ + GELIC_WL_STAT_WPA_PSK_SET, /* PMK specified by userspace */ + GELIC_WL_STAT_WPA_LEVEL_SET, /* WEP or WPA[2] selected */ +}; + +/* for 'scan_stat' member of gelic_wl_info */ +enum gelic_wl_scan_state { + /* just initialized or get last scan result failed */ + GELIC_WL_SCAN_STAT_INIT, + /* scan request issued, accepted or chip is scanning */ + GELIC_WL_SCAN_STAT_SCANNING, + /* scan results retrieved */ + GELIC_WL_SCAN_STAT_GOT_LIST, +}; + +/* for 'cipher_method' */ +enum gelic_wl_cipher_method { + GELIC_WL_CIPHER_NONE, + GELIC_WL_CIPHER_WEP, + GELIC_WL_CIPHER_TKIP, + GELIC_WL_CIPHER_AES, +}; + +/* for 'wpa_level' */ +enum gelic_wl_wpa_level { + GELIC_WL_WPA_LEVEL_NONE, + GELIC_WL_WPA_LEVEL_WPA, + GELIC_WL_WPA_LEVEL_WPA2, +}; + +/* for 'assoc_stat' */ +enum gelic_wl_assoc_state { + GELIC_WL_ASSOC_STAT_DISCONN, + GELIC_WL_ASSOC_STAT_ASSOCIATING, + GELIC_WL_ASSOC_STAT_ASSOCIATED, +}; +/* part of private data alloc_etherdev() allocated */ +#define GELIC_WEP_KEYS 4 +struct gelic_wl_info { + /* bss list */ + struct mutex scan_lock; + struct list_head network_list; + struct list_head network_free_list; + struct gelic_wl_scan_info *networks; + + unsigned long scan_age; /* last scanned time */ + enum gelic_wl_scan_state scan_stat; + struct completion scan_done; + + /* eurus command queue */ + struct workqueue_struct *eurus_cmd_queue; + struct completion cmd_done_intr; + + /* eurus event handling */ + struct workqueue_struct *event_queue; + struct delayed_work event_work; + + /* wl status bits */ + unsigned long stat; + enum gelic_eurus_auth_method auth_method; /* open/shared */ + enum gelic_wl_cipher_method group_cipher_method; + enum gelic_wl_cipher_method pairwise_cipher_method; + enum gelic_wl_wpa_level wpa_level; /* wpa/wpa2 */ + + /* association handling */ + struct mutex assoc_stat_lock; + struct delayed_work assoc_work; + enum gelic_wl_assoc_state assoc_stat; + struct completion assoc_done; + + spinlock_t lock; + u16 ch_info; /* available channels. bit0 = ch1 */ + /* WEP keys */ + u8 key[GELIC_WEP_KEYS][IW_ENCODING_TOKEN_MAX]; + unsigned long key_enabled; + unsigned int key_len[GELIC_WEP_KEYS]; + unsigned int current_key; + /* WWPA PSK */ + u8 psk[GELIC_WL_EURUS_PSK_MAX_LEN]; + enum gelic_eurus_wpa_psk_type psk_type; + unsigned int psk_len; + + u8 essid[IW_ESSID_MAX_SIZE]; + u8 bssid[ETH_ALEN]; /* userland requested */ + u8 active_bssid[ETH_ALEN]; /* associated bssid */ + unsigned int essid_len; + + struct iw_public_data wireless_data; + struct iw_statistics iwstat; +}; + +#define GELIC_WL_BSS_MAX_ENT 32 +#define GELIC_WL_ASSOC_RETRY 50 +static inline struct gelic_port *wl_port(struct gelic_wl_info *wl) +{ + return container_of((void *)wl, struct gelic_port, priv); +} +static inline struct gelic_wl_info *port_wl(struct gelic_port *port) +{ + return port_priv(port); +} + +struct gelic_eurus_cmd { + struct work_struct work; + struct gelic_wl_info *wl; + unsigned int cmd; /* command code */ + u64 tag; + u64 size; + void *buffer; + unsigned int buf_size; + struct completion done; + int status; + u64 cmd_status; +}; + +/* private ioctls to pass PSK */ +#define GELIC_WL_PRIV_SET_PSK (SIOCIWFIRSTPRIV + 0) +#define GELIC_WL_PRIV_GET_PSK (SIOCIWFIRSTPRIV + 1) + +extern int gelic_wl_driver_probe(struct gelic_card *card); +extern int gelic_wl_driver_remove(struct gelic_card *card); +extern void gelic_wl_interrupt(struct net_device *netdev, u64 status); +#endif /* _GELIC_WIRELESS_H */ diff --git a/drivers/net/ethernet/toshiba/spider_net.c b/drivers/net/ethernet/toshiba/spider_net.c new file mode 100644 index 000000000000..1ff3491c8240 --- /dev/null +++ b/drivers/net/ethernet/toshiba/spider_net.c @@ -0,0 +1,2603 @@ +/* + * Network device driver for Cell Processor-Based Blade and Celleb platform + * + * (C) Copyright IBM Corp. 2005 + * (C) Copyright 2006 TOSHIBA CORPORATION + * + * Authors : Utz Bacher + * Jens Osterkamp + * + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spider_net.h" + +MODULE_AUTHOR("Utz Bacher and Jens Osterkamp " \ + ""); +MODULE_DESCRIPTION("Spider Southbridge Gigabit Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(VERSION); +MODULE_FIRMWARE(SPIDER_NET_FIRMWARE_NAME); + +static int rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_DEFAULT; +static int tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_DEFAULT; + +module_param(rx_descriptors, int, 0444); +module_param(tx_descriptors, int, 0444); + +MODULE_PARM_DESC(rx_descriptors, "number of descriptors used " \ + "in rx chains"); +MODULE_PARM_DESC(tx_descriptors, "number of descriptors used " \ + "in tx chain"); + +char spider_net_driver_name[] = "spidernet"; + +static DEFINE_PCI_DEVICE_TABLE(spider_net_pci_tbl) = { + { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SPIDER_NET, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, spider_net_pci_tbl); + +/** + * spider_net_read_reg - reads an SMMIO register of a card + * @card: device structure + * @reg: register to read from + * + * returns the content of the specified SMMIO register. + */ +static inline u32 +spider_net_read_reg(struct spider_net_card *card, u32 reg) +{ + /* We use the powerpc specific variants instead of readl_be() because + * we know spidernet is not a real PCI device and we can thus avoid the + * performance hit caused by the PCI workarounds. + */ + return in_be32(card->regs + reg); +} + +/** + * spider_net_write_reg - writes to an SMMIO register of a card + * @card: device structure + * @reg: register to write to + * @value: value to write into the specified SMMIO register + */ +static inline void +spider_net_write_reg(struct spider_net_card *card, u32 reg, u32 value) +{ + /* We use the powerpc specific variants instead of writel_be() because + * we know spidernet is not a real PCI device and we can thus avoid the + * performance hit caused by the PCI workarounds. + */ + out_be32(card->regs + reg, value); +} + +/** spider_net_write_phy - write to phy register + * @netdev: adapter to be written to + * @mii_id: id of MII + * @reg: PHY register + * @val: value to be written to phy register + * + * spider_net_write_phy_register writes to an arbitrary PHY + * register via the spider GPCWOPCMD register. We assume the queue does + * not run full (not more than 15 commands outstanding). + **/ +static void +spider_net_write_phy(struct net_device *netdev, int mii_id, + int reg, int val) +{ + struct spider_net_card *card = netdev_priv(netdev); + u32 writevalue; + + writevalue = ((u32)mii_id << 21) | + ((u32)reg << 16) | ((u32)val); + + spider_net_write_reg(card, SPIDER_NET_GPCWOPCMD, writevalue); +} + +/** spider_net_read_phy - read from phy register + * @netdev: network device to be read from + * @mii_id: id of MII + * @reg: PHY register + * + * Returns value read from PHY register + * + * spider_net_write_phy reads from an arbitrary PHY + * register via the spider GPCROPCMD register + **/ +static int +spider_net_read_phy(struct net_device *netdev, int mii_id, int reg) +{ + struct spider_net_card *card = netdev_priv(netdev); + u32 readvalue; + + readvalue = ((u32)mii_id << 21) | ((u32)reg << 16); + spider_net_write_reg(card, SPIDER_NET_GPCROPCMD, readvalue); + + /* we don't use semaphores to wait for an SPIDER_NET_GPROPCMPINT + * interrupt, as we poll for the completion of the read operation + * in spider_net_read_phy. Should take about 50 us */ + do { + readvalue = spider_net_read_reg(card, SPIDER_NET_GPCROPCMD); + } while (readvalue & SPIDER_NET_GPREXEC); + + readvalue &= SPIDER_NET_GPRDAT_MASK; + + return readvalue; +} + +/** + * spider_net_setup_aneg - initial auto-negotiation setup + * @card: device structure + **/ +static void +spider_net_setup_aneg(struct spider_net_card *card) +{ + struct mii_phy *phy = &card->phy; + u32 advertise = 0; + u16 bmsr, estat; + + bmsr = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR); + estat = spider_net_read_phy(card->netdev, phy->mii_id, MII_ESTATUS); + + if (bmsr & BMSR_10HALF) + advertise |= ADVERTISED_10baseT_Half; + if (bmsr & BMSR_10FULL) + advertise |= ADVERTISED_10baseT_Full; + if (bmsr & BMSR_100HALF) + advertise |= ADVERTISED_100baseT_Half; + if (bmsr & BMSR_100FULL) + advertise |= ADVERTISED_100baseT_Full; + + if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_TFULL)) + advertise |= SUPPORTED_1000baseT_Full; + if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_THALF)) + advertise |= SUPPORTED_1000baseT_Half; + + mii_phy_probe(phy, phy->mii_id); + phy->def->ops->setup_aneg(phy, advertise); + +} + +/** + * spider_net_rx_irq_off - switch off rx irq on this spider card + * @card: device structure + * + * switches off rx irq by masking them out in the GHIINTnMSK register + */ +static void +spider_net_rx_irq_off(struct spider_net_card *card) +{ + u32 regvalue; + + regvalue = SPIDER_NET_INT0_MASK_VALUE & (~SPIDER_NET_RXINT); + spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue); +} + +/** + * spider_net_rx_irq_on - switch on rx irq on this spider card + * @card: device structure + * + * switches on rx irq by enabling them in the GHIINTnMSK register + */ +static void +spider_net_rx_irq_on(struct spider_net_card *card) +{ + u32 regvalue; + + regvalue = SPIDER_NET_INT0_MASK_VALUE | SPIDER_NET_RXINT; + spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue); +} + +/** + * spider_net_set_promisc - sets the unicast address or the promiscuous mode + * @card: card structure + * + * spider_net_set_promisc sets the unicast destination address filter and + * thus either allows for non-promisc mode or promisc mode + */ +static void +spider_net_set_promisc(struct spider_net_card *card) +{ + u32 macu, macl; + struct net_device *netdev = card->netdev; + + if (netdev->flags & IFF_PROMISC) { + /* clear destination entry 0 */ + spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR, 0); + spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR + 0x04, 0); + spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, + SPIDER_NET_PROMISC_VALUE); + } else { + macu = netdev->dev_addr[0]; + macu <<= 8; + macu |= netdev->dev_addr[1]; + memcpy(&macl, &netdev->dev_addr[2], sizeof(macl)); + + macu |= SPIDER_NET_UA_DESCR_VALUE; + spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR, macu); + spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR + 0x04, macl); + spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, + SPIDER_NET_NONPROMISC_VALUE); + } +} + +/** + * spider_net_get_mac_address - read mac address from spider card + * @card: device structure + * + * reads MAC address from GMACUNIMACU and GMACUNIMACL registers + */ +static int +spider_net_get_mac_address(struct net_device *netdev) +{ + struct spider_net_card *card = netdev_priv(netdev); + u32 macl, macu; + + macl = spider_net_read_reg(card, SPIDER_NET_GMACUNIMACL); + macu = spider_net_read_reg(card, SPIDER_NET_GMACUNIMACU); + + netdev->dev_addr[0] = (macu >> 24) & 0xff; + netdev->dev_addr[1] = (macu >> 16) & 0xff; + netdev->dev_addr[2] = (macu >> 8) & 0xff; + netdev->dev_addr[3] = macu & 0xff; + netdev->dev_addr[4] = (macl >> 8) & 0xff; + netdev->dev_addr[5] = macl & 0xff; + + if (!is_valid_ether_addr(&netdev->dev_addr[0])) + return -EINVAL; + + return 0; +} + +/** + * spider_net_get_descr_status -- returns the status of a descriptor + * @descr: descriptor to look at + * + * returns the status as in the dmac_cmd_status field of the descriptor + */ +static inline int +spider_net_get_descr_status(struct spider_net_hw_descr *hwdescr) +{ + return hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_IND_PROC_MASK; +} + +/** + * spider_net_free_chain - free descriptor chain + * @card: card structure + * @chain: address of chain + * + */ +static void +spider_net_free_chain(struct spider_net_card *card, + struct spider_net_descr_chain *chain) +{ + struct spider_net_descr *descr; + + descr = chain->ring; + do { + descr->bus_addr = 0; + descr->hwdescr->next_descr_addr = 0; + descr = descr->next; + } while (descr != chain->ring); + + dma_free_coherent(&card->pdev->dev, chain->num_desc, + chain->hwring, chain->dma_addr); +} + +/** + * spider_net_init_chain - alloc and link descriptor chain + * @card: card structure + * @chain: address of chain + * + * We manage a circular list that mirrors the hardware structure, + * except that the hardware uses bus addresses. + * + * Returns 0 on success, <0 on failure + */ +static int +spider_net_init_chain(struct spider_net_card *card, + struct spider_net_descr_chain *chain) +{ + int i; + struct spider_net_descr *descr; + struct spider_net_hw_descr *hwdescr; + dma_addr_t buf; + size_t alloc_size; + + alloc_size = chain->num_desc * sizeof(struct spider_net_hw_descr); + + chain->hwring = dma_alloc_coherent(&card->pdev->dev, alloc_size, + &chain->dma_addr, GFP_KERNEL); + + if (!chain->hwring) + return -ENOMEM; + + memset(chain->ring, 0, chain->num_desc * sizeof(struct spider_net_descr)); + + /* Set up the hardware pointers in each descriptor */ + descr = chain->ring; + hwdescr = chain->hwring; + buf = chain->dma_addr; + for (i=0; i < chain->num_desc; i++, descr++, hwdescr++) { + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + hwdescr->next_descr_addr = 0; + + descr->hwdescr = hwdescr; + descr->bus_addr = buf; + descr->next = descr + 1; + descr->prev = descr - 1; + + buf += sizeof(struct spider_net_hw_descr); + } + /* do actual circular list */ + (descr-1)->next = chain->ring; + chain->ring->prev = descr-1; + + spin_lock_init(&chain->lock); + chain->head = chain->ring; + chain->tail = chain->ring; + return 0; +} + +/** + * spider_net_free_rx_chain_contents - frees descr contents in rx chain + * @card: card structure + * + * returns 0 on success, <0 on failure + */ +static void +spider_net_free_rx_chain_contents(struct spider_net_card *card) +{ + struct spider_net_descr *descr; + + descr = card->rx_chain.head; + do { + if (descr->skb) { + pci_unmap_single(card->pdev, descr->hwdescr->buf_addr, + SPIDER_NET_MAX_FRAME, + PCI_DMA_BIDIRECTIONAL); + dev_kfree_skb(descr->skb); + descr->skb = NULL; + } + descr = descr->next; + } while (descr != card->rx_chain.head); +} + +/** + * spider_net_prepare_rx_descr - Reinitialize RX descriptor + * @card: card structure + * @descr: descriptor to re-init + * + * Return 0 on success, <0 on failure. + * + * Allocates a new rx skb, iommu-maps it and attaches it to the + * descriptor. Mark the descriptor as activated, ready-to-use. + */ +static int +spider_net_prepare_rx_descr(struct spider_net_card *card, + struct spider_net_descr *descr) +{ + struct spider_net_hw_descr *hwdescr = descr->hwdescr; + dma_addr_t buf; + int offset; + int bufsize; + + /* we need to round up the buffer size to a multiple of 128 */ + bufsize = (SPIDER_NET_MAX_FRAME + SPIDER_NET_RXBUF_ALIGN - 1) & + (~(SPIDER_NET_RXBUF_ALIGN - 1)); + + /* and we need to have it 128 byte aligned, therefore we allocate a + * bit more */ + /* allocate an skb */ + descr->skb = netdev_alloc_skb(card->netdev, + bufsize + SPIDER_NET_RXBUF_ALIGN - 1); + if (!descr->skb) { + if (netif_msg_rx_err(card) && net_ratelimit()) + dev_err(&card->netdev->dev, + "Not enough memory to allocate rx buffer\n"); + card->spider_stats.alloc_rx_skb_error++; + return -ENOMEM; + } + hwdescr->buf_size = bufsize; + hwdescr->result_size = 0; + hwdescr->valid_size = 0; + hwdescr->data_status = 0; + hwdescr->data_error = 0; + + offset = ((unsigned long)descr->skb->data) & + (SPIDER_NET_RXBUF_ALIGN - 1); + if (offset) + skb_reserve(descr->skb, SPIDER_NET_RXBUF_ALIGN - offset); + /* iommu-map the skb */ + buf = pci_map_single(card->pdev, descr->skb->data, + SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(card->pdev, buf)) { + dev_kfree_skb_any(descr->skb); + descr->skb = NULL; + if (netif_msg_rx_err(card) && net_ratelimit()) + dev_err(&card->netdev->dev, "Could not iommu-map rx buffer\n"); + card->spider_stats.rx_iommu_map_error++; + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + } else { + hwdescr->buf_addr = buf; + wmb(); + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED | + SPIDER_NET_DMAC_NOINTR_COMPLETE; + } + + return 0; +} + +/** + * spider_net_enable_rxchtails - sets RX dmac chain tail addresses + * @card: card structure + * + * spider_net_enable_rxchtails sets the RX DMAC chain tail addresses in the + * chip by writing to the appropriate register. DMA is enabled in + * spider_net_enable_rxdmac. + */ +static inline void +spider_net_enable_rxchtails(struct spider_net_card *card) +{ + /* assume chain is aligned correctly */ + spider_net_write_reg(card, SPIDER_NET_GDADCHA , + card->rx_chain.tail->bus_addr); +} + +/** + * spider_net_enable_rxdmac - enables a receive DMA controller + * @card: card structure + * + * spider_net_enable_rxdmac enables the DMA controller by setting RX_DMA_EN + * in the GDADMACCNTR register + */ +static inline void +spider_net_enable_rxdmac(struct spider_net_card *card) +{ + wmb(); + spider_net_write_reg(card, SPIDER_NET_GDADMACCNTR, + SPIDER_NET_DMA_RX_VALUE); +} + +/** + * spider_net_disable_rxdmac - disables the receive DMA controller + * @card: card structure + * + * spider_net_disable_rxdmac terminates processing on the DMA controller + * by turing off the DMA controller, with the force-end flag set. + */ +static inline void +spider_net_disable_rxdmac(struct spider_net_card *card) +{ + spider_net_write_reg(card, SPIDER_NET_GDADMACCNTR, + SPIDER_NET_DMA_RX_FEND_VALUE); +} + +/** + * spider_net_refill_rx_chain - refills descriptors/skbs in the rx chains + * @card: card structure + * + * refills descriptors in the rx chain: allocates skbs and iommu-maps them. + */ +static void +spider_net_refill_rx_chain(struct spider_net_card *card) +{ + struct spider_net_descr_chain *chain = &card->rx_chain; + unsigned long flags; + + /* one context doing the refill (and a second context seeing that + * and omitting it) is ok. If called by NAPI, we'll be called again + * as spider_net_decode_one_descr is called several times. If some + * interrupt calls us, the NAPI is about to clean up anyway. */ + if (!spin_trylock_irqsave(&chain->lock, flags)) + return; + + while (spider_net_get_descr_status(chain->head->hwdescr) == + SPIDER_NET_DESCR_NOT_IN_USE) { + if (spider_net_prepare_rx_descr(card, chain->head)) + break; + chain->head = chain->head->next; + } + + spin_unlock_irqrestore(&chain->lock, flags); +} + +/** + * spider_net_alloc_rx_skbs - Allocates rx skbs in rx descriptor chains + * @card: card structure + * + * Returns 0 on success, <0 on failure. + */ +static int +spider_net_alloc_rx_skbs(struct spider_net_card *card) +{ + struct spider_net_descr_chain *chain = &card->rx_chain; + struct spider_net_descr *start = chain->tail; + struct spider_net_descr *descr = start; + + /* Link up the hardware chain pointers */ + do { + descr->prev->hwdescr->next_descr_addr = descr->bus_addr; + descr = descr->next; + } while (descr != start); + + /* Put at least one buffer into the chain. if this fails, + * we've got a problem. If not, spider_net_refill_rx_chain + * will do the rest at the end of this function. */ + if (spider_net_prepare_rx_descr(card, chain->head)) + goto error; + else + chain->head = chain->head->next; + + /* This will allocate the rest of the rx buffers; + * if not, it's business as usual later on. */ + spider_net_refill_rx_chain(card); + spider_net_enable_rxdmac(card); + return 0; + +error: + spider_net_free_rx_chain_contents(card); + return -ENOMEM; +} + +/** + * spider_net_get_multicast_hash - generates hash for multicast filter table + * @addr: multicast address + * + * returns the hash value. + * + * spider_net_get_multicast_hash calculates a hash value for a given multicast + * address, that is used to set the multicast filter tables + */ +static u8 +spider_net_get_multicast_hash(struct net_device *netdev, __u8 *addr) +{ + u32 crc; + u8 hash; + char addr_for_crc[ETH_ALEN] = { 0, }; + int i, bit; + + for (i = 0; i < ETH_ALEN * 8; i++) { + bit = (addr[i / 8] >> (i % 8)) & 1; + addr_for_crc[ETH_ALEN - 1 - i / 8] += bit << (7 - (i % 8)); + } + + crc = crc32_be(~0, addr_for_crc, netdev->addr_len); + + hash = (crc >> 27); + hash <<= 3; + hash |= crc & 7; + hash &= 0xff; + + return hash; +} + +/** + * spider_net_set_multi - sets multicast addresses and promisc flags + * @netdev: interface device structure + * + * spider_net_set_multi configures multicast addresses as needed for the + * netdev interface. It also sets up multicast, allmulti and promisc + * flags appropriately + */ +static void +spider_net_set_multi(struct net_device *netdev) +{ + struct netdev_hw_addr *ha; + u8 hash; + int i; + u32 reg; + struct spider_net_card *card = netdev_priv(netdev); + unsigned long bitmask[SPIDER_NET_MULTICAST_HASHES / BITS_PER_LONG] = + {0, }; + + spider_net_set_promisc(card); + + if (netdev->flags & IFF_ALLMULTI) { + for (i = 0; i < SPIDER_NET_MULTICAST_HASHES; i++) { + set_bit(i, bitmask); + } + goto write_hash; + } + + /* well, we know, what the broadcast hash value is: it's xfd + hash = spider_net_get_multicast_hash(netdev, netdev->broadcast); */ + set_bit(0xfd, bitmask); + + netdev_for_each_mc_addr(ha, netdev) { + hash = spider_net_get_multicast_hash(netdev, ha->addr); + set_bit(hash, bitmask); + } + +write_hash: + for (i = 0; i < SPIDER_NET_MULTICAST_HASHES / 4; i++) { + reg = 0; + if (test_bit(i * 4, bitmask)) + reg += 0x08; + reg <<= 8; + if (test_bit(i * 4 + 1, bitmask)) + reg += 0x08; + reg <<= 8; + if (test_bit(i * 4 + 2, bitmask)) + reg += 0x08; + reg <<= 8; + if (test_bit(i * 4 + 3, bitmask)) + reg += 0x08; + + spider_net_write_reg(card, SPIDER_NET_GMRMHFILnR + i * 4, reg); + } +} + +/** + * spider_net_prepare_tx_descr - fill tx descriptor with skb data + * @card: card structure + * @skb: packet to use + * + * returns 0 on success, <0 on failure. + * + * fills out the descriptor structure with skb data and len. Copies data, + * if needed (32bit DMA!) + */ +static int +spider_net_prepare_tx_descr(struct spider_net_card *card, + struct sk_buff *skb) +{ + struct spider_net_descr_chain *chain = &card->tx_chain; + struct spider_net_descr *descr; + struct spider_net_hw_descr *hwdescr; + dma_addr_t buf; + unsigned long flags; + + buf = pci_map_single(card->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(card->pdev, buf)) { + if (netif_msg_tx_err(card) && net_ratelimit()) + dev_err(&card->netdev->dev, "could not iommu-map packet (%p, %i). " + "Dropping packet\n", skb->data, skb->len); + card->spider_stats.tx_iommu_map_error++; + return -ENOMEM; + } + + spin_lock_irqsave(&chain->lock, flags); + descr = card->tx_chain.head; + if (descr->next == chain->tail->prev) { + spin_unlock_irqrestore(&chain->lock, flags); + pci_unmap_single(card->pdev, buf, skb->len, PCI_DMA_TODEVICE); + return -ENOMEM; + } + hwdescr = descr->hwdescr; + chain->head = descr->next; + + descr->skb = skb; + hwdescr->buf_addr = buf; + hwdescr->buf_size = skb->len; + hwdescr->next_descr_addr = 0; + hwdescr->data_status = 0; + + hwdescr->dmac_cmd_status = + SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_TXFRMTL; + spin_unlock_irqrestore(&chain->lock, flags); + + if (skb->ip_summed == CHECKSUM_PARTIAL) + switch (ip_hdr(skb)->protocol) { + case IPPROTO_TCP: + hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_TCP; + break; + case IPPROTO_UDP: + hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_UDP; + break; + } + + /* Chain the bus address, so that the DMA engine finds this descr. */ + wmb(); + descr->prev->hwdescr->next_descr_addr = descr->bus_addr; + + card->netdev->trans_start = jiffies; /* set netdev watchdog timer */ + return 0; +} + +static int +spider_net_set_low_watermark(struct spider_net_card *card) +{ + struct spider_net_descr *descr = card->tx_chain.tail; + struct spider_net_hw_descr *hwdescr; + unsigned long flags; + int status; + int cnt=0; + int i; + + /* Measure the length of the queue. Measurement does not + * need to be precise -- does not need a lock. */ + while (descr != card->tx_chain.head) { + status = descr->hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_NOT_IN_USE; + if (status == SPIDER_NET_DESCR_NOT_IN_USE) + break; + descr = descr->next; + cnt++; + } + + /* If TX queue is short, don't even bother with interrupts */ + if (cnt < card->tx_chain.num_desc/4) + return cnt; + + /* Set low-watermark 3/4th's of the way into the queue. */ + descr = card->tx_chain.tail; + cnt = (cnt*3)/4; + for (i=0;inext; + + /* Set the new watermark, clear the old watermark */ + spin_lock_irqsave(&card->tx_chain.lock, flags); + descr->hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_TXDESFLG; + if (card->low_watermark && card->low_watermark != descr) { + hwdescr = card->low_watermark->hwdescr; + hwdescr->dmac_cmd_status = + hwdescr->dmac_cmd_status & ~SPIDER_NET_DESCR_TXDESFLG; + } + card->low_watermark = descr; + spin_unlock_irqrestore(&card->tx_chain.lock, flags); + return cnt; +} + +/** + * spider_net_release_tx_chain - processes sent tx descriptors + * @card: adapter structure + * @brutal: if set, don't care about whether descriptor seems to be in use + * + * returns 0 if the tx ring is empty, otherwise 1. + * + * spider_net_release_tx_chain releases the tx descriptors that spider has + * finished with (if non-brutal) or simply release tx descriptors (if brutal). + * If some other context is calling this function, we return 1 so that we're + * scheduled again (if we were scheduled) and will not lose initiative. + */ +static int +spider_net_release_tx_chain(struct spider_net_card *card, int brutal) +{ + struct net_device *dev = card->netdev; + struct spider_net_descr_chain *chain = &card->tx_chain; + struct spider_net_descr *descr; + struct spider_net_hw_descr *hwdescr; + struct sk_buff *skb; + u32 buf_addr; + unsigned long flags; + int status; + + while (1) { + spin_lock_irqsave(&chain->lock, flags); + if (chain->tail == chain->head) { + spin_unlock_irqrestore(&chain->lock, flags); + return 0; + } + descr = chain->tail; + hwdescr = descr->hwdescr; + + status = spider_net_get_descr_status(hwdescr); + switch (status) { + case SPIDER_NET_DESCR_COMPLETE: + dev->stats.tx_packets++; + dev->stats.tx_bytes += descr->skb->len; + break; + + case SPIDER_NET_DESCR_CARDOWNED: + if (!brutal) { + spin_unlock_irqrestore(&chain->lock, flags); + return 1; + } + + /* fallthrough, if we release the descriptors + * brutally (then we don't care about + * SPIDER_NET_DESCR_CARDOWNED) */ + + case SPIDER_NET_DESCR_RESPONSE_ERROR: + case SPIDER_NET_DESCR_PROTECTION_ERROR: + case SPIDER_NET_DESCR_FORCE_END: + if (netif_msg_tx_err(card)) + dev_err(&card->netdev->dev, "forcing end of tx descriptor " + "with status x%02x\n", status); + dev->stats.tx_errors++; + break; + + default: + dev->stats.tx_dropped++; + if (!brutal) { + spin_unlock_irqrestore(&chain->lock, flags); + return 1; + } + } + + chain->tail = descr->next; + hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_NOT_IN_USE; + skb = descr->skb; + descr->skb = NULL; + buf_addr = hwdescr->buf_addr; + spin_unlock_irqrestore(&chain->lock, flags); + + /* unmap the skb */ + if (skb) { + pci_unmap_single(card->pdev, buf_addr, skb->len, + PCI_DMA_TODEVICE); + dev_kfree_skb(skb); + } + } + return 0; +} + +/** + * spider_net_kick_tx_dma - enables TX DMA processing + * @card: card structure + * + * This routine will start the transmit DMA running if + * it is not already running. This routine ned only be + * called when queueing a new packet to an empty tx queue. + * Writes the current tx chain head as start address + * of the tx descriptor chain and enables the transmission + * DMA engine. + */ +static inline void +spider_net_kick_tx_dma(struct spider_net_card *card) +{ + struct spider_net_descr *descr; + + if (spider_net_read_reg(card, SPIDER_NET_GDTDMACCNTR) & + SPIDER_NET_TX_DMA_EN) + goto out; + + descr = card->tx_chain.tail; + for (;;) { + if (spider_net_get_descr_status(descr->hwdescr) == + SPIDER_NET_DESCR_CARDOWNED) { + spider_net_write_reg(card, SPIDER_NET_GDTDCHA, + descr->bus_addr); + spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, + SPIDER_NET_DMA_TX_VALUE); + break; + } + if (descr == card->tx_chain.head) + break; + descr = descr->next; + } + +out: + mod_timer(&card->tx_timer, jiffies + SPIDER_NET_TX_TIMER); +} + +/** + * spider_net_xmit - transmits a frame over the device + * @skb: packet to send out + * @netdev: interface device structure + * + * returns 0 on success, !0 on failure + */ +static int +spider_net_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + int cnt; + struct spider_net_card *card = netdev_priv(netdev); + + spider_net_release_tx_chain(card, 0); + + if (spider_net_prepare_tx_descr(card, skb) != 0) { + netdev->stats.tx_dropped++; + netif_stop_queue(netdev); + return NETDEV_TX_BUSY; + } + + cnt = spider_net_set_low_watermark(card); + if (cnt < 5) + spider_net_kick_tx_dma(card); + return NETDEV_TX_OK; +} + +/** + * spider_net_cleanup_tx_ring - cleans up the TX ring + * @card: card structure + * + * spider_net_cleanup_tx_ring is called by either the tx_timer + * or from the NAPI polling routine. + * This routine releases resources associted with transmitted + * packets, including updating the queue tail pointer. + */ +static void +spider_net_cleanup_tx_ring(struct spider_net_card *card) +{ + if ((spider_net_release_tx_chain(card, 0) != 0) && + (card->netdev->flags & IFF_UP)) { + spider_net_kick_tx_dma(card); + netif_wake_queue(card->netdev); + } +} + +/** + * spider_net_do_ioctl - called for device ioctls + * @netdev: interface device structure + * @ifr: request parameter structure for ioctl + * @cmd: command code for ioctl + * + * returns 0 on success, <0 on failure. Currently, we have no special ioctls. + * -EOPNOTSUPP is returned, if an unknown ioctl was requested + */ +static int +spider_net_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + default: + return -EOPNOTSUPP; + } +} + +/** + * spider_net_pass_skb_up - takes an skb from a descriptor and passes it on + * @descr: descriptor to process + * @card: card structure + * + * Fills out skb structure and passes the data to the stack. + * The descriptor state is not changed. + */ +static void +spider_net_pass_skb_up(struct spider_net_descr *descr, + struct spider_net_card *card) +{ + struct spider_net_hw_descr *hwdescr = descr->hwdescr; + struct sk_buff *skb = descr->skb; + struct net_device *netdev = card->netdev; + u32 data_status = hwdescr->data_status; + u32 data_error = hwdescr->data_error; + + skb_put(skb, hwdescr->valid_size); + + /* the card seems to add 2 bytes of junk in front + * of the ethernet frame */ +#define SPIDER_MISALIGN 2 + skb_pull(skb, SPIDER_MISALIGN); + skb->protocol = eth_type_trans(skb, netdev); + + /* checksum offload */ + skb_checksum_none_assert(skb); + if (netdev->features & NETIF_F_RXCSUM) { + if ( ( (data_status & SPIDER_NET_DATA_STATUS_CKSUM_MASK) == + SPIDER_NET_DATA_STATUS_CKSUM_MASK) && + !(data_error & SPIDER_NET_DATA_ERR_CKSUM_MASK)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + + if (data_status & SPIDER_NET_VLAN_PACKET) { + /* further enhancements: HW-accel VLAN */ + } + + /* update netdevice statistics */ + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += skb->len; + + /* pass skb up to stack */ + netif_receive_skb(skb); +} + +static void show_rx_chain(struct spider_net_card *card) +{ + struct spider_net_descr_chain *chain = &card->rx_chain; + struct spider_net_descr *start= chain->tail; + struct spider_net_descr *descr= start; + struct spider_net_hw_descr *hwd = start->hwdescr; + struct device *dev = &card->netdev->dev; + u32 curr_desc, next_desc; + int status; + + int tot = 0; + int cnt = 0; + int off = start - chain->ring; + int cstat = hwd->dmac_cmd_status; + + dev_info(dev, "Total number of descrs=%d\n", + chain->num_desc); + dev_info(dev, "Chain tail located at descr=%d, status=0x%x\n", + off, cstat); + + curr_desc = spider_net_read_reg(card, SPIDER_NET_GDACTDPA); + next_desc = spider_net_read_reg(card, SPIDER_NET_GDACNEXTDA); + + status = cstat; + do + { + hwd = descr->hwdescr; + off = descr - chain->ring; + status = hwd->dmac_cmd_status; + + if (descr == chain->head) + dev_info(dev, "Chain head is at %d, head status=0x%x\n", + off, status); + + if (curr_desc == descr->bus_addr) + dev_info(dev, "HW curr desc (GDACTDPA) is at %d, status=0x%x\n", + off, status); + + if (next_desc == descr->bus_addr) + dev_info(dev, "HW next desc (GDACNEXTDA) is at %d, status=0x%x\n", + off, status); + + if (hwd->next_descr_addr == 0) + dev_info(dev, "chain is cut at %d\n", off); + + if (cstat != status) { + int from = (chain->num_desc + off - cnt) % chain->num_desc; + int to = (chain->num_desc + off - 1) % chain->num_desc; + dev_info(dev, "Have %d (from %d to %d) descrs " + "with stat=0x%08x\n", cnt, from, to, cstat); + cstat = status; + cnt = 0; + } + + cnt ++; + tot ++; + descr = descr->next; + } while (descr != start); + + dev_info(dev, "Last %d descrs with stat=0x%08x " + "for a total of %d descrs\n", cnt, cstat, tot); + +#ifdef DEBUG + /* Now dump the whole ring */ + descr = start; + do + { + struct spider_net_hw_descr *hwd = descr->hwdescr; + status = spider_net_get_descr_status(hwd); + cnt = descr - chain->ring; + dev_info(dev, "Descr %d stat=0x%08x skb=%p\n", + cnt, status, descr->skb); + dev_info(dev, "bus addr=%08x buf addr=%08x sz=%d\n", + descr->bus_addr, hwd->buf_addr, hwd->buf_size); + dev_info(dev, "next=%08x result sz=%d valid sz=%d\n", + hwd->next_descr_addr, hwd->result_size, + hwd->valid_size); + dev_info(dev, "dmac=%08x data stat=%08x data err=%08x\n", + hwd->dmac_cmd_status, hwd->data_status, + hwd->data_error); + dev_info(dev, "\n"); + + descr = descr->next; + } while (descr != start); +#endif + +} + +/** + * spider_net_resync_head_ptr - Advance head ptr past empty descrs + * + * If the driver fails to keep up and empty the queue, then the + * hardware wil run out of room to put incoming packets. This + * will cause the hardware to skip descrs that are full (instead + * of halting/retrying). Thus, once the driver runs, it wil need + * to "catch up" to where the hardware chain pointer is at. + */ +static void spider_net_resync_head_ptr(struct spider_net_card *card) +{ + unsigned long flags; + struct spider_net_descr_chain *chain = &card->rx_chain; + struct spider_net_descr *descr; + int i, status; + + /* Advance head pointer past any empty descrs */ + descr = chain->head; + status = spider_net_get_descr_status(descr->hwdescr); + + if (status == SPIDER_NET_DESCR_NOT_IN_USE) + return; + + spin_lock_irqsave(&chain->lock, flags); + + descr = chain->head; + status = spider_net_get_descr_status(descr->hwdescr); + for (i=0; inum_desc; i++) { + if (status != SPIDER_NET_DESCR_CARDOWNED) break; + descr = descr->next; + status = spider_net_get_descr_status(descr->hwdescr); + } + chain->head = descr; + + spin_unlock_irqrestore(&chain->lock, flags); +} + +static int spider_net_resync_tail_ptr(struct spider_net_card *card) +{ + struct spider_net_descr_chain *chain = &card->rx_chain; + struct spider_net_descr *descr; + int i, status; + + /* Advance tail pointer past any empty and reaped descrs */ + descr = chain->tail; + status = spider_net_get_descr_status(descr->hwdescr); + + for (i=0; inum_desc; i++) { + if ((status != SPIDER_NET_DESCR_CARDOWNED) && + (status != SPIDER_NET_DESCR_NOT_IN_USE)) break; + descr = descr->next; + status = spider_net_get_descr_status(descr->hwdescr); + } + chain->tail = descr; + + if ((i == chain->num_desc) || (i == 0)) + return 1; + return 0; +} + +/** + * spider_net_decode_one_descr - processes an RX descriptor + * @card: card structure + * + * Returns 1 if a packet has been sent to the stack, otherwise 0. + * + * Processes an RX descriptor by iommu-unmapping the data buffer + * and passing the packet up to the stack. This function is called + * in softirq context, e.g. either bottom half from interrupt or + * NAPI polling context. + */ +static int +spider_net_decode_one_descr(struct spider_net_card *card) +{ + struct net_device *dev = card->netdev; + struct spider_net_descr_chain *chain = &card->rx_chain; + struct spider_net_descr *descr = chain->tail; + struct spider_net_hw_descr *hwdescr = descr->hwdescr; + u32 hw_buf_addr; + int status; + + status = spider_net_get_descr_status(hwdescr); + + /* Nothing in the descriptor, or ring must be empty */ + if ((status == SPIDER_NET_DESCR_CARDOWNED) || + (status == SPIDER_NET_DESCR_NOT_IN_USE)) + return 0; + + /* descriptor definitively used -- move on tail */ + chain->tail = descr->next; + + /* unmap descriptor */ + hw_buf_addr = hwdescr->buf_addr; + hwdescr->buf_addr = 0xffffffff; + pci_unmap_single(card->pdev, hw_buf_addr, + SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); + + if ( (status == SPIDER_NET_DESCR_RESPONSE_ERROR) || + (status == SPIDER_NET_DESCR_PROTECTION_ERROR) || + (status == SPIDER_NET_DESCR_FORCE_END) ) { + if (netif_msg_rx_err(card)) + dev_err(&dev->dev, + "dropping RX descriptor with state %d\n", status); + dev->stats.rx_dropped++; + goto bad_desc; + } + + if ( (status != SPIDER_NET_DESCR_COMPLETE) && + (status != SPIDER_NET_DESCR_FRAME_END) ) { + if (netif_msg_rx_err(card)) + dev_err(&card->netdev->dev, + "RX descriptor with unknown state %d\n", status); + card->spider_stats.rx_desc_unk_state++; + goto bad_desc; + } + + /* The cases we'll throw away the packet immediately */ + if (hwdescr->data_error & SPIDER_NET_DESTROY_RX_FLAGS) { + if (netif_msg_rx_err(card)) + dev_err(&card->netdev->dev, + "error in received descriptor found, " + "data_status=x%08x, data_error=x%08x\n", + hwdescr->data_status, hwdescr->data_error); + goto bad_desc; + } + + if (hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_BAD_STATUS) { + dev_err(&card->netdev->dev, "bad status, cmd_status=x%08x\n", + hwdescr->dmac_cmd_status); + pr_err("buf_addr=x%08x\n", hw_buf_addr); + pr_err("buf_size=x%08x\n", hwdescr->buf_size); + pr_err("next_descr_addr=x%08x\n", hwdescr->next_descr_addr); + pr_err("result_size=x%08x\n", hwdescr->result_size); + pr_err("valid_size=x%08x\n", hwdescr->valid_size); + pr_err("data_status=x%08x\n", hwdescr->data_status); + pr_err("data_error=x%08x\n", hwdescr->data_error); + pr_err("which=%ld\n", descr - card->rx_chain.ring); + + card->spider_stats.rx_desc_error++; + goto bad_desc; + } + + /* Ok, we've got a packet in descr */ + spider_net_pass_skb_up(descr, card); + descr->skb = NULL; + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + return 1; + +bad_desc: + if (netif_msg_rx_err(card)) + show_rx_chain(card); + dev_kfree_skb_irq(descr->skb); + descr->skb = NULL; + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + return 0; +} + +/** + * spider_net_poll - NAPI poll function called by the stack to return packets + * @netdev: interface device structure + * @budget: number of packets we can pass to the stack at most + * + * returns 0 if no more packets available to the driver/stack. Returns 1, + * if the quota is exceeded, but the driver has still packets. + * + * spider_net_poll returns all packets from the rx descriptors to the stack + * (using netif_receive_skb). If all/enough packets are up, the driver + * reenables interrupts and returns 0. If not, 1 is returned. + */ +static int spider_net_poll(struct napi_struct *napi, int budget) +{ + struct spider_net_card *card = container_of(napi, struct spider_net_card, napi); + int packets_done = 0; + + while (packets_done < budget) { + if (!spider_net_decode_one_descr(card)) + break; + + packets_done++; + } + + if ((packets_done == 0) && (card->num_rx_ints != 0)) { + if (!spider_net_resync_tail_ptr(card)) + packets_done = budget; + spider_net_resync_head_ptr(card); + } + card->num_rx_ints = 0; + + spider_net_refill_rx_chain(card); + spider_net_enable_rxdmac(card); + + spider_net_cleanup_tx_ring(card); + + /* if all packets are in the stack, enable interrupts and return 0 */ + /* if not, return 1 */ + if (packets_done < budget) { + napi_complete(napi); + spider_net_rx_irq_on(card); + card->ignore_rx_ramfull = 0; + } + + return packets_done; +} + +/** + * spider_net_change_mtu - changes the MTU of an interface + * @netdev: interface device structure + * @new_mtu: new MTU value + * + * returns 0 on success, <0 on failure + */ +static int +spider_net_change_mtu(struct net_device *netdev, int new_mtu) +{ + /* no need to re-alloc skbs or so -- the max mtu is about 2.3k + * and mtu is outbound only anyway */ + if ( (new_mtu < SPIDER_NET_MIN_MTU ) || + (new_mtu > SPIDER_NET_MAX_MTU) ) + return -EINVAL; + netdev->mtu = new_mtu; + return 0; +} + +/** + * spider_net_set_mac - sets the MAC of an interface + * @netdev: interface device structure + * @ptr: pointer to new MAC address + * + * Returns 0 on success, <0 on failure. Currently, we don't support this + * and will always return EOPNOTSUPP. + */ +static int +spider_net_set_mac(struct net_device *netdev, void *p) +{ + struct spider_net_card *card = netdev_priv(netdev); + u32 macl, macu, regvalue; + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + /* switch off GMACTPE and GMACRPE */ + regvalue = spider_net_read_reg(card, SPIDER_NET_GMACOPEMD); + regvalue &= ~((1 << 5) | (1 << 6)); + spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, regvalue); + + /* write mac */ + macu = (addr->sa_data[0]<<24) + (addr->sa_data[1]<<16) + + (addr->sa_data[2]<<8) + (addr->sa_data[3]); + macl = (addr->sa_data[4]<<8) + (addr->sa_data[5]); + spider_net_write_reg(card, SPIDER_NET_GMACUNIMACU, macu); + spider_net_write_reg(card, SPIDER_NET_GMACUNIMACL, macl); + + /* switch GMACTPE and GMACRPE back on */ + regvalue = spider_net_read_reg(card, SPIDER_NET_GMACOPEMD); + regvalue |= ((1 << 5) | (1 << 6)); + spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, regvalue); + + spider_net_set_promisc(card); + + /* look up, whether we have been successful */ + if (spider_net_get_mac_address(netdev)) + return -EADDRNOTAVAIL; + if (memcmp(netdev->dev_addr,addr->sa_data,netdev->addr_len)) + return -EADDRNOTAVAIL; + + return 0; +} + +/** + * spider_net_link_reset + * @netdev: net device structure + * + * This is called when the PHY_LINK signal is asserted. For the blade this is + * not connected so we should never get here. + * + */ +static void +spider_net_link_reset(struct net_device *netdev) +{ + + struct spider_net_card *card = netdev_priv(netdev); + + del_timer_sync(&card->aneg_timer); + + /* clear interrupt, block further interrupts */ + spider_net_write_reg(card, SPIDER_NET_GMACST, + spider_net_read_reg(card, SPIDER_NET_GMACST)); + spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0); + + /* reset phy and setup aneg */ + card->aneg_count = 0; + card->medium = BCM54XX_COPPER; + spider_net_setup_aneg(card); + mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); + +} + +/** + * spider_net_handle_error_irq - handles errors raised by an interrupt + * @card: card structure + * @status_reg: interrupt status register 0 (GHIINT0STS) + * + * spider_net_handle_error_irq treats or ignores all error conditions + * found when an interrupt is presented + */ +static void +spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg, + u32 error_reg1, u32 error_reg2) +{ + u32 i; + int show_error = 1; + + /* check GHIINT0STS ************************************/ + if (status_reg) + for (i = 0; i < 32; i++) + if (status_reg & (1<netdev->dev, "PHY write queue full\n"); + show_error = 0; + break; + + /* case SPIDER_NET_GRMDADRINT: not used. print a message */ + /* case SPIDER_NET_GRMARPINT: not used. print a message */ + /* case SPIDER_NET_GRMMPINT: not used. print a message */ + + case SPIDER_NET_GDTDEN0INT: + /* someone has set TX_DMA_EN to 0 */ + show_error = 0; + break; + + case SPIDER_NET_GDDDEN0INT: /* fallthrough */ + case SPIDER_NET_GDCDEN0INT: /* fallthrough */ + case SPIDER_NET_GDBDEN0INT: /* fallthrough */ + case SPIDER_NET_GDADEN0INT: + /* someone has set RX_DMA_EN to 0 */ + show_error = 0; + break; + + /* RX interrupts */ + case SPIDER_NET_GDDFDCINT: + case SPIDER_NET_GDCFDCINT: + case SPIDER_NET_GDBFDCINT: + case SPIDER_NET_GDAFDCINT: + /* case SPIDER_NET_GDNMINT: not used. print a message */ + /* case SPIDER_NET_GCNMINT: not used. print a message */ + /* case SPIDER_NET_GBNMINT: not used. print a message */ + /* case SPIDER_NET_GANMINT: not used. print a message */ + /* case SPIDER_NET_GRFNMINT: not used. print a message */ + show_error = 0; + break; + + /* TX interrupts */ + case SPIDER_NET_GDTFDCINT: + show_error = 0; + break; + case SPIDER_NET_GTTEDINT: + show_error = 0; + break; + case SPIDER_NET_GDTDCEINT: + /* chain end. If a descriptor should be sent, kick off + * tx dma + if (card->tx_chain.tail != card->tx_chain.head) + spider_net_kick_tx_dma(card); + */ + show_error = 0; + break; + + /* case SPIDER_NET_G1TMCNTINT: not used. print a message */ + /* case SPIDER_NET_GFREECNTINT: not used. print a message */ + } + + /* check GHIINT1STS ************************************/ + if (error_reg1) + for (i = 0; i < 32; i++) + if (error_reg1 & (1<ignore_rx_ramfull == 0) { + card->ignore_rx_ramfull = 1; + spider_net_resync_head_ptr(card); + spider_net_refill_rx_chain(card); + spider_net_enable_rxdmac(card); + card->num_rx_ints ++; + napi_schedule(&card->napi); + } + show_error = 0; + break; + + /* case SPIDER_NET_GTMSHTINT: problem, print a message */ + case SPIDER_NET_GDTINVDINT: + /* allrighty. tx from previous descr ok */ + show_error = 0; + break; + + /* chain end */ + case SPIDER_NET_GDDDCEINT: /* fallthrough */ + case SPIDER_NET_GDCDCEINT: /* fallthrough */ + case SPIDER_NET_GDBDCEINT: /* fallthrough */ + case SPIDER_NET_GDADCEINT: + spider_net_resync_head_ptr(card); + spider_net_refill_rx_chain(card); + spider_net_enable_rxdmac(card); + card->num_rx_ints ++; + napi_schedule(&card->napi); + show_error = 0; + break; + + /* invalid descriptor */ + case SPIDER_NET_GDDINVDINT: /* fallthrough */ + case SPIDER_NET_GDCINVDINT: /* fallthrough */ + case SPIDER_NET_GDBINVDINT: /* fallthrough */ + case SPIDER_NET_GDAINVDINT: + /* Could happen when rx chain is full */ + spider_net_resync_head_ptr(card); + spider_net_refill_rx_chain(card); + spider_net_enable_rxdmac(card); + card->num_rx_ints ++; + napi_schedule(&card->napi); + show_error = 0; + break; + + /* case SPIDER_NET_GDTRSERINT: problem, print a message */ + /* case SPIDER_NET_GDDRSERINT: problem, print a message */ + /* case SPIDER_NET_GDCRSERINT: problem, print a message */ + /* case SPIDER_NET_GDBRSERINT: problem, print a message */ + /* case SPIDER_NET_GDARSERINT: problem, print a message */ + /* case SPIDER_NET_GDSERINT: problem, print a message */ + /* case SPIDER_NET_GDTPTERINT: problem, print a message */ + /* case SPIDER_NET_GDDPTERINT: problem, print a message */ + /* case SPIDER_NET_GDCPTERINT: problem, print a message */ + /* case SPIDER_NET_GDBPTERINT: problem, print a message */ + /* case SPIDER_NET_GDAPTERINT: problem, print a message */ + default: + show_error = 1; + break; + } + + /* check GHIINT2STS ************************************/ + if (error_reg2) + for (i = 0; i < 32; i++) + if (error_reg2 & (1<netdev->dev, "Error interrupt, GHIINT0STS = 0x%08x, " + "GHIINT1STS = 0x%08x, GHIINT2STS = 0x%08x\n", + status_reg, error_reg1, error_reg2); + + /* clear interrupt sources */ + spider_net_write_reg(card, SPIDER_NET_GHIINT1STS, error_reg1); + spider_net_write_reg(card, SPIDER_NET_GHIINT2STS, error_reg2); +} + +/** + * spider_net_interrupt - interrupt handler for spider_net + * @irq: interrupt number + * @ptr: pointer to net_device + * + * returns IRQ_HANDLED, if interrupt was for driver, or IRQ_NONE, if no + * interrupt found raised by card. + * + * This is the interrupt handler, that turns off + * interrupts for this device and makes the stack poll the driver + */ +static irqreturn_t +spider_net_interrupt(int irq, void *ptr) +{ + struct net_device *netdev = ptr; + struct spider_net_card *card = netdev_priv(netdev); + u32 status_reg, error_reg1, error_reg2; + + status_reg = spider_net_read_reg(card, SPIDER_NET_GHIINT0STS); + error_reg1 = spider_net_read_reg(card, SPIDER_NET_GHIINT1STS); + error_reg2 = spider_net_read_reg(card, SPIDER_NET_GHIINT2STS); + + if (!(status_reg & SPIDER_NET_INT0_MASK_VALUE) && + !(error_reg1 & SPIDER_NET_INT1_MASK_VALUE) && + !(error_reg2 & SPIDER_NET_INT2_MASK_VALUE)) + return IRQ_NONE; + + if (status_reg & SPIDER_NET_RXINT ) { + spider_net_rx_irq_off(card); + napi_schedule(&card->napi); + card->num_rx_ints ++; + } + if (status_reg & SPIDER_NET_TXINT) + napi_schedule(&card->napi); + + if (status_reg & SPIDER_NET_LINKINT) + spider_net_link_reset(netdev); + + if (status_reg & SPIDER_NET_ERRINT ) + spider_net_handle_error_irq(card, status_reg, + error_reg1, error_reg2); + + /* clear interrupt sources */ + spider_net_write_reg(card, SPIDER_NET_GHIINT0STS, status_reg); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/** + * spider_net_poll_controller - artificial interrupt for netconsole etc. + * @netdev: interface device structure + * + * see Documentation/networking/netconsole.txt + */ +static void +spider_net_poll_controller(struct net_device *netdev) +{ + disable_irq(netdev->irq); + spider_net_interrupt(netdev->irq, netdev); + enable_irq(netdev->irq); +} +#endif /* CONFIG_NET_POLL_CONTROLLER */ + +/** + * spider_net_enable_interrupts - enable interrupts + * @card: card structure + * + * spider_net_enable_interrupt enables several interrupts + */ +static void +spider_net_enable_interrupts(struct spider_net_card *card) +{ + spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, + SPIDER_NET_INT0_MASK_VALUE); + spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, + SPIDER_NET_INT1_MASK_VALUE); + spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, + SPIDER_NET_INT2_MASK_VALUE); +} + +/** + * spider_net_disable_interrupts - disable interrupts + * @card: card structure + * + * spider_net_disable_interrupts disables all the interrupts + */ +static void +spider_net_disable_interrupts(struct spider_net_card *card) +{ + spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 0); + spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, 0); + spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, 0); + spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0); +} + +/** + * spider_net_init_card - initializes the card + * @card: card structure + * + * spider_net_init_card initializes the card so that other registers can + * be used + */ +static void +spider_net_init_card(struct spider_net_card *card) +{ + spider_net_write_reg(card, SPIDER_NET_CKRCTRL, + SPIDER_NET_CKRCTRL_STOP_VALUE); + + spider_net_write_reg(card, SPIDER_NET_CKRCTRL, + SPIDER_NET_CKRCTRL_RUN_VALUE); + + /* trigger ETOMOD signal */ + spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, + spider_net_read_reg(card, SPIDER_NET_GMACOPEMD) | 0x4); + + spider_net_disable_interrupts(card); +} + +/** + * spider_net_enable_card - enables the card by setting all kinds of regs + * @card: card structure + * + * spider_net_enable_card sets a lot of SMMIO registers to enable the device + */ +static void +spider_net_enable_card(struct spider_net_card *card) +{ + int i; + /* the following array consists of (register),(value) pairs + * that are set in this function. A register of 0 ends the list */ + u32 regs[][2] = { + { SPIDER_NET_GRESUMINTNUM, 0 }, + { SPIDER_NET_GREINTNUM, 0 }, + + /* set interrupt frame number registers */ + /* clear the single DMA engine registers first */ + { SPIDER_NET_GFAFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, + { SPIDER_NET_GFBFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, + { SPIDER_NET_GFCFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, + { SPIDER_NET_GFDFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, + /* then set, what we really need */ + { SPIDER_NET_GFFRMNUM, SPIDER_NET_FRAMENUM_VALUE }, + + /* timer counter registers and stuff */ + { SPIDER_NET_GFREECNNUM, 0 }, + { SPIDER_NET_GONETIMENUM, 0 }, + { SPIDER_NET_GTOUTFRMNUM, 0 }, + + /* RX mode setting */ + { SPIDER_NET_GRXMDSET, SPIDER_NET_RXMODE_VALUE }, + /* TX mode setting */ + { SPIDER_NET_GTXMDSET, SPIDER_NET_TXMODE_VALUE }, + /* IPSEC mode setting */ + { SPIDER_NET_GIPSECINIT, SPIDER_NET_IPSECINIT_VALUE }, + + { SPIDER_NET_GFTRESTRT, SPIDER_NET_RESTART_VALUE }, + + { SPIDER_NET_GMRWOLCTRL, 0 }, + { SPIDER_NET_GTESTMD, 0x10000000 }, + { SPIDER_NET_GTTQMSK, 0x00400040 }, + + { SPIDER_NET_GMACINTEN, 0 }, + + /* flow control stuff */ + { SPIDER_NET_GMACAPAUSE, SPIDER_NET_MACAPAUSE_VALUE }, + { SPIDER_NET_GMACTXPAUSE, SPIDER_NET_TXPAUSE_VALUE }, + + { SPIDER_NET_GMACBSTLMT, SPIDER_NET_BURSTLMT_VALUE }, + { 0, 0} + }; + + i = 0; + while (regs[i][0]) { + spider_net_write_reg(card, regs[i][0], regs[i][1]); + i++; + } + + /* clear unicast filter table entries 1 to 14 */ + for (i = 1; i <= 14; i++) { + spider_net_write_reg(card, + SPIDER_NET_GMRUAFILnR + i * 8, + 0x00080000); + spider_net_write_reg(card, + SPIDER_NET_GMRUAFILnR + i * 8 + 4, + 0x00000000); + } + + spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, 0x08080000); + + spider_net_write_reg(card, SPIDER_NET_ECMODE, SPIDER_NET_ECMODE_VALUE); + + /* set chain tail address for RX chains and + * enable DMA */ + spider_net_enable_rxchtails(card); + spider_net_enable_rxdmac(card); + + spider_net_write_reg(card, SPIDER_NET_GRXDMAEN, SPIDER_NET_WOL_VALUE); + + spider_net_write_reg(card, SPIDER_NET_GMACLENLMT, + SPIDER_NET_LENLMT_VALUE); + spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, + SPIDER_NET_OPMODE_VALUE); + + spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, + SPIDER_NET_GDTBSTA); +} + +/** + * spider_net_download_firmware - loads firmware into the adapter + * @card: card structure + * @firmware_ptr: pointer to firmware data + * + * spider_net_download_firmware loads the firmware data into the + * adapter. It assumes the length etc. to be allright. + */ +static int +spider_net_download_firmware(struct spider_net_card *card, + const void *firmware_ptr) +{ + int sequencer, i; + const u32 *fw_ptr = firmware_ptr; + + /* stop sequencers */ + spider_net_write_reg(card, SPIDER_NET_GSINIT, + SPIDER_NET_STOP_SEQ_VALUE); + + for (sequencer = 0; sequencer < SPIDER_NET_FIRMWARE_SEQS; + sequencer++) { + spider_net_write_reg(card, + SPIDER_NET_GSnPRGADR + sequencer * 8, 0); + for (i = 0; i < SPIDER_NET_FIRMWARE_SEQWORDS; i++) { + spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT + + sequencer * 8, *fw_ptr); + fw_ptr++; + } + } + + if (spider_net_read_reg(card, SPIDER_NET_GSINIT)) + return -EIO; + + spider_net_write_reg(card, SPIDER_NET_GSINIT, + SPIDER_NET_RUN_SEQ_VALUE); + + return 0; +} + +/** + * spider_net_init_firmware - reads in firmware parts + * @card: card structure + * + * Returns 0 on success, <0 on failure + * + * spider_net_init_firmware opens the sequencer firmware and does some basic + * checks. This function opens and releases the firmware structure. A call + * to download the firmware is performed before the release. + * + * Firmware format + * =============== + * spider_fw.bin is expected to be a file containing 6*1024*4 bytes, 4k being + * the program for each sequencer. Use the command + * tail -q -n +2 Seq_code1_0x088.txt Seq_code2_0x090.txt \ + * Seq_code3_0x098.txt Seq_code4_0x0A0.txt Seq_code5_0x0A8.txt \ + * Seq_code6_0x0B0.txt | xxd -r -p -c4 > spider_fw.bin + * + * to generate spider_fw.bin, if you have sequencer programs with something + * like the following contents for each sequencer: + * + * + * + * ... + * <1024th 4-BYTES-WORD FOR SEQUENCER> + */ +static int +spider_net_init_firmware(struct spider_net_card *card) +{ + struct firmware *firmware = NULL; + struct device_node *dn; + const u8 *fw_prop = NULL; + int err = -ENOENT; + int fw_size; + + if (request_firmware((const struct firmware **)&firmware, + SPIDER_NET_FIRMWARE_NAME, &card->pdev->dev) == 0) { + if ( (firmware->size != SPIDER_NET_FIRMWARE_LEN) && + netif_msg_probe(card) ) { + dev_err(&card->netdev->dev, + "Incorrect size of spidernet firmware in " \ + "filesystem. Looking in host firmware...\n"); + goto try_host_fw; + } + err = spider_net_download_firmware(card, firmware->data); + + release_firmware(firmware); + if (err) + goto try_host_fw; + + goto done; + } + +try_host_fw: + dn = pci_device_to_OF_node(card->pdev); + if (!dn) + goto out_err; + + fw_prop = of_get_property(dn, "firmware", &fw_size); + if (!fw_prop) + goto out_err; + + if ( (fw_size != SPIDER_NET_FIRMWARE_LEN) && + netif_msg_probe(card) ) { + dev_err(&card->netdev->dev, + "Incorrect size of spidernet firmware in host firmware\n"); + goto done; + } + + err = spider_net_download_firmware(card, fw_prop); + +done: + return err; +out_err: + if (netif_msg_probe(card)) + dev_err(&card->netdev->dev, + "Couldn't find spidernet firmware in filesystem " \ + "or host firmware\n"); + return err; +} + +/** + * spider_net_open - called upon ifonfig up + * @netdev: interface device structure + * + * returns 0 on success, <0 on failure + * + * spider_net_open allocates all the descriptors and memory needed for + * operation, sets up multicast list and enables interrupts + */ +int +spider_net_open(struct net_device *netdev) +{ + struct spider_net_card *card = netdev_priv(netdev); + int result; + + result = spider_net_init_firmware(card); + if (result) + goto init_firmware_failed; + + /* start probing with copper */ + card->aneg_count = 0; + card->medium = BCM54XX_COPPER; + spider_net_setup_aneg(card); + if (card->phy.def->phy_id) + mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); + + result = spider_net_init_chain(card, &card->tx_chain); + if (result) + goto alloc_tx_failed; + card->low_watermark = NULL; + + result = spider_net_init_chain(card, &card->rx_chain); + if (result) + goto alloc_rx_failed; + + /* Allocate rx skbs */ + if (spider_net_alloc_rx_skbs(card)) + goto alloc_skbs_failed; + + spider_net_set_multi(netdev); + + /* further enhancement: setup hw vlan, if needed */ + + result = -EBUSY; + if (request_irq(netdev->irq, spider_net_interrupt, + IRQF_SHARED, netdev->name, netdev)) + goto register_int_failed; + + spider_net_enable_card(card); + + netif_start_queue(netdev); + netif_carrier_on(netdev); + napi_enable(&card->napi); + + spider_net_enable_interrupts(card); + + return 0; + +register_int_failed: + spider_net_free_rx_chain_contents(card); +alloc_skbs_failed: + spider_net_free_chain(card, &card->rx_chain); +alloc_rx_failed: + spider_net_free_chain(card, &card->tx_chain); +alloc_tx_failed: + del_timer_sync(&card->aneg_timer); +init_firmware_failed: + return result; +} + +/** + * spider_net_link_phy + * @data: used for pointer to card structure + * + */ +static void spider_net_link_phy(unsigned long data) +{ + struct spider_net_card *card = (struct spider_net_card *)data; + struct mii_phy *phy = &card->phy; + + /* if link didn't come up after SPIDER_NET_ANEG_TIMEOUT tries, setup phy again */ + if (card->aneg_count > SPIDER_NET_ANEG_TIMEOUT) { + + pr_debug("%s: link is down trying to bring it up\n", + card->netdev->name); + + switch (card->medium) { + case BCM54XX_COPPER: + /* enable fiber with autonegotiation first */ + if (phy->def->ops->enable_fiber) + phy->def->ops->enable_fiber(phy, 1); + card->medium = BCM54XX_FIBER; + break; + + case BCM54XX_FIBER: + /* fiber didn't come up, try to disable fiber autoneg */ + if (phy->def->ops->enable_fiber) + phy->def->ops->enable_fiber(phy, 0); + card->medium = BCM54XX_UNKNOWN; + break; + + case BCM54XX_UNKNOWN: + /* copper, fiber with and without failed, + * retry from beginning */ + spider_net_setup_aneg(card); + card->medium = BCM54XX_COPPER; + break; + } + + card->aneg_count = 0; + mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); + return; + } + + /* link still not up, try again later */ + if (!(phy->def->ops->poll_link(phy))) { + card->aneg_count++; + mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); + return; + } + + /* link came up, get abilities */ + phy->def->ops->read_link(phy); + + spider_net_write_reg(card, SPIDER_NET_GMACST, + spider_net_read_reg(card, SPIDER_NET_GMACST)); + spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0x4); + + if (phy->speed == 1000) + spider_net_write_reg(card, SPIDER_NET_GMACMODE, 0x00000001); + else + spider_net_write_reg(card, SPIDER_NET_GMACMODE, 0); + + card->aneg_count = 0; + + pr_info("%s: link up, %i Mbps, %s-duplex %sautoneg.\n", + card->netdev->name, phy->speed, + phy->duplex == 1 ? "Full" : "Half", + phy->autoneg == 1 ? "" : "no "); +} + +/** + * spider_net_setup_phy - setup PHY + * @card: card structure + * + * returns 0 on success, <0 on failure + * + * spider_net_setup_phy is used as part of spider_net_probe. + **/ +static int +spider_net_setup_phy(struct spider_net_card *card) +{ + struct mii_phy *phy = &card->phy; + + spider_net_write_reg(card, SPIDER_NET_GDTDMASEL, + SPIDER_NET_DMASEL_VALUE); + spider_net_write_reg(card, SPIDER_NET_GPCCTRL, + SPIDER_NET_PHY_CTRL_VALUE); + + phy->dev = card->netdev; + phy->mdio_read = spider_net_read_phy; + phy->mdio_write = spider_net_write_phy; + + for (phy->mii_id = 1; phy->mii_id <= 31; phy->mii_id++) { + unsigned short id; + id = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR); + if (id != 0x0000 && id != 0xffff) { + if (!mii_phy_probe(phy, phy->mii_id)) { + pr_info("Found %s.\n", phy->def->name); + break; + } + } + } + + return 0; +} + +/** + * spider_net_workaround_rxramfull - work around firmware bug + * @card: card structure + * + * no return value + **/ +static void +spider_net_workaround_rxramfull(struct spider_net_card *card) +{ + int i, sequencer = 0; + + /* cancel reset */ + spider_net_write_reg(card, SPIDER_NET_CKRCTRL, + SPIDER_NET_CKRCTRL_RUN_VALUE); + + /* empty sequencer data */ + for (sequencer = 0; sequencer < SPIDER_NET_FIRMWARE_SEQS; + sequencer++) { + spider_net_write_reg(card, SPIDER_NET_GSnPRGADR + + sequencer * 8, 0x0); + for (i = 0; i < SPIDER_NET_FIRMWARE_SEQWORDS; i++) { + spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT + + sequencer * 8, 0x0); + } + } + + /* set sequencer operation */ + spider_net_write_reg(card, SPIDER_NET_GSINIT, 0x000000fe); + + /* reset */ + spider_net_write_reg(card, SPIDER_NET_CKRCTRL, + SPIDER_NET_CKRCTRL_STOP_VALUE); +} + +/** + * spider_net_stop - called upon ifconfig down + * @netdev: interface device structure + * + * always returns 0 + */ +int +spider_net_stop(struct net_device *netdev) +{ + struct spider_net_card *card = netdev_priv(netdev); + + napi_disable(&card->napi); + netif_carrier_off(netdev); + netif_stop_queue(netdev); + del_timer_sync(&card->tx_timer); + del_timer_sync(&card->aneg_timer); + + spider_net_disable_interrupts(card); + + free_irq(netdev->irq, netdev); + + spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, + SPIDER_NET_DMA_TX_FEND_VALUE); + + /* turn off DMA, force end */ + spider_net_disable_rxdmac(card); + + /* release chains */ + spider_net_release_tx_chain(card, 1); + spider_net_free_rx_chain_contents(card); + + spider_net_free_chain(card, &card->tx_chain); + spider_net_free_chain(card, &card->rx_chain); + + return 0; +} + +/** + * spider_net_tx_timeout_task - task scheduled by the watchdog timeout + * function (to be called not under interrupt status) + * @data: data, is interface device structure + * + * called as task when tx hangs, resets interface (if interface is up) + */ +static void +spider_net_tx_timeout_task(struct work_struct *work) +{ + struct spider_net_card *card = + container_of(work, struct spider_net_card, tx_timeout_task); + struct net_device *netdev = card->netdev; + + if (!(netdev->flags & IFF_UP)) + goto out; + + netif_device_detach(netdev); + spider_net_stop(netdev); + + spider_net_workaround_rxramfull(card); + spider_net_init_card(card); + + if (spider_net_setup_phy(card)) + goto out; + + spider_net_open(netdev); + spider_net_kick_tx_dma(card); + netif_device_attach(netdev); + +out: + atomic_dec(&card->tx_timeout_task_counter); +} + +/** + * spider_net_tx_timeout - called when the tx timeout watchdog kicks in. + * @netdev: interface device structure + * + * called, if tx hangs. Schedules a task that resets the interface + */ +static void +spider_net_tx_timeout(struct net_device *netdev) +{ + struct spider_net_card *card; + + card = netdev_priv(netdev); + atomic_inc(&card->tx_timeout_task_counter); + if (netdev->flags & IFF_UP) + schedule_work(&card->tx_timeout_task); + else + atomic_dec(&card->tx_timeout_task_counter); + card->spider_stats.tx_timeouts++; +} + +static const struct net_device_ops spider_net_ops = { + .ndo_open = spider_net_open, + .ndo_stop = spider_net_stop, + .ndo_start_xmit = spider_net_xmit, + .ndo_set_multicast_list = spider_net_set_multi, + .ndo_set_mac_address = spider_net_set_mac, + .ndo_change_mtu = spider_net_change_mtu, + .ndo_do_ioctl = spider_net_do_ioctl, + .ndo_tx_timeout = spider_net_tx_timeout, + .ndo_validate_addr = eth_validate_addr, + /* HW VLAN */ +#ifdef CONFIG_NET_POLL_CONTROLLER + /* poll controller */ + .ndo_poll_controller = spider_net_poll_controller, +#endif /* CONFIG_NET_POLL_CONTROLLER */ +}; + +/** + * spider_net_setup_netdev_ops - initialization of net_device operations + * @netdev: net_device structure + * + * fills out function pointers in the net_device structure + */ +static void +spider_net_setup_netdev_ops(struct net_device *netdev) +{ + netdev->netdev_ops = &spider_net_ops; + netdev->watchdog_timeo = SPIDER_NET_WATCHDOG_TIMEOUT; + /* ethtool ops */ + netdev->ethtool_ops = &spider_net_ethtool_ops; +} + +/** + * spider_net_setup_netdev - initialization of net_device + * @card: card structure + * + * Returns 0 on success or <0 on failure + * + * spider_net_setup_netdev initializes the net_device structure + **/ +static int +spider_net_setup_netdev(struct spider_net_card *card) +{ + int result; + struct net_device *netdev = card->netdev; + struct device_node *dn; + struct sockaddr addr; + const u8 *mac; + + SET_NETDEV_DEV(netdev, &card->pdev->dev); + + pci_set_drvdata(card->pdev, netdev); + + init_timer(&card->tx_timer); + card->tx_timer.function = + (void (*)(unsigned long)) spider_net_cleanup_tx_ring; + card->tx_timer.data = (unsigned long) card; + netdev->irq = card->pdev->irq; + + card->aneg_count = 0; + init_timer(&card->aneg_timer); + card->aneg_timer.function = spider_net_link_phy; + card->aneg_timer.data = (unsigned long) card; + + netif_napi_add(netdev, &card->napi, + spider_net_poll, SPIDER_NET_NAPI_WEIGHT); + + spider_net_setup_netdev_ops(netdev); + + netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM; + if (SPIDER_NET_RX_CSUM_DEFAULT) + netdev->features |= NETIF_F_RXCSUM; + netdev->features |= NETIF_F_IP_CSUM | NETIF_F_LLTX; + /* some time: NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | + * NETIF_F_HW_VLAN_FILTER */ + + netdev->irq = card->pdev->irq; + card->num_rx_ints = 0; + card->ignore_rx_ramfull = 0; + + dn = pci_device_to_OF_node(card->pdev); + if (!dn) + return -EIO; + + mac = of_get_property(dn, "local-mac-address", NULL); + if (!mac) + return -EIO; + memcpy(addr.sa_data, mac, ETH_ALEN); + + result = spider_net_set_mac(netdev, &addr); + if ((result) && (netif_msg_probe(card))) + dev_err(&card->netdev->dev, + "Failed to set MAC address: %i\n", result); + + result = register_netdev(netdev); + if (result) { + if (netif_msg_probe(card)) + dev_err(&card->netdev->dev, + "Couldn't register net_device: %i\n", result); + return result; + } + + if (netif_msg_probe(card)) + pr_info("Initialized device %s.\n", netdev->name); + + return 0; +} + +/** + * spider_net_alloc_card - allocates net_device and card structure + * + * returns the card structure or NULL in case of errors + * + * the card and net_device structures are linked to each other + */ +static struct spider_net_card * +spider_net_alloc_card(void) +{ + struct net_device *netdev; + struct spider_net_card *card; + size_t alloc_size; + + alloc_size = sizeof(struct spider_net_card) + + (tx_descriptors + rx_descriptors) * sizeof(struct spider_net_descr); + netdev = alloc_etherdev(alloc_size); + if (!netdev) + return NULL; + + card = netdev_priv(netdev); + card->netdev = netdev; + card->msg_enable = SPIDER_NET_DEFAULT_MSG; + INIT_WORK(&card->tx_timeout_task, spider_net_tx_timeout_task); + init_waitqueue_head(&card->waitq); + atomic_set(&card->tx_timeout_task_counter, 0); + + card->rx_chain.num_desc = rx_descriptors; + card->rx_chain.ring = card->darray; + card->tx_chain.num_desc = tx_descriptors; + card->tx_chain.ring = card->darray + rx_descriptors; + + return card; +} + +/** + * spider_net_undo_pci_setup - releases PCI ressources + * @card: card structure + * + * spider_net_undo_pci_setup releases the mapped regions + */ +static void +spider_net_undo_pci_setup(struct spider_net_card *card) +{ + iounmap(card->regs); + pci_release_regions(card->pdev); +} + +/** + * spider_net_setup_pci_dev - sets up the device in terms of PCI operations + * @pdev: PCI device + * + * Returns the card structure or NULL if any errors occur + * + * spider_net_setup_pci_dev initializes pdev and together with the + * functions called in spider_net_open configures the device so that + * data can be transferred over it + * The net_device structure is attached to the card structure, if the + * function returns without error. + **/ +static struct spider_net_card * +spider_net_setup_pci_dev(struct pci_dev *pdev) +{ + struct spider_net_card *card; + unsigned long mmio_start, mmio_len; + + if (pci_enable_device(pdev)) { + dev_err(&pdev->dev, "Couldn't enable PCI device\n"); + return NULL; + } + + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { + dev_err(&pdev->dev, + "Couldn't find proper PCI device base address.\n"); + goto out_disable_dev; + } + + if (pci_request_regions(pdev, spider_net_driver_name)) { + dev_err(&pdev->dev, + "Couldn't obtain PCI resources, aborting.\n"); + goto out_disable_dev; + } + + pci_set_master(pdev); + + card = spider_net_alloc_card(); + if (!card) { + dev_err(&pdev->dev, + "Couldn't allocate net_device structure, aborting.\n"); + goto out_release_regions; + } + card->pdev = pdev; + + /* fetch base address and length of first resource */ + mmio_start = pci_resource_start(pdev, 0); + mmio_len = pci_resource_len(pdev, 0); + + card->netdev->mem_start = mmio_start; + card->netdev->mem_end = mmio_start + mmio_len; + card->regs = ioremap(mmio_start, mmio_len); + + if (!card->regs) { + dev_err(&pdev->dev, + "Couldn't obtain PCI resources, aborting.\n"); + goto out_release_regions; + } + + return card; + +out_release_regions: + pci_release_regions(pdev); +out_disable_dev: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + return NULL; +} + +/** + * spider_net_probe - initialization of a device + * @pdev: PCI device + * @ent: entry in the device id list + * + * Returns 0 on success, <0 on failure + * + * spider_net_probe initializes pdev and registers a net_device + * structure for it. After that, the device can be ifconfig'ed up + **/ +static int __devinit +spider_net_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -EIO; + struct spider_net_card *card; + + card = spider_net_setup_pci_dev(pdev); + if (!card) + goto out; + + spider_net_workaround_rxramfull(card); + spider_net_init_card(card); + + err = spider_net_setup_phy(card); + if (err) + goto out_undo_pci; + + err = spider_net_setup_netdev(card); + if (err) + goto out_undo_pci; + + return 0; + +out_undo_pci: + spider_net_undo_pci_setup(card); + free_netdev(card->netdev); +out: + return err; +} + +/** + * spider_net_remove - removal of a device + * @pdev: PCI device + * + * Returns 0 on success, <0 on failure + * + * spider_net_remove is called to remove the device and unregisters the + * net_device + **/ +static void __devexit +spider_net_remove(struct pci_dev *pdev) +{ + struct net_device *netdev; + struct spider_net_card *card; + + netdev = pci_get_drvdata(pdev); + card = netdev_priv(netdev); + + wait_event(card->waitq, + atomic_read(&card->tx_timeout_task_counter) == 0); + + unregister_netdev(netdev); + + /* switch off card */ + spider_net_write_reg(card, SPIDER_NET_CKRCTRL, + SPIDER_NET_CKRCTRL_STOP_VALUE); + spider_net_write_reg(card, SPIDER_NET_CKRCTRL, + SPIDER_NET_CKRCTRL_RUN_VALUE); + + spider_net_undo_pci_setup(card); + free_netdev(netdev); +} + +static struct pci_driver spider_net_driver = { + .name = spider_net_driver_name, + .id_table = spider_net_pci_tbl, + .probe = spider_net_probe, + .remove = __devexit_p(spider_net_remove) +}; + +/** + * spider_net_init - init function when the driver is loaded + * + * spider_net_init registers the device driver + */ +static int __init spider_net_init(void) +{ + printk(KERN_INFO "Spidernet version %s.\n", VERSION); + + if (rx_descriptors < SPIDER_NET_RX_DESCRIPTORS_MIN) { + rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_MIN; + pr_info("adjusting rx descriptors to %i.\n", rx_descriptors); + } + if (rx_descriptors > SPIDER_NET_RX_DESCRIPTORS_MAX) { + rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_MAX; + pr_info("adjusting rx descriptors to %i.\n", rx_descriptors); + } + if (tx_descriptors < SPIDER_NET_TX_DESCRIPTORS_MIN) { + tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_MIN; + pr_info("adjusting tx descriptors to %i.\n", tx_descriptors); + } + if (tx_descriptors > SPIDER_NET_TX_DESCRIPTORS_MAX) { + tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_MAX; + pr_info("adjusting tx descriptors to %i.\n", tx_descriptors); + } + + return pci_register_driver(&spider_net_driver); +} + +/** + * spider_net_cleanup - exit function when driver is unloaded + * + * spider_net_cleanup unregisters the device driver + */ +static void __exit spider_net_cleanup(void) +{ + pci_unregister_driver(&spider_net_driver); +} + +module_init(spider_net_init); +module_exit(spider_net_cleanup); diff --git a/drivers/net/ethernet/toshiba/spider_net.h b/drivers/net/ethernet/toshiba/spider_net.h new file mode 100644 index 000000000000..a891ad00054b --- /dev/null +++ b/drivers/net/ethernet/toshiba/spider_net.h @@ -0,0 +1,489 @@ +/* + * Network device driver for Cell Processor-Based Blade and Celleb platform + * + * (C) Copyright IBM Corp. 2005 + * (C) Copyright 2006 TOSHIBA CORPORATION + * + * Authors : Utz Bacher + * Jens Osterkamp + * + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _SPIDER_NET_H +#define _SPIDER_NET_H + +#define VERSION "2.0 B" + +#include "./ethernet/sun/sungem_phy.h" + +extern int spider_net_stop(struct net_device *netdev); +extern int spider_net_open(struct net_device *netdev); + +extern const struct ethtool_ops spider_net_ethtool_ops; + +extern char spider_net_driver_name[]; + +#define SPIDER_NET_MAX_FRAME 2312 +#define SPIDER_NET_MAX_MTU 2294 +#define SPIDER_NET_MIN_MTU 64 + +#define SPIDER_NET_RXBUF_ALIGN 128 + +#define SPIDER_NET_RX_DESCRIPTORS_DEFAULT 256 +#define SPIDER_NET_RX_DESCRIPTORS_MIN 16 +#define SPIDER_NET_RX_DESCRIPTORS_MAX 512 + +#define SPIDER_NET_TX_DESCRIPTORS_DEFAULT 256 +#define SPIDER_NET_TX_DESCRIPTORS_MIN 16 +#define SPIDER_NET_TX_DESCRIPTORS_MAX 512 + +#define SPIDER_NET_TX_TIMER (HZ/5) +#define SPIDER_NET_ANEG_TIMER (HZ) +#define SPIDER_NET_ANEG_TIMEOUT 5 + +#define SPIDER_NET_RX_CSUM_DEFAULT 1 + +#define SPIDER_NET_WATCHDOG_TIMEOUT 50*HZ +#define SPIDER_NET_NAPI_WEIGHT 64 + +#define SPIDER_NET_FIRMWARE_SEQS 6 +#define SPIDER_NET_FIRMWARE_SEQWORDS 1024 +#define SPIDER_NET_FIRMWARE_LEN (SPIDER_NET_FIRMWARE_SEQS * \ + SPIDER_NET_FIRMWARE_SEQWORDS * \ + sizeof(u32)) +#define SPIDER_NET_FIRMWARE_NAME "spider_fw.bin" + +/** spider_net SMMIO registers */ +#define SPIDER_NET_GHIINT0STS 0x00000000 +#define SPIDER_NET_GHIINT1STS 0x00000004 +#define SPIDER_NET_GHIINT2STS 0x00000008 +#define SPIDER_NET_GHIINT0MSK 0x00000010 +#define SPIDER_NET_GHIINT1MSK 0x00000014 +#define SPIDER_NET_GHIINT2MSK 0x00000018 + +#define SPIDER_NET_GRESUMINTNUM 0x00000020 +#define SPIDER_NET_GREINTNUM 0x00000024 + +#define SPIDER_NET_GFFRMNUM 0x00000028 +#define SPIDER_NET_GFAFRMNUM 0x0000002c +#define SPIDER_NET_GFBFRMNUM 0x00000030 +#define SPIDER_NET_GFCFRMNUM 0x00000034 +#define SPIDER_NET_GFDFRMNUM 0x00000038 + +/* clear them (don't use it) */ +#define SPIDER_NET_GFREECNNUM 0x0000003c +#define SPIDER_NET_GONETIMENUM 0x00000040 + +#define SPIDER_NET_GTOUTFRMNUM 0x00000044 + +#define SPIDER_NET_GTXMDSET 0x00000050 +#define SPIDER_NET_GPCCTRL 0x00000054 +#define SPIDER_NET_GRXMDSET 0x00000058 +#define SPIDER_NET_GIPSECINIT 0x0000005c +#define SPIDER_NET_GFTRESTRT 0x00000060 +#define SPIDER_NET_GRXDMAEN 0x00000064 +#define SPIDER_NET_GMRWOLCTRL 0x00000068 +#define SPIDER_NET_GPCWOPCMD 0x0000006c +#define SPIDER_NET_GPCROPCMD 0x00000070 +#define SPIDER_NET_GTTFRMCNT 0x00000078 +#define SPIDER_NET_GTESTMD 0x0000007c + +#define SPIDER_NET_GSINIT 0x00000080 +#define SPIDER_NET_GSnPRGADR 0x00000084 +#define SPIDER_NET_GSnPRGDAT 0x00000088 + +#define SPIDER_NET_GMACOPEMD 0x00000100 +#define SPIDER_NET_GMACLENLMT 0x00000108 +#define SPIDER_NET_GMACST 0x00000110 +#define SPIDER_NET_GMACINTEN 0x00000118 +#define SPIDER_NET_GMACPHYCTRL 0x00000120 + +#define SPIDER_NET_GMACAPAUSE 0x00000154 +#define SPIDER_NET_GMACTXPAUSE 0x00000164 + +#define SPIDER_NET_GMACMODE 0x000001b0 +#define SPIDER_NET_GMACBSTLMT 0x000001b4 + +#define SPIDER_NET_GMACUNIMACU 0x000001c0 +#define SPIDER_NET_GMACUNIMACL 0x000001c8 + +#define SPIDER_NET_GMRMHFILnR 0x00000400 +#define SPIDER_NET_MULTICAST_HASHES 256 + +#define SPIDER_NET_GMRUAFILnR 0x00000500 +#define SPIDER_NET_GMRUA0FIL15R 0x00000578 + +#define SPIDER_NET_GTTQMSK 0x00000934 + +/* RX DMA controller registers, all 0x00000a.. are for DMA controller A, + * 0x00000b.. for DMA controller B, etc. */ +#define SPIDER_NET_GDADCHA 0x00000a00 +#define SPIDER_NET_GDADMACCNTR 0x00000a04 +#define SPIDER_NET_GDACTDPA 0x00000a08 +#define SPIDER_NET_GDACTDCNT 0x00000a0c +#define SPIDER_NET_GDACDBADDR 0x00000a20 +#define SPIDER_NET_GDACDBSIZE 0x00000a24 +#define SPIDER_NET_GDACNEXTDA 0x00000a28 +#define SPIDER_NET_GDACCOMST 0x00000a2c +#define SPIDER_NET_GDAWBCOMST 0x00000a30 +#define SPIDER_NET_GDAWBRSIZE 0x00000a34 +#define SPIDER_NET_GDAWBVSIZE 0x00000a38 +#define SPIDER_NET_GDAWBTRST 0x00000a3c +#define SPIDER_NET_GDAWBTRERR 0x00000a40 + +/* TX DMA controller registers */ +#define SPIDER_NET_GDTDCHA 0x00000e00 +#define SPIDER_NET_GDTDMACCNTR 0x00000e04 +#define SPIDER_NET_GDTCDPA 0x00000e08 +#define SPIDER_NET_GDTDMASEL 0x00000e14 + +#define SPIDER_NET_ECMODE 0x00000f00 +/* clock and reset control register */ +#define SPIDER_NET_CKRCTRL 0x00000ff0 + +/** SCONFIG registers */ +#define SPIDER_NET_SCONFIG_IOACTE 0x00002810 + +/** interrupt mask registers */ +#define SPIDER_NET_INT0_MASK_VALUE 0x3f7fe2c7 +#define SPIDER_NET_INT1_MASK_VALUE 0x0000fff2 +#define SPIDER_NET_INT2_MASK_VALUE 0x000003f1 + +/* we rely on flagged descriptor interrupts */ +#define SPIDER_NET_FRAMENUM_VALUE 0x00000000 +/* set this first, then the FRAMENUM_VALUE */ +#define SPIDER_NET_GFXFRAMES_VALUE 0x00000000 + +#define SPIDER_NET_STOP_SEQ_VALUE 0x00000000 +#define SPIDER_NET_RUN_SEQ_VALUE 0x0000007e + +#define SPIDER_NET_PHY_CTRL_VALUE 0x00040040 +/* #define SPIDER_NET_PHY_CTRL_VALUE 0x01070080*/ +#define SPIDER_NET_RXMODE_VALUE 0x00000011 +/* auto retransmission in case of MAC aborts */ +#define SPIDER_NET_TXMODE_VALUE 0x00010000 +#define SPIDER_NET_RESTART_VALUE 0x00000000 +#define SPIDER_NET_WOL_VALUE 0x00001111 +#if 0 +#define SPIDER_NET_WOL_VALUE 0x00000000 +#endif +#define SPIDER_NET_IPSECINIT_VALUE 0x6f716f71 + +/* pause frames: automatic, no upper retransmission count */ +/* outside loopback mode: ETOMOD signal dont matter, not connected */ +/* ETOMOD signal is brought to PHY reset. bit 2 must be 1 in Celleb */ +#define SPIDER_NET_OPMODE_VALUE 0x00000067 +/*#define SPIDER_NET_OPMODE_VALUE 0x001b0062*/ +#define SPIDER_NET_LENLMT_VALUE 0x00000908 + +#define SPIDER_NET_MACAPAUSE_VALUE 0x00000800 /* about 1 ms */ +#define SPIDER_NET_TXPAUSE_VALUE 0x00000000 + +#define SPIDER_NET_MACMODE_VALUE 0x00000001 +#define SPIDER_NET_BURSTLMT_VALUE 0x00000200 /* about 16 us */ + +/* DMAC control register GDMACCNTR + * + * 1(0) enable r/tx dma + * 0000000 fixed to 0 + * + * 000000 fixed to 0 + * 0(1) en/disable descr writeback on force end + * 0(1) force end + * + * 000000 fixed to 0 + * 00 burst alignment: 128 bytes + * 11 burst alignment: 1024 bytes + * + * 00000 fixed to 0 + * 0 descr writeback size 32 bytes + * 0(1) descr chain end interrupt enable + * 0(1) descr status writeback enable */ + +/* to set RX_DMA_EN */ +#define SPIDER_NET_DMA_RX_VALUE 0x80000000 +#define SPIDER_NET_DMA_RX_FEND_VALUE 0x00030003 +/* to set TX_DMA_EN */ +#define SPIDER_NET_TX_DMA_EN 0x80000000 +#define SPIDER_NET_GDTBSTA 0x00000300 +#define SPIDER_NET_GDTDCEIDIS 0x00000002 +#define SPIDER_NET_DMA_TX_VALUE SPIDER_NET_TX_DMA_EN | \ + SPIDER_NET_GDTDCEIDIS | \ + SPIDER_NET_GDTBSTA + +#define SPIDER_NET_DMA_TX_FEND_VALUE 0x00030003 + +/* SPIDER_NET_UA_DESCR_VALUE is OR'ed with the unicast address */ +#define SPIDER_NET_UA_DESCR_VALUE 0x00080000 +#define SPIDER_NET_PROMISC_VALUE 0x00080000 +#define SPIDER_NET_NONPROMISC_VALUE 0x00000000 + +#define SPIDER_NET_DMASEL_VALUE 0x00000001 + +#define SPIDER_NET_ECMODE_VALUE 0x00000000 + +#define SPIDER_NET_CKRCTRL_RUN_VALUE 0x1fff010f +#define SPIDER_NET_CKRCTRL_STOP_VALUE 0x0000010f + +#define SPIDER_NET_SBIMSTATE_VALUE 0x00000000 +#define SPIDER_NET_SBTMSTATE_VALUE 0x00000000 + +/* SPIDER_NET_GHIINT0STS bits, in reverse order so that they can be used + * with 1 << SPIDER_NET_... */ +enum spider_net_int0_status { + SPIDER_NET_GPHYINT = 0, + SPIDER_NET_GMAC2INT, + SPIDER_NET_GMAC1INT, + SPIDER_NET_GIPSINT, + SPIDER_NET_GFIFOINT, + SPIDER_NET_GDMACINT, + SPIDER_NET_GSYSINT, + SPIDER_NET_GPWOPCMPINT, + SPIDER_NET_GPROPCMPINT, + SPIDER_NET_GPWFFINT, + SPIDER_NET_GRMDADRINT, + SPIDER_NET_GRMARPINT, + SPIDER_NET_GRMMPINT, + SPIDER_NET_GDTDEN0INT, + SPIDER_NET_GDDDEN0INT, + SPIDER_NET_GDCDEN0INT, + SPIDER_NET_GDBDEN0INT, + SPIDER_NET_GDADEN0INT, + SPIDER_NET_GDTFDCINT, + SPIDER_NET_GDDFDCINT, + SPIDER_NET_GDCFDCINT, + SPIDER_NET_GDBFDCINT, + SPIDER_NET_GDAFDCINT, + SPIDER_NET_GTTEDINT, + SPIDER_NET_GDTDCEINT, + SPIDER_NET_GRFDNMINT, + SPIDER_NET_GRFCNMINT, + SPIDER_NET_GRFBNMINT, + SPIDER_NET_GRFANMINT, + SPIDER_NET_GRFNMINT, + SPIDER_NET_G1TMCNTINT, + SPIDER_NET_GFREECNTINT +}; +/* GHIINT1STS bits */ +enum spider_net_int1_status { + SPIDER_NET_GTMFLLINT = 0, + SPIDER_NET_GRMFLLINT, + SPIDER_NET_GTMSHTINT, + SPIDER_NET_GDTINVDINT, + SPIDER_NET_GRFDFLLINT, + SPIDER_NET_GDDDCEINT, + SPIDER_NET_GDDINVDINT, + SPIDER_NET_GRFCFLLINT, + SPIDER_NET_GDCDCEINT, + SPIDER_NET_GDCINVDINT, + SPIDER_NET_GRFBFLLINT, + SPIDER_NET_GDBDCEINT, + SPIDER_NET_GDBINVDINT, + SPIDER_NET_GRFAFLLINT, + SPIDER_NET_GDADCEINT, + SPIDER_NET_GDAINVDINT, + SPIDER_NET_GDTRSERINT, + SPIDER_NET_GDDRSERINT, + SPIDER_NET_GDCRSERINT, + SPIDER_NET_GDBRSERINT, + SPIDER_NET_GDARSERINT, + SPIDER_NET_GDSERINT, + SPIDER_NET_GDTPTERINT, + SPIDER_NET_GDDPTERINT, + SPIDER_NET_GDCPTERINT, + SPIDER_NET_GDBPTERINT, + SPIDER_NET_GDAPTERINT +}; +/* GHIINT2STS bits */ +enum spider_net_int2_status { + SPIDER_NET_GPROPERINT = 0, + SPIDER_NET_GMCTCRSNGINT, + SPIDER_NET_GMCTLCOLINT, + SPIDER_NET_GMCTTMOTINT, + SPIDER_NET_GMCRCAERINT, + SPIDER_NET_GMCRCALERINT, + SPIDER_NET_GMCRALNERINT, + SPIDER_NET_GMCROVRINT, + SPIDER_NET_GMCRRNTINT, + SPIDER_NET_GMCRRXERINT, + SPIDER_NET_GTITCSERINT, + SPIDER_NET_GTIFMTERINT, + SPIDER_NET_GTIPKTRVKINT, + SPIDER_NET_GTISPINGINT, + SPIDER_NET_GTISADNGINT, + SPIDER_NET_GTISPDNGINT, + SPIDER_NET_GRIFMTERINT, + SPIDER_NET_GRIPKTRVKINT, + SPIDER_NET_GRISPINGINT, + SPIDER_NET_GRISADNGINT, + SPIDER_NET_GRISPDNGINT +}; + +#define SPIDER_NET_TXINT (1 << SPIDER_NET_GDTFDCINT) + +/* We rely on flagged descriptor interrupts */ +#define SPIDER_NET_RXINT ( (1 << SPIDER_NET_GDAFDCINT) ) + +#define SPIDER_NET_LINKINT ( 1 << SPIDER_NET_GMAC2INT ) + +#define SPIDER_NET_ERRINT ( 0xffffffff & \ + (~SPIDER_NET_TXINT) & \ + (~SPIDER_NET_RXINT) & \ + (~SPIDER_NET_LINKINT) ) + +#define SPIDER_NET_GPREXEC 0x80000000 +#define SPIDER_NET_GPRDAT_MASK 0x0000ffff + +#define SPIDER_NET_DMAC_NOINTR_COMPLETE 0x00800000 +#define SPIDER_NET_DMAC_TXFRMTL 0x00040000 +#define SPIDER_NET_DMAC_TCP 0x00020000 +#define SPIDER_NET_DMAC_UDP 0x00030000 +#define SPIDER_NET_TXDCEST 0x08000000 + +#define SPIDER_NET_DESCR_RXFDIS 0x00000001 +#define SPIDER_NET_DESCR_RXDCEIS 0x00000002 +#define SPIDER_NET_DESCR_RXDEN0IS 0x00000004 +#define SPIDER_NET_DESCR_RXINVDIS 0x00000008 +#define SPIDER_NET_DESCR_RXRERRIS 0x00000010 +#define SPIDER_NET_DESCR_RXFDCIMS 0x00000100 +#define SPIDER_NET_DESCR_RXDCEIMS 0x00000200 +#define SPIDER_NET_DESCR_RXDEN0IMS 0x00000400 +#define SPIDER_NET_DESCR_RXINVDIMS 0x00000800 +#define SPIDER_NET_DESCR_RXRERRMIS 0x00001000 +#define SPIDER_NET_DESCR_UNUSED 0x077fe0e0 + +#define SPIDER_NET_DESCR_IND_PROC_MASK 0xF0000000 +#define SPIDER_NET_DESCR_COMPLETE 0x00000000 /* used in rx and tx */ +#define SPIDER_NET_DESCR_RESPONSE_ERROR 0x10000000 /* used in rx and tx */ +#define SPIDER_NET_DESCR_PROTECTION_ERROR 0x20000000 /* used in rx and tx */ +#define SPIDER_NET_DESCR_FRAME_END 0x40000000 /* used in rx */ +#define SPIDER_NET_DESCR_FORCE_END 0x50000000 /* used in rx and tx */ +#define SPIDER_NET_DESCR_CARDOWNED 0xA0000000 /* used in rx and tx */ +#define SPIDER_NET_DESCR_NOT_IN_USE 0xF0000000 +#define SPIDER_NET_DESCR_TXDESFLG 0x00800000 + +#define SPIDER_NET_DESCR_BAD_STATUS (SPIDER_NET_DESCR_RXDEN0IS | \ + SPIDER_NET_DESCR_RXRERRIS | \ + SPIDER_NET_DESCR_RXDEN0IMS | \ + SPIDER_NET_DESCR_RXINVDIMS | \ + SPIDER_NET_DESCR_RXRERRMIS | \ + SPIDER_NET_DESCR_UNUSED) + +/* Descriptor, as defined by the hardware */ +struct spider_net_hw_descr { + u32 buf_addr; + u32 buf_size; + u32 next_descr_addr; + u32 dmac_cmd_status; + u32 result_size; + u32 valid_size; /* all zeroes for tx */ + u32 data_status; + u32 data_error; /* all zeroes for tx */ +} __attribute__((aligned(32))); + +struct spider_net_descr { + struct spider_net_hw_descr *hwdescr; + struct sk_buff *skb; + u32 bus_addr; + struct spider_net_descr *next; + struct spider_net_descr *prev; +}; + +struct spider_net_descr_chain { + spinlock_t lock; + struct spider_net_descr *head; + struct spider_net_descr *tail; + struct spider_net_descr *ring; + int num_desc; + struct spider_net_hw_descr *hwring; + dma_addr_t dma_addr; +}; + +/* descriptor data_status bits */ +#define SPIDER_NET_RX_IPCHK 29 +#define SPIDER_NET_RX_TCPCHK 28 +#define SPIDER_NET_VLAN_PACKET 21 +#define SPIDER_NET_DATA_STATUS_CKSUM_MASK ( (1 << SPIDER_NET_RX_IPCHK) | \ + (1 << SPIDER_NET_RX_TCPCHK) ) + +/* descriptor data_error bits */ +#define SPIDER_NET_RX_IPCHKERR 27 +#define SPIDER_NET_RX_RXTCPCHKERR 28 + +#define SPIDER_NET_DATA_ERR_CKSUM_MASK (1 << SPIDER_NET_RX_IPCHKERR) + +/* the cases we don't pass the packet to the stack. + * 701b8000 would be correct, but every packets gets that flag */ +#define SPIDER_NET_DESTROY_RX_FLAGS 0x700b8000 + +#define SPIDER_NET_DEFAULT_MSG ( NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_TIMER | \ + NETIF_MSG_IFDOWN | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR | \ + NETIF_MSG_TX_QUEUED | \ + NETIF_MSG_INTR | \ + NETIF_MSG_TX_DONE | \ + NETIF_MSG_RX_STATUS | \ + NETIF_MSG_PKTDATA | \ + NETIF_MSG_HW | \ + NETIF_MSG_WOL ) + +struct spider_net_extra_stats { + unsigned long rx_desc_error; + unsigned long tx_timeouts; + unsigned long alloc_rx_skb_error; + unsigned long rx_iommu_map_error; + unsigned long tx_iommu_map_error; + unsigned long rx_desc_unk_state; +}; + +struct spider_net_card { + struct net_device *netdev; + struct pci_dev *pdev; + struct mii_phy phy; + + struct napi_struct napi; + + int medium; + + void __iomem *regs; + + struct spider_net_descr_chain tx_chain; + struct spider_net_descr_chain rx_chain; + struct spider_net_descr *low_watermark; + + int aneg_count; + struct timer_list aneg_timer; + struct timer_list tx_timer; + struct work_struct tx_timeout_task; + atomic_t tx_timeout_task_counter; + wait_queue_head_t waitq; + int num_rx_ints; + int ignore_rx_ramfull; + + /* for ethtool */ + int msg_enable; + struct spider_net_extra_stats spider_stats; + + /* Must be last item in struct */ + struct spider_net_descr darray[0]; +}; + +#endif diff --git a/drivers/net/ethernet/toshiba/spider_net_ethtool.c b/drivers/net/ethernet/toshiba/spider_net_ethtool.c new file mode 100644 index 000000000000..9c288cd7d171 --- /dev/null +++ b/drivers/net/ethernet/toshiba/spider_net_ethtool.c @@ -0,0 +1,179 @@ +/* + * Network device driver for Cell Processor-Based Blade + * + * (C) Copyright IBM Corp. 2005 + * + * Authors : Utz Bacher + * Jens Osterkamp + * + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include "spider_net.h" + + +static struct { + const char str[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "tx_packets" }, + { "tx_bytes" }, + { "rx_packets" }, + { "rx_bytes" }, + { "tx_errors" }, + { "tx_dropped" }, + { "rx_dropped" }, + { "rx_descriptor_error" }, + { "tx_timeouts" }, + { "alloc_rx_skb_error" }, + { "rx_iommu_map_error" }, + { "tx_iommu_map_error" }, + { "rx_desc_unk_state" }, +}; + +static int +spider_net_ethtool_get_settings(struct net_device *netdev, + struct ethtool_cmd *cmd) +{ + struct spider_net_card *card; + card = netdev_priv(netdev); + + cmd->supported = (SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE); + cmd->advertising = (ADVERTISED_1000baseT_Full | + ADVERTISED_FIBRE); + cmd->port = PORT_FIBRE; + ethtool_cmd_speed_set(cmd, card->phy.speed); + cmd->duplex = DUPLEX_FULL; + + return 0; +} + +static void +spider_net_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct spider_net_card *card; + card = netdev_priv(netdev); + + /* clear and fill out info */ + memset(drvinfo, 0, sizeof(struct ethtool_drvinfo)); + strncpy(drvinfo->driver, spider_net_driver_name, 32); + strncpy(drvinfo->version, VERSION, 32); + strcpy(drvinfo->fw_version, "no information"); + strncpy(drvinfo->bus_info, pci_name(card->pdev), 32); +} + +static void +spider_net_ethtool_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wolinfo) +{ + /* no support for wol */ + wolinfo->supported = 0; + wolinfo->wolopts = 0; +} + +static u32 +spider_net_ethtool_get_msglevel(struct net_device *netdev) +{ + struct spider_net_card *card; + card = netdev_priv(netdev); + return card->msg_enable; +} + +static void +spider_net_ethtool_set_msglevel(struct net_device *netdev, + u32 level) +{ + struct spider_net_card *card; + card = netdev_priv(netdev); + card->msg_enable = level; +} + +static int +spider_net_ethtool_nway_reset(struct net_device *netdev) +{ + if (netif_running(netdev)) { + spider_net_stop(netdev); + spider_net_open(netdev); + } + return 0; +} + +static void +spider_net_ethtool_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ering) +{ + struct spider_net_card *card = netdev_priv(netdev); + + ering->tx_max_pending = SPIDER_NET_TX_DESCRIPTORS_MAX; + ering->tx_pending = card->tx_chain.num_desc; + ering->rx_max_pending = SPIDER_NET_RX_DESCRIPTORS_MAX; + ering->rx_pending = card->rx_chain.num_desc; +} + +static int spider_net_get_sset_count(struct net_device *netdev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(ethtool_stats_keys); + default: + return -EOPNOTSUPP; + } +} + +static void spider_net_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct spider_net_card *card = netdev_priv(netdev); + + data[0] = netdev->stats.tx_packets; + data[1] = netdev->stats.tx_bytes; + data[2] = netdev->stats.rx_packets; + data[3] = netdev->stats.rx_bytes; + data[4] = netdev->stats.tx_errors; + data[5] = netdev->stats.tx_dropped; + data[6] = netdev->stats.rx_dropped; + data[7] = card->spider_stats.rx_desc_error; + data[8] = card->spider_stats.tx_timeouts; + data[9] = card->spider_stats.alloc_rx_skb_error; + data[10] = card->spider_stats.rx_iommu_map_error; + data[11] = card->spider_stats.tx_iommu_map_error; + data[12] = card->spider_stats.rx_desc_unk_state; +} + +static void spider_net_get_strings(struct net_device *netdev, u32 stringset, + u8 *data) +{ + memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); +} + +const struct ethtool_ops spider_net_ethtool_ops = { + .get_settings = spider_net_ethtool_get_settings, + .get_drvinfo = spider_net_ethtool_get_drvinfo, + .get_wol = spider_net_ethtool_get_wol, + .get_msglevel = spider_net_ethtool_get_msglevel, + .set_msglevel = spider_net_ethtool_set_msglevel, + .get_link = ethtool_op_get_link, + .nway_reset = spider_net_ethtool_nway_reset, + .get_ringparam = spider_net_ethtool_get_ringparam, + .get_strings = spider_net_get_strings, + .get_sset_count = spider_net_get_sset_count, + .get_ethtool_stats = spider_net_get_ethtool_stats, +}; + diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c new file mode 100644 index 000000000000..4a55a162dfe6 --- /dev/null +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -0,0 +1,2228 @@ +/* + * tc35815.c: A TOSHIBA TC35815CF PCI 10/100Mbps ethernet driver for linux. + * + * Based on skelton.c by Donald Becker. + * + * This driver is a replacement of older and less maintained version. + * This is a header of the older version: + * ---------- + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ahennessy@mvista.com + * Copyright (C) 2000-2001 Toshiba Corporation + * static const char *version = + * "tc35815.c:v0.00 26/07/2000 by Toshiba Corporation\n"; + * ---------- + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * (C) Copyright TOSHIBA CORPORATION 2004-2005 + * All Rights Reserved. + */ + +#define DRV_VERSION "1.39" +static const char *version = "tc35815.c:v" DRV_VERSION "\n"; +#define MODNAME "tc35815" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum tc35815_chiptype { + TC35815CF = 0, + TC35815_NWU, + TC35815_TX4939, +}; + +/* indexed by tc35815_chiptype, above */ +static const struct { + const char *name; +} chip_info[] __devinitdata = { + { "TOSHIBA TC35815CF 10/100BaseTX" }, + { "TOSHIBA TC35815 with Wake on LAN" }, + { "TOSHIBA TC35815/TX4939" }, +}; + +static DEFINE_PCI_DEVICE_TABLE(tc35815_pci_tbl) = { + {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815CF), .driver_data = TC35815CF }, + {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815_NWU), .driver_data = TC35815_NWU }, + {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939), .driver_data = TC35815_TX4939 }, + {0,} +}; +MODULE_DEVICE_TABLE(pci, tc35815_pci_tbl); + +/* see MODULE_PARM_DESC */ +static struct tc35815_options { + int speed; + int duplex; +} options; + +/* + * Registers + */ +struct tc35815_regs { + __u32 DMA_Ctl; /* 0x00 */ + __u32 TxFrmPtr; + __u32 TxThrsh; + __u32 TxPollCtr; + __u32 BLFrmPtr; + __u32 RxFragSize; + __u32 Int_En; + __u32 FDA_Bas; + __u32 FDA_Lim; /* 0x20 */ + __u32 Int_Src; + __u32 unused0[2]; + __u32 PauseCnt; + __u32 RemPauCnt; + __u32 TxCtlFrmStat; + __u32 unused1; + __u32 MAC_Ctl; /* 0x40 */ + __u32 CAM_Ctl; + __u32 Tx_Ctl; + __u32 Tx_Stat; + __u32 Rx_Ctl; + __u32 Rx_Stat; + __u32 MD_Data; + __u32 MD_CA; + __u32 CAM_Adr; /* 0x60 */ + __u32 CAM_Data; + __u32 CAM_Ena; + __u32 PROM_Ctl; + __u32 PROM_Data; + __u32 Algn_Cnt; + __u32 CRC_Cnt; + __u32 Miss_Cnt; +}; + +/* + * Bit assignments + */ +/* DMA_Ctl bit assign ------------------------------------------------------- */ +#define DMA_RxAlign 0x00c00000 /* 1:Reception Alignment */ +#define DMA_RxAlign_1 0x00400000 +#define DMA_RxAlign_2 0x00800000 +#define DMA_RxAlign_3 0x00c00000 +#define DMA_M66EnStat 0x00080000 /* 1:66MHz Enable State */ +#define DMA_IntMask 0x00040000 /* 1:Interrupt mask */ +#define DMA_SWIntReq 0x00020000 /* 1:Software Interrupt request */ +#define DMA_TxWakeUp 0x00010000 /* 1:Transmit Wake Up */ +#define DMA_RxBigE 0x00008000 /* 1:Receive Big Endian */ +#define DMA_TxBigE 0x00004000 /* 1:Transmit Big Endian */ +#define DMA_TestMode 0x00002000 /* 1:Test Mode */ +#define DMA_PowrMgmnt 0x00001000 /* 1:Power Management */ +#define DMA_DmBurst_Mask 0x000001fc /* DMA Burst size */ + +/* RxFragSize bit assign ---------------------------------------------------- */ +#define RxFrag_EnPack 0x00008000 /* 1:Enable Packing */ +#define RxFrag_MinFragMask 0x00000ffc /* Minimum Fragment */ + +/* MAC_Ctl bit assign ------------------------------------------------------- */ +#define MAC_Link10 0x00008000 /* 1:Link Status 10Mbits */ +#define MAC_EnMissRoll 0x00002000 /* 1:Enable Missed Roll */ +#define MAC_MissRoll 0x00000400 /* 1:Missed Roll */ +#define MAC_Loop10 0x00000080 /* 1:Loop 10 Mbps */ +#define MAC_Conn_Auto 0x00000000 /*00:Connection mode (Automatic) */ +#define MAC_Conn_10M 0x00000020 /*01: (10Mbps endec)*/ +#define MAC_Conn_Mll 0x00000040 /*10: (Mll clock) */ +#define MAC_MacLoop 0x00000010 /* 1:MAC Loopback */ +#define MAC_FullDup 0x00000008 /* 1:Full Duplex 0:Half Duplex */ +#define MAC_Reset 0x00000004 /* 1:Software Reset */ +#define MAC_HaltImm 0x00000002 /* 1:Halt Immediate */ +#define MAC_HaltReq 0x00000001 /* 1:Halt request */ + +/* PROM_Ctl bit assign ------------------------------------------------------ */ +#define PROM_Busy 0x00008000 /* 1:Busy (Start Operation) */ +#define PROM_Read 0x00004000 /*10:Read operation */ +#define PROM_Write 0x00002000 /*01:Write operation */ +#define PROM_Erase 0x00006000 /*11:Erase operation */ + /*00:Enable or Disable Writting, */ + /* as specified in PROM_Addr. */ +#define PROM_Addr_Ena 0x00000030 /*11xxxx:PROM Write enable */ + /*00xxxx: disable */ + +/* CAM_Ctl bit assign ------------------------------------------------------- */ +#define CAM_CompEn 0x00000010 /* 1:CAM Compare Enable */ +#define CAM_NegCAM 0x00000008 /* 1:Reject packets CAM recognizes,*/ + /* accept other */ +#define CAM_BroadAcc 0x00000004 /* 1:Broadcast assept */ +#define CAM_GroupAcc 0x00000002 /* 1:Multicast assept */ +#define CAM_StationAcc 0x00000001 /* 1:unicast accept */ + +/* CAM_Ena bit assign ------------------------------------------------------- */ +#define CAM_ENTRY_MAX 21 /* CAM Data entry max count */ +#define CAM_Ena_Mask ((1<chiptype != TC35815CF) + +/* Tuning parameters */ +#define DMA_BURST_SIZE 32 +#define TX_THRESHOLD 1024 +/* used threshold with packet max byte for low pci transfer ability.*/ +#define TX_THRESHOLD_MAX 1536 +/* setting threshold max value when overrun error occurred this count. */ +#define TX_THRESHOLD_KEEP_LIMIT 10 + +/* 16 + RX_BUF_NUM * 8 + RX_FD_NUM * 16 + TX_FD_NUM * 32 <= PAGE_SIZE*FD_PAGE_NUM */ +#define FD_PAGE_NUM 4 +#define RX_BUF_NUM 128 /* < 256 */ +#define RX_FD_NUM 256 /* >= 32 */ +#define TX_FD_NUM 128 +#if RX_CTL_CMD & Rx_LongEn +#define RX_BUF_SIZE PAGE_SIZE +#elif RX_CTL_CMD & Rx_StripCRC +#define RX_BUF_SIZE \ + L1_CACHE_ALIGN(ETH_FRAME_LEN + VLAN_HLEN + NET_IP_ALIGN) +#else +#define RX_BUF_SIZE \ + L1_CACHE_ALIGN(ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN + NET_IP_ALIGN) +#endif +#define RX_FD_RESERVE (2 / 2) /* max 2 BD per RxFD */ +#define NAPI_WEIGHT 16 + +struct TxFD { + struct FDesc fd; + struct BDesc bd; + struct BDesc unused; +}; + +struct RxFD { + struct FDesc fd; + struct BDesc bd[0]; /* variable length */ +}; + +struct FrFD { + struct FDesc fd; + struct BDesc bd[RX_BUF_NUM]; +}; + + +#define tc_readl(addr) ioread32(addr) +#define tc_writel(d, addr) iowrite32(d, addr) + +#define TC35815_TX_TIMEOUT msecs_to_jiffies(400) + +/* Information that need to be kept for each controller. */ +struct tc35815_local { + struct pci_dev *pci_dev; + + struct net_device *dev; + struct napi_struct napi; + + /* statistics */ + struct { + int max_tx_qlen; + int tx_ints; + int rx_ints; + int tx_underrun; + } lstats; + + /* Tx control lock. This protects the transmit buffer ring + * state along with the "tx full" state of the driver. This + * means all netif_queue flow control actions are protected + * by this lock as well. + */ + spinlock_t lock; + spinlock_t rx_lock; + + struct mii_bus *mii_bus; + struct phy_device *phy_dev; + int duplex; + int speed; + int link; + struct work_struct restart_work; + + /* + * Transmitting: Batch Mode. + * 1 BD in 1 TxFD. + * Receiving: Non-Packing Mode. + * 1 circular FD for Free Buffer List. + * RX_BUF_NUM BD in Free Buffer FD. + * One Free Buffer BD has ETH_FRAME_LEN data buffer. + */ + void *fd_buf; /* for TxFD, RxFD, FrFD */ + dma_addr_t fd_buf_dma; + struct TxFD *tfd_base; + unsigned int tfd_start; + unsigned int tfd_end; + struct RxFD *rfd_base; + struct RxFD *rfd_limit; + struct RxFD *rfd_cur; + struct FrFD *fbl_ptr; + unsigned int fbl_count; + struct { + struct sk_buff *skb; + dma_addr_t skb_dma; + } tx_skbs[TX_FD_NUM], rx_skbs[RX_BUF_NUM]; + u32 msg_enable; + enum tc35815_chiptype chiptype; +}; + +static inline dma_addr_t fd_virt_to_bus(struct tc35815_local *lp, void *virt) +{ + return lp->fd_buf_dma + ((u8 *)virt - (u8 *)lp->fd_buf); +} +#ifdef DEBUG +static inline void *fd_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus) +{ + return (void *)((u8 *)lp->fd_buf + (bus - lp->fd_buf_dma)); +} +#endif +static struct sk_buff *alloc_rxbuf_skb(struct net_device *dev, + struct pci_dev *hwdev, + dma_addr_t *dma_handle) +{ + struct sk_buff *skb; + skb = dev_alloc_skb(RX_BUF_SIZE); + if (!skb) + return NULL; + *dma_handle = pci_map_single(hwdev, skb->data, RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(hwdev, *dma_handle)) { + dev_kfree_skb_any(skb); + return NULL; + } + skb_reserve(skb, 2); /* make IP header 4byte aligned */ + return skb; +} + +static void free_rxbuf_skb(struct pci_dev *hwdev, struct sk_buff *skb, dma_addr_t dma_handle) +{ + pci_unmap_single(hwdev, dma_handle, RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); +} + +/* Index to functions, as function prototypes. */ + +static int tc35815_open(struct net_device *dev); +static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t tc35815_interrupt(int irq, void *dev_id); +static int tc35815_rx(struct net_device *dev, int limit); +static int tc35815_poll(struct napi_struct *napi, int budget); +static void tc35815_txdone(struct net_device *dev); +static int tc35815_close(struct net_device *dev); +static struct net_device_stats *tc35815_get_stats(struct net_device *dev); +static void tc35815_set_multicast_list(struct net_device *dev); +static void tc35815_tx_timeout(struct net_device *dev); +static int tc35815_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void tc35815_poll_controller(struct net_device *dev); +#endif +static const struct ethtool_ops tc35815_ethtool_ops; + +/* Example routines you must write ;->. */ +static void tc35815_chip_reset(struct net_device *dev); +static void tc35815_chip_init(struct net_device *dev); + +#ifdef DEBUG +static void panic_queues(struct net_device *dev); +#endif + +static void tc35815_restart_work(struct work_struct *work); + +static int tc_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct net_device *dev = bus->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + unsigned long timeout = jiffies + HZ; + + tc_writel(MD_CA_Busy | (mii_id << 5) | (regnum & 0x1f), &tr->MD_CA); + udelay(12); /* it takes 32 x 400ns at least */ + while (tc_readl(&tr->MD_CA) & MD_CA_Busy) { + if (time_after(jiffies, timeout)) + return -EIO; + cpu_relax(); + } + return tc_readl(&tr->MD_Data) & 0xffff; +} + +static int tc_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 val) +{ + struct net_device *dev = bus->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + unsigned long timeout = jiffies + HZ; + + tc_writel(val, &tr->MD_Data); + tc_writel(MD_CA_Busy | MD_CA_Wr | (mii_id << 5) | (regnum & 0x1f), + &tr->MD_CA); + udelay(12); /* it takes 32 x 400ns at least */ + while (tc_readl(&tr->MD_CA) & MD_CA_Busy) { + if (time_after(jiffies, timeout)) + return -EIO; + cpu_relax(); + } + return 0; +} + +static void tc_handle_link_change(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + struct phy_device *phydev = lp->phy_dev; + unsigned long flags; + int status_change = 0; + + spin_lock_irqsave(&lp->lock, flags); + if (phydev->link && + (lp->speed != phydev->speed || lp->duplex != phydev->duplex)) { + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + u32 reg; + + reg = tc_readl(&tr->MAC_Ctl); + reg |= MAC_HaltReq; + tc_writel(reg, &tr->MAC_Ctl); + if (phydev->duplex == DUPLEX_FULL) + reg |= MAC_FullDup; + else + reg &= ~MAC_FullDup; + tc_writel(reg, &tr->MAC_Ctl); + reg &= ~MAC_HaltReq; + tc_writel(reg, &tr->MAC_Ctl); + + /* + * TX4939 PCFG.SPEEDn bit will be changed on + * NETDEV_CHANGE event. + */ + /* + * WORKAROUND: enable LostCrS only if half duplex + * operation. + * (TX4939 does not have EnLCarr) + */ + if (phydev->duplex == DUPLEX_HALF && + lp->chiptype != TC35815_TX4939) + tc_writel(tc_readl(&tr->Tx_Ctl) | Tx_EnLCarr, + &tr->Tx_Ctl); + + lp->speed = phydev->speed; + lp->duplex = phydev->duplex; + status_change = 1; + } + + if (phydev->link != lp->link) { + if (phydev->link) { + /* delayed promiscuous enabling */ + if (dev->flags & IFF_PROMISC) + tc35815_set_multicast_list(dev); + } else { + lp->speed = 0; + lp->duplex = -1; + } + lp->link = phydev->link; + + status_change = 1; + } + spin_unlock_irqrestore(&lp->lock, flags); + + if (status_change && netif_msg_link(lp)) { + phy_print_status(phydev); + pr_debug("%s: MII BMCR %04x BMSR %04x LPA %04x\n", + dev->name, + phy_read(phydev, MII_BMCR), + phy_read(phydev, MII_BMSR), + phy_read(phydev, MII_LPA)); + } +} + +static int tc_mii_probe(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + struct phy_device *phydev = NULL; + int phy_addr; + u32 dropmask; + + /* find the first phy */ + for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { + if (lp->mii_bus->phy_map[phy_addr]) { + if (phydev) { + printk(KERN_ERR "%s: multiple PHYs found\n", + dev->name); + return -EINVAL; + } + phydev = lp->mii_bus->phy_map[phy_addr]; + break; + } + } + + if (!phydev) { + printk(KERN_ERR "%s: no PHY found\n", dev->name); + return -ENODEV; + } + + /* attach the mac to the phy */ + phydev = phy_connect(dev, dev_name(&phydev->dev), + &tc_handle_link_change, 0, + lp->chiptype == TC35815_TX4939 ? + PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII); + if (IS_ERR(phydev)) { + printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); + return PTR_ERR(phydev); + } + printk(KERN_INFO "%s: attached PHY driver [%s] " + "(mii_bus:phy_addr=%s, id=%x)\n", + dev->name, phydev->drv->name, dev_name(&phydev->dev), + phydev->phy_id); + + /* mask with MAC supported features */ + phydev->supported &= PHY_BASIC_FEATURES; + dropmask = 0; + if (options.speed == 10) + dropmask |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; + else if (options.speed == 100) + dropmask |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; + if (options.duplex == 1) + dropmask |= SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full; + else if (options.duplex == 2) + dropmask |= SUPPORTED_10baseT_Half | SUPPORTED_100baseT_Half; + phydev->supported &= ~dropmask; + phydev->advertising = phydev->supported; + + lp->link = 0; + lp->speed = 0; + lp->duplex = -1; + lp->phy_dev = phydev; + + return 0; +} + +static int tc_mii_init(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + int err; + int i; + + lp->mii_bus = mdiobus_alloc(); + if (lp->mii_bus == NULL) { + err = -ENOMEM; + goto err_out; + } + + lp->mii_bus->name = "tc35815_mii_bus"; + lp->mii_bus->read = tc_mdio_read; + lp->mii_bus->write = tc_mdio_write; + snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%x", + (lp->pci_dev->bus->number << 8) | lp->pci_dev->devfn); + lp->mii_bus->priv = dev; + lp->mii_bus->parent = &lp->pci_dev->dev; + lp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + if (!lp->mii_bus->irq) { + err = -ENOMEM; + goto err_out_free_mii_bus; + } + + for (i = 0; i < PHY_MAX_ADDR; i++) + lp->mii_bus->irq[i] = PHY_POLL; + + err = mdiobus_register(lp->mii_bus); + if (err) + goto err_out_free_mdio_irq; + err = tc_mii_probe(dev); + if (err) + goto err_out_unregister_bus; + return 0; + +err_out_unregister_bus: + mdiobus_unregister(lp->mii_bus); +err_out_free_mdio_irq: + kfree(lp->mii_bus->irq); +err_out_free_mii_bus: + mdiobus_free(lp->mii_bus); +err_out: + return err; +} + +#ifdef CONFIG_CPU_TX49XX +/* + * Find a platform_device providing a MAC address. The platform code + * should provide a "tc35815-mac" device with a MAC address in its + * platform_data. + */ +static int __devinit tc35815_mac_match(struct device *dev, void *data) +{ + struct platform_device *plat_dev = to_platform_device(dev); + struct pci_dev *pci_dev = data; + unsigned int id = pci_dev->irq; + return !strcmp(plat_dev->name, "tc35815-mac") && plat_dev->id == id; +} + +static int __devinit tc35815_read_plat_dev_addr(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + struct device *pd = bus_find_device(&platform_bus_type, NULL, + lp->pci_dev, tc35815_mac_match); + if (pd) { + if (pd->platform_data) + memcpy(dev->dev_addr, pd->platform_data, ETH_ALEN); + put_device(pd); + return is_valid_ether_addr(dev->dev_addr) ? 0 : -ENODEV; + } + return -ENODEV; +} +#else +static int __devinit tc35815_read_plat_dev_addr(struct net_device *dev) +{ + return -ENODEV; +} +#endif + +static int __devinit tc35815_init_dev_addr(struct net_device *dev) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int i; + + while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) + ; + for (i = 0; i < 6; i += 2) { + unsigned short data; + tc_writel(PROM_Busy | PROM_Read | (i / 2 + 2), &tr->PROM_Ctl); + while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) + ; + data = tc_readl(&tr->PROM_Data); + dev->dev_addr[i] = data & 0xff; + dev->dev_addr[i+1] = data >> 8; + } + if (!is_valid_ether_addr(dev->dev_addr)) + return tc35815_read_plat_dev_addr(dev); + return 0; +} + +static const struct net_device_ops tc35815_netdev_ops = { + .ndo_open = tc35815_open, + .ndo_stop = tc35815_close, + .ndo_start_xmit = tc35815_send_packet, + .ndo_get_stats = tc35815_get_stats, + .ndo_set_multicast_list = tc35815_set_multicast_list, + .ndo_tx_timeout = tc35815_tx_timeout, + .ndo_do_ioctl = tc35815_ioctl, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = tc35815_poll_controller, +#endif +}; + +static int __devinit tc35815_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + void __iomem *ioaddr = NULL; + struct net_device *dev; + struct tc35815_local *lp; + int rc; + + static int printed_version; + if (!printed_version++) { + printk(version); + dev_printk(KERN_DEBUG, &pdev->dev, + "speed:%d duplex:%d\n", + options.speed, options.duplex); + } + + if (!pdev->irq) { + dev_warn(&pdev->dev, "no IRQ assigned.\n"); + return -ENODEV; + } + + /* dev zeroed in alloc_etherdev */ + dev = alloc_etherdev(sizeof(*lp)); + if (dev == NULL) { + dev_err(&pdev->dev, "unable to alloc new ethernet\n"); + return -ENOMEM; + } + SET_NETDEV_DEV(dev, &pdev->dev); + lp = netdev_priv(dev); + lp->dev = dev; + + /* enable device (incl. PCI PM wakeup), and bus-mastering */ + rc = pcim_enable_device(pdev); + if (rc) + goto err_out; + rc = pcim_iomap_regions(pdev, 1 << 1, MODNAME); + if (rc) + goto err_out; + pci_set_master(pdev); + ioaddr = pcim_iomap_table(pdev)[1]; + + /* Initialize the device structure. */ + dev->netdev_ops = &tc35815_netdev_ops; + dev->ethtool_ops = &tc35815_ethtool_ops; + dev->watchdog_timeo = TC35815_TX_TIMEOUT; + netif_napi_add(dev, &lp->napi, tc35815_poll, NAPI_WEIGHT); + + dev->irq = pdev->irq; + dev->base_addr = (unsigned long)ioaddr; + + INIT_WORK(&lp->restart_work, tc35815_restart_work); + spin_lock_init(&lp->lock); + spin_lock_init(&lp->rx_lock); + lp->pci_dev = pdev; + lp->chiptype = ent->driver_data; + + lp->msg_enable = NETIF_MSG_TX_ERR | NETIF_MSG_HW | NETIF_MSG_DRV | NETIF_MSG_LINK; + pci_set_drvdata(pdev, dev); + + /* Soft reset the chip. */ + tc35815_chip_reset(dev); + + /* Retrieve the ethernet address. */ + if (tc35815_init_dev_addr(dev)) { + dev_warn(&pdev->dev, "not valid ether addr\n"); + random_ether_addr(dev->dev_addr); + } + + rc = register_netdev(dev); + if (rc) + goto err_out; + + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); + printk(KERN_INFO "%s: %s at 0x%lx, %pM, IRQ %d\n", + dev->name, + chip_info[ent->driver_data].name, + dev->base_addr, + dev->dev_addr, + dev->irq); + + rc = tc_mii_init(dev); + if (rc) + goto err_out_unregister; + + return 0; + +err_out_unregister: + unregister_netdev(dev); +err_out: + free_netdev(dev); + return rc; +} + + +static void __devexit tc35815_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tc35815_local *lp = netdev_priv(dev); + + phy_disconnect(lp->phy_dev); + mdiobus_unregister(lp->mii_bus); + kfree(lp->mii_bus->irq); + mdiobus_free(lp->mii_bus); + unregister_netdev(dev); + free_netdev(dev); + pci_set_drvdata(pdev, NULL); +} + +static int +tc35815_init_queues(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + int i; + unsigned long fd_addr; + + if (!lp->fd_buf) { + BUG_ON(sizeof(struct FDesc) + + sizeof(struct BDesc) * RX_BUF_NUM + + sizeof(struct FDesc) * RX_FD_NUM + + sizeof(struct TxFD) * TX_FD_NUM > + PAGE_SIZE * FD_PAGE_NUM); + + lp->fd_buf = pci_alloc_consistent(lp->pci_dev, + PAGE_SIZE * FD_PAGE_NUM, + &lp->fd_buf_dma); + if (!lp->fd_buf) + return -ENOMEM; + for (i = 0; i < RX_BUF_NUM; i++) { + lp->rx_skbs[i].skb = + alloc_rxbuf_skb(dev, lp->pci_dev, + &lp->rx_skbs[i].skb_dma); + if (!lp->rx_skbs[i].skb) { + while (--i >= 0) { + free_rxbuf_skb(lp->pci_dev, + lp->rx_skbs[i].skb, + lp->rx_skbs[i].skb_dma); + lp->rx_skbs[i].skb = NULL; + } + pci_free_consistent(lp->pci_dev, + PAGE_SIZE * FD_PAGE_NUM, + lp->fd_buf, + lp->fd_buf_dma); + lp->fd_buf = NULL; + return -ENOMEM; + } + } + printk(KERN_DEBUG "%s: FD buf %p DataBuf", + dev->name, lp->fd_buf); + printk("\n"); + } else { + for (i = 0; i < FD_PAGE_NUM; i++) + clear_page((void *)((unsigned long)lp->fd_buf + + i * PAGE_SIZE)); + } + fd_addr = (unsigned long)lp->fd_buf; + + /* Free Descriptors (for Receive) */ + lp->rfd_base = (struct RxFD *)fd_addr; + fd_addr += sizeof(struct RxFD) * RX_FD_NUM; + for (i = 0; i < RX_FD_NUM; i++) + lp->rfd_base[i].fd.FDCtl = cpu_to_le32(FD_CownsFD); + lp->rfd_cur = lp->rfd_base; + lp->rfd_limit = (struct RxFD *)fd_addr - (RX_FD_RESERVE + 1); + + /* Transmit Descriptors */ + lp->tfd_base = (struct TxFD *)fd_addr; + fd_addr += sizeof(struct TxFD) * TX_FD_NUM; + for (i = 0; i < TX_FD_NUM; i++) { + lp->tfd_base[i].fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, &lp->tfd_base[i+1])); + lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); + lp->tfd_base[i].fd.FDCtl = cpu_to_le32(0); + } + lp->tfd_base[TX_FD_NUM-1].fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, &lp->tfd_base[0])); + lp->tfd_start = 0; + lp->tfd_end = 0; + + /* Buffer List (for Receive) */ + lp->fbl_ptr = (struct FrFD *)fd_addr; + lp->fbl_ptr->fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, lp->fbl_ptr)); + lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_NUM | FD_CownsFD); + /* + * move all allocated skbs to head of rx_skbs[] array. + * fbl_count mighe not be RX_BUF_NUM if alloc_rxbuf_skb() in + * tc35815_rx() had failed. + */ + lp->fbl_count = 0; + for (i = 0; i < RX_BUF_NUM; i++) { + if (lp->rx_skbs[i].skb) { + if (i != lp->fbl_count) { + lp->rx_skbs[lp->fbl_count].skb = + lp->rx_skbs[i].skb; + lp->rx_skbs[lp->fbl_count].skb_dma = + lp->rx_skbs[i].skb_dma; + } + lp->fbl_count++; + } + } + for (i = 0; i < RX_BUF_NUM; i++) { + if (i >= lp->fbl_count) { + lp->fbl_ptr->bd[i].BuffData = 0; + lp->fbl_ptr->bd[i].BDCtl = 0; + continue; + } + lp->fbl_ptr->bd[i].BuffData = + cpu_to_le32(lp->rx_skbs[i].skb_dma); + /* BDID is index of FrFD.bd[] */ + lp->fbl_ptr->bd[i].BDCtl = + cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) | + RX_BUF_SIZE); + } + + printk(KERN_DEBUG "%s: TxFD %p RxFD %p FrFD %p\n", + dev->name, lp->tfd_base, lp->rfd_base, lp->fbl_ptr); + return 0; +} + +static void +tc35815_clear_queues(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + int i; + + for (i = 0; i < TX_FD_NUM; i++) { + u32 fdsystem = le32_to_cpu(lp->tfd_base[i].fd.FDSystem); + struct sk_buff *skb = + fdsystem != 0xffffffff ? + lp->tx_skbs[fdsystem].skb : NULL; +#ifdef DEBUG + if (lp->tx_skbs[i].skb != skb) { + printk("%s: tx_skbs mismatch(%d).\n", dev->name, i); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[i].skb != skb); +#endif + if (skb) { + pci_unmap_single(lp->pci_dev, lp->tx_skbs[i].skb_dma, skb->len, PCI_DMA_TODEVICE); + lp->tx_skbs[i].skb = NULL; + lp->tx_skbs[i].skb_dma = 0; + dev_kfree_skb_any(skb); + } + lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); + } + + tc35815_init_queues(dev); +} + +static void +tc35815_free_queues(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + int i; + + if (lp->tfd_base) { + for (i = 0; i < TX_FD_NUM; i++) { + u32 fdsystem = le32_to_cpu(lp->tfd_base[i].fd.FDSystem); + struct sk_buff *skb = + fdsystem != 0xffffffff ? + lp->tx_skbs[fdsystem].skb : NULL; +#ifdef DEBUG + if (lp->tx_skbs[i].skb != skb) { + printk("%s: tx_skbs mismatch(%d).\n", dev->name, i); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[i].skb != skb); +#endif + if (skb) { + dev_kfree_skb(skb); + pci_unmap_single(lp->pci_dev, lp->tx_skbs[i].skb_dma, skb->len, PCI_DMA_TODEVICE); + lp->tx_skbs[i].skb = NULL; + lp->tx_skbs[i].skb_dma = 0; + } + lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); + } + } + + lp->rfd_base = NULL; + lp->rfd_limit = NULL; + lp->rfd_cur = NULL; + lp->fbl_ptr = NULL; + + for (i = 0; i < RX_BUF_NUM; i++) { + if (lp->rx_skbs[i].skb) { + free_rxbuf_skb(lp->pci_dev, lp->rx_skbs[i].skb, + lp->rx_skbs[i].skb_dma); + lp->rx_skbs[i].skb = NULL; + } + } + if (lp->fd_buf) { + pci_free_consistent(lp->pci_dev, PAGE_SIZE * FD_PAGE_NUM, + lp->fd_buf, lp->fd_buf_dma); + lp->fd_buf = NULL; + } +} + +static void +dump_txfd(struct TxFD *fd) +{ + printk("TxFD(%p): %08x %08x %08x %08x\n", fd, + le32_to_cpu(fd->fd.FDNext), + le32_to_cpu(fd->fd.FDSystem), + le32_to_cpu(fd->fd.FDStat), + le32_to_cpu(fd->fd.FDCtl)); + printk("BD: "); + printk(" %08x %08x", + le32_to_cpu(fd->bd.BuffData), + le32_to_cpu(fd->bd.BDCtl)); + printk("\n"); +} + +static int +dump_rxfd(struct RxFD *fd) +{ + int i, bd_count = (le32_to_cpu(fd->fd.FDCtl) & FD_BDCnt_MASK) >> FD_BDCnt_SHIFT; + if (bd_count > 8) + bd_count = 8; + printk("RxFD(%p): %08x %08x %08x %08x\n", fd, + le32_to_cpu(fd->fd.FDNext), + le32_to_cpu(fd->fd.FDSystem), + le32_to_cpu(fd->fd.FDStat), + le32_to_cpu(fd->fd.FDCtl)); + if (le32_to_cpu(fd->fd.FDCtl) & FD_CownsFD) + return 0; + printk("BD: "); + for (i = 0; i < bd_count; i++) + printk(" %08x %08x", + le32_to_cpu(fd->bd[i].BuffData), + le32_to_cpu(fd->bd[i].BDCtl)); + printk("\n"); + return bd_count; +} + +#ifdef DEBUG +static void +dump_frfd(struct FrFD *fd) +{ + int i; + printk("FrFD(%p): %08x %08x %08x %08x\n", fd, + le32_to_cpu(fd->fd.FDNext), + le32_to_cpu(fd->fd.FDSystem), + le32_to_cpu(fd->fd.FDStat), + le32_to_cpu(fd->fd.FDCtl)); + printk("BD: "); + for (i = 0; i < RX_BUF_NUM; i++) + printk(" %08x %08x", + le32_to_cpu(fd->bd[i].BuffData), + le32_to_cpu(fd->bd[i].BDCtl)); + printk("\n"); +} + +static void +panic_queues(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + int i; + + printk("TxFD base %p, start %u, end %u\n", + lp->tfd_base, lp->tfd_start, lp->tfd_end); + printk("RxFD base %p limit %p cur %p\n", + lp->rfd_base, lp->rfd_limit, lp->rfd_cur); + printk("FrFD %p\n", lp->fbl_ptr); + for (i = 0; i < TX_FD_NUM; i++) + dump_txfd(&lp->tfd_base[i]); + for (i = 0; i < RX_FD_NUM; i++) { + int bd_count = dump_rxfd(&lp->rfd_base[i]); + i += (bd_count + 1) / 2; /* skip BDs */ + } + dump_frfd(lp->fbl_ptr); + panic("%s: Illegal queue state.", dev->name); +} +#endif + +static void print_eth(const u8 *add) +{ + printk(KERN_DEBUG "print_eth(%p)\n", add); + printk(KERN_DEBUG " %pM => %pM : %02x%02x\n", + add + 6, add, add[12], add[13]); +} + +static int tc35815_tx_full(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + return (lp->tfd_start + 1) % TX_FD_NUM == lp->tfd_end; +} + +static void tc35815_restart(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + + if (lp->phy_dev) { + int timeout; + + phy_write(lp->phy_dev, MII_BMCR, BMCR_RESET); + timeout = 100; + while (--timeout) { + if (!(phy_read(lp->phy_dev, MII_BMCR) & BMCR_RESET)) + break; + udelay(1); + } + if (!timeout) + printk(KERN_ERR "%s: BMCR reset failed.\n", dev->name); + } + + spin_lock_bh(&lp->rx_lock); + spin_lock_irq(&lp->lock); + tc35815_chip_reset(dev); + tc35815_clear_queues(dev); + tc35815_chip_init(dev); + /* Reconfigure CAM again since tc35815_chip_init() initialize it. */ + tc35815_set_multicast_list(dev); + spin_unlock_irq(&lp->lock); + spin_unlock_bh(&lp->rx_lock); + + netif_wake_queue(dev); +} + +static void tc35815_restart_work(struct work_struct *work) +{ + struct tc35815_local *lp = + container_of(work, struct tc35815_local, restart_work); + struct net_device *dev = lp->dev; + + tc35815_restart(dev); +} + +static void tc35815_schedule_restart(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + unsigned long flags; + + /* disable interrupts */ + spin_lock_irqsave(&lp->lock, flags); + tc_writel(0, &tr->Int_En); + tc_writel(tc_readl(&tr->DMA_Ctl) | DMA_IntMask, &tr->DMA_Ctl); + schedule_work(&lp->restart_work); + spin_unlock_irqrestore(&lp->lock, flags); +} + +static void tc35815_tx_timeout(struct net_device *dev) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + + printk(KERN_WARNING "%s: transmit timed out, status %#x\n", + dev->name, tc_readl(&tr->Tx_Stat)); + + /* Try to restart the adaptor. */ + tc35815_schedule_restart(dev); + dev->stats.tx_errors++; +} + +/* + * Open/initialize the controller. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is non-reboot way to recover if something goes wrong. + */ +static int +tc35815_open(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + + /* + * This is used if the interrupt line can turned off (shared). + * See 3c503.c for an example of selecting the IRQ at config-time. + */ + if (request_irq(dev->irq, tc35815_interrupt, IRQF_SHARED, + dev->name, dev)) + return -EAGAIN; + + tc35815_chip_reset(dev); + + if (tc35815_init_queues(dev) != 0) { + free_irq(dev->irq, dev); + return -EAGAIN; + } + + napi_enable(&lp->napi); + + /* Reset the hardware here. Don't forget to set the station address. */ + spin_lock_irq(&lp->lock); + tc35815_chip_init(dev); + spin_unlock_irq(&lp->lock); + + netif_carrier_off(dev); + /* schedule a link state check */ + phy_start(lp->phy_dev); + + /* We are now ready to accept transmit requeusts from + * the queueing layer of the networking. + */ + netif_start_queue(dev); + + return 0; +} + +/* This will only be invoked if your driver is _not_ in XOFF state. + * What this means is that you need not check it, and that this + * invariant will hold if you make sure that the netif_*_queue() + * calls are done at the proper times. + */ +static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + struct TxFD *txfd; + unsigned long flags; + + /* If some error occurs while trying to transmit this + * packet, you should return '1' from this function. + * In such a case you _may not_ do anything to the + * SKB, it is still owned by the network queueing + * layer when an error is returned. This means you + * may not modify any SKB fields, you may not free + * the SKB, etc. + */ + + /* This is the most common case for modern hardware. + * The spinlock protects this code from the TX complete + * hardware interrupt handler. Queue flow control is + * thus managed under this lock as well. + */ + spin_lock_irqsave(&lp->lock, flags); + + /* failsafe... (handle txdone now if half of FDs are used) */ + if ((lp->tfd_start + TX_FD_NUM - lp->tfd_end) % TX_FD_NUM > + TX_FD_NUM / 2) + tc35815_txdone(dev); + + if (netif_msg_pktdata(lp)) + print_eth(skb->data); +#ifdef DEBUG + if (lp->tx_skbs[lp->tfd_start].skb) { + printk("%s: tx_skbs conflict.\n", dev->name); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[lp->tfd_start].skb); +#endif + lp->tx_skbs[lp->tfd_start].skb = skb; + lp->tx_skbs[lp->tfd_start].skb_dma = pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); + + /*add to ring */ + txfd = &lp->tfd_base[lp->tfd_start]; + txfd->bd.BuffData = cpu_to_le32(lp->tx_skbs[lp->tfd_start].skb_dma); + txfd->bd.BDCtl = cpu_to_le32(skb->len); + txfd->fd.FDSystem = cpu_to_le32(lp->tfd_start); + txfd->fd.FDCtl = cpu_to_le32(FD_CownsFD | (1 << FD_BDCnt_SHIFT)); + + if (lp->tfd_start == lp->tfd_end) { + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + /* Start DMA Transmitter. */ + txfd->fd.FDNext |= cpu_to_le32(FD_Next_EOL); + txfd->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); + if (netif_msg_tx_queued(lp)) { + printk("%s: starting TxFD.\n", dev->name); + dump_txfd(txfd); + } + tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr); + } else { + txfd->fd.FDNext &= cpu_to_le32(~FD_Next_EOL); + if (netif_msg_tx_queued(lp)) { + printk("%s: queueing TxFD.\n", dev->name); + dump_txfd(txfd); + } + } + lp->tfd_start = (lp->tfd_start + 1) % TX_FD_NUM; + + /* If we just used up the very last entry in the + * TX ring on this device, tell the queueing + * layer to send no more. + */ + if (tc35815_tx_full(dev)) { + if (netif_msg_tx_queued(lp)) + printk(KERN_WARNING "%s: TxFD Exhausted.\n", dev->name); + netif_stop_queue(dev); + } + + /* When the TX completion hw interrupt arrives, this + * is when the transmit statistics are updated. + */ + + spin_unlock_irqrestore(&lp->lock, flags); + return NETDEV_TX_OK; +} + +#define FATAL_ERROR_INT \ + (Int_IntPCI | Int_DmParErr | Int_IntNRAbt) +static void tc35815_fatal_error_interrupt(struct net_device *dev, u32 status) +{ + static int count; + printk(KERN_WARNING "%s: Fatal Error Intterrupt (%#x):", + dev->name, status); + if (status & Int_IntPCI) + printk(" IntPCI"); + if (status & Int_DmParErr) + printk(" DmParErr"); + if (status & Int_IntNRAbt) + printk(" IntNRAbt"); + printk("\n"); + if (count++ > 100) + panic("%s: Too many fatal errors.", dev->name); + printk(KERN_WARNING "%s: Resetting ...\n", dev->name); + /* Try to restart the adaptor. */ + tc35815_schedule_restart(dev); +} + +static int tc35815_do_interrupt(struct net_device *dev, u32 status, int limit) +{ + struct tc35815_local *lp = netdev_priv(dev); + int ret = -1; + + /* Fatal errors... */ + if (status & FATAL_ERROR_INT) { + tc35815_fatal_error_interrupt(dev, status); + return 0; + } + /* recoverable errors */ + if (status & Int_IntFDAEx) { + if (netif_msg_rx_err(lp)) + dev_warn(&dev->dev, + "Free Descriptor Area Exhausted (%#x).\n", + status); + dev->stats.rx_dropped++; + ret = 0; + } + if (status & Int_IntBLEx) { + if (netif_msg_rx_err(lp)) + dev_warn(&dev->dev, + "Buffer List Exhausted (%#x).\n", + status); + dev->stats.rx_dropped++; + ret = 0; + } + if (status & Int_IntExBD) { + if (netif_msg_rx_err(lp)) + dev_warn(&dev->dev, + "Excessive Buffer Descriptiors (%#x).\n", + status); + dev->stats.rx_length_errors++; + ret = 0; + } + + /* normal notification */ + if (status & Int_IntMacRx) { + /* Got a packet(s). */ + ret = tc35815_rx(dev, limit); + lp->lstats.rx_ints++; + } + if (status & Int_IntMacTx) { + /* Transmit complete. */ + lp->lstats.tx_ints++; + spin_lock_irq(&lp->lock); + tc35815_txdone(dev); + spin_unlock_irq(&lp->lock); + if (ret < 0) + ret = 0; + } + return ret; +} + +/* + * The typical workload of the driver: + * Handle the network interface interrupts. + */ +static irqreturn_t tc35815_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct tc35815_local *lp = netdev_priv(dev); + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + u32 dmactl = tc_readl(&tr->DMA_Ctl); + + if (!(dmactl & DMA_IntMask)) { + /* disable interrupts */ + tc_writel(dmactl | DMA_IntMask, &tr->DMA_Ctl); + if (napi_schedule_prep(&lp->napi)) + __napi_schedule(&lp->napi); + else { + printk(KERN_ERR "%s: interrupt taken in poll\n", + dev->name); + BUG(); + } + (void)tc_readl(&tr->Int_Src); /* flush */ + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void tc35815_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + tc35815_interrupt(dev->irq, dev); + enable_irq(dev->irq); +} +#endif + +/* We have a good packet(s), get it/them out of the buffers. */ +static int +tc35815_rx(struct net_device *dev, int limit) +{ + struct tc35815_local *lp = netdev_priv(dev); + unsigned int fdctl; + int i; + int received = 0; + + while (!((fdctl = le32_to_cpu(lp->rfd_cur->fd.FDCtl)) & FD_CownsFD)) { + int status = le32_to_cpu(lp->rfd_cur->fd.FDStat); + int pkt_len = fdctl & FD_FDLength_MASK; + int bd_count = (fdctl & FD_BDCnt_MASK) >> FD_BDCnt_SHIFT; +#ifdef DEBUG + struct RxFD *next_rfd; +#endif +#if (RX_CTL_CMD & Rx_StripCRC) == 0 + pkt_len -= ETH_FCS_LEN; +#endif + + if (netif_msg_rx_status(lp)) + dump_rxfd(lp->rfd_cur); + if (status & Rx_Good) { + struct sk_buff *skb; + unsigned char *data; + int cur_bd; + + if (--limit < 0) + break; + BUG_ON(bd_count > 1); + cur_bd = (le32_to_cpu(lp->rfd_cur->bd[0].BDCtl) + & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT; +#ifdef DEBUG + if (cur_bd >= RX_BUF_NUM) { + printk("%s: invalid BDID.\n", dev->name); + panic_queues(dev); + } + BUG_ON(lp->rx_skbs[cur_bd].skb_dma != + (le32_to_cpu(lp->rfd_cur->bd[0].BuffData) & ~3)); + if (!lp->rx_skbs[cur_bd].skb) { + printk("%s: NULL skb.\n", dev->name); + panic_queues(dev); + } +#else + BUG_ON(cur_bd >= RX_BUF_NUM); +#endif + skb = lp->rx_skbs[cur_bd].skb; + prefetch(skb->data); + lp->rx_skbs[cur_bd].skb = NULL; + pci_unmap_single(lp->pci_dev, + lp->rx_skbs[cur_bd].skb_dma, + RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + if (!HAVE_DMA_RXALIGN(lp) && NET_IP_ALIGN) + memmove(skb->data, skb->data - NET_IP_ALIGN, + pkt_len); + data = skb_put(skb, pkt_len); + if (netif_msg_pktdata(lp)) + print_eth(data); + skb->protocol = eth_type_trans(skb, dev); + netif_receive_skb(skb); + received++; + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } else { + dev->stats.rx_errors++; + if (netif_msg_rx_err(lp)) + dev_info(&dev->dev, "Rx error (status %x)\n", + status & Rx_Stat_Mask); + /* WORKAROUND: LongErr and CRCErr means Overflow. */ + if ((status & Rx_LongErr) && (status & Rx_CRCErr)) { + status &= ~(Rx_LongErr|Rx_CRCErr); + status |= Rx_Over; + } + if (status & Rx_LongErr) + dev->stats.rx_length_errors++; + if (status & Rx_Over) + dev->stats.rx_fifo_errors++; + if (status & Rx_CRCErr) + dev->stats.rx_crc_errors++; + if (status & Rx_Align) + dev->stats.rx_frame_errors++; + } + + if (bd_count > 0) { + /* put Free Buffer back to controller */ + int bdctl = le32_to_cpu(lp->rfd_cur->bd[bd_count - 1].BDCtl); + unsigned char id = + (bdctl & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT; +#ifdef DEBUG + if (id >= RX_BUF_NUM) { + printk("%s: invalid BDID.\n", dev->name); + panic_queues(dev); + } +#else + BUG_ON(id >= RX_BUF_NUM); +#endif + /* free old buffers */ + lp->fbl_count--; + while (lp->fbl_count < RX_BUF_NUM) + { + unsigned char curid = + (id + 1 + lp->fbl_count) % RX_BUF_NUM; + struct BDesc *bd = &lp->fbl_ptr->bd[curid]; +#ifdef DEBUG + bdctl = le32_to_cpu(bd->BDCtl); + if (bdctl & BD_CownsBD) { + printk("%s: Freeing invalid BD.\n", + dev->name); + panic_queues(dev); + } +#endif + /* pass BD to controller */ + if (!lp->rx_skbs[curid].skb) { + lp->rx_skbs[curid].skb = + alloc_rxbuf_skb(dev, + lp->pci_dev, + &lp->rx_skbs[curid].skb_dma); + if (!lp->rx_skbs[curid].skb) + break; /* try on next reception */ + bd->BuffData = cpu_to_le32(lp->rx_skbs[curid].skb_dma); + } + /* Note: BDLength was modified by chip. */ + bd->BDCtl = cpu_to_le32(BD_CownsBD | + (curid << BD_RxBDID_SHIFT) | + RX_BUF_SIZE); + lp->fbl_count++; + } + } + + /* put RxFD back to controller */ +#ifdef DEBUG + next_rfd = fd_bus_to_virt(lp, + le32_to_cpu(lp->rfd_cur->fd.FDNext)); + if (next_rfd < lp->rfd_base || next_rfd > lp->rfd_limit) { + printk("%s: RxFD FDNext invalid.\n", dev->name); + panic_queues(dev); + } +#endif + for (i = 0; i < (bd_count + 1) / 2 + 1; i++) { + /* pass FD to controller */ +#ifdef DEBUG + lp->rfd_cur->fd.FDNext = cpu_to_le32(0xdeaddead); +#else + lp->rfd_cur->fd.FDNext = cpu_to_le32(FD_Next_EOL); +#endif + lp->rfd_cur->fd.FDCtl = cpu_to_le32(FD_CownsFD); + lp->rfd_cur++; + } + if (lp->rfd_cur > lp->rfd_limit) + lp->rfd_cur = lp->rfd_base; +#ifdef DEBUG + if (lp->rfd_cur != next_rfd) + printk("rfd_cur = %p, next_rfd %p\n", + lp->rfd_cur, next_rfd); +#endif + } + + return received; +} + +static int tc35815_poll(struct napi_struct *napi, int budget) +{ + struct tc35815_local *lp = container_of(napi, struct tc35815_local, napi); + struct net_device *dev = lp->dev; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int received = 0, handled; + u32 status; + + spin_lock(&lp->rx_lock); + status = tc_readl(&tr->Int_Src); + do { + /* BLEx, FDAEx will be cleared later */ + tc_writel(status & ~(Int_BLEx | Int_FDAEx), + &tr->Int_Src); /* write to clear */ + + handled = tc35815_do_interrupt(dev, status, budget - received); + if (status & (Int_BLEx | Int_FDAEx)) + tc_writel(status & (Int_BLEx | Int_FDAEx), + &tr->Int_Src); + if (handled >= 0) { + received += handled; + if (received >= budget) + break; + } + status = tc_readl(&tr->Int_Src); + } while (status); + spin_unlock(&lp->rx_lock); + + if (received < budget) { + napi_complete(napi); + /* enable interrupts */ + tc_writel(tc_readl(&tr->DMA_Ctl) & ~DMA_IntMask, &tr->DMA_Ctl); + } + return received; +} + +#define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_NCarr|Tx_LateColl|Tx_TxPar|Tx_SQErr) + +static void +tc35815_check_tx_stat(struct net_device *dev, int status) +{ + struct tc35815_local *lp = netdev_priv(dev); + const char *msg = NULL; + + /* count collisions */ + if (status & Tx_ExColl) + dev->stats.collisions += 16; + if (status & Tx_TxColl_MASK) + dev->stats.collisions += status & Tx_TxColl_MASK; + + /* TX4939 does not have NCarr */ + if (lp->chiptype == TC35815_TX4939) + status &= ~Tx_NCarr; + /* WORKAROUND: ignore LostCrS in full duplex operation */ + if (!lp->link || lp->duplex == DUPLEX_FULL) + status &= ~Tx_NCarr; + + if (!(status & TX_STA_ERR)) { + /* no error. */ + dev->stats.tx_packets++; + return; + } + + dev->stats.tx_errors++; + if (status & Tx_ExColl) { + dev->stats.tx_aborted_errors++; + msg = "Excessive Collision."; + } + if (status & Tx_Under) { + dev->stats.tx_fifo_errors++; + msg = "Tx FIFO Underrun."; + if (lp->lstats.tx_underrun < TX_THRESHOLD_KEEP_LIMIT) { + lp->lstats.tx_underrun++; + if (lp->lstats.tx_underrun >= TX_THRESHOLD_KEEP_LIMIT) { + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + tc_writel(TX_THRESHOLD_MAX, &tr->TxThrsh); + msg = "Tx FIFO Underrun.Change Tx threshold to max."; + } + } + } + if (status & Tx_Defer) { + dev->stats.tx_fifo_errors++; + msg = "Excessive Deferral."; + } + if (status & Tx_NCarr) { + dev->stats.tx_carrier_errors++; + msg = "Lost Carrier Sense."; + } + if (status & Tx_LateColl) { + dev->stats.tx_aborted_errors++; + msg = "Late Collision."; + } + if (status & Tx_TxPar) { + dev->stats.tx_fifo_errors++; + msg = "Transmit Parity Error."; + } + if (status & Tx_SQErr) { + dev->stats.tx_heartbeat_errors++; + msg = "Signal Quality Error."; + } + if (msg && netif_msg_tx_err(lp)) + printk(KERN_WARNING "%s: %s (%#x)\n", dev->name, msg, status); +} + +/* This handles TX complete events posted by the device + * via interrupts. + */ +static void +tc35815_txdone(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + struct TxFD *txfd; + unsigned int fdctl; + + txfd = &lp->tfd_base[lp->tfd_end]; + while (lp->tfd_start != lp->tfd_end && + !((fdctl = le32_to_cpu(txfd->fd.FDCtl)) & FD_CownsFD)) { + int status = le32_to_cpu(txfd->fd.FDStat); + struct sk_buff *skb; + unsigned long fdnext = le32_to_cpu(txfd->fd.FDNext); + u32 fdsystem = le32_to_cpu(txfd->fd.FDSystem); + + if (netif_msg_tx_done(lp)) { + printk("%s: complete TxFD.\n", dev->name); + dump_txfd(txfd); + } + tc35815_check_tx_stat(dev, status); + + skb = fdsystem != 0xffffffff ? + lp->tx_skbs[fdsystem].skb : NULL; +#ifdef DEBUG + if (lp->tx_skbs[lp->tfd_end].skb != skb) { + printk("%s: tx_skbs mismatch.\n", dev->name); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[lp->tfd_end].skb != skb); +#endif + if (skb) { + dev->stats.tx_bytes += skb->len; + pci_unmap_single(lp->pci_dev, lp->tx_skbs[lp->tfd_end].skb_dma, skb->len, PCI_DMA_TODEVICE); + lp->tx_skbs[lp->tfd_end].skb = NULL; + lp->tx_skbs[lp->tfd_end].skb_dma = 0; + dev_kfree_skb_any(skb); + } + txfd->fd.FDSystem = cpu_to_le32(0xffffffff); + + lp->tfd_end = (lp->tfd_end + 1) % TX_FD_NUM; + txfd = &lp->tfd_base[lp->tfd_end]; +#ifdef DEBUG + if ((fdnext & ~FD_Next_EOL) != fd_virt_to_bus(lp, txfd)) { + printk("%s: TxFD FDNext invalid.\n", dev->name); + panic_queues(dev); + } +#endif + if (fdnext & FD_Next_EOL) { + /* DMA Transmitter has been stopping... */ + if (lp->tfd_end != lp->tfd_start) { + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int head = (lp->tfd_start + TX_FD_NUM - 1) % TX_FD_NUM; + struct TxFD *txhead = &lp->tfd_base[head]; + int qlen = (lp->tfd_start + TX_FD_NUM + - lp->tfd_end) % TX_FD_NUM; + +#ifdef DEBUG + if (!(le32_to_cpu(txfd->fd.FDCtl) & FD_CownsFD)) { + printk("%s: TxFD FDCtl invalid.\n", dev->name); + panic_queues(dev); + } +#endif + /* log max queue length */ + if (lp->lstats.max_tx_qlen < qlen) + lp->lstats.max_tx_qlen = qlen; + + + /* start DMA Transmitter again */ + txhead->fd.FDNext |= cpu_to_le32(FD_Next_EOL); + txhead->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); + if (netif_msg_tx_queued(lp)) { + printk("%s: start TxFD on queue.\n", + dev->name); + dump_txfd(txfd); + } + tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr); + } + break; + } + } + + /* If we had stopped the queue due to a "tx full" + * condition, and space has now been made available, + * wake up the queue. + */ + if (netif_queue_stopped(dev) && !tc35815_tx_full(dev)) + netif_wake_queue(dev); +} + +/* The inverse routine to tc35815_open(). */ +static int +tc35815_close(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + + netif_stop_queue(dev); + napi_disable(&lp->napi); + if (lp->phy_dev) + phy_stop(lp->phy_dev); + cancel_work_sync(&lp->restart_work); + + /* Flush the Tx and disable Rx here. */ + tc35815_chip_reset(dev); + free_irq(dev->irq, dev); + + tc35815_free_queues(dev); + + return 0; + +} + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ +static struct net_device_stats *tc35815_get_stats(struct net_device *dev) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + if (netif_running(dev)) + /* Update the statistics from the device registers. */ + dev->stats.rx_missed_errors += tc_readl(&tr->Miss_Cnt); + + return &dev->stats; +} + +static void tc35815_set_cam_entry(struct net_device *dev, int index, unsigned char *addr) +{ + struct tc35815_local *lp = netdev_priv(dev); + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int cam_index = index * 6; + u32 cam_data; + u32 saved_addr; + + saved_addr = tc_readl(&tr->CAM_Adr); + + if (netif_msg_hw(lp)) + printk(KERN_DEBUG "%s: CAM %d: %pM\n", + dev->name, index, addr); + if (index & 1) { + /* read modify write */ + tc_writel(cam_index - 2, &tr->CAM_Adr); + cam_data = tc_readl(&tr->CAM_Data) & 0xffff0000; + cam_data |= addr[0] << 8 | addr[1]; + tc_writel(cam_data, &tr->CAM_Data); + /* write whole word */ + tc_writel(cam_index + 2, &tr->CAM_Adr); + cam_data = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; + tc_writel(cam_data, &tr->CAM_Data); + } else { + /* write whole word */ + tc_writel(cam_index, &tr->CAM_Adr); + cam_data = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; + tc_writel(cam_data, &tr->CAM_Data); + /* read modify write */ + tc_writel(cam_index + 4, &tr->CAM_Adr); + cam_data = tc_readl(&tr->CAM_Data) & 0x0000ffff; + cam_data |= addr[4] << 24 | (addr[5] << 16); + tc_writel(cam_data, &tr->CAM_Data); + } + + tc_writel(saved_addr, &tr->CAM_Adr); +} + + +/* + * Set or clear the multicast filter for this adaptor. + * num_addrs == -1 Promiscuous mode, receive all packets + * num_addrs == 0 Normal mode, clear multicast list + * num_addrs > 0 Multicast mode, receive normal and MC packets, + * and do best-effort filtering. + */ +static void +tc35815_set_multicast_list(struct net_device *dev) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + + if (dev->flags & IFF_PROMISC) { + /* With some (all?) 100MHalf HUB, controller will hang + * if we enabled promiscuous mode before linkup... */ + struct tc35815_local *lp = netdev_priv(dev); + + if (!lp->link) + return; + /* Enable promiscuous mode */ + tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc | CAM_StationAcc, &tr->CAM_Ctl); + } else if ((dev->flags & IFF_ALLMULTI) || + netdev_mc_count(dev) > CAM_ENTRY_MAX - 3) { + /* CAM 0, 1, 20 are reserved. */ + /* Disable promiscuous mode, use normal mode. */ + tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc, &tr->CAM_Ctl); + } else if (!netdev_mc_empty(dev)) { + struct netdev_hw_addr *ha; + int i; + int ena_bits = CAM_Ena_Bit(CAM_ENTRY_SOURCE); + + tc_writel(0, &tr->CAM_Ctl); + /* Walk the address list, and load the filter */ + i = 0; + netdev_for_each_mc_addr(ha, dev) { + /* entry 0,1 is reserved. */ + tc35815_set_cam_entry(dev, i + 2, ha->addr); + ena_bits |= CAM_Ena_Bit(i + 2); + i++; + } + tc_writel(ena_bits, &tr->CAM_Ena); + tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl); + } else { + tc_writel(CAM_Ena_Bit(CAM_ENTRY_SOURCE), &tr->CAM_Ena); + tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl); + } +} + +static void tc35815_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct tc35815_local *lp = netdev_priv(dev); + strcpy(info->driver, MODNAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(lp->pci_dev)); +} + +static int tc35815_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct tc35815_local *lp = netdev_priv(dev); + + if (!lp->phy_dev) + return -ENODEV; + return phy_ethtool_gset(lp->phy_dev, cmd); +} + +static int tc35815_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct tc35815_local *lp = netdev_priv(dev); + + if (!lp->phy_dev) + return -ENODEV; + return phy_ethtool_sset(lp->phy_dev, cmd); +} + +static u32 tc35815_get_msglevel(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + return lp->msg_enable; +} + +static void tc35815_set_msglevel(struct net_device *dev, u32 datum) +{ + struct tc35815_local *lp = netdev_priv(dev); + lp->msg_enable = datum; +} + +static int tc35815_get_sset_count(struct net_device *dev, int sset) +{ + struct tc35815_local *lp = netdev_priv(dev); + + switch (sset) { + case ETH_SS_STATS: + return sizeof(lp->lstats) / sizeof(int); + default: + return -EOPNOTSUPP; + } +} + +static void tc35815_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) +{ + struct tc35815_local *lp = netdev_priv(dev); + data[0] = lp->lstats.max_tx_qlen; + data[1] = lp->lstats.tx_ints; + data[2] = lp->lstats.rx_ints; + data[3] = lp->lstats.tx_underrun; +} + +static struct { + const char str[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "max_tx_qlen" }, + { "tx_ints" }, + { "rx_ints" }, + { "tx_underrun" }, +}; + +static void tc35815_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); +} + +static const struct ethtool_ops tc35815_ethtool_ops = { + .get_drvinfo = tc35815_get_drvinfo, + .get_settings = tc35815_get_settings, + .set_settings = tc35815_set_settings, + .get_link = ethtool_op_get_link, + .get_msglevel = tc35815_get_msglevel, + .set_msglevel = tc35815_set_msglevel, + .get_strings = tc35815_get_strings, + .get_sset_count = tc35815_get_sset_count, + .get_ethtool_stats = tc35815_get_ethtool_stats, +}; + +static int tc35815_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct tc35815_local *lp = netdev_priv(dev); + + if (!netif_running(dev)) + return -EINVAL; + if (!lp->phy_dev) + return -ENODEV; + return phy_mii_ioctl(lp->phy_dev, rq, cmd); +} + +static void tc35815_chip_reset(struct net_device *dev) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int i; + /* reset the controller */ + tc_writel(MAC_Reset, &tr->MAC_Ctl); + udelay(4); /* 3200ns */ + i = 0; + while (tc_readl(&tr->MAC_Ctl) & MAC_Reset) { + if (i++ > 100) { + printk(KERN_ERR "%s: MAC reset failed.\n", dev->name); + break; + } + mdelay(1); + } + tc_writel(0, &tr->MAC_Ctl); + + /* initialize registers to default value */ + tc_writel(0, &tr->DMA_Ctl); + tc_writel(0, &tr->TxThrsh); + tc_writel(0, &tr->TxPollCtr); + tc_writel(0, &tr->RxFragSize); + tc_writel(0, &tr->Int_En); + tc_writel(0, &tr->FDA_Bas); + tc_writel(0, &tr->FDA_Lim); + tc_writel(0xffffffff, &tr->Int_Src); /* Write 1 to clear */ + tc_writel(0, &tr->CAM_Ctl); + tc_writel(0, &tr->Tx_Ctl); + tc_writel(0, &tr->Rx_Ctl); + tc_writel(0, &tr->CAM_Ena); + (void)tc_readl(&tr->Miss_Cnt); /* Read to clear */ + + /* initialize internal SRAM */ + tc_writel(DMA_TestMode, &tr->DMA_Ctl); + for (i = 0; i < 0x1000; i += 4) { + tc_writel(i, &tr->CAM_Adr); + tc_writel(0, &tr->CAM_Data); + } + tc_writel(0, &tr->DMA_Ctl); +} + +static void tc35815_chip_init(struct net_device *dev) +{ + struct tc35815_local *lp = netdev_priv(dev); + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + unsigned long txctl = TX_CTL_CMD; + + /* load station address to CAM */ + tc35815_set_cam_entry(dev, CAM_ENTRY_SOURCE, dev->dev_addr); + + /* Enable CAM (broadcast and unicast) */ + tc_writel(CAM_Ena_Bit(CAM_ENTRY_SOURCE), &tr->CAM_Ena); + tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl); + + /* Use DMA_RxAlign_2 to make IP header 4-byte aligned. */ + if (HAVE_DMA_RXALIGN(lp)) + tc_writel(DMA_BURST_SIZE | DMA_RxAlign_2, &tr->DMA_Ctl); + else + tc_writel(DMA_BURST_SIZE, &tr->DMA_Ctl); + tc_writel(0, &tr->TxPollCtr); /* Batch mode */ + tc_writel(TX_THRESHOLD, &tr->TxThrsh); + tc_writel(INT_EN_CMD, &tr->Int_En); + + /* set queues */ + tc_writel(fd_virt_to_bus(lp, lp->rfd_base), &tr->FDA_Bas); + tc_writel((unsigned long)lp->rfd_limit - (unsigned long)lp->rfd_base, + &tr->FDA_Lim); + /* + * Activation method: + * First, enable the MAC Transmitter and the DMA Receive circuits. + * Then enable the DMA Transmitter and the MAC Receive circuits. + */ + tc_writel(fd_virt_to_bus(lp, lp->fbl_ptr), &tr->BLFrmPtr); /* start DMA receiver */ + tc_writel(RX_CTL_CMD, &tr->Rx_Ctl); /* start MAC receiver */ + + /* start MAC transmitter */ + /* TX4939 does not have EnLCarr */ + if (lp->chiptype == TC35815_TX4939) + txctl &= ~Tx_EnLCarr; + /* WORKAROUND: ignore LostCrS in full duplex operation */ + if (!lp->phy_dev || !lp->link || lp->duplex == DUPLEX_FULL) + txctl &= ~Tx_EnLCarr; + tc_writel(txctl, &tr->Tx_Ctl); +} + +#ifdef CONFIG_PM +static int tc35815_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tc35815_local *lp = netdev_priv(dev); + unsigned long flags; + + pci_save_state(pdev); + if (!netif_running(dev)) + return 0; + netif_device_detach(dev); + if (lp->phy_dev) + phy_stop(lp->phy_dev); + spin_lock_irqsave(&lp->lock, flags); + tc35815_chip_reset(dev); + spin_unlock_irqrestore(&lp->lock, flags); + pci_set_power_state(pdev, PCI_D3hot); + return 0; +} + +static int tc35815_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tc35815_local *lp = netdev_priv(dev); + + pci_restore_state(pdev); + if (!netif_running(dev)) + return 0; + pci_set_power_state(pdev, PCI_D0); + tc35815_restart(dev); + netif_carrier_off(dev); + if (lp->phy_dev) + phy_start(lp->phy_dev); + netif_device_attach(dev); + return 0; +} +#endif /* CONFIG_PM */ + +static struct pci_driver tc35815_pci_driver = { + .name = MODNAME, + .id_table = tc35815_pci_tbl, + .probe = tc35815_init_one, + .remove = __devexit_p(tc35815_remove_one), +#ifdef CONFIG_PM + .suspend = tc35815_suspend, + .resume = tc35815_resume, +#endif +}; + +module_param_named(speed, options.speed, int, 0); +MODULE_PARM_DESC(speed, "0:auto, 10:10Mbps, 100:100Mbps"); +module_param_named(duplex, options.duplex, int, 0); +MODULE_PARM_DESC(duplex, "0:auto, 1:half, 2:full"); + +static int __init tc35815_init_module(void) +{ + return pci_register_driver(&tc35815_pci_driver); +} + +static void __exit tc35815_cleanup_module(void) +{ + pci_unregister_driver(&tc35815_pci_driver); +} + +module_init(tc35815_init_module); +module_exit(tc35815_cleanup_module); + +MODULE_DESCRIPTION("TOSHIBA TC35815 PCI 10M/100M Ethernet driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c deleted file mode 100644 index d82a82d9870c..000000000000 --- a/drivers/net/ps3_gelic_net.c +++ /dev/null @@ -1,1896 +0,0 @@ -/* - * PS3 gelic network driver. - * - * Copyright (C) 2007 Sony Computer Entertainment Inc. - * Copyright 2006, 2007 Sony Corporation - * - * This file is based on: spider_net.c - * - * (C) Copyright IBM Corp. 2005 - * - * Authors : Utz Bacher - * Jens Osterkamp - * - * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#undef DEBUG - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "ps3_gelic_net.h" -#include "ps3_gelic_wireless.h" - -#define DRV_NAME "Gelic Network Driver" -#define DRV_VERSION "2.0" - -MODULE_AUTHOR("SCE Inc."); -MODULE_DESCRIPTION("Gelic Network driver"); -MODULE_LICENSE("GPL"); - - -static inline void gelic_card_enable_rxdmac(struct gelic_card *card); -static inline void gelic_card_disable_rxdmac(struct gelic_card *card); -static inline void gelic_card_disable_txdmac(struct gelic_card *card); -static inline void gelic_card_reset_chain(struct gelic_card *card, - struct gelic_descr_chain *chain, - struct gelic_descr *start_descr); - -/* set irq_mask */ -int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask) -{ - int status; - - status = lv1_net_set_interrupt_mask(bus_id(card), dev_id(card), - mask, 0); - if (status) - dev_info(ctodev(card), - "%s failed %d\n", __func__, status); - return status; -} - -static inline void gelic_card_rx_irq_on(struct gelic_card *card) -{ - card->irq_mask |= GELIC_CARD_RXINT; - gelic_card_set_irq_mask(card, card->irq_mask); -} -static inline void gelic_card_rx_irq_off(struct gelic_card *card) -{ - card->irq_mask &= ~GELIC_CARD_RXINT; - gelic_card_set_irq_mask(card, card->irq_mask); -} - -static void gelic_card_get_ether_port_status(struct gelic_card *card, - int inform) -{ - u64 v2; - struct net_device *ether_netdev; - - lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_GET_ETH_PORT_STATUS, - GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0, - &card->ether_port_status, &v2); - - if (inform) { - ether_netdev = card->netdev[GELIC_PORT_ETHERNET_0]; - if (card->ether_port_status & GELIC_LV1_ETHER_LINK_UP) - netif_carrier_on(ether_netdev); - else - netif_carrier_off(ether_netdev); - } -} - -static int gelic_card_set_link_mode(struct gelic_card *card, int mode) -{ - int status; - u64 v1, v2; - - status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_SET_NEGOTIATION_MODE, - GELIC_LV1_PHY_ETHERNET_0, mode, 0, &v1, &v2); - if (status) { - pr_info("%s: failed setting negotiation mode %d\n", __func__, - status); - return -EBUSY; - } - - card->link_mode = mode; - return 0; -} - -void gelic_card_up(struct gelic_card *card) -{ - pr_debug("%s: called\n", __func__); - mutex_lock(&card->updown_lock); - if (atomic_inc_return(&card->users) == 1) { - pr_debug("%s: real do\n", __func__); - /* enable irq */ - gelic_card_set_irq_mask(card, card->irq_mask); - /* start rx */ - gelic_card_enable_rxdmac(card); - - napi_enable(&card->napi); - } - mutex_unlock(&card->updown_lock); - pr_debug("%s: done\n", __func__); -} - -void gelic_card_down(struct gelic_card *card) -{ - u64 mask; - pr_debug("%s: called\n", __func__); - mutex_lock(&card->updown_lock); - if (atomic_dec_if_positive(&card->users) == 0) { - pr_debug("%s: real do\n", __func__); - napi_disable(&card->napi); - /* - * Disable irq. Wireless interrupts will - * be disabled later if any - */ - mask = card->irq_mask & (GELIC_CARD_WLAN_EVENT_RECEIVED | - GELIC_CARD_WLAN_COMMAND_COMPLETED); - gelic_card_set_irq_mask(card, mask); - /* stop rx */ - gelic_card_disable_rxdmac(card); - gelic_card_reset_chain(card, &card->rx_chain, - card->descr + GELIC_NET_TX_DESCRIPTORS); - /* stop tx */ - gelic_card_disable_txdmac(card); - } - mutex_unlock(&card->updown_lock); - pr_debug("%s: done\n", __func__); -} - -/** - * gelic_descr_get_status -- returns the status of a descriptor - * @descr: descriptor to look at - * - * returns the status as in the dmac_cmd_status field of the descriptor - */ -static enum gelic_descr_dma_status -gelic_descr_get_status(struct gelic_descr *descr) -{ - return be32_to_cpu(descr->dmac_cmd_status) & GELIC_DESCR_DMA_STAT_MASK; -} - -/** - * gelic_descr_set_status -- sets the status of a descriptor - * @descr: descriptor to change - * @status: status to set in the descriptor - * - * changes the status to the specified value. Doesn't change other bits - * in the status - */ -static void gelic_descr_set_status(struct gelic_descr *descr, - enum gelic_descr_dma_status status) -{ - descr->dmac_cmd_status = cpu_to_be32(status | - (be32_to_cpu(descr->dmac_cmd_status) & - ~GELIC_DESCR_DMA_STAT_MASK)); - /* - * dma_cmd_status field is used to indicate whether the descriptor - * is valid or not. - * Usually caller of this function wants to inform that to the - * hardware, so we assure here the hardware sees the change. - */ - wmb(); -} - -/** - * gelic_card_free_chain - free descriptor chain - * @card: card structure - * @descr_in: address of desc - */ -static void gelic_card_free_chain(struct gelic_card *card, - struct gelic_descr *descr_in) -{ - struct gelic_descr *descr; - - for (descr = descr_in; descr && descr->bus_addr; descr = descr->next) { - dma_unmap_single(ctodev(card), descr->bus_addr, - GELIC_DESCR_SIZE, DMA_BIDIRECTIONAL); - descr->bus_addr = 0; - } -} - -/** - * gelic_card_init_chain - links descriptor chain - * @card: card structure - * @chain: address of chain - * @start_descr: address of descriptor array - * @no: number of descriptors - * - * we manage a circular list that mirrors the hardware structure, - * except that the hardware uses bus addresses. - * - * returns 0 on success, <0 on failure - */ -static int __devinit gelic_card_init_chain(struct gelic_card *card, - struct gelic_descr_chain *chain, - struct gelic_descr *start_descr, - int no) -{ - int i; - struct gelic_descr *descr; - - descr = start_descr; - memset(descr, 0, sizeof(*descr) * no); - - /* set up the hardware pointers in each descriptor */ - for (i = 0; i < no; i++, descr++) { - gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE); - descr->bus_addr = - dma_map_single(ctodev(card), descr, - GELIC_DESCR_SIZE, - DMA_BIDIRECTIONAL); - - if (!descr->bus_addr) - goto iommu_error; - - descr->next = descr + 1; - descr->prev = descr - 1; - } - /* make them as ring */ - (descr - 1)->next = start_descr; - start_descr->prev = (descr - 1); - - /* chain bus addr of hw descriptor */ - descr = start_descr; - for (i = 0; i < no; i++, descr++) { - descr->next_descr_addr = cpu_to_be32(descr->next->bus_addr); - } - - chain->head = start_descr; - chain->tail = start_descr; - - /* do not chain last hw descriptor */ - (descr - 1)->next_descr_addr = 0; - - return 0; - -iommu_error: - for (i--, descr--; 0 <= i; i--, descr--) - if (descr->bus_addr) - dma_unmap_single(ctodev(card), descr->bus_addr, - GELIC_DESCR_SIZE, - DMA_BIDIRECTIONAL); - return -ENOMEM; -} - -/** - * gelic_card_reset_chain - reset status of a descriptor chain - * @card: card structure - * @chain: address of chain - * @start_descr: address of descriptor array - * - * Reset the status of dma descriptors to ready state - * and re-initialize the hardware chain for later use - */ -static void gelic_card_reset_chain(struct gelic_card *card, - struct gelic_descr_chain *chain, - struct gelic_descr *start_descr) -{ - struct gelic_descr *descr; - - for (descr = start_descr; start_descr != descr->next; descr++) { - gelic_descr_set_status(descr, GELIC_DESCR_DMA_CARDOWNED); - descr->next_descr_addr = cpu_to_be32(descr->next->bus_addr); - } - - chain->head = start_descr; - chain->tail = (descr - 1); - - (descr - 1)->next_descr_addr = 0; -} -/** - * gelic_descr_prepare_rx - reinitializes a rx descriptor - * @card: card structure - * @descr: descriptor to re-init - * - * return 0 on success, <0 on failure - * - * allocates a new rx skb, iommu-maps it and attaches it to the descriptor. - * Activate the descriptor state-wise - */ -static int gelic_descr_prepare_rx(struct gelic_card *card, - struct gelic_descr *descr) -{ - int offset; - unsigned int bufsize; - - if (gelic_descr_get_status(descr) != GELIC_DESCR_DMA_NOT_IN_USE) - dev_info(ctodev(card), "%s: ERROR status\n", __func__); - /* we need to round up the buffer size to a multiple of 128 */ - bufsize = ALIGN(GELIC_NET_MAX_MTU, GELIC_NET_RXBUF_ALIGN); - - /* and we need to have it 128 byte aligned, therefore we allocate a - * bit more */ - descr->skb = dev_alloc_skb(bufsize + GELIC_NET_RXBUF_ALIGN - 1); - if (!descr->skb) { - descr->buf_addr = 0; /* tell DMAC don't touch memory */ - dev_info(ctodev(card), - "%s:allocate skb failed !!\n", __func__); - return -ENOMEM; - } - descr->buf_size = cpu_to_be32(bufsize); - descr->dmac_cmd_status = 0; - descr->result_size = 0; - descr->valid_size = 0; - descr->data_error = 0; - - offset = ((unsigned long)descr->skb->data) & - (GELIC_NET_RXBUF_ALIGN - 1); - if (offset) - skb_reserve(descr->skb, GELIC_NET_RXBUF_ALIGN - offset); - /* io-mmu-map the skb */ - descr->buf_addr = cpu_to_be32(dma_map_single(ctodev(card), - descr->skb->data, - GELIC_NET_MAX_MTU, - DMA_FROM_DEVICE)); - if (!descr->buf_addr) { - dev_kfree_skb_any(descr->skb); - descr->skb = NULL; - dev_info(ctodev(card), - "%s:Could not iommu-map rx buffer\n", __func__); - gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE); - return -ENOMEM; - } else { - gelic_descr_set_status(descr, GELIC_DESCR_DMA_CARDOWNED); - return 0; - } -} - -/** - * gelic_card_release_rx_chain - free all skb of rx descr - * @card: card structure - * - */ -static void gelic_card_release_rx_chain(struct gelic_card *card) -{ - struct gelic_descr *descr = card->rx_chain.head; - - do { - if (descr->skb) { - dma_unmap_single(ctodev(card), - be32_to_cpu(descr->buf_addr), - descr->skb->len, - DMA_FROM_DEVICE); - descr->buf_addr = 0; - dev_kfree_skb_any(descr->skb); - descr->skb = NULL; - gelic_descr_set_status(descr, - GELIC_DESCR_DMA_NOT_IN_USE); - } - descr = descr->next; - } while (descr != card->rx_chain.head); -} - -/** - * gelic_card_fill_rx_chain - fills descriptors/skbs in the rx chains - * @card: card structure - * - * fills all descriptors in the rx chain: allocates skbs - * and iommu-maps them. - * returns 0 on success, < 0 on failure - */ -static int gelic_card_fill_rx_chain(struct gelic_card *card) -{ - struct gelic_descr *descr = card->rx_chain.head; - int ret; - - do { - if (!descr->skb) { - ret = gelic_descr_prepare_rx(card, descr); - if (ret) - goto rewind; - } - descr = descr->next; - } while (descr != card->rx_chain.head); - - return 0; -rewind: - gelic_card_release_rx_chain(card); - return ret; -} - -/** - * gelic_card_alloc_rx_skbs - allocates rx skbs in rx descriptor chains - * @card: card structure - * - * returns 0 on success, < 0 on failure - */ -static int __devinit gelic_card_alloc_rx_skbs(struct gelic_card *card) -{ - struct gelic_descr_chain *chain; - int ret; - chain = &card->rx_chain; - ret = gelic_card_fill_rx_chain(card); - chain->tail = card->rx_top->prev; /* point to the last */ - return ret; -} - -/** - * gelic_descr_release_tx - processes a used tx descriptor - * @card: card structure - * @descr: descriptor to release - * - * releases a used tx descriptor (unmapping, freeing of skb) - */ -static void gelic_descr_release_tx(struct gelic_card *card, - struct gelic_descr *descr) -{ - struct sk_buff *skb = descr->skb; - - BUG_ON(!(be32_to_cpu(descr->data_status) & GELIC_DESCR_TX_TAIL)); - - dma_unmap_single(ctodev(card), be32_to_cpu(descr->buf_addr), skb->len, - DMA_TO_DEVICE); - dev_kfree_skb_any(skb); - - descr->buf_addr = 0; - descr->buf_size = 0; - descr->next_descr_addr = 0; - descr->result_size = 0; - descr->valid_size = 0; - descr->data_status = 0; - descr->data_error = 0; - descr->skb = NULL; - - /* set descr status */ - gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE); -} - -static void gelic_card_stop_queues(struct gelic_card *card) -{ - netif_stop_queue(card->netdev[GELIC_PORT_ETHERNET_0]); - - if (card->netdev[GELIC_PORT_WIRELESS]) - netif_stop_queue(card->netdev[GELIC_PORT_WIRELESS]); -} -static void gelic_card_wake_queues(struct gelic_card *card) -{ - netif_wake_queue(card->netdev[GELIC_PORT_ETHERNET_0]); - - if (card->netdev[GELIC_PORT_WIRELESS]) - netif_wake_queue(card->netdev[GELIC_PORT_WIRELESS]); -} -/** - * gelic_card_release_tx_chain - processes sent tx descriptors - * @card: adapter structure - * @stop: net_stop sequence - * - * releases the tx descriptors that gelic has finished with - */ -static void gelic_card_release_tx_chain(struct gelic_card *card, int stop) -{ - struct gelic_descr_chain *tx_chain; - enum gelic_descr_dma_status status; - struct net_device *netdev; - int release = 0; - - for (tx_chain = &card->tx_chain; - tx_chain->head != tx_chain->tail && tx_chain->tail; - tx_chain->tail = tx_chain->tail->next) { - status = gelic_descr_get_status(tx_chain->tail); - netdev = tx_chain->tail->skb->dev; - switch (status) { - case GELIC_DESCR_DMA_RESPONSE_ERROR: - case GELIC_DESCR_DMA_PROTECTION_ERROR: - case GELIC_DESCR_DMA_FORCE_END: - if (printk_ratelimit()) - dev_info(ctodev(card), - "%s: forcing end of tx descriptor " \ - "with status %x\n", - __func__, status); - netdev->stats.tx_dropped++; - break; - - case GELIC_DESCR_DMA_COMPLETE: - if (tx_chain->tail->skb) { - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += - tx_chain->tail->skb->len; - } - break; - - case GELIC_DESCR_DMA_CARDOWNED: - /* pending tx request */ - default: - /* any other value (== GELIC_DESCR_DMA_NOT_IN_USE) */ - if (!stop) - goto out; - } - gelic_descr_release_tx(card, tx_chain->tail); - release ++; - } -out: - if (!stop && release) - gelic_card_wake_queues(card); -} - -/** - * gelic_net_set_multi - sets multicast addresses and promisc flags - * @netdev: interface device structure - * - * gelic_net_set_multi configures multicast addresses as needed for the - * netdev interface. It also sets up multicast, allmulti and promisc - * flags appropriately - */ -void gelic_net_set_multi(struct net_device *netdev) -{ - struct gelic_card *card = netdev_card(netdev); - struct netdev_hw_addr *ha; - unsigned int i; - uint8_t *p; - u64 addr; - int status; - - /* clear all multicast address */ - status = lv1_net_remove_multicast_address(bus_id(card), dev_id(card), - 0, 1); - if (status) - dev_err(ctodev(card), - "lv1_net_remove_multicast_address failed %d\n", - status); - /* set broadcast address */ - status = lv1_net_add_multicast_address(bus_id(card), dev_id(card), - GELIC_NET_BROADCAST_ADDR, 0); - if (status) - dev_err(ctodev(card), - "lv1_net_add_multicast_address failed, %d\n", - status); - - if ((netdev->flags & IFF_ALLMULTI) || - (netdev_mc_count(netdev) > GELIC_NET_MC_COUNT_MAX)) { - status = lv1_net_add_multicast_address(bus_id(card), - dev_id(card), - 0, 1); - if (status) - dev_err(ctodev(card), - "lv1_net_add_multicast_address failed, %d\n", - status); - return; - } - - /* set multicast addresses */ - netdev_for_each_mc_addr(ha, netdev) { - addr = 0; - p = ha->addr; - for (i = 0; i < ETH_ALEN; i++) { - addr <<= 8; - addr |= *p++; - } - status = lv1_net_add_multicast_address(bus_id(card), - dev_id(card), - addr, 0); - if (status) - dev_err(ctodev(card), - "lv1_net_add_multicast_address failed, %d\n", - status); - } -} - -/** - * gelic_card_enable_rxdmac - enables the receive DMA controller - * @card: card structure - * - * gelic_card_enable_rxdmac enables the DMA controller by setting RX_DMA_EN - * in the GDADMACCNTR register - */ -static inline void gelic_card_enable_rxdmac(struct gelic_card *card) -{ - int status; - -#ifdef DEBUG - if (gelic_descr_get_status(card->rx_chain.head) != - GELIC_DESCR_DMA_CARDOWNED) { - printk(KERN_ERR "%s: status=%x\n", __func__, - be32_to_cpu(card->rx_chain.head->dmac_cmd_status)); - printk(KERN_ERR "%s: nextphy=%x\n", __func__, - be32_to_cpu(card->rx_chain.head->next_descr_addr)); - printk(KERN_ERR "%s: head=%p\n", __func__, - card->rx_chain.head); - } -#endif - status = lv1_net_start_rx_dma(bus_id(card), dev_id(card), - card->rx_chain.head->bus_addr, 0); - if (status) - dev_info(ctodev(card), - "lv1_net_start_rx_dma failed, status=%d\n", status); -} - -/** - * gelic_card_disable_rxdmac - disables the receive DMA controller - * @card: card structure - * - * gelic_card_disable_rxdmac terminates processing on the DMA controller by - * turing off DMA and issuing a force end - */ -static inline void gelic_card_disable_rxdmac(struct gelic_card *card) -{ - int status; - - /* this hvc blocks until the DMA in progress really stopped */ - status = lv1_net_stop_rx_dma(bus_id(card), dev_id(card), 0); - if (status) - dev_err(ctodev(card), - "lv1_net_stop_rx_dma failed, %d\n", status); -} - -/** - * gelic_card_disable_txdmac - disables the transmit DMA controller - * @card: card structure - * - * gelic_card_disable_txdmac terminates processing on the DMA controller by - * turing off DMA and issuing a force end - */ -static inline void gelic_card_disable_txdmac(struct gelic_card *card) -{ - int status; - - /* this hvc blocks until the DMA in progress really stopped */ - status = lv1_net_stop_tx_dma(bus_id(card), dev_id(card), 0); - if (status) - dev_err(ctodev(card), - "lv1_net_stop_tx_dma failed, status=%d\n", status); -} - -/** - * gelic_net_stop - called upon ifconfig down - * @netdev: interface device structure - * - * always returns 0 - */ -int gelic_net_stop(struct net_device *netdev) -{ - struct gelic_card *card; - - pr_debug("%s: start\n", __func__); - - netif_stop_queue(netdev); - netif_carrier_off(netdev); - - card = netdev_card(netdev); - gelic_card_down(card); - - pr_debug("%s: done\n", __func__); - return 0; -} - -/** - * gelic_card_get_next_tx_descr - returns the next available tx descriptor - * @card: device structure to get descriptor from - * - * returns the address of the next descriptor, or NULL if not available. - */ -static struct gelic_descr * -gelic_card_get_next_tx_descr(struct gelic_card *card) -{ - if (!card->tx_chain.head) - return NULL; - /* see if the next descriptor is free */ - if (card->tx_chain.tail != card->tx_chain.head->next && - gelic_descr_get_status(card->tx_chain.head) == - GELIC_DESCR_DMA_NOT_IN_USE) - return card->tx_chain.head; - else - return NULL; - -} - -/** - * gelic_net_set_txdescr_cmdstat - sets the tx descriptor command field - * @descr: descriptor structure to fill out - * @skb: packet to consider - * - * fills out the command and status field of the descriptor structure, - * depending on hardware checksum settings. This function assumes a wmb() - * has executed before. - */ -static void gelic_descr_set_tx_cmdstat(struct gelic_descr *descr, - struct sk_buff *skb) -{ - if (skb->ip_summed != CHECKSUM_PARTIAL) - descr->dmac_cmd_status = - cpu_to_be32(GELIC_DESCR_DMA_CMD_NO_CHKSUM | - GELIC_DESCR_TX_DMA_FRAME_TAIL); - else { - /* is packet ip? - * if yes: tcp? udp? */ - if (skb->protocol == htons(ETH_P_IP)) { - if (ip_hdr(skb)->protocol == IPPROTO_TCP) - descr->dmac_cmd_status = - cpu_to_be32(GELIC_DESCR_DMA_CMD_TCP_CHKSUM | - GELIC_DESCR_TX_DMA_FRAME_TAIL); - - else if (ip_hdr(skb)->protocol == IPPROTO_UDP) - descr->dmac_cmd_status = - cpu_to_be32(GELIC_DESCR_DMA_CMD_UDP_CHKSUM | - GELIC_DESCR_TX_DMA_FRAME_TAIL); - else /* - * the stack should checksum non-tcp and non-udp - * packets on his own: NETIF_F_IP_CSUM - */ - descr->dmac_cmd_status = - cpu_to_be32(GELIC_DESCR_DMA_CMD_NO_CHKSUM | - GELIC_DESCR_TX_DMA_FRAME_TAIL); - } - } -} - -static inline struct sk_buff *gelic_put_vlan_tag(struct sk_buff *skb, - unsigned short tag) -{ - struct vlan_ethhdr *veth; - static unsigned int c; - - if (skb_headroom(skb) < VLAN_HLEN) { - struct sk_buff *sk_tmp = skb; - pr_debug("%s: hd=%d c=%ud\n", __func__, skb_headroom(skb), c); - skb = skb_realloc_headroom(sk_tmp, VLAN_HLEN); - if (!skb) - return NULL; - dev_kfree_skb_any(sk_tmp); - } - veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN); - - /* Move the mac addresses to the top of buffer */ - memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN); - - veth->h_vlan_proto = cpu_to_be16(ETH_P_8021Q); - veth->h_vlan_TCI = htons(tag); - - return skb; -} - -/** - * gelic_descr_prepare_tx - setup a descriptor for sending packets - * @card: card structure - * @descr: descriptor structure - * @skb: packet to use - * - * returns 0 on success, <0 on failure. - * - */ -static int gelic_descr_prepare_tx(struct gelic_card *card, - struct gelic_descr *descr, - struct sk_buff *skb) -{ - dma_addr_t buf; - - if (card->vlan_required) { - struct sk_buff *skb_tmp; - enum gelic_port_type type; - - type = netdev_port(skb->dev)->type; - skb_tmp = gelic_put_vlan_tag(skb, - card->vlan[type].tx); - if (!skb_tmp) - return -ENOMEM; - skb = skb_tmp; - } - - buf = dma_map_single(ctodev(card), skb->data, skb->len, DMA_TO_DEVICE); - - if (!buf) { - dev_err(ctodev(card), - "dma map 2 failed (%p, %i). Dropping packet\n", - skb->data, skb->len); - return -ENOMEM; - } - - descr->buf_addr = cpu_to_be32(buf); - descr->buf_size = cpu_to_be32(skb->len); - descr->skb = skb; - descr->data_status = 0; - descr->next_descr_addr = 0; /* terminate hw descr */ - gelic_descr_set_tx_cmdstat(descr, skb); - - /* bump free descriptor pointer */ - card->tx_chain.head = descr->next; - return 0; -} - -/** - * gelic_card_kick_txdma - enables TX DMA processing - * @card: card structure - * @descr: descriptor address to enable TX processing at - * - */ -static int gelic_card_kick_txdma(struct gelic_card *card, - struct gelic_descr *descr) -{ - int status = 0; - - if (card->tx_dma_progress) - return 0; - - if (gelic_descr_get_status(descr) == GELIC_DESCR_DMA_CARDOWNED) { - card->tx_dma_progress = 1; - status = lv1_net_start_tx_dma(bus_id(card), dev_id(card), - descr->bus_addr, 0); - if (status) { - card->tx_dma_progress = 0; - dev_info(ctodev(card), "lv1_net_start_txdma failed," \ - "status=%d\n", status); - } - } - return status; -} - -/** - * gelic_net_xmit - transmits a frame over the device - * @skb: packet to send out - * @netdev: interface device structure - * - * returns 0 on success, <0 on failure - */ -int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev) -{ - struct gelic_card *card = netdev_card(netdev); - struct gelic_descr *descr; - int result; - unsigned long flags; - - spin_lock_irqsave(&card->tx_lock, flags); - - gelic_card_release_tx_chain(card, 0); - - descr = gelic_card_get_next_tx_descr(card); - if (!descr) { - /* - * no more descriptors free - */ - gelic_card_stop_queues(card); - spin_unlock_irqrestore(&card->tx_lock, flags); - return NETDEV_TX_BUSY; - } - - result = gelic_descr_prepare_tx(card, descr, skb); - if (result) { - /* - * DMA map failed. As chances are that failure - * would continue, just release skb and return - */ - netdev->stats.tx_dropped++; - dev_kfree_skb_any(skb); - spin_unlock_irqrestore(&card->tx_lock, flags); - return NETDEV_TX_OK; - } - /* - * link this prepared descriptor to previous one - * to achieve high performance - */ - descr->prev->next_descr_addr = cpu_to_be32(descr->bus_addr); - /* - * as hardware descriptor is modified in the above lines, - * ensure that the hardware sees it - */ - wmb(); - if (gelic_card_kick_txdma(card, descr)) { - /* - * kick failed. - * release descriptor which was just prepared - */ - netdev->stats.tx_dropped++; - /* don't trigger BUG_ON() in gelic_descr_release_tx */ - descr->data_status = cpu_to_be32(GELIC_DESCR_TX_TAIL); - gelic_descr_release_tx(card, descr); - /* reset head */ - card->tx_chain.head = descr; - /* reset hw termination */ - descr->prev->next_descr_addr = 0; - dev_info(ctodev(card), "%s: kick failure\n", __func__); - } - - spin_unlock_irqrestore(&card->tx_lock, flags); - return NETDEV_TX_OK; -} - -/** - * gelic_net_pass_skb_up - takes an skb from a descriptor and passes it on - * @descr: descriptor to process - * @card: card structure - * @netdev: net_device structure to be passed packet - * - * iommu-unmaps the skb, fills out skb structure and passes the data to the - * stack. The descriptor state is not changed. - */ -static void gelic_net_pass_skb_up(struct gelic_descr *descr, - struct gelic_card *card, - struct net_device *netdev) - -{ - struct sk_buff *skb = descr->skb; - u32 data_status, data_error; - - data_status = be32_to_cpu(descr->data_status); - data_error = be32_to_cpu(descr->data_error); - /* unmap skb buffer */ - dma_unmap_single(ctodev(card), be32_to_cpu(descr->buf_addr), - GELIC_NET_MAX_MTU, - DMA_FROM_DEVICE); - - skb_put(skb, be32_to_cpu(descr->valid_size)? - be32_to_cpu(descr->valid_size) : - be32_to_cpu(descr->result_size)); - if (!descr->valid_size) - dev_info(ctodev(card), "buffer full %x %x %x\n", - be32_to_cpu(descr->result_size), - be32_to_cpu(descr->buf_size), - be32_to_cpu(descr->dmac_cmd_status)); - - descr->skb = NULL; - /* - * the card put 2 bytes vlan tag in front - * of the ethernet frame - */ - skb_pull(skb, 2); - skb->protocol = eth_type_trans(skb, netdev); - - /* checksum offload */ - if (netdev->features & NETIF_F_RXCSUM) { - if ((data_status & GELIC_DESCR_DATA_STATUS_CHK_MASK) && - (!(data_error & GELIC_DESCR_DATA_ERROR_CHK_MASK))) - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb_checksum_none_assert(skb); - } else - skb_checksum_none_assert(skb); - - /* update netdevice statistics */ - netdev->stats.rx_packets++; - netdev->stats.rx_bytes += skb->len; - - /* pass skb up to stack */ - netif_receive_skb(skb); -} - -/** - * gelic_card_decode_one_descr - processes an rx descriptor - * @card: card structure - * - * returns 1 if a packet has been sent to the stack, otherwise 0 - * - * processes an rx descriptor by iommu-unmapping the data buffer and passing - * the packet up to the stack - */ -static int gelic_card_decode_one_descr(struct gelic_card *card) -{ - enum gelic_descr_dma_status status; - struct gelic_descr_chain *chain = &card->rx_chain; - struct gelic_descr *descr = chain->head; - struct net_device *netdev = NULL; - int dmac_chain_ended; - - status = gelic_descr_get_status(descr); - - if (status == GELIC_DESCR_DMA_CARDOWNED) - return 0; - - if (status == GELIC_DESCR_DMA_NOT_IN_USE) { - dev_dbg(ctodev(card), "dormant descr? %p\n", descr); - return 0; - } - - /* netdevice select */ - if (card->vlan_required) { - unsigned int i; - u16 vid; - vid = *(u16 *)(descr->skb->data) & VLAN_VID_MASK; - for (i = 0; i < GELIC_PORT_MAX; i++) { - if (card->vlan[i].rx == vid) { - netdev = card->netdev[i]; - break; - } - } - if (GELIC_PORT_MAX <= i) { - pr_info("%s: unknown packet vid=%x\n", __func__, vid); - goto refill; - } - } else - netdev = card->netdev[GELIC_PORT_ETHERNET_0]; - - if ((status == GELIC_DESCR_DMA_RESPONSE_ERROR) || - (status == GELIC_DESCR_DMA_PROTECTION_ERROR) || - (status == GELIC_DESCR_DMA_FORCE_END)) { - dev_info(ctodev(card), "dropping RX descriptor with state %x\n", - status); - netdev->stats.rx_dropped++; - goto refill; - } - - if (status == GELIC_DESCR_DMA_BUFFER_FULL) { - /* - * Buffer full would occur if and only if - * the frame length was longer than the size of this - * descriptor's buffer. If the frame length was equal - * to or shorter than buffer'size, FRAME_END condition - * would occur. - * Anyway this frame was longer than the MTU, - * just drop it. - */ - dev_info(ctodev(card), "overlength frame\n"); - goto refill; - } - /* - * descriptors any other than FRAME_END here should - * be treated as error. - */ - if (status != GELIC_DESCR_DMA_FRAME_END) { - dev_dbg(ctodev(card), "RX descriptor with state %x\n", - status); - goto refill; - } - - /* ok, we've got a packet in descr */ - gelic_net_pass_skb_up(descr, card, netdev); -refill: - - /* is the current descriptor terminated with next_descr == NULL? */ - dmac_chain_ended = - be32_to_cpu(descr->dmac_cmd_status) & - GELIC_DESCR_RX_DMA_CHAIN_END; - /* - * So that always DMAC can see the end - * of the descriptor chain to avoid - * from unwanted DMAC overrun. - */ - descr->next_descr_addr = 0; - - /* change the descriptor state: */ - gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE); - - /* - * this call can fail, but for now, just leave this - * decriptor without skb - */ - gelic_descr_prepare_rx(card, descr); - - chain->tail = descr; - chain->head = descr->next; - - /* - * Set this descriptor the end of the chain. - */ - descr->prev->next_descr_addr = cpu_to_be32(descr->bus_addr); - - /* - * If dmac chain was met, DMAC stopped. - * thus re-enable it - */ - - if (dmac_chain_ended) - gelic_card_enable_rxdmac(card); - - return 1; -} - -/** - * gelic_net_poll - NAPI poll function called by the stack to return packets - * @napi: napi structure - * @budget: number of packets we can pass to the stack at most - * - * returns the number of the processed packets - * - */ -static int gelic_net_poll(struct napi_struct *napi, int budget) -{ - struct gelic_card *card = container_of(napi, struct gelic_card, napi); - int packets_done = 0; - - while (packets_done < budget) { - if (!gelic_card_decode_one_descr(card)) - break; - - packets_done++; - } - - if (packets_done < budget) { - napi_complete(napi); - gelic_card_rx_irq_on(card); - } - return packets_done; -} -/** - * gelic_net_change_mtu - changes the MTU of an interface - * @netdev: interface device structure - * @new_mtu: new MTU value - * - * returns 0 on success, <0 on failure - */ -int gelic_net_change_mtu(struct net_device *netdev, int new_mtu) -{ - /* no need to re-alloc skbs or so -- the max mtu is about 2.3k - * and mtu is outbound only anyway */ - if ((new_mtu < GELIC_NET_MIN_MTU) || - (new_mtu > GELIC_NET_MAX_MTU)) { - return -EINVAL; - } - netdev->mtu = new_mtu; - return 0; -} - -/** - * gelic_card_interrupt - event handler for gelic_net - */ -static irqreturn_t gelic_card_interrupt(int irq, void *ptr) -{ - unsigned long flags; - struct gelic_card *card = ptr; - u64 status; - - status = card->irq_status; - - if (!status) - return IRQ_NONE; - - status &= card->irq_mask; - - if (status & GELIC_CARD_RXINT) { - gelic_card_rx_irq_off(card); - napi_schedule(&card->napi); - } - - if (status & GELIC_CARD_TXINT) { - spin_lock_irqsave(&card->tx_lock, flags); - card->tx_dma_progress = 0; - gelic_card_release_tx_chain(card, 0); - /* kick outstanding tx descriptor if any */ - gelic_card_kick_txdma(card, card->tx_chain.tail); - spin_unlock_irqrestore(&card->tx_lock, flags); - } - - /* ether port status changed */ - if (status & GELIC_CARD_PORT_STATUS_CHANGED) - gelic_card_get_ether_port_status(card, 1); - -#ifdef CONFIG_GELIC_WIRELESS - if (status & (GELIC_CARD_WLAN_EVENT_RECEIVED | - GELIC_CARD_WLAN_COMMAND_COMPLETED)) - gelic_wl_interrupt(card->netdev[GELIC_PORT_WIRELESS], status); -#endif - - return IRQ_HANDLED; -} - -#ifdef CONFIG_NET_POLL_CONTROLLER -/** - * gelic_net_poll_controller - artificial interrupt for netconsole etc. - * @netdev: interface device structure - * - * see Documentation/networking/netconsole.txt - */ -void gelic_net_poll_controller(struct net_device *netdev) -{ - struct gelic_card *card = netdev_card(netdev); - - gelic_card_set_irq_mask(card, 0); - gelic_card_interrupt(netdev->irq, netdev); - gelic_card_set_irq_mask(card, card->irq_mask); -} -#endif /* CONFIG_NET_POLL_CONTROLLER */ - -/** - * gelic_net_open - called upon ifconfig up - * @netdev: interface device structure - * - * returns 0 on success, <0 on failure - * - * gelic_net_open allocates all the descriptors and memory needed for - * operation, sets up multicast list and enables interrupts - */ -int gelic_net_open(struct net_device *netdev) -{ - struct gelic_card *card = netdev_card(netdev); - - dev_dbg(ctodev(card), " -> %s %p\n", __func__, netdev); - - gelic_card_up(card); - - netif_start_queue(netdev); - gelic_card_get_ether_port_status(card, 1); - - dev_dbg(ctodev(card), " <- %s\n", __func__); - return 0; -} - -void gelic_net_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *info) -{ - strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1); - strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1); -} - -static int gelic_ether_get_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) -{ - struct gelic_card *card = netdev_card(netdev); - - gelic_card_get_ether_port_status(card, 0); - - if (card->ether_port_status & GELIC_LV1_ETHER_FULL_DUPLEX) - cmd->duplex = DUPLEX_FULL; - else - cmd->duplex = DUPLEX_HALF; - - switch (card->ether_port_status & GELIC_LV1_ETHER_SPEED_MASK) { - case GELIC_LV1_ETHER_SPEED_10: - ethtool_cmd_speed_set(cmd, SPEED_10); - break; - case GELIC_LV1_ETHER_SPEED_100: - ethtool_cmd_speed_set(cmd, SPEED_100); - break; - case GELIC_LV1_ETHER_SPEED_1000: - ethtool_cmd_speed_set(cmd, SPEED_1000); - break; - default: - pr_info("%s: speed unknown\n", __func__); - ethtool_cmd_speed_set(cmd, SPEED_10); - break; - } - - cmd->supported = SUPPORTED_TP | SUPPORTED_Autoneg | - SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full; - cmd->advertising = cmd->supported; - if (card->link_mode & GELIC_LV1_ETHER_AUTO_NEG) { - cmd->autoneg = AUTONEG_ENABLE; - } else { - cmd->autoneg = AUTONEG_DISABLE; - cmd->advertising &= ~ADVERTISED_Autoneg; - } - cmd->port = PORT_TP; - - return 0; -} - -static int gelic_ether_set_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) -{ - struct gelic_card *card = netdev_card(netdev); - u64 mode; - int ret; - - if (cmd->autoneg == AUTONEG_ENABLE) { - mode = GELIC_LV1_ETHER_AUTO_NEG; - } else { - switch (cmd->speed) { - case SPEED_10: - mode = GELIC_LV1_ETHER_SPEED_10; - break; - case SPEED_100: - mode = GELIC_LV1_ETHER_SPEED_100; - break; - case SPEED_1000: - mode = GELIC_LV1_ETHER_SPEED_1000; - break; - default: - return -EINVAL; - } - if (cmd->duplex == DUPLEX_FULL) - mode |= GELIC_LV1_ETHER_FULL_DUPLEX; - else if (cmd->speed == SPEED_1000) { - pr_info("1000 half duplex is not supported.\n"); - return -EINVAL; - } - } - - ret = gelic_card_set_link_mode(card, mode); - - if (ret) - return ret; - - return 0; -} - -static void gelic_net_get_wol(struct net_device *netdev, - struct ethtool_wolinfo *wol) -{ - if (0 <= ps3_compare_firmware_version(2, 2, 0)) - wol->supported = WAKE_MAGIC; - else - wol->supported = 0; - - wol->wolopts = ps3_sys_manager_get_wol() ? wol->supported : 0; - memset(&wol->sopass, 0, sizeof(wol->sopass)); -} -static int gelic_net_set_wol(struct net_device *netdev, - struct ethtool_wolinfo *wol) -{ - int status; - struct gelic_card *card; - u64 v1, v2; - - if (ps3_compare_firmware_version(2, 2, 0) < 0 || - !capable(CAP_NET_ADMIN)) - return -EPERM; - - if (wol->wolopts & ~WAKE_MAGIC) - return -EINVAL; - - card = netdev_card(netdev); - if (wol->wolopts & WAKE_MAGIC) { - status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_SET_WOL, - GELIC_LV1_WOL_MAGIC_PACKET, - 0, GELIC_LV1_WOL_MP_ENABLE, - &v1, &v2); - if (status) { - pr_info("%s: enabling WOL failed %d\n", __func__, - status); - status = -EIO; - goto done; - } - status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_SET_WOL, - GELIC_LV1_WOL_ADD_MATCH_ADDR, - 0, GELIC_LV1_WOL_MATCH_ALL, - &v1, &v2); - if (!status) - ps3_sys_manager_set_wol(1); - else { - pr_info("%s: enabling WOL filter failed %d\n", - __func__, status); - status = -EIO; - } - } else { - status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_SET_WOL, - GELIC_LV1_WOL_MAGIC_PACKET, - 0, GELIC_LV1_WOL_MP_DISABLE, - &v1, &v2); - if (status) { - pr_info("%s: disabling WOL failed %d\n", __func__, - status); - status = -EIO; - goto done; - } - status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_SET_WOL, - GELIC_LV1_WOL_DELETE_MATCH_ADDR, - 0, GELIC_LV1_WOL_MATCH_ALL, - &v1, &v2); - if (!status) - ps3_sys_manager_set_wol(0); - else { - pr_info("%s: removing WOL filter failed %d\n", - __func__, status); - status = -EIO; - } - } -done: - return status; -} - -static const struct ethtool_ops gelic_ether_ethtool_ops = { - .get_drvinfo = gelic_net_get_drvinfo, - .get_settings = gelic_ether_get_settings, - .set_settings = gelic_ether_set_settings, - .get_link = ethtool_op_get_link, - .get_wol = gelic_net_get_wol, - .set_wol = gelic_net_set_wol, -}; - -/** - * gelic_net_tx_timeout_task - task scheduled by the watchdog timeout - * function (to be called not under interrupt status) - * @work: work is context of tx timout task - * - * called as task when tx hangs, resets interface (if interface is up) - */ -static void gelic_net_tx_timeout_task(struct work_struct *work) -{ - struct gelic_card *card = - container_of(work, struct gelic_card, tx_timeout_task); - struct net_device *netdev = card->netdev[GELIC_PORT_ETHERNET_0]; - - dev_info(ctodev(card), "%s:Timed out. Restarting...\n", __func__); - - if (!(netdev->flags & IFF_UP)) - goto out; - - netif_device_detach(netdev); - gelic_net_stop(netdev); - - gelic_net_open(netdev); - netif_device_attach(netdev); - -out: - atomic_dec(&card->tx_timeout_task_counter); -} - -/** - * gelic_net_tx_timeout - called when the tx timeout watchdog kicks in. - * @netdev: interface device structure - * - * called, if tx hangs. Schedules a task that resets the interface - */ -void gelic_net_tx_timeout(struct net_device *netdev) -{ - struct gelic_card *card; - - card = netdev_card(netdev); - atomic_inc(&card->tx_timeout_task_counter); - if (netdev->flags & IFF_UP) - schedule_work(&card->tx_timeout_task); - else - atomic_dec(&card->tx_timeout_task_counter); -} - -static const struct net_device_ops gelic_netdevice_ops = { - .ndo_open = gelic_net_open, - .ndo_stop = gelic_net_stop, - .ndo_start_xmit = gelic_net_xmit, - .ndo_set_multicast_list = gelic_net_set_multi, - .ndo_change_mtu = gelic_net_change_mtu, - .ndo_tx_timeout = gelic_net_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = gelic_net_poll_controller, -#endif -}; - -/** - * gelic_ether_setup_netdev_ops - initialization of net_device operations - * @netdev: net_device structure - * - * fills out function pointers in the net_device structure - */ -static void __devinit gelic_ether_setup_netdev_ops(struct net_device *netdev, - struct napi_struct *napi) -{ - netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT; - /* NAPI */ - netif_napi_add(netdev, napi, - gelic_net_poll, GELIC_NET_NAPI_WEIGHT); - netdev->ethtool_ops = &gelic_ether_ethtool_ops; - netdev->netdev_ops = &gelic_netdevice_ops; -} - -/** - * gelic_ether_setup_netdev - initialization of net_device - * @netdev: net_device structure - * @card: card structure - * - * Returns 0 on success or <0 on failure - * - * gelic_ether_setup_netdev initializes the net_device structure - * and register it. - **/ -int __devinit gelic_net_setup_netdev(struct net_device *netdev, - struct gelic_card *card) -{ - int status; - u64 v1, v2; - - netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM; - - netdev->features = NETIF_F_IP_CSUM; - if (GELIC_CARD_RX_CSUM_DEFAULT) - netdev->features |= NETIF_F_RXCSUM; - - status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_GET_MAC_ADDRESS, - 0, 0, 0, &v1, &v2); - v1 <<= 16; - if (status || !is_valid_ether_addr((u8 *)&v1)) { - dev_info(ctodev(card), - "%s:lv1_net_control GET_MAC_ADDR failed %d\n", - __func__, status); - return -EINVAL; - } - memcpy(netdev->dev_addr, &v1, ETH_ALEN); - - if (card->vlan_required) { - netdev->hard_header_len += VLAN_HLEN; - /* - * As vlan is internally used, - * we can not receive vlan packets - */ - netdev->features |= NETIF_F_VLAN_CHALLENGED; - } - - status = register_netdev(netdev); - if (status) { - dev_err(ctodev(card), "%s:Couldn't register %s %d\n", - __func__, netdev->name, status); - return status; - } - dev_info(ctodev(card), "%s: MAC addr %pM\n", - netdev->name, netdev->dev_addr); - - return 0; -} - -/** - * gelic_alloc_card_net - allocates net_device and card structure - * - * returns the card structure or NULL in case of errors - * - * the card and net_device structures are linked to each other - */ -#define GELIC_ALIGN (32) -static struct gelic_card * __devinit gelic_alloc_card_net(struct net_device **netdev) -{ - struct gelic_card *card; - struct gelic_port *port; - void *p; - size_t alloc_size; - /* - * gelic requires dma descriptor is 32 bytes aligned and - * the hypervisor requires irq_status is 8 bytes aligned. - */ - BUILD_BUG_ON(offsetof(struct gelic_card, irq_status) % 8); - BUILD_BUG_ON(offsetof(struct gelic_card, descr) % 32); - alloc_size = - sizeof(struct gelic_card) + - sizeof(struct gelic_descr) * GELIC_NET_RX_DESCRIPTORS + - sizeof(struct gelic_descr) * GELIC_NET_TX_DESCRIPTORS + - GELIC_ALIGN - 1; - - p = kzalloc(alloc_size, GFP_KERNEL); - if (!p) - return NULL; - card = PTR_ALIGN(p, GELIC_ALIGN); - card->unalign = p; - - /* - * alloc netdev - */ - *netdev = alloc_etherdev(sizeof(struct gelic_port)); - if (!netdev) { - kfree(card->unalign); - return NULL; - } - port = netdev_priv(*netdev); - - /* gelic_port */ - port->netdev = *netdev; - port->card = card; - port->type = GELIC_PORT_ETHERNET_0; - - /* gelic_card */ - card->netdev[GELIC_PORT_ETHERNET_0] = *netdev; - - INIT_WORK(&card->tx_timeout_task, gelic_net_tx_timeout_task); - init_waitqueue_head(&card->waitq); - atomic_set(&card->tx_timeout_task_counter, 0); - mutex_init(&card->updown_lock); - atomic_set(&card->users, 0); - - return card; -} - -static void __devinit gelic_card_get_vlan_info(struct gelic_card *card) -{ - u64 v1, v2; - int status; - unsigned int i; - struct { - int tx; - int rx; - } vlan_id_ix[2] = { - [GELIC_PORT_ETHERNET_0] = { - .tx = GELIC_LV1_VLAN_TX_ETHERNET_0, - .rx = GELIC_LV1_VLAN_RX_ETHERNET_0 - }, - [GELIC_PORT_WIRELESS] = { - .tx = GELIC_LV1_VLAN_TX_WIRELESS, - .rx = GELIC_LV1_VLAN_RX_WIRELESS - } - }; - - for (i = 0; i < ARRAY_SIZE(vlan_id_ix); i++) { - /* tx tag */ - status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_GET_VLAN_ID, - vlan_id_ix[i].tx, - 0, 0, &v1, &v2); - if (status || !v1) { - if (status != LV1_NO_ENTRY) - dev_dbg(ctodev(card), - "get vlan id for tx(%d) failed(%d)\n", - vlan_id_ix[i].tx, status); - card->vlan[i].tx = 0; - card->vlan[i].rx = 0; - continue; - } - card->vlan[i].tx = (u16)v1; - - /* rx tag */ - status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_GET_VLAN_ID, - vlan_id_ix[i].rx, - 0, 0, &v1, &v2); - if (status || !v1) { - if (status != LV1_NO_ENTRY) - dev_info(ctodev(card), - "get vlan id for rx(%d) failed(%d)\n", - vlan_id_ix[i].rx, status); - card->vlan[i].tx = 0; - card->vlan[i].rx = 0; - continue; - } - card->vlan[i].rx = (u16)v1; - - dev_dbg(ctodev(card), "vlan_id[%d] tx=%02x rx=%02x\n", - i, card->vlan[i].tx, card->vlan[i].rx); - } - - if (card->vlan[GELIC_PORT_ETHERNET_0].tx) { - BUG_ON(!card->vlan[GELIC_PORT_WIRELESS].tx); - card->vlan_required = 1; - } else - card->vlan_required = 0; - - /* check wirelss capable firmware */ - if (ps3_compare_firmware_version(1, 6, 0) < 0) { - card->vlan[GELIC_PORT_WIRELESS].tx = 0; - card->vlan[GELIC_PORT_WIRELESS].rx = 0; - } - - dev_info(ctodev(card), "internal vlan %s\n", - card->vlan_required? "enabled" : "disabled"); -} -/** - * ps3_gelic_driver_probe - add a device to the control of this driver - */ -static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev) -{ - struct gelic_card *card; - struct net_device *netdev; - int result; - - pr_debug("%s: called\n", __func__); - result = ps3_open_hv_device(dev); - - if (result) { - dev_dbg(&dev->core, "%s:ps3_open_hv_device failed\n", - __func__); - goto fail_open; - } - - result = ps3_dma_region_create(dev->d_region); - - if (result) { - dev_dbg(&dev->core, "%s:ps3_dma_region_create failed(%d)\n", - __func__, result); - BUG_ON("check region type"); - goto fail_dma_region; - } - - /* alloc card/netdevice */ - card = gelic_alloc_card_net(&netdev); - if (!card) { - dev_info(&dev->core, "%s:gelic_net_alloc_card failed\n", - __func__); - result = -ENOMEM; - goto fail_alloc_card; - } - ps3_system_bus_set_drvdata(dev, card); - card->dev = dev; - - /* get internal vlan info */ - gelic_card_get_vlan_info(card); - - card->link_mode = GELIC_LV1_ETHER_AUTO_NEG; - - /* setup interrupt */ - result = lv1_net_set_interrupt_status_indicator(bus_id(card), - dev_id(card), - ps3_mm_phys_to_lpar(__pa(&card->irq_status)), - 0); - - if (result) { - dev_dbg(&dev->core, - "%s:set_interrupt_status_indicator failed: %s\n", - __func__, ps3_result(result)); - result = -EIO; - goto fail_status_indicator; - } - - result = ps3_sb_event_receive_port_setup(dev, PS3_BINDING_CPU_ANY, - &card->irq); - - if (result) { - dev_info(ctodev(card), - "%s:gelic_net_open_device failed (%d)\n", - __func__, result); - result = -EPERM; - goto fail_alloc_irq; - } - result = request_irq(card->irq, gelic_card_interrupt, - IRQF_DISABLED, netdev->name, card); - - if (result) { - dev_info(ctodev(card), "%s:request_irq failed (%d)\n", - __func__, result); - goto fail_request_irq; - } - - /* setup card structure */ - card->irq_mask = GELIC_CARD_RXINT | GELIC_CARD_TXINT | - GELIC_CARD_PORT_STATUS_CHANGED; - - - if (gelic_card_init_chain(card, &card->tx_chain, - card->descr, GELIC_NET_TX_DESCRIPTORS)) - goto fail_alloc_tx; - if (gelic_card_init_chain(card, &card->rx_chain, - card->descr + GELIC_NET_TX_DESCRIPTORS, - GELIC_NET_RX_DESCRIPTORS)) - goto fail_alloc_rx; - - /* head of chain */ - card->tx_top = card->tx_chain.head; - card->rx_top = card->rx_chain.head; - dev_dbg(ctodev(card), "descr rx %p, tx %p, size %#lx, num %#x\n", - card->rx_top, card->tx_top, sizeof(struct gelic_descr), - GELIC_NET_RX_DESCRIPTORS); - /* allocate rx skbs */ - if (gelic_card_alloc_rx_skbs(card)) - goto fail_alloc_skbs; - - spin_lock_init(&card->tx_lock); - card->tx_dma_progress = 0; - - /* setup net_device structure */ - netdev->irq = card->irq; - SET_NETDEV_DEV(netdev, &card->dev->core); - gelic_ether_setup_netdev_ops(netdev, &card->napi); - result = gelic_net_setup_netdev(netdev, card); - if (result) { - dev_dbg(&dev->core, "%s: setup_netdev failed %d", - __func__, result); - goto fail_setup_netdev; - } - -#ifdef CONFIG_GELIC_WIRELESS - if (gelic_wl_driver_probe(card)) { - dev_dbg(&dev->core, "%s: WL init failed\n", __func__); - goto fail_setup_netdev; - } -#endif - pr_debug("%s: done\n", __func__); - return 0; - -fail_setup_netdev: -fail_alloc_skbs: - gelic_card_free_chain(card, card->rx_chain.head); -fail_alloc_rx: - gelic_card_free_chain(card, card->tx_chain.head); -fail_alloc_tx: - free_irq(card->irq, card); - netdev->irq = NO_IRQ; -fail_request_irq: - ps3_sb_event_receive_port_destroy(dev, card->irq); -fail_alloc_irq: - lv1_net_set_interrupt_status_indicator(bus_id(card), - bus_id(card), - 0, 0); -fail_status_indicator: - ps3_system_bus_set_drvdata(dev, NULL); - kfree(netdev_card(netdev)->unalign); - free_netdev(netdev); -fail_alloc_card: - ps3_dma_region_free(dev->d_region); -fail_dma_region: - ps3_close_hv_device(dev); -fail_open: - return result; -} - -/** - * ps3_gelic_driver_remove - remove a device from the control of this driver - */ - -static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev) -{ - struct gelic_card *card = ps3_system_bus_get_drvdata(dev); - struct net_device *netdev0; - pr_debug("%s: called\n", __func__); - - /* set auto-negotiation */ - gelic_card_set_link_mode(card, GELIC_LV1_ETHER_AUTO_NEG); - -#ifdef CONFIG_GELIC_WIRELESS - gelic_wl_driver_remove(card); -#endif - /* stop interrupt */ - gelic_card_set_irq_mask(card, 0); - - /* turn off DMA, force end */ - gelic_card_disable_rxdmac(card); - gelic_card_disable_txdmac(card); - - /* release chains */ - gelic_card_release_tx_chain(card, 1); - gelic_card_release_rx_chain(card); - - gelic_card_free_chain(card, card->tx_top); - gelic_card_free_chain(card, card->rx_top); - - netdev0 = card->netdev[GELIC_PORT_ETHERNET_0]; - /* disconnect event port */ - free_irq(card->irq, card); - netdev0->irq = NO_IRQ; - ps3_sb_event_receive_port_destroy(card->dev, card->irq); - - wait_event(card->waitq, - atomic_read(&card->tx_timeout_task_counter) == 0); - - lv1_net_set_interrupt_status_indicator(bus_id(card), dev_id(card), - 0 , 0); - - unregister_netdev(netdev0); - kfree(netdev_card(netdev0)->unalign); - free_netdev(netdev0); - - ps3_system_bus_set_drvdata(dev, NULL); - - ps3_dma_region_free(dev->d_region); - - ps3_close_hv_device(dev); - - pr_debug("%s: done\n", __func__); - return 0; -} - -static struct ps3_system_bus_driver ps3_gelic_driver = { - .match_id = PS3_MATCH_ID_GELIC, - .probe = ps3_gelic_driver_probe, - .remove = ps3_gelic_driver_remove, - .shutdown = ps3_gelic_driver_remove, - .core.name = "ps3_gelic_driver", - .core.owner = THIS_MODULE, -}; - -static int __init ps3_gelic_driver_init (void) -{ - return firmware_has_feature(FW_FEATURE_PS3_LV1) - ? ps3_system_bus_driver_register(&ps3_gelic_driver) - : -ENODEV; -} - -static void __exit ps3_gelic_driver_exit (void) -{ - ps3_system_bus_driver_unregister(&ps3_gelic_driver); -} - -module_init(ps3_gelic_driver_init); -module_exit(ps3_gelic_driver_exit); - -MODULE_ALIAS(PS3_MODULE_ALIAS_GELIC); - diff --git a/drivers/net/ps3_gelic_net.h b/drivers/net/ps3_gelic_net.h deleted file mode 100644 index d3fadfbc3bcc..000000000000 --- a/drivers/net/ps3_gelic_net.h +++ /dev/null @@ -1,380 +0,0 @@ -/* - * PS3 Platfom gelic network driver. - * - * Copyright (C) 2007 Sony Computer Entertainment Inc. - * Copyright 2006, 2007 Sony Corporation. - * - * This file is based on: spider_net.h - * - * (C) Copyright IBM Corp. 2005 - * - * Authors : Utz Bacher - * Jens Osterkamp - * - * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#ifndef _GELIC_NET_H -#define _GELIC_NET_H - -/* descriptors */ -#define GELIC_NET_RX_DESCRIPTORS 128 /* num of descriptors */ -#define GELIC_NET_TX_DESCRIPTORS 128 /* num of descriptors */ - -#define GELIC_NET_MAX_MTU VLAN_ETH_FRAME_LEN -#define GELIC_NET_MIN_MTU VLAN_ETH_ZLEN -#define GELIC_NET_RXBUF_ALIGN 128 -#define GELIC_CARD_RX_CSUM_DEFAULT 1 /* hw chksum */ -#define GELIC_NET_WATCHDOG_TIMEOUT 5*HZ -#define GELIC_NET_NAPI_WEIGHT (GELIC_NET_RX_DESCRIPTORS) -#define GELIC_NET_BROADCAST_ADDR 0xffffffffffffL - -#define GELIC_NET_MC_COUNT_MAX 32 /* multicast address list */ - -/* virtual interrupt status register bits */ - /* INT1 */ -#define GELIC_CARD_TX_RAM_FULL_ERR 0x0000000000000001L -#define GELIC_CARD_RX_RAM_FULL_ERR 0x0000000000000002L -#define GELIC_CARD_TX_SHORT_FRAME_ERR 0x0000000000000004L -#define GELIC_CARD_TX_INVALID_DESCR_ERR 0x0000000000000008L -#define GELIC_CARD_RX_FIFO_FULL_ERR 0x0000000000002000L -#define GELIC_CARD_RX_DESCR_CHAIN_END 0x0000000000004000L -#define GELIC_CARD_RX_INVALID_DESCR_ERR 0x0000000000008000L -#define GELIC_CARD_TX_RESPONCE_ERR 0x0000000000010000L -#define GELIC_CARD_RX_RESPONCE_ERR 0x0000000000100000L -#define GELIC_CARD_TX_PROTECTION_ERR 0x0000000000400000L -#define GELIC_CARD_RX_PROTECTION_ERR 0x0000000004000000L -#define GELIC_CARD_TX_TCP_UDP_CHECKSUM_ERR 0x0000000008000000L -#define GELIC_CARD_PORT_STATUS_CHANGED 0x0000000020000000L -#define GELIC_CARD_WLAN_EVENT_RECEIVED 0x0000000040000000L -#define GELIC_CARD_WLAN_COMMAND_COMPLETED 0x0000000080000000L - /* INT 0 */ -#define GELIC_CARD_TX_FLAGGED_DESCR 0x0004000000000000L -#define GELIC_CARD_RX_FLAGGED_DESCR 0x0040000000000000L -#define GELIC_CARD_TX_TRANSFER_END 0x0080000000000000L -#define GELIC_CARD_TX_DESCR_CHAIN_END 0x0100000000000000L -#define GELIC_CARD_NUMBER_OF_RX_FRAME 0x1000000000000000L -#define GELIC_CARD_ONE_TIME_COUNT_TIMER 0x4000000000000000L -#define GELIC_CARD_FREE_RUN_COUNT_TIMER 0x8000000000000000L - -/* initial interrupt mask */ -#define GELIC_CARD_TXINT GELIC_CARD_TX_DESCR_CHAIN_END - -#define GELIC_CARD_RXINT (GELIC_CARD_RX_DESCR_CHAIN_END | \ - GELIC_CARD_NUMBER_OF_RX_FRAME) - - /* RX descriptor data_status bits */ -enum gelic_descr_rx_status { - GELIC_DESCR_RXDMADU = 0x80000000, /* destination MAC addr unknown */ - GELIC_DESCR_RXLSTFBF = 0x40000000, /* last frame buffer */ - GELIC_DESCR_RXIPCHK = 0x20000000, /* IP checksum performed */ - GELIC_DESCR_RXTCPCHK = 0x10000000, /* TCP/UDP checksup performed */ - GELIC_DESCR_RXWTPKT = 0x00C00000, /* - * wakeup trigger packet - * 01: Magic Packet (TM) - * 10: ARP packet - * 11: Multicast MAC addr - */ - GELIC_DESCR_RXVLNPKT = 0x00200000, /* VLAN packet */ - /* bit 20..16 reserved */ - GELIC_DESCR_RXRRECNUM = 0x0000ff00, /* reception receipt number */ - /* bit 7..0 reserved */ -}; - -#define GELIC_DESCR_DATA_STATUS_CHK_MASK \ - (GELIC_DESCR_RXIPCHK | GELIC_DESCR_RXTCPCHK) - - /* TX descriptor data_status bits */ -enum gelic_descr_tx_status { - GELIC_DESCR_TX_TAIL = 0x00000001, /* gelic treated this - * descriptor was end of - * a tx frame - */ -}; - -/* RX descriptor data error bits */ -enum gelic_descr_rx_error { - /* bit 31 reserved */ - GELIC_DESCR_RXALNERR = 0x40000000, /* alignement error 10/100M */ - GELIC_DESCR_RXOVERERR = 0x20000000, /* oversize error */ - GELIC_DESCR_RXRNTERR = 0x10000000, /* Runt error */ - GELIC_DESCR_RXIPCHKERR = 0x08000000, /* IP checksum error */ - GELIC_DESCR_RXTCPCHKERR = 0x04000000, /* TCP/UDP checksum error */ - GELIC_DESCR_RXDRPPKT = 0x00100000, /* drop packet */ - GELIC_DESCR_RXIPFMTERR = 0x00080000, /* IP packet format error */ - /* bit 18 reserved */ - GELIC_DESCR_RXDATAERR = 0x00020000, /* IP packet format error */ - GELIC_DESCR_RXCALERR = 0x00010000, /* cariier extension length - * error */ - GELIC_DESCR_RXCREXERR = 0x00008000, /* carrier extension error */ - GELIC_DESCR_RXMLTCST = 0x00004000, /* multicast address frame */ - /* bit 13..0 reserved */ -}; -#define GELIC_DESCR_DATA_ERROR_CHK_MASK \ - (GELIC_DESCR_RXIPCHKERR | GELIC_DESCR_RXTCPCHKERR) - -/* DMA command and status (RX and TX)*/ -enum gelic_descr_dma_status { - GELIC_DESCR_DMA_COMPLETE = 0x00000000, /* used in tx */ - GELIC_DESCR_DMA_BUFFER_FULL = 0x00000000, /* used in rx */ - GELIC_DESCR_DMA_RESPONSE_ERROR = 0x10000000, /* used in rx, tx */ - GELIC_DESCR_DMA_PROTECTION_ERROR = 0x20000000, /* used in rx, tx */ - GELIC_DESCR_DMA_FRAME_END = 0x40000000, /* used in rx */ - GELIC_DESCR_DMA_FORCE_END = 0x50000000, /* used in rx, tx */ - GELIC_DESCR_DMA_CARDOWNED = 0xa0000000, /* used in rx, tx */ - GELIC_DESCR_DMA_NOT_IN_USE = 0xb0000000, /* any other value */ -}; - -#define GELIC_DESCR_DMA_STAT_MASK (0xf0000000) - -/* tx descriptor command and status */ -enum gelic_descr_tx_dma_status { - /* [19] */ - GELIC_DESCR_TX_DMA_IKE = 0x00080000, /* IPSEC off */ - /* [18] */ - GELIC_DESCR_TX_DMA_FRAME_TAIL = 0x00040000, /* last descriptor of - * the packet - */ - /* [17..16] */ - GELIC_DESCR_TX_DMA_TCP_CHKSUM = 0x00020000, /* TCP packet */ - GELIC_DESCR_TX_DMA_UDP_CHKSUM = 0x00030000, /* UDP packet */ - GELIC_DESCR_TX_DMA_NO_CHKSUM = 0x00000000, /* no checksum */ - - /* [1] */ - GELIC_DESCR_TX_DMA_CHAIN_END = 0x00000002, /* DMA terminated - * due to chain end - */ -}; - -#define GELIC_DESCR_DMA_CMD_NO_CHKSUM \ - (GELIC_DESCR_DMA_CARDOWNED | GELIC_DESCR_TX_DMA_IKE | \ - GELIC_DESCR_TX_DMA_NO_CHKSUM) - -#define GELIC_DESCR_DMA_CMD_TCP_CHKSUM \ - (GELIC_DESCR_DMA_CARDOWNED | GELIC_DESCR_TX_DMA_IKE | \ - GELIC_DESCR_TX_DMA_TCP_CHKSUM) - -#define GELIC_DESCR_DMA_CMD_UDP_CHKSUM \ - (GELIC_DESCR_DMA_CARDOWNED | GELIC_DESCR_TX_DMA_IKE | \ - GELIC_DESCR_TX_DMA_UDP_CHKSUM) - -enum gelic_descr_rx_dma_status { - /* [ 1 ] */ - GELIC_DESCR_RX_DMA_CHAIN_END = 0x00000002, /* DMA terminated - * due to chain end - */ -}; - -/* for lv1_net_control */ -enum gelic_lv1_net_control_code { - GELIC_LV1_GET_MAC_ADDRESS = 1, - GELIC_LV1_GET_ETH_PORT_STATUS = 2, - GELIC_LV1_SET_NEGOTIATION_MODE = 3, - GELIC_LV1_GET_VLAN_ID = 4, - GELIC_LV1_SET_WOL = 5, - GELIC_LV1_GET_CHANNEL = 6, - GELIC_LV1_POST_WLAN_CMD = 9, - GELIC_LV1_GET_WLAN_CMD_RESULT = 10, - GELIC_LV1_GET_WLAN_EVENT = 11, -}; - -/* for GELIC_LV1_SET_WOL */ -enum gelic_lv1_wol_command { - GELIC_LV1_WOL_MAGIC_PACKET = 1, - GELIC_LV1_WOL_ADD_MATCH_ADDR = 6, - GELIC_LV1_WOL_DELETE_MATCH_ADDR = 7, -}; - -/* for GELIC_LV1_WOL_MAGIC_PACKET */ -enum gelic_lv1_wol_mp_arg { - GELIC_LV1_WOL_MP_DISABLE = 0, - GELIC_LV1_WOL_MP_ENABLE = 1, -}; - -/* for GELIC_LV1_WOL_{ADD,DELETE}_MATCH_ADDR */ -enum gelic_lv1_wol_match_arg { - GELIC_LV1_WOL_MATCH_INDIVIDUAL = 0, - GELIC_LV1_WOL_MATCH_ALL = 1, -}; - -/* status returened from GET_ETH_PORT_STATUS */ -enum gelic_lv1_ether_port_status { - GELIC_LV1_ETHER_LINK_UP = 0x0000000000000001L, - GELIC_LV1_ETHER_FULL_DUPLEX = 0x0000000000000002L, - GELIC_LV1_ETHER_AUTO_NEG = 0x0000000000000004L, - - GELIC_LV1_ETHER_SPEED_10 = 0x0000000000000010L, - GELIC_LV1_ETHER_SPEED_100 = 0x0000000000000020L, - GELIC_LV1_ETHER_SPEED_1000 = 0x0000000000000040L, - GELIC_LV1_ETHER_SPEED_MASK = 0x0000000000000070L, -}; - -enum gelic_lv1_vlan_index { - /* for outgoing packets */ - GELIC_LV1_VLAN_TX_ETHERNET_0 = 0x0000000000000002L, - GELIC_LV1_VLAN_TX_WIRELESS = 0x0000000000000003L, - - /* for incoming packets */ - GELIC_LV1_VLAN_RX_ETHERNET_0 = 0x0000000000000012L, - GELIC_LV1_VLAN_RX_WIRELESS = 0x0000000000000013L, -}; - -enum gelic_lv1_phy { - GELIC_LV1_PHY_ETHERNET_0 = 0x0000000000000002L, -}; - -/* size of hardware part of gelic descriptor */ -#define GELIC_DESCR_SIZE (32) - -enum gelic_port_type { - GELIC_PORT_ETHERNET_0 = 0, - GELIC_PORT_WIRELESS = 1, - GELIC_PORT_MAX -}; - -struct gelic_descr { - /* as defined by the hardware */ - __be32 buf_addr; - __be32 buf_size; - __be32 next_descr_addr; - __be32 dmac_cmd_status; - __be32 result_size; - __be32 valid_size; /* all zeroes for tx */ - __be32 data_status; - __be32 data_error; /* all zeroes for tx */ - - /* used in the driver */ - struct sk_buff *skb; - dma_addr_t bus_addr; - struct gelic_descr *next; - struct gelic_descr *prev; -} __attribute__((aligned(32))); - -struct gelic_descr_chain { - /* we walk from tail to head */ - struct gelic_descr *head; - struct gelic_descr *tail; -}; - -struct gelic_vlan_id { - u16 tx; - u16 rx; -}; - -struct gelic_card { - struct napi_struct napi; - struct net_device *netdev[GELIC_PORT_MAX]; - /* - * hypervisor requires irq_status should be - * 8 bytes aligned, but u64 member is - * always disposed in that manner - */ - u64 irq_status; - u64 irq_mask; - - struct ps3_system_bus_device *dev; - struct gelic_vlan_id vlan[GELIC_PORT_MAX]; - int vlan_required; - - struct gelic_descr_chain tx_chain; - struct gelic_descr_chain rx_chain; - /* - * tx_lock guards tx descriptor list and - * tx_dma_progress. - */ - spinlock_t tx_lock; - int tx_dma_progress; - - struct work_struct tx_timeout_task; - atomic_t tx_timeout_task_counter; - wait_queue_head_t waitq; - - /* only first user should up the card */ - struct mutex updown_lock; - atomic_t users; - - u64 ether_port_status; - int link_mode; - - /* original address returned by kzalloc */ - void *unalign; - - /* - * each netdevice has copy of irq - */ - unsigned int irq; - struct gelic_descr *tx_top, *rx_top; - struct gelic_descr descr[0]; /* must be the last */ -}; - -struct gelic_port { - struct gelic_card *card; - struct net_device *netdev; - enum gelic_port_type type; - long priv[0]; /* long for alignment */ -}; - -static inline struct gelic_card *port_to_card(struct gelic_port *p) -{ - return p->card; -} -static inline struct net_device *port_to_netdev(struct gelic_port *p) -{ - return p->netdev; -} -static inline struct gelic_card *netdev_card(struct net_device *d) -{ - return ((struct gelic_port *)netdev_priv(d))->card; -} -static inline struct gelic_port *netdev_port(struct net_device *d) -{ - return (struct gelic_port *)netdev_priv(d); -} -static inline struct device *ctodev(struct gelic_card *card) -{ - return &card->dev->core; -} -static inline u64 bus_id(struct gelic_card *card) -{ - return card->dev->bus_id; -} -static inline u64 dev_id(struct gelic_card *card) -{ - return card->dev->dev_id; -} - -static inline void *port_priv(struct gelic_port *port) -{ - return port->priv; -} - -extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask); -/* shared netdev ops */ -extern void gelic_card_up(struct gelic_card *card); -extern void gelic_card_down(struct gelic_card *card); -extern int gelic_net_open(struct net_device *netdev); -extern int gelic_net_stop(struct net_device *netdev); -extern int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev); -extern void gelic_net_set_multi(struct net_device *netdev); -extern void gelic_net_tx_timeout(struct net_device *netdev); -extern int gelic_net_change_mtu(struct net_device *netdev, int new_mtu); -extern int gelic_net_setup_netdev(struct net_device *netdev, - struct gelic_card *card); - -/* shared ethtool ops */ -extern void gelic_net_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *info); -extern void gelic_net_poll_controller(struct net_device *netdev); - -#endif /* _GELIC_NET_H */ diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ps3_gelic_wireless.c deleted file mode 100644 index 2e62938c0f82..000000000000 --- a/drivers/net/ps3_gelic_wireless.c +++ /dev/null @@ -1,2681 +0,0 @@ -/* - * PS3 gelic network driver. - * - * Copyright (C) 2007 Sony Computer Entertainment Inc. - * Copyright 2007 Sony Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#undef DEBUG - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "ps3_gelic_net.h" -#include "ps3_gelic_wireless.h" - - -static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan, - u8 *essid, size_t essid_len); -static int gelic_wl_try_associate(struct net_device *netdev); - -/* - * tables - */ - -/* 802.11b/g channel to freq in MHz */ -static const int channel_freq[] = { - 2412, 2417, 2422, 2427, 2432, - 2437, 2442, 2447, 2452, 2457, - 2462, 2467, 2472, 2484 -}; -#define NUM_CHANNELS ARRAY_SIZE(channel_freq) - -/* in bps */ -static const int bitrate_list[] = { - 1000000, - 2000000, - 5500000, - 11000000, - 6000000, - 9000000, - 12000000, - 18000000, - 24000000, - 36000000, - 48000000, - 54000000 -}; -#define NUM_BITRATES ARRAY_SIZE(bitrate_list) - -/* - * wpa2 support requires the hypervisor version 2.0 or later - */ -static inline int wpa2_capable(void) -{ - return 0 <= ps3_compare_firmware_version(2, 0, 0); -} - -static inline int precise_ie(void) -{ - return 0 <= ps3_compare_firmware_version(2, 2, 0); -} -/* - * post_eurus_cmd helpers - */ -struct eurus_cmd_arg_info { - int pre_arg; /* command requires arg1, arg2 at POST COMMAND */ - int post_arg; /* command requires arg1, arg2 at GET_RESULT */ -}; - -static const struct eurus_cmd_arg_info cmd_info[GELIC_EURUS_CMD_MAX_INDEX] = { - [GELIC_EURUS_CMD_SET_COMMON_CFG] = { .pre_arg = 1}, - [GELIC_EURUS_CMD_SET_WEP_CFG] = { .pre_arg = 1}, - [GELIC_EURUS_CMD_SET_WPA_CFG] = { .pre_arg = 1}, - [GELIC_EURUS_CMD_GET_COMMON_CFG] = { .post_arg = 1}, - [GELIC_EURUS_CMD_GET_WEP_CFG] = { .post_arg = 1}, - [GELIC_EURUS_CMD_GET_WPA_CFG] = { .post_arg = 1}, - [GELIC_EURUS_CMD_GET_RSSI_CFG] = { .post_arg = 1}, - [GELIC_EURUS_CMD_START_SCAN] = { .pre_arg = 1}, - [GELIC_EURUS_CMD_GET_SCAN] = { .post_arg = 1}, -}; - -#ifdef DEBUG -static const char *cmdstr(enum gelic_eurus_command ix) -{ - switch (ix) { - case GELIC_EURUS_CMD_ASSOC: - return "ASSOC"; - case GELIC_EURUS_CMD_DISASSOC: - return "DISASSOC"; - case GELIC_EURUS_CMD_START_SCAN: - return "SCAN"; - case GELIC_EURUS_CMD_GET_SCAN: - return "GET SCAN"; - case GELIC_EURUS_CMD_SET_COMMON_CFG: - return "SET_COMMON_CFG"; - case GELIC_EURUS_CMD_GET_COMMON_CFG: - return "GET_COMMON_CFG"; - case GELIC_EURUS_CMD_SET_WEP_CFG: - return "SET_WEP_CFG"; - case GELIC_EURUS_CMD_GET_WEP_CFG: - return "GET_WEP_CFG"; - case GELIC_EURUS_CMD_SET_WPA_CFG: - return "SET_WPA_CFG"; - case GELIC_EURUS_CMD_GET_WPA_CFG: - return "GET_WPA_CFG"; - case GELIC_EURUS_CMD_GET_RSSI_CFG: - return "GET_RSSI"; - default: - break; - } - return ""; -}; -#else -static inline const char *cmdstr(enum gelic_eurus_command ix) -{ - return ""; -} -#endif - -/* synchronously do eurus commands */ -static void gelic_eurus_sync_cmd_worker(struct work_struct *work) -{ - struct gelic_eurus_cmd *cmd; - struct gelic_card *card; - struct gelic_wl_info *wl; - - u64 arg1, arg2; - - pr_debug("%s: <-\n", __func__); - cmd = container_of(work, struct gelic_eurus_cmd, work); - BUG_ON(cmd_info[cmd->cmd].pre_arg && - cmd_info[cmd->cmd].post_arg); - wl = cmd->wl; - card = port_to_card(wl_port(wl)); - - if (cmd_info[cmd->cmd].pre_arg) { - arg1 = (cmd->buffer) ? - ps3_mm_phys_to_lpar(__pa(cmd->buffer)) : - 0; - arg2 = cmd->buf_size; - } else { - arg1 = 0; - arg2 = 0; - } - init_completion(&wl->cmd_done_intr); - pr_debug("%s: cmd='%s' start\n", __func__, cmdstr(cmd->cmd)); - cmd->status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_POST_WLAN_CMD, - cmd->cmd, arg1, arg2, - &cmd->tag, &cmd->size); - if (cmd->status) { - complete(&cmd->done); - pr_info("%s: cmd issue failed\n", __func__); - return; - } - - wait_for_completion(&wl->cmd_done_intr); - - if (cmd_info[cmd->cmd].post_arg) { - arg1 = ps3_mm_phys_to_lpar(__pa(cmd->buffer)); - arg2 = cmd->buf_size; - } else { - arg1 = 0; - arg2 = 0; - } - - cmd->status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_GET_WLAN_CMD_RESULT, - cmd->tag, arg1, arg2, - &cmd->cmd_status, &cmd->size); -#ifdef DEBUG - if (cmd->status || cmd->cmd_status) { - pr_debug("%s: cmd done tag=%#lx arg1=%#lx, arg2=%#lx\n", __func__, - cmd->tag, arg1, arg2); - pr_debug("%s: cmd done status=%#x cmd_status=%#lx size=%#lx\n", - __func__, cmd->status, cmd->cmd_status, cmd->size); - } -#endif - complete(&cmd->done); - pr_debug("%s: cmd='%s' done\n", __func__, cmdstr(cmd->cmd)); -} - -static struct gelic_eurus_cmd *gelic_eurus_sync_cmd(struct gelic_wl_info *wl, - unsigned int eurus_cmd, - void *buffer, - unsigned int buf_size) -{ - struct gelic_eurus_cmd *cmd; - - /* allocate cmd */ - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) - return NULL; - - /* initialize members */ - cmd->cmd = eurus_cmd; - cmd->buffer = buffer; - cmd->buf_size = buf_size; - cmd->wl = wl; - INIT_WORK(&cmd->work, gelic_eurus_sync_cmd_worker); - init_completion(&cmd->done); - queue_work(wl->eurus_cmd_queue, &cmd->work); - - /* wait for command completion */ - wait_for_completion(&cmd->done); - - return cmd; -} - -static u32 gelic_wl_get_link(struct net_device *netdev) -{ - struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); - u32 ret; - - pr_debug("%s: <-\n", __func__); - mutex_lock(&wl->assoc_stat_lock); - if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) - ret = 1; - else - ret = 0; - mutex_unlock(&wl->assoc_stat_lock); - pr_debug("%s: ->\n", __func__); - return ret; -} - -static void gelic_wl_send_iwap_event(struct gelic_wl_info *wl, u8 *bssid) -{ - union iwreq_data data; - - memset(&data, 0, sizeof(data)); - if (bssid) - memcpy(data.ap_addr.sa_data, bssid, ETH_ALEN); - data.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWAP, - &data, NULL); -} - -/* - * wireless extension handlers and helpers - */ - -/* SIOGIWNAME */ -static int gelic_wl_get_name(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *iwreq, char *extra) -{ - strcpy(iwreq->name, "IEEE 802.11bg"); - return 0; -} - -static void gelic_wl_get_ch_info(struct gelic_wl_info *wl) -{ - struct gelic_card *card = port_to_card(wl_port(wl)); - u64 ch_info_raw, tmp; - int status; - - if (!test_and_set_bit(GELIC_WL_STAT_CH_INFO, &wl->stat)) { - status = lv1_net_control(bus_id(card), dev_id(card), - GELIC_LV1_GET_CHANNEL, 0, 0, 0, - &ch_info_raw, - &tmp); - /* some fw versions may return error */ - if (status) { - if (status != LV1_NO_ENTRY) - pr_info("%s: available ch unknown\n", __func__); - wl->ch_info = 0x07ff;/* 11 ch */ - } else - /* 16 bits of MSB has available channels */ - wl->ch_info = ch_info_raw >> 48; - } -} - -/* SIOGIWRANGE */ -static int gelic_wl_get_range(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *iwreq, char *extra) -{ - struct iw_point *point = &iwreq->data; - struct iw_range *range = (struct iw_range *)extra; - struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); - unsigned int i, chs; - - pr_debug("%s: <-\n", __func__); - point->length = sizeof(struct iw_range); - memset(range, 0, sizeof(struct iw_range)); - - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 22; - - /* available channels and frequencies */ - gelic_wl_get_ch_info(wl); - - for (i = 0, chs = 0; - i < NUM_CHANNELS && chs < IW_MAX_FREQUENCIES; i++) - if (wl->ch_info & (1 << i)) { - range->freq[chs].i = i + 1; - range->freq[chs].m = channel_freq[i]; - range->freq[chs].e = 6; - chs++; - } - range->num_frequency = chs; - range->old_num_frequency = chs; - range->num_channels = chs; - range->old_num_channels = chs; - - /* bitrates */ - for (i = 0; i < NUM_BITRATES; i++) - range->bitrate[i] = bitrate_list[i]; - range->num_bitrates = i; - - /* signal levels */ - range->max_qual.qual = 100; /* relative value */ - range->max_qual.level = 100; - range->avg_qual.qual = 50; - range->avg_qual.level = 50; - range->sensitivity = 0; - - /* Event capability */ - IW_EVENT_CAPA_SET_KERNEL(range->event_capa); - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); - - /* encryption capability */ - range->enc_capa = IW_ENC_CAPA_WPA | - IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP | - IW_ENC_CAPA_4WAY_HANDSHAKE; - if (wpa2_capable()) - range->enc_capa |= IW_ENC_CAPA_WPA2; - range->encoding_size[0] = 5; /* 40bit WEP */ - range->encoding_size[1] = 13; /* 104bit WEP */ - range->encoding_size[2] = 32; /* WPA-PSK */ - range->num_encoding_sizes = 3; - range->max_encoding_tokens = GELIC_WEP_KEYS; - - /* scan capability */ - range->scan_capa = IW_SCAN_CAPA_ESSID; - - pr_debug("%s: ->\n", __func__); - return 0; - -} - -/* SIOC{G,S}IWSCAN */ -static int gelic_wl_set_scan(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - struct iw_scan_req *req; - u8 *essid = NULL; - size_t essid_len = 0; - - if (wrqu->data.length == sizeof(struct iw_scan_req) && - wrqu->data.flags & IW_SCAN_THIS_ESSID) { - req = (struct iw_scan_req*)extra; - essid = req->essid; - essid_len = req->essid_len; - pr_debug("%s: ESSID scan =%s\n", __func__, essid); - } - return gelic_wl_start_scan(wl, 1, essid, essid_len); -} - -#define OUI_LEN 3 -static const u8 rsn_oui[OUI_LEN] = { 0x00, 0x0f, 0xac }; -static const u8 wpa_oui[OUI_LEN] = { 0x00, 0x50, 0xf2 }; - -/* - * synthesize WPA/RSN IE data - * See WiFi WPA specification and IEEE 802.11-2007 7.3.2.25 - * for the format - */ -static size_t gelic_wl_synthesize_ie(u8 *buf, - struct gelic_eurus_scan_info *scan) -{ - - const u8 *oui_header; - u8 *start = buf; - int rsn; - int ccmp; - - pr_debug("%s: <- sec=%16x\n", __func__, scan->security); - switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_MASK) { - case GELIC_EURUS_SCAN_SEC_WPA: - rsn = 0; - break; - case GELIC_EURUS_SCAN_SEC_WPA2: - rsn = 1; - break; - default: - /* WEP or none. No IE returned */ - return 0; - } - - switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_WPA_MASK) { - case GELIC_EURUS_SCAN_SEC_WPA_TKIP: - ccmp = 0; - break; - case GELIC_EURUS_SCAN_SEC_WPA_AES: - ccmp = 1; - break; - default: - if (rsn) { - ccmp = 1; - pr_info("%s: no cipher info. defaulted to CCMP\n", - __func__); - } else { - ccmp = 0; - pr_info("%s: no cipher info. defaulted to TKIP\n", - __func__); - } - } - - if (rsn) - oui_header = rsn_oui; - else - oui_header = wpa_oui; - - /* element id */ - if (rsn) - *buf++ = WLAN_EID_RSN; - else - *buf++ = WLAN_EID_GENERIC; - - /* length filed; set later */ - buf++; - - /* wpa special header */ - if (!rsn) { - memcpy(buf, wpa_oui, OUI_LEN); - buf += OUI_LEN; - *buf++ = 0x01; - } - - /* version */ - *buf++ = 0x01; /* version 1.0 */ - *buf++ = 0x00; - - /* group cipher */ - memcpy(buf, oui_header, OUI_LEN); - buf += OUI_LEN; - - if (ccmp) - *buf++ = 0x04; /* CCMP */ - else - *buf++ = 0x02; /* TKIP */ - - /* pairwise key count always 1 */ - *buf++ = 0x01; - *buf++ = 0x00; - - /* pairwise key suit */ - memcpy(buf, oui_header, OUI_LEN); - buf += OUI_LEN; - if (ccmp) - *buf++ = 0x04; /* CCMP */ - else - *buf++ = 0x02; /* TKIP */ - - /* AKM count is 1 */ - *buf++ = 0x01; - *buf++ = 0x00; - - /* AKM suite is assumed as PSK*/ - memcpy(buf, oui_header, OUI_LEN); - buf += OUI_LEN; - *buf++ = 0x02; /* PSK */ - - /* RSN capabilities is 0 */ - *buf++ = 0x00; - *buf++ = 0x00; - - /* set length field */ - start[1] = (buf - start - 2); - - pr_debug("%s: ->\n", __func__); - return buf - start; -} - -struct ie_item { - u8 *data; - u8 len; -}; - -struct ie_info { - struct ie_item wpa; - struct ie_item rsn; -}; - -static void gelic_wl_parse_ie(u8 *data, size_t len, - struct ie_info *ie_info) -{ - size_t data_left = len; - u8 *pos = data; - u8 item_len; - u8 item_id; - - pr_debug("%s: data=%p len=%ld\n", __func__, - data, len); - memset(ie_info, 0, sizeof(struct ie_info)); - - while (2 <= data_left) { - item_id = *pos++; - item_len = *pos++; - data_left -= 2; - - if (data_left < item_len) - break; - - switch (item_id) { - case WLAN_EID_GENERIC: - if ((OUI_LEN + 1 <= item_len) && - !memcmp(pos, wpa_oui, OUI_LEN) && - pos[OUI_LEN] == 0x01) { - ie_info->wpa.data = pos - 2; - ie_info->wpa.len = item_len + 2; - } - break; - case WLAN_EID_RSN: - ie_info->rsn.data = pos - 2; - /* length includes the header */ - ie_info->rsn.len = item_len + 2; - break; - default: - pr_debug("%s: ignore %#x,%d\n", __func__, - item_id, item_len); - break; - } - pos += item_len; - data_left -= item_len; - } - pr_debug("%s: wpa=%p,%d wpa2=%p,%d\n", __func__, - ie_info->wpa.data, ie_info->wpa.len, - ie_info->rsn.data, ie_info->rsn.len); -} - - -/* - * translate the scan informations from hypervisor to a - * independent format - */ -static char *gelic_wl_translate_scan(struct net_device *netdev, - struct iw_request_info *info, - char *ev, - char *stop, - struct gelic_wl_scan_info *network) -{ - struct iw_event iwe; - struct gelic_eurus_scan_info *scan = network->hwinfo; - char *tmp; - u8 rate; - unsigned int i, j, len; - u8 buf[64]; /* arbitrary size large enough */ - - pr_debug("%s: <-\n", __func__); - - /* first entry should be AP's mac address */ - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, &scan->bssid[2], ETH_ALEN); - ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_ADDR_LEN); - - /* ESSID */ - iwe.cmd = SIOCGIWESSID; - iwe.u.data.flags = 1; - iwe.u.data.length = strnlen(scan->essid, 32); - ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid); - - /* FREQUENCY */ - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = be16_to_cpu(scan->channel); - iwe.u.freq.e = 0; /* table value in MHz */ - iwe.u.freq.i = 0; - ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_FREQ_LEN); - - /* RATES */ - iwe.cmd = SIOCGIWRATE; - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - /* to stuff multiple values in one event */ - tmp = ev + iwe_stream_lcp_len(info); - /* put them in ascendant order (older is first) */ - i = 0; - j = 0; - pr_debug("%s: rates=%d rate=%d\n", __func__, - network->rate_len, network->rate_ext_len); - while (i < network->rate_len) { - if (j < network->rate_ext_len && - ((scan->ext_rate[j] & 0x7f) < (scan->rate[i] & 0x7f))) - rate = scan->ext_rate[j++] & 0x7f; - else - rate = scan->rate[i++] & 0x7f; - iwe.u.bitrate.value = rate * 500000; /* 500kbps unit */ - tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe, - IW_EV_PARAM_LEN); - } - while (j < network->rate_ext_len) { - iwe.u.bitrate.value = (scan->ext_rate[j++] & 0x7f) * 500000; - tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe, - IW_EV_PARAM_LEN); - } - /* Check if we added any rate */ - if (iwe_stream_lcp_len(info) < (tmp - ev)) - ev = tmp; - - /* ENCODE */ - iwe.cmd = SIOCGIWENCODE; - if (be16_to_cpu(scan->capability) & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid); - - /* MODE */ - iwe.cmd = SIOCGIWMODE; - if (be16_to_cpu(scan->capability) & - (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { - if (be16_to_cpu(scan->capability) & WLAN_CAPABILITY_ESS) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_UINT_LEN); - } - - /* QUAL */ - iwe.cmd = IWEVQUAL; - iwe.u.qual.updated = IW_QUAL_ALL_UPDATED | - IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; - iwe.u.qual.level = be16_to_cpu(scan->rssi); - iwe.u.qual.qual = be16_to_cpu(scan->rssi); - iwe.u.qual.noise = 0; - ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_QUAL_LEN); - - /* RSN */ - memset(&iwe, 0, sizeof(iwe)); - if (be16_to_cpu(scan->size) <= sizeof(*scan)) { - /* If wpa[2] capable station, synthesize IE and put it */ - len = gelic_wl_synthesize_ie(buf, scan); - if (len) { - iwe.cmd = IWEVGENIE; - iwe.u.data.length = len; - ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); - } - } else { - /* this scan info has IE data */ - struct ie_info ie_info; - size_t data_len; - - data_len = be16_to_cpu(scan->size) - sizeof(*scan); - - gelic_wl_parse_ie(scan->elements, data_len, &ie_info); - - if (ie_info.wpa.len && (ie_info.wpa.len <= sizeof(buf))) { - memcpy(buf, ie_info.wpa.data, ie_info.wpa.len); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = ie_info.wpa.len; - ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); - } - - if (ie_info.rsn.len && (ie_info.rsn.len <= sizeof(buf))) { - memset(&iwe, 0, sizeof(iwe)); - memcpy(buf, ie_info.rsn.data, ie_info.rsn.len); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = ie_info.rsn.len; - ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); - } - } - - pr_debug("%s: ->\n", __func__); - return ev; -} - - -static int gelic_wl_get_scan(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - struct gelic_wl_scan_info *scan_info; - char *ev = extra; - char *stop = ev + wrqu->data.length; - int ret = 0; - unsigned long this_time = jiffies; - - pr_debug("%s: <-\n", __func__); - if (mutex_lock_interruptible(&wl->scan_lock)) - return -EAGAIN; - - switch (wl->scan_stat) { - case GELIC_WL_SCAN_STAT_SCANNING: - /* If a scan in progress, caller should call me again */ - ret = -EAGAIN; - goto out; - break; - - case GELIC_WL_SCAN_STAT_INIT: - /* last scan request failed or never issued */ - ret = -ENODEV; - goto out; - break; - case GELIC_WL_SCAN_STAT_GOT_LIST: - /* ok, use current list */ - break; - } - - list_for_each_entry(scan_info, &wl->network_list, list) { - if (wl->scan_age == 0 || - time_after(scan_info->last_scanned + wl->scan_age, - this_time)) - ev = gelic_wl_translate_scan(netdev, info, - ev, stop, - scan_info); - else - pr_debug("%s:entry too old\n", __func__); - - if (stop - ev <= IW_EV_ADDR_LEN) { - ret = -E2BIG; - goto out; - } - } - - wrqu->data.length = ev - extra; - wrqu->data.flags = 0; -out: - mutex_unlock(&wl->scan_lock); - pr_debug("%s: -> %d %d\n", __func__, ret, wrqu->data.length); - return ret; -} - -#ifdef DEBUG -static void scan_list_dump(struct gelic_wl_info *wl) -{ - struct gelic_wl_scan_info *scan_info; - int i; - - i = 0; - list_for_each_entry(scan_info, &wl->network_list, list) { - pr_debug("%s: item %d\n", __func__, i++); - pr_debug("valid=%d eurusindex=%d last=%lx\n", - scan_info->valid, scan_info->eurus_index, - scan_info->last_scanned); - pr_debug("r_len=%d r_ext_len=%d essid_len=%d\n", - scan_info->rate_len, scan_info->rate_ext_len, - scan_info->essid_len); - /* -- */ - pr_debug("bssid=%pM\n", &scan_info->hwinfo->bssid[2]); - pr_debug("essid=%s\n", scan_info->hwinfo->essid); - } -} -#endif - -static int gelic_wl_set_auth(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct iw_param *param = &data->param; - struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); - unsigned long irqflag; - int ret = 0; - - pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX); - spin_lock_irqsave(&wl->lock, irqflag); - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - if (param->value & IW_AUTH_WPA_VERSION_DISABLED) { - pr_debug("%s: NO WPA selected\n", __func__); - wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; - wl->group_cipher_method = GELIC_WL_CIPHER_WEP; - wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP; - } - if (param->value & IW_AUTH_WPA_VERSION_WPA) { - pr_debug("%s: WPA version 1 selected\n", __func__); - wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA; - wl->group_cipher_method = GELIC_WL_CIPHER_TKIP; - wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP; - wl->auth_method = GELIC_EURUS_AUTH_OPEN; - } - if (param->value & IW_AUTH_WPA_VERSION_WPA2) { - /* - * As the hypervisor may not tell the cipher - * information of the AP if it is WPA2, - * you will not decide suitable cipher from - * its beacon. - * You should have knowledge about the AP's - * cipher information in other method prior to - * the association. - */ - if (!precise_ie()) - pr_info("%s: WPA2 may not work\n", __func__); - if (wpa2_capable()) { - wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA2; - wl->group_cipher_method = GELIC_WL_CIPHER_AES; - wl->pairwise_cipher_method = - GELIC_WL_CIPHER_AES; - wl->auth_method = GELIC_EURUS_AUTH_OPEN; - } else - ret = -EINVAL; - } - break; - - case IW_AUTH_CIPHER_PAIRWISE: - if (param->value & - (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) { - pr_debug("%s: WEP selected\n", __func__); - wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP; - } - if (param->value & IW_AUTH_CIPHER_TKIP) { - pr_debug("%s: TKIP selected\n", __func__); - wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP; - } - if (param->value & IW_AUTH_CIPHER_CCMP) { - pr_debug("%s: CCMP selected\n", __func__); - wl->pairwise_cipher_method = GELIC_WL_CIPHER_AES; - } - if (param->value & IW_AUTH_CIPHER_NONE) { - pr_debug("%s: no auth selected\n", __func__); - wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE; - } - break; - case IW_AUTH_CIPHER_GROUP: - if (param->value & - (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) { - pr_debug("%s: WEP selected\n", __func__); - wl->group_cipher_method = GELIC_WL_CIPHER_WEP; - } - if (param->value & IW_AUTH_CIPHER_TKIP) { - pr_debug("%s: TKIP selected\n", __func__); - wl->group_cipher_method = GELIC_WL_CIPHER_TKIP; - } - if (param->value & IW_AUTH_CIPHER_CCMP) { - pr_debug("%s: CCMP selected\n", __func__); - wl->group_cipher_method = GELIC_WL_CIPHER_AES; - } - if (param->value & IW_AUTH_CIPHER_NONE) { - pr_debug("%s: no auth selected\n", __func__); - wl->group_cipher_method = GELIC_WL_CIPHER_NONE; - } - break; - case IW_AUTH_80211_AUTH_ALG: - if (param->value & IW_AUTH_ALG_SHARED_KEY) { - pr_debug("%s: shared key specified\n", __func__); - wl->auth_method = GELIC_EURUS_AUTH_SHARED; - } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { - pr_debug("%s: open system specified\n", __func__); - wl->auth_method = GELIC_EURUS_AUTH_OPEN; - } else - ret = -EINVAL; - break; - - case IW_AUTH_WPA_ENABLED: - if (param->value) { - pr_debug("%s: WPA enabled\n", __func__); - wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA; - } else { - pr_debug("%s: WPA disabled\n", __func__); - wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; - } - break; - - case IW_AUTH_KEY_MGMT: - if (param->value & IW_AUTH_KEY_MGMT_PSK) - break; - /* intentionally fall through */ - default: - ret = -EOPNOTSUPP; - break; - } - - if (!ret) - set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); - - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s: -> %d\n", __func__, ret); - return ret; -} - -static int gelic_wl_get_auth(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *iwreq, char *extra) -{ - struct iw_param *param = &iwreq->param; - struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); - unsigned long irqflag; - int ret = 0; - - pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX); - spin_lock_irqsave(&wl->lock, irqflag); - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - switch (wl->wpa_level) { - case GELIC_WL_WPA_LEVEL_WPA: - param->value |= IW_AUTH_WPA_VERSION_WPA; - break; - case GELIC_WL_WPA_LEVEL_WPA2: - param->value |= IW_AUTH_WPA_VERSION_WPA2; - break; - default: - param->value |= IW_AUTH_WPA_VERSION_DISABLED; - } - break; - - case IW_AUTH_80211_AUTH_ALG: - if (wl->auth_method == GELIC_EURUS_AUTH_SHARED) - param->value = IW_AUTH_ALG_SHARED_KEY; - else if (wl->auth_method == GELIC_EURUS_AUTH_OPEN) - param->value = IW_AUTH_ALG_OPEN_SYSTEM; - break; - - case IW_AUTH_WPA_ENABLED: - switch (wl->wpa_level) { - case GELIC_WL_WPA_LEVEL_WPA: - case GELIC_WL_WPA_LEVEL_WPA2: - param->value = 1; - break; - default: - param->value = 0; - break; - } - break; - default: - ret = -EOPNOTSUPP; - } - - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s: -> %d\n", __func__, ret); - return ret; -} - -/* SIOC{S,G}IWESSID */ -static int gelic_wl_set_essid(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - unsigned long irqflag; - - pr_debug("%s: <- l=%d f=%d\n", __func__, - data->essid.length, data->essid.flags); - if (IW_ESSID_MAX_SIZE < data->essid.length) - return -EINVAL; - - spin_lock_irqsave(&wl->lock, irqflag); - if (data->essid.flags) { - wl->essid_len = data->essid.length; - memcpy(wl->essid, extra, wl->essid_len); - pr_debug("%s: essid = '%s'\n", __func__, extra); - set_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat); - } else { - pr_debug("%s: ESSID any\n", __func__); - clear_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat); - } - set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); - spin_unlock_irqrestore(&wl->lock, irqflag); - - - gelic_wl_try_associate(netdev); /* FIXME */ - pr_debug("%s: ->\n", __func__); - return 0; -} - -static int gelic_wl_get_essid(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - unsigned long irqflag; - - pr_debug("%s: <-\n", __func__); - mutex_lock(&wl->assoc_stat_lock); - spin_lock_irqsave(&wl->lock, irqflag); - if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat) || - wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) { - memcpy(extra, wl->essid, wl->essid_len); - data->essid.length = wl->essid_len; - data->essid.flags = 1; - } else - data->essid.flags = 0; - - mutex_unlock(&wl->assoc_stat_lock); - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s: -> len=%d\n", __func__, data->essid.length); - - return 0; -} - -/* SIO{S,G}IWENCODE */ -static int gelic_wl_set_encode(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - struct iw_point *enc = &data->encoding; - __u16 flags; - unsigned long irqflag; - int key_index, index_specified; - int ret = 0; - - pr_debug("%s: <-\n", __func__); - flags = enc->flags & IW_ENCODE_FLAGS; - key_index = enc->flags & IW_ENCODE_INDEX; - - pr_debug("%s: key_index = %d\n", __func__, key_index); - pr_debug("%s: key_len = %d\n", __func__, enc->length); - pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS); - - if (GELIC_WEP_KEYS < key_index) - return -EINVAL; - - spin_lock_irqsave(&wl->lock, irqflag); - if (key_index) { - index_specified = 1; - key_index--; - } else { - index_specified = 0; - key_index = wl->current_key; - } - - if (flags & IW_ENCODE_NOKEY) { - /* if just IW_ENCODE_NOKEY, change current key index */ - if (!flags && index_specified) { - wl->current_key = key_index; - goto done; - } - - if (flags & IW_ENCODE_DISABLED) { - if (!index_specified) { - /* disable encryption */ - wl->group_cipher_method = GELIC_WL_CIPHER_NONE; - wl->pairwise_cipher_method = - GELIC_WL_CIPHER_NONE; - /* invalidate all key */ - wl->key_enabled = 0; - } else - clear_bit(key_index, &wl->key_enabled); - } - - if (flags & IW_ENCODE_OPEN) - wl->auth_method = GELIC_EURUS_AUTH_OPEN; - if (flags & IW_ENCODE_RESTRICTED) { - pr_info("%s: shared key mode enabled\n", __func__); - wl->auth_method = GELIC_EURUS_AUTH_SHARED; - } - } else { - if (IW_ENCODING_TOKEN_MAX < enc->length) { - ret = -EINVAL; - goto done; - } - wl->key_len[key_index] = enc->length; - memcpy(wl->key[key_index], extra, enc->length); - set_bit(key_index, &wl->key_enabled); - wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP; - wl->group_cipher_method = GELIC_WL_CIPHER_WEP; - } - set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); -done: - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s: ->\n", __func__); - return ret; -} - -static int gelic_wl_get_encode(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - struct iw_point *enc = &data->encoding; - unsigned long irqflag; - unsigned int key_index, index_specified; - int ret = 0; - - pr_debug("%s: <-\n", __func__); - key_index = enc->flags & IW_ENCODE_INDEX; - pr_debug("%s: flag=%#x point=%p len=%d extra=%p\n", __func__, - enc->flags, enc->pointer, enc->length, extra); - if (GELIC_WEP_KEYS < key_index) - return -EINVAL; - - spin_lock_irqsave(&wl->lock, irqflag); - if (key_index) { - index_specified = 1; - key_index--; - } else { - index_specified = 0; - key_index = wl->current_key; - } - - if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { - switch (wl->auth_method) { - case GELIC_EURUS_AUTH_OPEN: - enc->flags = IW_ENCODE_OPEN; - break; - case GELIC_EURUS_AUTH_SHARED: - enc->flags = IW_ENCODE_RESTRICTED; - break; - } - } else - enc->flags = IW_ENCODE_DISABLED; - - if (test_bit(key_index, &wl->key_enabled)) { - if (enc->length < wl->key_len[key_index]) { - ret = -EINVAL; - goto done; - } - enc->length = wl->key_len[key_index]; - memcpy(extra, wl->key[key_index], wl->key_len[key_index]); - } else { - enc->length = 0; - enc->flags |= IW_ENCODE_NOKEY; - } - enc->flags |= key_index + 1; - pr_debug("%s: -> flag=%x len=%d\n", __func__, - enc->flags, enc->length); - -done: - spin_unlock_irqrestore(&wl->lock, irqflag); - return ret; -} - -/* SIOC{S,G}IWAP */ -static int gelic_wl_set_ap(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - unsigned long irqflag; - - pr_debug("%s: <-\n", __func__); - if (data->ap_addr.sa_family != ARPHRD_ETHER) - return -EINVAL; - - spin_lock_irqsave(&wl->lock, irqflag); - if (is_valid_ether_addr(data->ap_addr.sa_data)) { - memcpy(wl->bssid, data->ap_addr.sa_data, - ETH_ALEN); - set_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat); - set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); - pr_debug("%s: bss=%pM\n", __func__, wl->bssid); - } else { - pr_debug("%s: clear bssid\n", __func__); - clear_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat); - memset(wl->bssid, 0, ETH_ALEN); - } - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s: ->\n", __func__); - return 0; -} - -static int gelic_wl_get_ap(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - unsigned long irqflag; - - pr_debug("%s: <-\n", __func__); - mutex_lock(&wl->assoc_stat_lock); - spin_lock_irqsave(&wl->lock, irqflag); - if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) { - data->ap_addr.sa_family = ARPHRD_ETHER; - memcpy(data->ap_addr.sa_data, wl->active_bssid, - ETH_ALEN); - } else - memset(data->ap_addr.sa_data, 0, ETH_ALEN); - - spin_unlock_irqrestore(&wl->lock, irqflag); - mutex_unlock(&wl->assoc_stat_lock); - pr_debug("%s: ->\n", __func__); - return 0; -} - -/* SIOC{S,G}IWENCODEEXT */ -static int gelic_wl_set_encodeext(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - struct iw_point *enc = &data->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - __u16 alg; - __u16 flags; - unsigned long irqflag; - int key_index; - int ret = 0; - - pr_debug("%s: <-\n", __func__); - flags = enc->flags & IW_ENCODE_FLAGS; - alg = ext->alg; - key_index = enc->flags & IW_ENCODE_INDEX; - - pr_debug("%s: key_index = %d\n", __func__, key_index); - pr_debug("%s: key_len = %d\n", __func__, enc->length); - pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS); - pr_debug("%s: ext_flag=%x\n", __func__, ext->ext_flags); - pr_debug("%s: ext_key_len=%x\n", __func__, ext->key_len); - - if (GELIC_WEP_KEYS < key_index) - return -EINVAL; - - spin_lock_irqsave(&wl->lock, irqflag); - if (key_index) - key_index--; - else - key_index = wl->current_key; - - if (!enc->length && (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) { - /* reques to change default key index */ - pr_debug("%s: request to change default key to %d\n", - __func__, key_index); - wl->current_key = key_index; - goto done; - } - - if (alg == IW_ENCODE_ALG_NONE || (flags & IW_ENCODE_DISABLED)) { - pr_debug("%s: alg disabled\n", __func__); - wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; - wl->group_cipher_method = GELIC_WL_CIPHER_NONE; - wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE; - wl->auth_method = GELIC_EURUS_AUTH_OPEN; /* should be open */ - } else if (alg == IW_ENCODE_ALG_WEP) { - pr_debug("%s: WEP requested\n", __func__); - if (flags & IW_ENCODE_OPEN) { - pr_debug("%s: open key mode\n", __func__); - wl->auth_method = GELIC_EURUS_AUTH_OPEN; - } - if (flags & IW_ENCODE_RESTRICTED) { - pr_debug("%s: shared key mode\n", __func__); - wl->auth_method = GELIC_EURUS_AUTH_SHARED; - } - if (IW_ENCODING_TOKEN_MAX < ext->key_len) { - pr_info("%s: key is too long %d\n", __func__, - ext->key_len); - ret = -EINVAL; - goto done; - } - /* OK, update the key */ - wl->key_len[key_index] = ext->key_len; - memset(wl->key[key_index], 0, IW_ENCODING_TOKEN_MAX); - memcpy(wl->key[key_index], ext->key, ext->key_len); - set_bit(key_index, &wl->key_enabled); - /* remember wep info changed */ - set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); - } else if (alg == IW_ENCODE_ALG_PMK) { - if (ext->key_len != WPA_PSK_LEN) { - pr_err("%s: PSK length wrong %d\n", __func__, - ext->key_len); - ret = -EINVAL; - goto done; - } - memset(wl->psk, 0, sizeof(wl->psk)); - memcpy(wl->psk, ext->key, ext->key_len); - wl->psk_len = ext->key_len; - wl->psk_type = GELIC_EURUS_WPA_PSK_BIN; - /* remember PSK configured */ - set_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat); - } -done: - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s: ->\n", __func__); - return ret; -} - -static int gelic_wl_get_encodeext(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - struct iw_point *enc = &data->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - unsigned long irqflag; - int key_index; - int ret = 0; - int max_key_len; - - pr_debug("%s: <-\n", __func__); - - max_key_len = enc->length - sizeof(struct iw_encode_ext); - if (max_key_len < 0) - return -EINVAL; - key_index = enc->flags & IW_ENCODE_INDEX; - - pr_debug("%s: key_index = %d\n", __func__, key_index); - pr_debug("%s: key_len = %d\n", __func__, enc->length); - pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS); - - if (GELIC_WEP_KEYS < key_index) - return -EINVAL; - - spin_lock_irqsave(&wl->lock, irqflag); - if (key_index) - key_index--; - else - key_index = wl->current_key; - - memset(ext, 0, sizeof(struct iw_encode_ext)); - switch (wl->group_cipher_method) { - case GELIC_WL_CIPHER_WEP: - ext->alg = IW_ENCODE_ALG_WEP; - enc->flags |= IW_ENCODE_ENABLED; - break; - case GELIC_WL_CIPHER_TKIP: - ext->alg = IW_ENCODE_ALG_TKIP; - enc->flags |= IW_ENCODE_ENABLED; - break; - case GELIC_WL_CIPHER_AES: - ext->alg = IW_ENCODE_ALG_CCMP; - enc->flags |= IW_ENCODE_ENABLED; - break; - case GELIC_WL_CIPHER_NONE: - default: - ext->alg = IW_ENCODE_ALG_NONE; - enc->flags |= IW_ENCODE_NOKEY; - break; - } - - if (!(enc->flags & IW_ENCODE_NOKEY)) { - if (max_key_len < wl->key_len[key_index]) { - ret = -E2BIG; - goto out; - } - if (test_bit(key_index, &wl->key_enabled)) - memcpy(ext->key, wl->key[key_index], - wl->key_len[key_index]); - else - pr_debug("%s: disabled key requested ix=%d\n", - __func__, key_index); - } -out: - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s: ->\n", __func__); - return ret; -} -/* SIOC{S,G}IWMODE */ -static int gelic_wl_set_mode(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - __u32 mode = data->mode; - int ret; - - pr_debug("%s: <-\n", __func__); - if (mode == IW_MODE_INFRA) - ret = 0; - else - ret = -EOPNOTSUPP; - pr_debug("%s: -> %d\n", __func__, ret); - return ret; -} - -static int gelic_wl_get_mode(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - __u32 *mode = &data->mode; - pr_debug("%s: <-\n", __func__); - *mode = IW_MODE_INFRA; - pr_debug("%s: ->\n", __func__); - return 0; -} - -/* SIOCGIWNICKN */ -static int gelic_wl_get_nick(struct net_device *net_dev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - strcpy(extra, "gelic_wl"); - data->data.length = strlen(extra); - data->data.flags = 1; - return 0; -} - - -/* --- */ - -static struct iw_statistics *gelic_wl_get_wireless_stats( - struct net_device *netdev) -{ - - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - struct gelic_eurus_cmd *cmd; - struct iw_statistics *is; - struct gelic_eurus_rssi_info *rssi; - void *buf; - - pr_debug("%s: <-\n", __func__); - - buf = (void *)__get_free_page(GFP_KERNEL); - if (!buf) - return NULL; - - is = &wl->iwstat; - memset(is, 0, sizeof(*is)); - cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_RSSI_CFG, - buf, sizeof(*rssi)); - if (cmd && !cmd->status && !cmd->cmd_status) { - rssi = buf; - is->qual.level = be16_to_cpu(rssi->rssi); - is->qual.updated = IW_QUAL_LEVEL_UPDATED | - IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; - } else - /* not associated */ - is->qual.updated = IW_QUAL_ALL_INVALID; - - kfree(cmd); - free_page((unsigned long)buf); - pr_debug("%s: ->\n", __func__); - return is; -} - -/* - * scanning helpers - */ -static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan, - u8 *essid, size_t essid_len) -{ - struct gelic_eurus_cmd *cmd; - int ret = 0; - void *buf = NULL; - size_t len; - - pr_debug("%s: <- always=%d\n", __func__, always_scan); - if (mutex_lock_interruptible(&wl->scan_lock)) - return -ERESTARTSYS; - - /* - * If already a scan in progress, do not trigger more - */ - if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) { - pr_debug("%s: scanning now\n", __func__); - goto out; - } - - init_completion(&wl->scan_done); - /* - * If we have already a bss list, don't try to get new - * unless we are doing an ESSID scan - */ - if ((!essid_len && !always_scan) - && wl->scan_stat == GELIC_WL_SCAN_STAT_GOT_LIST) { - pr_debug("%s: already has the list\n", __func__); - complete(&wl->scan_done); - goto out; - } - - /* ESSID scan ? */ - if (essid_len && essid) { - buf = (void *)__get_free_page(GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto out; - } - len = IW_ESSID_MAX_SIZE; /* hypervisor always requires 32 */ - memset(buf, 0, len); - memcpy(buf, essid, essid_len); - pr_debug("%s: essid scan='%s'\n", __func__, (char *)buf); - } else - len = 0; - - /* - * issue start scan request - */ - wl->scan_stat = GELIC_WL_SCAN_STAT_SCANNING; - cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_START_SCAN, - buf, len); - if (!cmd || cmd->status || cmd->cmd_status) { - wl->scan_stat = GELIC_WL_SCAN_STAT_INIT; - complete(&wl->scan_done); - ret = -ENOMEM; - goto out; - } - kfree(cmd); -out: - free_page((unsigned long)buf); - mutex_unlock(&wl->scan_lock); - pr_debug("%s: ->\n", __func__); - return ret; -} - -/* - * retrieve scan result from the chip (hypervisor) - * this function is invoked by schedule work. - */ -static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl) -{ - struct gelic_eurus_cmd *cmd = NULL; - struct gelic_wl_scan_info *target, *tmp; - struct gelic_wl_scan_info *oldest = NULL; - struct gelic_eurus_scan_info *scan_info; - unsigned int scan_info_size; - union iwreq_data data; - unsigned long this_time = jiffies; - unsigned int data_len, i, found, r; - void *buf; - - pr_debug("%s:start\n", __func__); - mutex_lock(&wl->scan_lock); - - buf = (void *)__get_free_page(GFP_KERNEL); - if (!buf) { - pr_info("%s: scan buffer alloc failed\n", __func__); - goto out; - } - - if (wl->scan_stat != GELIC_WL_SCAN_STAT_SCANNING) { - /* - * stop() may be called while scanning, ignore result - */ - pr_debug("%s: scan complete when stat != scanning(%d)\n", - __func__, wl->scan_stat); - goto out; - } - - cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_SCAN, - buf, PAGE_SIZE); - if (!cmd || cmd->status || cmd->cmd_status) { - wl->scan_stat = GELIC_WL_SCAN_STAT_INIT; - pr_info("%s:cmd failed\n", __func__); - kfree(cmd); - goto out; - } - data_len = cmd->size; - pr_debug("%s: data_len = %d\n", __func__, data_len); - kfree(cmd); - - /* OK, bss list retrieved */ - wl->scan_stat = GELIC_WL_SCAN_STAT_GOT_LIST; - - /* mark all entries are old */ - list_for_each_entry_safe(target, tmp, &wl->network_list, list) { - target->valid = 0; - /* expire too old entries */ - if (time_before(target->last_scanned + wl->scan_age, - this_time)) { - kfree(target->hwinfo); - target->hwinfo = NULL; - list_move_tail(&target->list, &wl->network_free_list); - } - } - - /* put them in the network_list */ - for (i = 0, scan_info_size = 0, scan_info = buf; - scan_info_size < data_len; - i++, scan_info_size += be16_to_cpu(scan_info->size), - scan_info = (void *)scan_info + be16_to_cpu(scan_info->size)) { - pr_debug("%s:size=%d bssid=%pM scan_info=%p\n", __func__, - be16_to_cpu(scan_info->size), - &scan_info->bssid[2], scan_info); - - /* - * The wireless firmware may return invalid channel 0 and/or - * invalid rate if the AP emits zero length SSID ie. As this - * scan information is useless, ignore it - */ - if (!be16_to_cpu(scan_info->channel) || !scan_info->rate[0]) { - pr_debug("%s: invalid scan info\n", __func__); - continue; - } - - found = 0; - oldest = NULL; - list_for_each_entry(target, &wl->network_list, list) { - if (!compare_ether_addr(&target->hwinfo->bssid[2], - &scan_info->bssid[2])) { - found = 1; - pr_debug("%s: same BBS found scanned list\n", - __func__); - break; - } - if (!oldest || - (target->last_scanned < oldest->last_scanned)) - oldest = target; - } - - if (!found) { - /* not found in the list */ - if (list_empty(&wl->network_free_list)) { - /* expire oldest */ - target = oldest; - } else { - target = list_entry(wl->network_free_list.next, - struct gelic_wl_scan_info, - list); - } - } - - /* update the item */ - target->last_scanned = this_time; - target->valid = 1; - target->eurus_index = i; - kfree(target->hwinfo); - target->hwinfo = kzalloc(be16_to_cpu(scan_info->size), - GFP_KERNEL); - if (!target->hwinfo) { - pr_info("%s: kzalloc failed\n", __func__); - continue; - } - /* copy hw scan info */ - memcpy(target->hwinfo, scan_info, scan_info->size); - target->essid_len = strnlen(scan_info->essid, - sizeof(scan_info->essid)); - target->rate_len = 0; - for (r = 0; r < 12; r++) - if (scan_info->rate[r]) - target->rate_len++; - if (8 < target->rate_len) - pr_info("%s: AP returns %d rates\n", __func__, - target->rate_len); - target->rate_ext_len = 0; - for (r = 0; r < 16; r++) - if (scan_info->ext_rate[r]) - target->rate_ext_len++; - list_move_tail(&target->list, &wl->network_list); - } - memset(&data, 0, sizeof(data)); - wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWSCAN, &data, - NULL); -out: - free_page((unsigned long)buf); - complete(&wl->scan_done); - mutex_unlock(&wl->scan_lock); - pr_debug("%s:end\n", __func__); -} - -/* - * Select an appropriate bss from current scan list regarding - * current settings from userspace. - * The caller must hold wl->scan_lock, - * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST - */ -static void update_best(struct gelic_wl_scan_info **best, - struct gelic_wl_scan_info *candid, - int *best_weight, - int *weight) -{ - if (*best_weight < ++(*weight)) { - *best_weight = *weight; - *best = candid; - } -} - -static -struct gelic_wl_scan_info *gelic_wl_find_best_bss(struct gelic_wl_info *wl) -{ - struct gelic_wl_scan_info *scan_info; - struct gelic_wl_scan_info *best_bss; - int weight, best_weight; - u16 security; - - pr_debug("%s: <-\n", __func__); - - best_bss = NULL; - best_weight = 0; - - list_for_each_entry(scan_info, &wl->network_list, list) { - pr_debug("%s: station %p\n", __func__, scan_info); - - if (!scan_info->valid) { - pr_debug("%s: station invalid\n", __func__); - continue; - } - - /* If bss specified, check it only */ - if (test_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat)) { - if (!compare_ether_addr(&scan_info->hwinfo->bssid[2], - wl->bssid)) { - best_bss = scan_info; - pr_debug("%s: bssid matched\n", __func__); - break; - } else { - pr_debug("%s: bssid unmached\n", __func__); - continue; - } - } - - weight = 0; - - /* security */ - security = be16_to_cpu(scan_info->hwinfo->security) & - GELIC_EURUS_SCAN_SEC_MASK; - if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) { - if (security == GELIC_EURUS_SCAN_SEC_WPA2) - update_best(&best_bss, scan_info, - &best_weight, &weight); - else - continue; - } else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA) { - if (security == GELIC_EURUS_SCAN_SEC_WPA) - update_best(&best_bss, scan_info, - &best_weight, &weight); - else - continue; - } else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_NONE && - wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { - if (security == GELIC_EURUS_SCAN_SEC_WEP) - update_best(&best_bss, scan_info, - &best_weight, &weight); - else - continue; - } - - /* If ESSID is set, check it */ - if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) { - if ((scan_info->essid_len == wl->essid_len) && - !strncmp(wl->essid, - scan_info->hwinfo->essid, - scan_info->essid_len)) - update_best(&best_bss, scan_info, - &best_weight, &weight); - else - continue; - } - } - -#ifdef DEBUG - pr_debug("%s: -> bss=%p\n", __func__, best_bss); - if (best_bss) { - pr_debug("%s:addr=%pM\n", __func__, - &best_bss->hwinfo->bssid[2]); - } -#endif - return best_bss; -} - -/* - * Setup WEP configuration to the chip - * The caller must hold wl->scan_lock, - * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST - */ -static int gelic_wl_do_wep_setup(struct gelic_wl_info *wl) -{ - unsigned int i; - struct gelic_eurus_wep_cfg *wep; - struct gelic_eurus_cmd *cmd; - int wep104 = 0; - int have_key = 0; - int ret = 0; - - pr_debug("%s: <-\n", __func__); - /* we can assume no one should uses the buffer */ - wep = (struct gelic_eurus_wep_cfg *)__get_free_page(GFP_KERNEL); - if (!wep) - return -ENOMEM; - - memset(wep, 0, sizeof(*wep)); - - if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { - pr_debug("%s: WEP mode\n", __func__); - for (i = 0; i < GELIC_WEP_KEYS; i++) { - if (!test_bit(i, &wl->key_enabled)) - continue; - - pr_debug("%s: key#%d enabled\n", __func__, i); - have_key = 1; - if (wl->key_len[i] == 13) - wep104 = 1; - else if (wl->key_len[i] != 5) { - pr_info("%s: wrong wep key[%d]=%d\n", - __func__, i, wl->key_len[i]); - ret = -EINVAL; - goto out; - } - memcpy(wep->key[i], wl->key[i], wl->key_len[i]); - } - - if (!have_key) { - pr_info("%s: all wep key disabled\n", __func__); - ret = -EINVAL; - goto out; - } - - if (wep104) { - pr_debug("%s: 104bit key\n", __func__); - wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_104BIT); - } else { - pr_debug("%s: 40bit key\n", __func__); - wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_40BIT); - } - } else { - pr_debug("%s: NO encryption\n", __func__); - wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_NONE); - } - - /* issue wep setup */ - cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WEP_CFG, - wep, sizeof(*wep)); - if (!cmd) - ret = -ENOMEM; - else if (cmd->status || cmd->cmd_status) - ret = -ENXIO; - - kfree(cmd); -out: - free_page((unsigned long)wep); - pr_debug("%s: ->\n", __func__); - return ret; -} - -#ifdef DEBUG -static const char *wpasecstr(enum gelic_eurus_wpa_security sec) -{ - switch (sec) { - case GELIC_EURUS_WPA_SEC_NONE: - return "NONE"; - break; - case GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP: - return "WPA_TKIP_TKIP"; - break; - case GELIC_EURUS_WPA_SEC_WPA_TKIP_AES: - return "WPA_TKIP_AES"; - break; - case GELIC_EURUS_WPA_SEC_WPA_AES_AES: - return "WPA_AES_AES"; - break; - case GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP: - return "WPA2_TKIP_TKIP"; - break; - case GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES: - return "WPA2_TKIP_AES"; - break; - case GELIC_EURUS_WPA_SEC_WPA2_AES_AES: - return "WPA2_AES_AES"; - break; - } - return ""; -}; -#endif - -static int gelic_wl_do_wpa_setup(struct gelic_wl_info *wl) -{ - struct gelic_eurus_wpa_cfg *wpa; - struct gelic_eurus_cmd *cmd; - u16 security; - int ret = 0; - - pr_debug("%s: <-\n", __func__); - /* we can assume no one should uses the buffer */ - wpa = (struct gelic_eurus_wpa_cfg *)__get_free_page(GFP_KERNEL); - if (!wpa) - return -ENOMEM; - - memset(wpa, 0, sizeof(*wpa)); - - if (!test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat)) - pr_info("%s: PSK not configured yet\n", __func__); - - /* copy key */ - memcpy(wpa->psk, wl->psk, wl->psk_len); - - /* set security level */ - if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) { - if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) { - security = GELIC_EURUS_WPA_SEC_WPA2_AES_AES; - } else { - if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES && - precise_ie()) - security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES; - else - security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP; - } - } else { - if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) { - security = GELIC_EURUS_WPA_SEC_WPA_AES_AES; - } else { - if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES && - precise_ie()) - security = GELIC_EURUS_WPA_SEC_WPA_TKIP_AES; - else - security = GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP; - } - } - wpa->security = cpu_to_be16(security); - - /* PSK type */ - wpa->psk_type = cpu_to_be16(wl->psk_type); -#ifdef DEBUG - pr_debug("%s: sec=%s psktype=%s\n", __func__, - wpasecstr(wpa->security), - (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ? - "BIN" : "passphrase"); -#if 0 - /* - * don't enable here if you plan to submit - * the debug log because this dumps your precious - * passphrase/key. - */ - pr_debug("%s: psk=%s\n", __func__, - (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ? - "N/A" : wpa->psk); -#endif -#endif - /* issue wpa setup */ - cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WPA_CFG, - wpa, sizeof(*wpa)); - if (!cmd) - ret = -ENOMEM; - else if (cmd->status || cmd->cmd_status) - ret = -ENXIO; - kfree(cmd); - free_page((unsigned long)wpa); - pr_debug("%s: --> %d\n", __func__, ret); - return ret; -} - -/* - * Start association. caller must hold assoc_stat_lock - */ -static int gelic_wl_associate_bss(struct gelic_wl_info *wl, - struct gelic_wl_scan_info *bss) -{ - struct gelic_eurus_cmd *cmd; - struct gelic_eurus_common_cfg *common; - int ret = 0; - unsigned long rc; - - pr_debug("%s: <-\n", __func__); - - /* do common config */ - common = (struct gelic_eurus_common_cfg *)__get_free_page(GFP_KERNEL); - if (!common) - return -ENOMEM; - - memset(common, 0, sizeof(*common)); - common->bss_type = cpu_to_be16(GELIC_EURUS_BSS_INFRA); - common->op_mode = cpu_to_be16(GELIC_EURUS_OPMODE_11BG); - - common->scan_index = cpu_to_be16(bss->eurus_index); - switch (wl->auth_method) { - case GELIC_EURUS_AUTH_OPEN: - common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_OPEN); - break; - case GELIC_EURUS_AUTH_SHARED: - common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_SHARED); - break; - } - -#ifdef DEBUG - scan_list_dump(wl); -#endif - pr_debug("%s: common cfg index=%d bsstype=%d auth=%d\n", __func__, - be16_to_cpu(common->scan_index), - be16_to_cpu(common->bss_type), - be16_to_cpu(common->auth_method)); - - cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_COMMON_CFG, - common, sizeof(*common)); - if (!cmd || cmd->status || cmd->cmd_status) { - ret = -ENOMEM; - kfree(cmd); - goto out; - } - kfree(cmd); - - /* WEP/WPA */ - switch (wl->wpa_level) { - case GELIC_WL_WPA_LEVEL_NONE: - /* If WEP or no security, setup WEP config */ - ret = gelic_wl_do_wep_setup(wl); - break; - case GELIC_WL_WPA_LEVEL_WPA: - case GELIC_WL_WPA_LEVEL_WPA2: - ret = gelic_wl_do_wpa_setup(wl); - break; - } - - if (ret) { - pr_debug("%s: WEP/WPA setup failed %d\n", __func__, - ret); - ret = -EPERM; - gelic_wl_send_iwap_event(wl, NULL); - goto out; - } - - /* start association */ - init_completion(&wl->assoc_done); - wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATING; - cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_ASSOC, - NULL, 0); - if (!cmd || cmd->status || cmd->cmd_status) { - pr_debug("%s: assoc request failed\n", __func__); - wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; - kfree(cmd); - ret = -ENOMEM; - gelic_wl_send_iwap_event(wl, NULL); - goto out; - } - kfree(cmd); - - /* wait for connected event */ - rc = wait_for_completion_timeout(&wl->assoc_done, HZ * 4);/*FIXME*/ - - if (!rc) { - /* timeouted. Maybe key or cyrpt mode is wrong */ - pr_info("%s: connect timeout\n", __func__); - cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, - NULL, 0); - kfree(cmd); - wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; - gelic_wl_send_iwap_event(wl, NULL); - ret = -ENXIO; - } else { - wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATED; - /* copy bssid */ - memcpy(wl->active_bssid, &bss->hwinfo->bssid[2], ETH_ALEN); - - /* send connect event */ - gelic_wl_send_iwap_event(wl, wl->active_bssid); - pr_info("%s: connected\n", __func__); - } -out: - free_page((unsigned long)common); - pr_debug("%s: ->\n", __func__); - return ret; -} - -/* - * connected event - */ -static void gelic_wl_connected_event(struct gelic_wl_info *wl, - u64 event) -{ - u64 desired_event = 0; - - switch (wl->wpa_level) { - case GELIC_WL_WPA_LEVEL_NONE: - desired_event = GELIC_LV1_WL_EVENT_CONNECTED; - break; - case GELIC_WL_WPA_LEVEL_WPA: - case GELIC_WL_WPA_LEVEL_WPA2: - desired_event = GELIC_LV1_WL_EVENT_WPA_CONNECTED; - break; - } - - if (desired_event == event) { - pr_debug("%s: completed\n", __func__); - complete(&wl->assoc_done); - netif_carrier_on(port_to_netdev(wl_port(wl))); - } else - pr_debug("%s: event %#llx under wpa\n", - __func__, event); -} - -/* - * disconnect event - */ -static void gelic_wl_disconnect_event(struct gelic_wl_info *wl, - u64 event) -{ - struct gelic_eurus_cmd *cmd; - int lock; - - /* - * If we fall here in the middle of association, - * associate_bss() should be waiting for complation of - * wl->assoc_done. - * As it waits with timeout, just leave assoc_done - * uncompleted, then it terminates with timeout - */ - if (!mutex_trylock(&wl->assoc_stat_lock)) { - pr_debug("%s: already locked\n", __func__); - lock = 0; - } else { - pr_debug("%s: obtain lock\n", __func__); - lock = 1; - } - - cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0); - kfree(cmd); - - /* send disconnected event to the supplicant */ - if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) - gelic_wl_send_iwap_event(wl, NULL); - - wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; - netif_carrier_off(port_to_netdev(wl_port(wl))); - - if (lock) - mutex_unlock(&wl->assoc_stat_lock); -} -/* - * event worker - */ -#ifdef DEBUG -static const char *eventstr(enum gelic_lv1_wl_event event) -{ - static char buf[32]; - char *ret; - if (event & GELIC_LV1_WL_EVENT_DEVICE_READY) - ret = "EURUS_READY"; - else if (event & GELIC_LV1_WL_EVENT_SCAN_COMPLETED) - ret = "SCAN_COMPLETED"; - else if (event & GELIC_LV1_WL_EVENT_DEAUTH) - ret = "DEAUTH"; - else if (event & GELIC_LV1_WL_EVENT_BEACON_LOST) - ret = "BEACON_LOST"; - else if (event & GELIC_LV1_WL_EVENT_CONNECTED) - ret = "CONNECTED"; - else if (event & GELIC_LV1_WL_EVENT_WPA_CONNECTED) - ret = "WPA_CONNECTED"; - else if (event & GELIC_LV1_WL_EVENT_WPA_ERROR) - ret = "WPA_ERROR"; - else { - sprintf(buf, "Unknown(%#x)", event); - ret = buf; - } - return ret; -} -#else -static const char *eventstr(enum gelic_lv1_wl_event event) -{ - return NULL; -} -#endif -static void gelic_wl_event_worker(struct work_struct *work) -{ - struct gelic_wl_info *wl; - struct gelic_port *port; - u64 event, tmp; - int status; - - pr_debug("%s:start\n", __func__); - wl = container_of(work, struct gelic_wl_info, event_work.work); - port = wl_port(wl); - while (1) { - status = lv1_net_control(bus_id(port->card), dev_id(port->card), - GELIC_LV1_GET_WLAN_EVENT, 0, 0, 0, - &event, &tmp); - if (status) { - if (status != LV1_NO_ENTRY) - pr_debug("%s:wlan event failed %d\n", - __func__, status); - /* got all events */ - pr_debug("%s:end\n", __func__); - return; - } - pr_debug("%s: event=%s\n", __func__, eventstr(event)); - switch (event) { - case GELIC_LV1_WL_EVENT_SCAN_COMPLETED: - gelic_wl_scan_complete_event(wl); - break; - case GELIC_LV1_WL_EVENT_BEACON_LOST: - case GELIC_LV1_WL_EVENT_DEAUTH: - gelic_wl_disconnect_event(wl, event); - break; - case GELIC_LV1_WL_EVENT_CONNECTED: - case GELIC_LV1_WL_EVENT_WPA_CONNECTED: - gelic_wl_connected_event(wl, event); - break; - default: - break; - } - } /* while */ -} -/* - * association worker - */ -static void gelic_wl_assoc_worker(struct work_struct *work) -{ - struct gelic_wl_info *wl; - - struct gelic_wl_scan_info *best_bss; - int ret; - unsigned long irqflag; - u8 *essid; - size_t essid_len; - - wl = container_of(work, struct gelic_wl_info, assoc_work.work); - - mutex_lock(&wl->assoc_stat_lock); - - if (wl->assoc_stat != GELIC_WL_ASSOC_STAT_DISCONN) - goto out; - - spin_lock_irqsave(&wl->lock, irqflag); - if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) { - pr_debug("%s: assoc ESSID configured %s\n", __func__, - wl->essid); - essid = wl->essid; - essid_len = wl->essid_len; - } else { - essid = NULL; - essid_len = 0; - } - spin_unlock_irqrestore(&wl->lock, irqflag); - - ret = gelic_wl_start_scan(wl, 0, essid, essid_len); - if (ret == -ERESTARTSYS) { - pr_debug("%s: scan start failed association\n", __func__); - schedule_delayed_work(&wl->assoc_work, HZ/10); /*FIXME*/ - goto out; - } else if (ret) { - pr_info("%s: scan prerequisite failed\n", __func__); - goto out; - } - - /* - * Wait for bss scan completion - * If we have scan list already, gelic_wl_start_scan() - * returns OK and raises the complete. Thus, - * it's ok to wait unconditionally here - */ - wait_for_completion(&wl->scan_done); - - pr_debug("%s: scan done\n", __func__); - mutex_lock(&wl->scan_lock); - if (wl->scan_stat != GELIC_WL_SCAN_STAT_GOT_LIST) { - gelic_wl_send_iwap_event(wl, NULL); - pr_info("%s: no scan list. association failed\n", __func__); - goto scan_lock_out; - } - - /* find best matching bss */ - best_bss = gelic_wl_find_best_bss(wl); - if (!best_bss) { - gelic_wl_send_iwap_event(wl, NULL); - pr_info("%s: no bss matched. association failed\n", __func__); - goto scan_lock_out; - } - - /* ok, do association */ - ret = gelic_wl_associate_bss(wl, best_bss); - if (ret) - pr_info("%s: association failed %d\n", __func__, ret); -scan_lock_out: - mutex_unlock(&wl->scan_lock); -out: - mutex_unlock(&wl->assoc_stat_lock); -} -/* - * Interrupt handler - * Called from the ethernet interrupt handler - * Processes wireless specific virtual interrupts only - */ -void gelic_wl_interrupt(struct net_device *netdev, u64 status) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - - if (status & GELIC_CARD_WLAN_COMMAND_COMPLETED) { - pr_debug("%s:cmd complete\n", __func__); - complete(&wl->cmd_done_intr); - } - - if (status & GELIC_CARD_WLAN_EVENT_RECEIVED) { - pr_debug("%s:event received\n", __func__); - queue_delayed_work(wl->event_queue, &wl->event_work, 0); - } -} - -/* - * driver helpers - */ -static const iw_handler gelic_wl_wext_handler[] = -{ - IW_HANDLER(SIOCGIWNAME, gelic_wl_get_name), - IW_HANDLER(SIOCGIWRANGE, gelic_wl_get_range), - IW_HANDLER(SIOCSIWSCAN, gelic_wl_set_scan), - IW_HANDLER(SIOCGIWSCAN, gelic_wl_get_scan), - IW_HANDLER(SIOCSIWAUTH, gelic_wl_set_auth), - IW_HANDLER(SIOCGIWAUTH, gelic_wl_get_auth), - IW_HANDLER(SIOCSIWESSID, gelic_wl_set_essid), - IW_HANDLER(SIOCGIWESSID, gelic_wl_get_essid), - IW_HANDLER(SIOCSIWENCODE, gelic_wl_set_encode), - IW_HANDLER(SIOCGIWENCODE, gelic_wl_get_encode), - IW_HANDLER(SIOCSIWAP, gelic_wl_set_ap), - IW_HANDLER(SIOCGIWAP, gelic_wl_get_ap), - IW_HANDLER(SIOCSIWENCODEEXT, gelic_wl_set_encodeext), - IW_HANDLER(SIOCGIWENCODEEXT, gelic_wl_get_encodeext), - IW_HANDLER(SIOCSIWMODE, gelic_wl_set_mode), - IW_HANDLER(SIOCGIWMODE, gelic_wl_get_mode), - IW_HANDLER(SIOCGIWNICKN, gelic_wl_get_nick), -}; - -static const struct iw_handler_def gelic_wl_wext_handler_def = { - .num_standard = ARRAY_SIZE(gelic_wl_wext_handler), - .standard = gelic_wl_wext_handler, - .get_wireless_stats = gelic_wl_get_wireless_stats, -}; - -static struct net_device * __devinit gelic_wl_alloc(struct gelic_card *card) -{ - struct net_device *netdev; - struct gelic_port *port; - struct gelic_wl_info *wl; - unsigned int i; - - pr_debug("%s:start\n", __func__); - netdev = alloc_etherdev(sizeof(struct gelic_port) + - sizeof(struct gelic_wl_info)); - pr_debug("%s: netdev =%p card=%p\n", __func__, netdev, card); - if (!netdev) - return NULL; - - strcpy(netdev->name, "wlan%d"); - - port = netdev_priv(netdev); - port->netdev = netdev; - port->card = card; - port->type = GELIC_PORT_WIRELESS; - - wl = port_wl(port); - pr_debug("%s: wl=%p port=%p\n", __func__, wl, port); - - /* allocate scan list */ - wl->networks = kzalloc(sizeof(struct gelic_wl_scan_info) * - GELIC_WL_BSS_MAX_ENT, GFP_KERNEL); - - if (!wl->networks) - goto fail_bss; - - wl->eurus_cmd_queue = create_singlethread_workqueue("gelic_cmd"); - if (!wl->eurus_cmd_queue) - goto fail_cmd_workqueue; - - wl->event_queue = create_singlethread_workqueue("gelic_event"); - if (!wl->event_queue) - goto fail_event_workqueue; - - INIT_LIST_HEAD(&wl->network_free_list); - INIT_LIST_HEAD(&wl->network_list); - for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++) - list_add_tail(&wl->networks[i].list, - &wl->network_free_list); - init_completion(&wl->cmd_done_intr); - - INIT_DELAYED_WORK(&wl->event_work, gelic_wl_event_worker); - INIT_DELAYED_WORK(&wl->assoc_work, gelic_wl_assoc_worker); - mutex_init(&wl->scan_lock); - mutex_init(&wl->assoc_stat_lock); - - init_completion(&wl->scan_done); - /* for the case that no scan request is issued and stop() is called */ - complete(&wl->scan_done); - - spin_lock_init(&wl->lock); - - wl->scan_age = 5*HZ; /* FIXME */ - - /* buffer for receiving scanned list etc */ - BUILD_BUG_ON(PAGE_SIZE < - sizeof(struct gelic_eurus_scan_info) * - GELIC_EURUS_MAX_SCAN); - pr_debug("%s:end\n", __func__); - return netdev; - -fail_event_workqueue: - destroy_workqueue(wl->eurus_cmd_queue); -fail_cmd_workqueue: - kfree(wl->networks); -fail_bss: - free_netdev(netdev); - pr_debug("%s:end error\n", __func__); - return NULL; - -} - -static void gelic_wl_free(struct gelic_wl_info *wl) -{ - struct gelic_wl_scan_info *scan_info; - unsigned int i; - - pr_debug("%s: <-\n", __func__); - - pr_debug("%s: destroy queues\n", __func__); - destroy_workqueue(wl->eurus_cmd_queue); - destroy_workqueue(wl->event_queue); - - scan_info = wl->networks; - for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++, scan_info++) - kfree(scan_info->hwinfo); - kfree(wl->networks); - - free_netdev(port_to_netdev(wl_port(wl))); - - pr_debug("%s: ->\n", __func__); -} - -static int gelic_wl_try_associate(struct net_device *netdev) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); - int ret = -1; - unsigned int i; - - pr_debug("%s: <-\n", __func__); - - /* check constraits for start association */ - /* for no access restriction AP */ - if (wl->group_cipher_method == GELIC_WL_CIPHER_NONE) { - if (test_bit(GELIC_WL_STAT_CONFIGURED, - &wl->stat)) - goto do_associate; - else { - pr_debug("%s: no wep, not configured\n", __func__); - return ret; - } - } - - /* for WEP, one of four keys should be set */ - if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { - /* one of keys set */ - for (i = 0; i < GELIC_WEP_KEYS; i++) { - if (test_bit(i, &wl->key_enabled)) - goto do_associate; - } - pr_debug("%s: WEP, but no key specified\n", __func__); - return ret; - } - - /* for WPA[2], psk should be set */ - if ((wl->group_cipher_method == GELIC_WL_CIPHER_TKIP) || - (wl->group_cipher_method == GELIC_WL_CIPHER_AES)) { - if (test_bit(GELIC_WL_STAT_WPA_PSK_SET, - &wl->stat)) - goto do_associate; - else { - pr_debug("%s: AES/TKIP, but PSK not configured\n", - __func__); - return ret; - } - } - -do_associate: - ret = schedule_delayed_work(&wl->assoc_work, 0); - pr_debug("%s: start association work %d\n", __func__, ret); - return ret; -} - -/* - * netdev handlers - */ -static int gelic_wl_open(struct net_device *netdev) -{ - struct gelic_card *card = netdev_card(netdev); - - pr_debug("%s:->%p\n", __func__, netdev); - - gelic_card_up(card); - - /* try to associate */ - gelic_wl_try_associate(netdev); - - netif_start_queue(netdev); - - pr_debug("%s:<-\n", __func__); - return 0; -} - -/* - * reset state machine - */ -static int gelic_wl_reset_state(struct gelic_wl_info *wl) -{ - struct gelic_wl_scan_info *target; - struct gelic_wl_scan_info *tmp; - - /* empty scan list */ - list_for_each_entry_safe(target, tmp, &wl->network_list, list) { - list_move_tail(&target->list, &wl->network_free_list); - } - wl->scan_stat = GELIC_WL_SCAN_STAT_INIT; - - /* clear configuration */ - wl->auth_method = GELIC_EURUS_AUTH_OPEN; - wl->group_cipher_method = GELIC_WL_CIPHER_NONE; - wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE; - wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; - - wl->key_enabled = 0; - wl->current_key = 0; - - wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE; - wl->psk_len = 0; - - wl->essid_len = 0; - memset(wl->essid, 0, sizeof(wl->essid)); - memset(wl->bssid, 0, sizeof(wl->bssid)); - memset(wl->active_bssid, 0, sizeof(wl->active_bssid)); - - wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; - - memset(&wl->iwstat, 0, sizeof(wl->iwstat)); - /* all status bit clear */ - wl->stat = 0; - return 0; -} - -/* - * Tell eurus to terminate association - */ -static void gelic_wl_disconnect(struct net_device *netdev) -{ - struct gelic_port *port = netdev_priv(netdev); - struct gelic_wl_info *wl = port_wl(port); - struct gelic_eurus_cmd *cmd; - - /* - * If scann process is running on chip, - * further requests will be rejected - */ - if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) - wait_for_completion_timeout(&wl->scan_done, HZ); - - cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0); - kfree(cmd); - gelic_wl_send_iwap_event(wl, NULL); -}; - -static int gelic_wl_stop(struct net_device *netdev) -{ - struct gelic_port *port = netdev_priv(netdev); - struct gelic_wl_info *wl = port_wl(port); - struct gelic_card *card = netdev_card(netdev); - - pr_debug("%s:<-\n", __func__); - - /* - * Cancel pending association work. - * event work can run after netdev down - */ - cancel_delayed_work(&wl->assoc_work); - - if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) - gelic_wl_disconnect(netdev); - - /* reset our state machine */ - gelic_wl_reset_state(wl); - - netif_stop_queue(netdev); - - gelic_card_down(card); - - pr_debug("%s:->\n", __func__); - return 0; -} - -/* -- */ - -static const struct net_device_ops gelic_wl_netdevice_ops = { - .ndo_open = gelic_wl_open, - .ndo_stop = gelic_wl_stop, - .ndo_start_xmit = gelic_net_xmit, - .ndo_set_multicast_list = gelic_net_set_multi, - .ndo_change_mtu = gelic_net_change_mtu, - .ndo_tx_timeout = gelic_net_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = gelic_net_poll_controller, -#endif -}; - -static const struct ethtool_ops gelic_wl_ethtool_ops = { - .get_drvinfo = gelic_net_get_drvinfo, - .get_link = gelic_wl_get_link, -}; - -static void __devinit gelic_wl_setup_netdev_ops(struct net_device *netdev) -{ - struct gelic_wl_info *wl; - wl = port_wl(netdev_priv(netdev)); - BUG_ON(!wl); - netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT; - - netdev->ethtool_ops = &gelic_wl_ethtool_ops; - netdev->netdev_ops = &gelic_wl_netdevice_ops; - netdev->wireless_data = &wl->wireless_data; - netdev->wireless_handlers = &gelic_wl_wext_handler_def; -} - -/* - * driver probe/remove - */ -int __devinit gelic_wl_driver_probe(struct gelic_card *card) -{ - int ret; - struct net_device *netdev; - - pr_debug("%s:start\n", __func__); - - if (ps3_compare_firmware_version(1, 6, 0) < 0) - return 0; - if (!card->vlan[GELIC_PORT_WIRELESS].tx) - return 0; - - /* alloc netdevice for wireless */ - netdev = gelic_wl_alloc(card); - if (!netdev) - return -ENOMEM; - - /* setup net_device structure */ - SET_NETDEV_DEV(netdev, &card->dev->core); - gelic_wl_setup_netdev_ops(netdev); - - /* setup some of net_device and register it */ - ret = gelic_net_setup_netdev(netdev, card); - if (ret) - goto fail_setup; - card->netdev[GELIC_PORT_WIRELESS] = netdev; - - /* add enable wireless interrupt */ - card->irq_mask |= GELIC_CARD_WLAN_EVENT_RECEIVED | - GELIC_CARD_WLAN_COMMAND_COMPLETED; - /* to allow wireless commands while both interfaces are down */ - gelic_card_set_irq_mask(card, GELIC_CARD_WLAN_EVENT_RECEIVED | - GELIC_CARD_WLAN_COMMAND_COMPLETED); - pr_debug("%s:end\n", __func__); - return 0; - -fail_setup: - gelic_wl_free(port_wl(netdev_port(netdev))); - - return ret; -} - -int gelic_wl_driver_remove(struct gelic_card *card) -{ - struct gelic_wl_info *wl; - struct net_device *netdev; - - pr_debug("%s:start\n", __func__); - - if (ps3_compare_firmware_version(1, 6, 0) < 0) - return 0; - if (!card->vlan[GELIC_PORT_WIRELESS].tx) - return 0; - - netdev = card->netdev[GELIC_PORT_WIRELESS]; - wl = port_wl(netdev_priv(netdev)); - - /* if the interface was not up, but associated */ - if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) - gelic_wl_disconnect(netdev); - - complete(&wl->cmd_done_intr); - - /* cancel all work queue */ - cancel_delayed_work(&wl->assoc_work); - cancel_delayed_work(&wl->event_work); - flush_workqueue(wl->eurus_cmd_queue); - flush_workqueue(wl->event_queue); - - unregister_netdev(netdev); - - /* disable wireless interrupt */ - pr_debug("%s: disable intr\n", __func__); - card->irq_mask &= ~(GELIC_CARD_WLAN_EVENT_RECEIVED | - GELIC_CARD_WLAN_COMMAND_COMPLETED); - /* free bss list, netdev*/ - gelic_wl_free(wl); - pr_debug("%s:end\n", __func__); - return 0; -} diff --git a/drivers/net/ps3_gelic_wireless.h b/drivers/net/ps3_gelic_wireless.h deleted file mode 100644 index f7e51b7d7049..000000000000 --- a/drivers/net/ps3_gelic_wireless.h +++ /dev/null @@ -1,326 +0,0 @@ -/* - * PS3 gelic network driver. - * - * Copyright (C) 2007 Sony Computer Entertainment Inc. - * Copyright 2007 Sony Corporation - * - * 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 version 2. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#ifndef _GELIC_WIRELESS_H -#define _GELIC_WIRELESS_H - -#include -#include - - -/* return value from GELIC_LV1_GET_WLAN_EVENT netcontrol */ -enum gelic_lv1_wl_event { - GELIC_LV1_WL_EVENT_DEVICE_READY = 0x01, /* Eurus ready */ - GELIC_LV1_WL_EVENT_SCAN_COMPLETED = 0x02, /* Scan has completed */ - GELIC_LV1_WL_EVENT_DEAUTH = 0x04, /* Deauthed by the AP */ - GELIC_LV1_WL_EVENT_BEACON_LOST = 0x08, /* Beacon lost detected */ - GELIC_LV1_WL_EVENT_CONNECTED = 0x10, /* Connected to AP */ - GELIC_LV1_WL_EVENT_WPA_CONNECTED = 0x20, /* WPA connection */ - GELIC_LV1_WL_EVENT_WPA_ERROR = 0x40, /* MIC error */ -}; - -/* arguments for GELIC_LV1_POST_WLAN_COMMAND netcontrol */ -enum gelic_eurus_command { - GELIC_EURUS_CMD_ASSOC = 1, /* association start */ - GELIC_EURUS_CMD_DISASSOC = 2, /* disassociate */ - GELIC_EURUS_CMD_START_SCAN = 3, /* scan start */ - GELIC_EURUS_CMD_GET_SCAN = 4, /* get scan result */ - GELIC_EURUS_CMD_SET_COMMON_CFG = 5, /* set common config */ - GELIC_EURUS_CMD_GET_COMMON_CFG = 6, /* set common config */ - GELIC_EURUS_CMD_SET_WEP_CFG = 7, /* set WEP config */ - GELIC_EURUS_CMD_GET_WEP_CFG = 8, /* get WEP config */ - GELIC_EURUS_CMD_SET_WPA_CFG = 9, /* set WPA config */ - GELIC_EURUS_CMD_GET_WPA_CFG = 10, /* get WPA config */ - GELIC_EURUS_CMD_GET_RSSI_CFG = 11, /* get RSSI info. */ - GELIC_EURUS_CMD_MAX_INDEX -}; - -/* for GELIC_EURUS_CMD_COMMON_CFG */ -enum gelic_eurus_bss_type { - GELIC_EURUS_BSS_INFRA = 0, - GELIC_EURUS_BSS_ADHOC = 1, /* not supported */ -}; - -enum gelic_eurus_auth_method { - GELIC_EURUS_AUTH_OPEN = 0, /* FIXME: WLAN_AUTH_OPEN */ - GELIC_EURUS_AUTH_SHARED = 1, /* not supported */ -}; - -enum gelic_eurus_opmode { - GELIC_EURUS_OPMODE_11BG = 0, /* 802.11b/g */ - GELIC_EURUS_OPMODE_11B = 1, /* 802.11b only */ - GELIC_EURUS_OPMODE_11G = 2, /* 802.11g only */ -}; - -struct gelic_eurus_common_cfg { - /* all fields are big endian */ - u16 scan_index; - u16 bss_type; /* infra or adhoc */ - u16 auth_method; /* shared key or open */ - u16 op_mode; /* B/G */ -} __packed; - - -/* for GELIC_EURUS_CMD_WEP_CFG */ -enum gelic_eurus_wep_security { - GELIC_EURUS_WEP_SEC_NONE = 0, - GELIC_EURUS_WEP_SEC_40BIT = 1, - GELIC_EURUS_WEP_SEC_104BIT = 2, -}; - -struct gelic_eurus_wep_cfg { - /* all fields are big endian */ - u16 security; - u8 key[4][16]; -} __packed; - -/* for GELIC_EURUS_CMD_WPA_CFG */ -enum gelic_eurus_wpa_security { - GELIC_EURUS_WPA_SEC_NONE = 0x0000, - /* group=TKIP, pairwise=TKIP */ - GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP = 0x0001, - /* group=AES, pairwise=AES */ - GELIC_EURUS_WPA_SEC_WPA_AES_AES = 0x0002, - /* group=TKIP, pairwise=TKIP */ - GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP = 0x0004, - /* group=AES, pairwise=AES */ - GELIC_EURUS_WPA_SEC_WPA2_AES_AES = 0x0008, - /* group=TKIP, pairwise=AES */ - GELIC_EURUS_WPA_SEC_WPA_TKIP_AES = 0x0010, - /* group=TKIP, pairwise=AES */ - GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES = 0x0020, -}; - -enum gelic_eurus_wpa_psk_type { - GELIC_EURUS_WPA_PSK_PASSPHRASE = 0, /* passphrase string */ - GELIC_EURUS_WPA_PSK_BIN = 1, /* 32 bytes binary key */ -}; - -#define GELIC_WL_EURUS_PSK_MAX_LEN 64 -#define WPA_PSK_LEN 32 /* WPA spec says 256bit */ - -struct gelic_eurus_wpa_cfg { - /* all fields are big endian */ - u16 security; - u16 psk_type; /* psk key encoding type */ - u8 psk[GELIC_WL_EURUS_PSK_MAX_LEN]; /* psk key; hex or passphrase */ -} __packed; - -/* for GELIC_EURUS_CMD_{START,GET}_SCAN */ -enum gelic_eurus_scan_capability { - GELIC_EURUS_SCAN_CAP_ADHOC = 0x0000, - GELIC_EURUS_SCAN_CAP_INFRA = 0x0001, - GELIC_EURUS_SCAN_CAP_MASK = 0x0001, -}; - -enum gelic_eurus_scan_sec_type { - GELIC_EURUS_SCAN_SEC_NONE = 0x0000, - GELIC_EURUS_SCAN_SEC_WEP = 0x0100, - GELIC_EURUS_SCAN_SEC_WPA = 0x0200, - GELIC_EURUS_SCAN_SEC_WPA2 = 0x0400, - GELIC_EURUS_SCAN_SEC_MASK = 0x0f00, -}; - -enum gelic_eurus_scan_sec_wep_type { - GELIC_EURUS_SCAN_SEC_WEP_UNKNOWN = 0x0000, - GELIC_EURUS_SCAN_SEC_WEP_40 = 0x0001, - GELIC_EURUS_SCAN_SEC_WEP_104 = 0x0002, - GELIC_EURUS_SCAN_SEC_WEP_MASK = 0x0003, -}; - -enum gelic_eurus_scan_sec_wpa_type { - GELIC_EURUS_SCAN_SEC_WPA_UNKNOWN = 0x0000, - GELIC_EURUS_SCAN_SEC_WPA_TKIP = 0x0001, - GELIC_EURUS_SCAN_SEC_WPA_AES = 0x0002, - GELIC_EURUS_SCAN_SEC_WPA_MASK = 0x0003, -}; - -/* - * hw BSS information structure returned from GELIC_EURUS_CMD_GET_SCAN - */ -struct gelic_eurus_scan_info { - /* all fields are big endian */ - __be16 size; - __be16 rssi; /* percentage */ - __be16 channel; /* channel number */ - __be16 beacon_period; /* FIXME: in msec unit */ - __be16 capability; - __be16 security; - u8 bssid[8]; /* last ETH_ALEN are valid. bssid[0],[1] are unused */ - u8 essid[32]; /* IW_ESSID_MAX_SIZE */ - u8 rate[16]; /* first 12 are valid */ - u8 ext_rate[16]; /* first 16 are valid */ - __be32 reserved1; - __be32 reserved2; - __be32 reserved3; - __be32 reserved4; - u8 elements[0]; /* ie */ -} __packed; - -/* the hypervisor returns bbs up to 16 */ -#define GELIC_EURUS_MAX_SCAN (16) -struct gelic_wl_scan_info { - struct list_head list; - struct gelic_eurus_scan_info *hwinfo; - - int valid; /* set 1 if this entry was in latest scanned list - * from Eurus */ - unsigned int eurus_index; /* index in the Eurus list */ - unsigned long last_scanned; /* acquired time */ - - unsigned int rate_len; - unsigned int rate_ext_len; - unsigned int essid_len; -}; - -/* for GELIC_EURUS_CMD_GET_RSSI */ -struct gelic_eurus_rssi_info { - /* big endian */ - __be16 rssi; -} __packed; - - -/* for 'stat' member of gelic_wl_info */ -enum gelic_wl_info_status_bit { - GELIC_WL_STAT_CONFIGURED, - GELIC_WL_STAT_CH_INFO, /* ch info acquired */ - GELIC_WL_STAT_ESSID_SET, /* ESSID specified by userspace */ - GELIC_WL_STAT_BSSID_SET, /* BSSID specified by userspace */ - GELIC_WL_STAT_WPA_PSK_SET, /* PMK specified by userspace */ - GELIC_WL_STAT_WPA_LEVEL_SET, /* WEP or WPA[2] selected */ -}; - -/* for 'scan_stat' member of gelic_wl_info */ -enum gelic_wl_scan_state { - /* just initialized or get last scan result failed */ - GELIC_WL_SCAN_STAT_INIT, - /* scan request issued, accepted or chip is scanning */ - GELIC_WL_SCAN_STAT_SCANNING, - /* scan results retrieved */ - GELIC_WL_SCAN_STAT_GOT_LIST, -}; - -/* for 'cipher_method' */ -enum gelic_wl_cipher_method { - GELIC_WL_CIPHER_NONE, - GELIC_WL_CIPHER_WEP, - GELIC_WL_CIPHER_TKIP, - GELIC_WL_CIPHER_AES, -}; - -/* for 'wpa_level' */ -enum gelic_wl_wpa_level { - GELIC_WL_WPA_LEVEL_NONE, - GELIC_WL_WPA_LEVEL_WPA, - GELIC_WL_WPA_LEVEL_WPA2, -}; - -/* for 'assoc_stat' */ -enum gelic_wl_assoc_state { - GELIC_WL_ASSOC_STAT_DISCONN, - GELIC_WL_ASSOC_STAT_ASSOCIATING, - GELIC_WL_ASSOC_STAT_ASSOCIATED, -}; -/* part of private data alloc_etherdev() allocated */ -#define GELIC_WEP_KEYS 4 -struct gelic_wl_info { - /* bss list */ - struct mutex scan_lock; - struct list_head network_list; - struct list_head network_free_list; - struct gelic_wl_scan_info *networks; - - unsigned long scan_age; /* last scanned time */ - enum gelic_wl_scan_state scan_stat; - struct completion scan_done; - - /* eurus command queue */ - struct workqueue_struct *eurus_cmd_queue; - struct completion cmd_done_intr; - - /* eurus event handling */ - struct workqueue_struct *event_queue; - struct delayed_work event_work; - - /* wl status bits */ - unsigned long stat; - enum gelic_eurus_auth_method auth_method; /* open/shared */ - enum gelic_wl_cipher_method group_cipher_method; - enum gelic_wl_cipher_method pairwise_cipher_method; - enum gelic_wl_wpa_level wpa_level; /* wpa/wpa2 */ - - /* association handling */ - struct mutex assoc_stat_lock; - struct delayed_work assoc_work; - enum gelic_wl_assoc_state assoc_stat; - struct completion assoc_done; - - spinlock_t lock; - u16 ch_info; /* available channels. bit0 = ch1 */ - /* WEP keys */ - u8 key[GELIC_WEP_KEYS][IW_ENCODING_TOKEN_MAX]; - unsigned long key_enabled; - unsigned int key_len[GELIC_WEP_KEYS]; - unsigned int current_key; - /* WWPA PSK */ - u8 psk[GELIC_WL_EURUS_PSK_MAX_LEN]; - enum gelic_eurus_wpa_psk_type psk_type; - unsigned int psk_len; - - u8 essid[IW_ESSID_MAX_SIZE]; - u8 bssid[ETH_ALEN]; /* userland requested */ - u8 active_bssid[ETH_ALEN]; /* associated bssid */ - unsigned int essid_len; - - struct iw_public_data wireless_data; - struct iw_statistics iwstat; -}; - -#define GELIC_WL_BSS_MAX_ENT 32 -#define GELIC_WL_ASSOC_RETRY 50 -static inline struct gelic_port *wl_port(struct gelic_wl_info *wl) -{ - return container_of((void *)wl, struct gelic_port, priv); -} -static inline struct gelic_wl_info *port_wl(struct gelic_port *port) -{ - return port_priv(port); -} - -struct gelic_eurus_cmd { - struct work_struct work; - struct gelic_wl_info *wl; - unsigned int cmd; /* command code */ - u64 tag; - u64 size; - void *buffer; - unsigned int buf_size; - struct completion done; - int status; - u64 cmd_status; -}; - -/* private ioctls to pass PSK */ -#define GELIC_WL_PRIV_SET_PSK (SIOCIWFIRSTPRIV + 0) -#define GELIC_WL_PRIV_GET_PSK (SIOCIWFIRSTPRIV + 1) - -extern int gelic_wl_driver_probe(struct gelic_card *card); -extern int gelic_wl_driver_remove(struct gelic_card *card); -extern void gelic_wl_interrupt(struct net_device *netdev, u64 status); -#endif /* _GELIC_WIRELESS_H */ diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c deleted file mode 100644 index 1ff3491c8240..000000000000 --- a/drivers/net/spider_net.c +++ /dev/null @@ -1,2603 +0,0 @@ -/* - * Network device driver for Cell Processor-Based Blade and Celleb platform - * - * (C) Copyright IBM Corp. 2005 - * (C) Copyright 2006 TOSHIBA CORPORATION - * - * Authors : Utz Bacher - * Jens Osterkamp - * - * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "spider_net.h" - -MODULE_AUTHOR("Utz Bacher and Jens Osterkamp " \ - ""); -MODULE_DESCRIPTION("Spider Southbridge Gigabit Ethernet driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(VERSION); -MODULE_FIRMWARE(SPIDER_NET_FIRMWARE_NAME); - -static int rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_DEFAULT; -static int tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_DEFAULT; - -module_param(rx_descriptors, int, 0444); -module_param(tx_descriptors, int, 0444); - -MODULE_PARM_DESC(rx_descriptors, "number of descriptors used " \ - "in rx chains"); -MODULE_PARM_DESC(tx_descriptors, "number of descriptors used " \ - "in tx chain"); - -char spider_net_driver_name[] = "spidernet"; - -static DEFINE_PCI_DEVICE_TABLE(spider_net_pci_tbl) = { - { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SPIDER_NET, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, spider_net_pci_tbl); - -/** - * spider_net_read_reg - reads an SMMIO register of a card - * @card: device structure - * @reg: register to read from - * - * returns the content of the specified SMMIO register. - */ -static inline u32 -spider_net_read_reg(struct spider_net_card *card, u32 reg) -{ - /* We use the powerpc specific variants instead of readl_be() because - * we know spidernet is not a real PCI device and we can thus avoid the - * performance hit caused by the PCI workarounds. - */ - return in_be32(card->regs + reg); -} - -/** - * spider_net_write_reg - writes to an SMMIO register of a card - * @card: device structure - * @reg: register to write to - * @value: value to write into the specified SMMIO register - */ -static inline void -spider_net_write_reg(struct spider_net_card *card, u32 reg, u32 value) -{ - /* We use the powerpc specific variants instead of writel_be() because - * we know spidernet is not a real PCI device and we can thus avoid the - * performance hit caused by the PCI workarounds. - */ - out_be32(card->regs + reg, value); -} - -/** spider_net_write_phy - write to phy register - * @netdev: adapter to be written to - * @mii_id: id of MII - * @reg: PHY register - * @val: value to be written to phy register - * - * spider_net_write_phy_register writes to an arbitrary PHY - * register via the spider GPCWOPCMD register. We assume the queue does - * not run full (not more than 15 commands outstanding). - **/ -static void -spider_net_write_phy(struct net_device *netdev, int mii_id, - int reg, int val) -{ - struct spider_net_card *card = netdev_priv(netdev); - u32 writevalue; - - writevalue = ((u32)mii_id << 21) | - ((u32)reg << 16) | ((u32)val); - - spider_net_write_reg(card, SPIDER_NET_GPCWOPCMD, writevalue); -} - -/** spider_net_read_phy - read from phy register - * @netdev: network device to be read from - * @mii_id: id of MII - * @reg: PHY register - * - * Returns value read from PHY register - * - * spider_net_write_phy reads from an arbitrary PHY - * register via the spider GPCROPCMD register - **/ -static int -spider_net_read_phy(struct net_device *netdev, int mii_id, int reg) -{ - struct spider_net_card *card = netdev_priv(netdev); - u32 readvalue; - - readvalue = ((u32)mii_id << 21) | ((u32)reg << 16); - spider_net_write_reg(card, SPIDER_NET_GPCROPCMD, readvalue); - - /* we don't use semaphores to wait for an SPIDER_NET_GPROPCMPINT - * interrupt, as we poll for the completion of the read operation - * in spider_net_read_phy. Should take about 50 us */ - do { - readvalue = spider_net_read_reg(card, SPIDER_NET_GPCROPCMD); - } while (readvalue & SPIDER_NET_GPREXEC); - - readvalue &= SPIDER_NET_GPRDAT_MASK; - - return readvalue; -} - -/** - * spider_net_setup_aneg - initial auto-negotiation setup - * @card: device structure - **/ -static void -spider_net_setup_aneg(struct spider_net_card *card) -{ - struct mii_phy *phy = &card->phy; - u32 advertise = 0; - u16 bmsr, estat; - - bmsr = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR); - estat = spider_net_read_phy(card->netdev, phy->mii_id, MII_ESTATUS); - - if (bmsr & BMSR_10HALF) - advertise |= ADVERTISED_10baseT_Half; - if (bmsr & BMSR_10FULL) - advertise |= ADVERTISED_10baseT_Full; - if (bmsr & BMSR_100HALF) - advertise |= ADVERTISED_100baseT_Half; - if (bmsr & BMSR_100FULL) - advertise |= ADVERTISED_100baseT_Full; - - if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_TFULL)) - advertise |= SUPPORTED_1000baseT_Full; - if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_THALF)) - advertise |= SUPPORTED_1000baseT_Half; - - mii_phy_probe(phy, phy->mii_id); - phy->def->ops->setup_aneg(phy, advertise); - -} - -/** - * spider_net_rx_irq_off - switch off rx irq on this spider card - * @card: device structure - * - * switches off rx irq by masking them out in the GHIINTnMSK register - */ -static void -spider_net_rx_irq_off(struct spider_net_card *card) -{ - u32 regvalue; - - regvalue = SPIDER_NET_INT0_MASK_VALUE & (~SPIDER_NET_RXINT); - spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue); -} - -/** - * spider_net_rx_irq_on - switch on rx irq on this spider card - * @card: device structure - * - * switches on rx irq by enabling them in the GHIINTnMSK register - */ -static void -spider_net_rx_irq_on(struct spider_net_card *card) -{ - u32 regvalue; - - regvalue = SPIDER_NET_INT0_MASK_VALUE | SPIDER_NET_RXINT; - spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue); -} - -/** - * spider_net_set_promisc - sets the unicast address or the promiscuous mode - * @card: card structure - * - * spider_net_set_promisc sets the unicast destination address filter and - * thus either allows for non-promisc mode or promisc mode - */ -static void -spider_net_set_promisc(struct spider_net_card *card) -{ - u32 macu, macl; - struct net_device *netdev = card->netdev; - - if (netdev->flags & IFF_PROMISC) { - /* clear destination entry 0 */ - spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR, 0); - spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR + 0x04, 0); - spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, - SPIDER_NET_PROMISC_VALUE); - } else { - macu = netdev->dev_addr[0]; - macu <<= 8; - macu |= netdev->dev_addr[1]; - memcpy(&macl, &netdev->dev_addr[2], sizeof(macl)); - - macu |= SPIDER_NET_UA_DESCR_VALUE; - spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR, macu); - spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR + 0x04, macl); - spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, - SPIDER_NET_NONPROMISC_VALUE); - } -} - -/** - * spider_net_get_mac_address - read mac address from spider card - * @card: device structure - * - * reads MAC address from GMACUNIMACU and GMACUNIMACL registers - */ -static int -spider_net_get_mac_address(struct net_device *netdev) -{ - struct spider_net_card *card = netdev_priv(netdev); - u32 macl, macu; - - macl = spider_net_read_reg(card, SPIDER_NET_GMACUNIMACL); - macu = spider_net_read_reg(card, SPIDER_NET_GMACUNIMACU); - - netdev->dev_addr[0] = (macu >> 24) & 0xff; - netdev->dev_addr[1] = (macu >> 16) & 0xff; - netdev->dev_addr[2] = (macu >> 8) & 0xff; - netdev->dev_addr[3] = macu & 0xff; - netdev->dev_addr[4] = (macl >> 8) & 0xff; - netdev->dev_addr[5] = macl & 0xff; - - if (!is_valid_ether_addr(&netdev->dev_addr[0])) - return -EINVAL; - - return 0; -} - -/** - * spider_net_get_descr_status -- returns the status of a descriptor - * @descr: descriptor to look at - * - * returns the status as in the dmac_cmd_status field of the descriptor - */ -static inline int -spider_net_get_descr_status(struct spider_net_hw_descr *hwdescr) -{ - return hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_IND_PROC_MASK; -} - -/** - * spider_net_free_chain - free descriptor chain - * @card: card structure - * @chain: address of chain - * - */ -static void -spider_net_free_chain(struct spider_net_card *card, - struct spider_net_descr_chain *chain) -{ - struct spider_net_descr *descr; - - descr = chain->ring; - do { - descr->bus_addr = 0; - descr->hwdescr->next_descr_addr = 0; - descr = descr->next; - } while (descr != chain->ring); - - dma_free_coherent(&card->pdev->dev, chain->num_desc, - chain->hwring, chain->dma_addr); -} - -/** - * spider_net_init_chain - alloc and link descriptor chain - * @card: card structure - * @chain: address of chain - * - * We manage a circular list that mirrors the hardware structure, - * except that the hardware uses bus addresses. - * - * Returns 0 on success, <0 on failure - */ -static int -spider_net_init_chain(struct spider_net_card *card, - struct spider_net_descr_chain *chain) -{ - int i; - struct spider_net_descr *descr; - struct spider_net_hw_descr *hwdescr; - dma_addr_t buf; - size_t alloc_size; - - alloc_size = chain->num_desc * sizeof(struct spider_net_hw_descr); - - chain->hwring = dma_alloc_coherent(&card->pdev->dev, alloc_size, - &chain->dma_addr, GFP_KERNEL); - - if (!chain->hwring) - return -ENOMEM; - - memset(chain->ring, 0, chain->num_desc * sizeof(struct spider_net_descr)); - - /* Set up the hardware pointers in each descriptor */ - descr = chain->ring; - hwdescr = chain->hwring; - buf = chain->dma_addr; - for (i=0; i < chain->num_desc; i++, descr++, hwdescr++) { - hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; - hwdescr->next_descr_addr = 0; - - descr->hwdescr = hwdescr; - descr->bus_addr = buf; - descr->next = descr + 1; - descr->prev = descr - 1; - - buf += sizeof(struct spider_net_hw_descr); - } - /* do actual circular list */ - (descr-1)->next = chain->ring; - chain->ring->prev = descr-1; - - spin_lock_init(&chain->lock); - chain->head = chain->ring; - chain->tail = chain->ring; - return 0; -} - -/** - * spider_net_free_rx_chain_contents - frees descr contents in rx chain - * @card: card structure - * - * returns 0 on success, <0 on failure - */ -static void -spider_net_free_rx_chain_contents(struct spider_net_card *card) -{ - struct spider_net_descr *descr; - - descr = card->rx_chain.head; - do { - if (descr->skb) { - pci_unmap_single(card->pdev, descr->hwdescr->buf_addr, - SPIDER_NET_MAX_FRAME, - PCI_DMA_BIDIRECTIONAL); - dev_kfree_skb(descr->skb); - descr->skb = NULL; - } - descr = descr->next; - } while (descr != card->rx_chain.head); -} - -/** - * spider_net_prepare_rx_descr - Reinitialize RX descriptor - * @card: card structure - * @descr: descriptor to re-init - * - * Return 0 on success, <0 on failure. - * - * Allocates a new rx skb, iommu-maps it and attaches it to the - * descriptor. Mark the descriptor as activated, ready-to-use. - */ -static int -spider_net_prepare_rx_descr(struct spider_net_card *card, - struct spider_net_descr *descr) -{ - struct spider_net_hw_descr *hwdescr = descr->hwdescr; - dma_addr_t buf; - int offset; - int bufsize; - - /* we need to round up the buffer size to a multiple of 128 */ - bufsize = (SPIDER_NET_MAX_FRAME + SPIDER_NET_RXBUF_ALIGN - 1) & - (~(SPIDER_NET_RXBUF_ALIGN - 1)); - - /* and we need to have it 128 byte aligned, therefore we allocate a - * bit more */ - /* allocate an skb */ - descr->skb = netdev_alloc_skb(card->netdev, - bufsize + SPIDER_NET_RXBUF_ALIGN - 1); - if (!descr->skb) { - if (netif_msg_rx_err(card) && net_ratelimit()) - dev_err(&card->netdev->dev, - "Not enough memory to allocate rx buffer\n"); - card->spider_stats.alloc_rx_skb_error++; - return -ENOMEM; - } - hwdescr->buf_size = bufsize; - hwdescr->result_size = 0; - hwdescr->valid_size = 0; - hwdescr->data_status = 0; - hwdescr->data_error = 0; - - offset = ((unsigned long)descr->skb->data) & - (SPIDER_NET_RXBUF_ALIGN - 1); - if (offset) - skb_reserve(descr->skb, SPIDER_NET_RXBUF_ALIGN - offset); - /* iommu-map the skb */ - buf = pci_map_single(card->pdev, descr->skb->data, - SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(card->pdev, buf)) { - dev_kfree_skb_any(descr->skb); - descr->skb = NULL; - if (netif_msg_rx_err(card) && net_ratelimit()) - dev_err(&card->netdev->dev, "Could not iommu-map rx buffer\n"); - card->spider_stats.rx_iommu_map_error++; - hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; - } else { - hwdescr->buf_addr = buf; - wmb(); - hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED | - SPIDER_NET_DMAC_NOINTR_COMPLETE; - } - - return 0; -} - -/** - * spider_net_enable_rxchtails - sets RX dmac chain tail addresses - * @card: card structure - * - * spider_net_enable_rxchtails sets the RX DMAC chain tail addresses in the - * chip by writing to the appropriate register. DMA is enabled in - * spider_net_enable_rxdmac. - */ -static inline void -spider_net_enable_rxchtails(struct spider_net_card *card) -{ - /* assume chain is aligned correctly */ - spider_net_write_reg(card, SPIDER_NET_GDADCHA , - card->rx_chain.tail->bus_addr); -} - -/** - * spider_net_enable_rxdmac - enables a receive DMA controller - * @card: card structure - * - * spider_net_enable_rxdmac enables the DMA controller by setting RX_DMA_EN - * in the GDADMACCNTR register - */ -static inline void -spider_net_enable_rxdmac(struct spider_net_card *card) -{ - wmb(); - spider_net_write_reg(card, SPIDER_NET_GDADMACCNTR, - SPIDER_NET_DMA_RX_VALUE); -} - -/** - * spider_net_disable_rxdmac - disables the receive DMA controller - * @card: card structure - * - * spider_net_disable_rxdmac terminates processing on the DMA controller - * by turing off the DMA controller, with the force-end flag set. - */ -static inline void -spider_net_disable_rxdmac(struct spider_net_card *card) -{ - spider_net_write_reg(card, SPIDER_NET_GDADMACCNTR, - SPIDER_NET_DMA_RX_FEND_VALUE); -} - -/** - * spider_net_refill_rx_chain - refills descriptors/skbs in the rx chains - * @card: card structure - * - * refills descriptors in the rx chain: allocates skbs and iommu-maps them. - */ -static void -spider_net_refill_rx_chain(struct spider_net_card *card) -{ - struct spider_net_descr_chain *chain = &card->rx_chain; - unsigned long flags; - - /* one context doing the refill (and a second context seeing that - * and omitting it) is ok. If called by NAPI, we'll be called again - * as spider_net_decode_one_descr is called several times. If some - * interrupt calls us, the NAPI is about to clean up anyway. */ - if (!spin_trylock_irqsave(&chain->lock, flags)) - return; - - while (spider_net_get_descr_status(chain->head->hwdescr) == - SPIDER_NET_DESCR_NOT_IN_USE) { - if (spider_net_prepare_rx_descr(card, chain->head)) - break; - chain->head = chain->head->next; - } - - spin_unlock_irqrestore(&chain->lock, flags); -} - -/** - * spider_net_alloc_rx_skbs - Allocates rx skbs in rx descriptor chains - * @card: card structure - * - * Returns 0 on success, <0 on failure. - */ -static int -spider_net_alloc_rx_skbs(struct spider_net_card *card) -{ - struct spider_net_descr_chain *chain = &card->rx_chain; - struct spider_net_descr *start = chain->tail; - struct spider_net_descr *descr = start; - - /* Link up the hardware chain pointers */ - do { - descr->prev->hwdescr->next_descr_addr = descr->bus_addr; - descr = descr->next; - } while (descr != start); - - /* Put at least one buffer into the chain. if this fails, - * we've got a problem. If not, spider_net_refill_rx_chain - * will do the rest at the end of this function. */ - if (spider_net_prepare_rx_descr(card, chain->head)) - goto error; - else - chain->head = chain->head->next; - - /* This will allocate the rest of the rx buffers; - * if not, it's business as usual later on. */ - spider_net_refill_rx_chain(card); - spider_net_enable_rxdmac(card); - return 0; - -error: - spider_net_free_rx_chain_contents(card); - return -ENOMEM; -} - -/** - * spider_net_get_multicast_hash - generates hash for multicast filter table - * @addr: multicast address - * - * returns the hash value. - * - * spider_net_get_multicast_hash calculates a hash value for a given multicast - * address, that is used to set the multicast filter tables - */ -static u8 -spider_net_get_multicast_hash(struct net_device *netdev, __u8 *addr) -{ - u32 crc; - u8 hash; - char addr_for_crc[ETH_ALEN] = { 0, }; - int i, bit; - - for (i = 0; i < ETH_ALEN * 8; i++) { - bit = (addr[i / 8] >> (i % 8)) & 1; - addr_for_crc[ETH_ALEN - 1 - i / 8] += bit << (7 - (i % 8)); - } - - crc = crc32_be(~0, addr_for_crc, netdev->addr_len); - - hash = (crc >> 27); - hash <<= 3; - hash |= crc & 7; - hash &= 0xff; - - return hash; -} - -/** - * spider_net_set_multi - sets multicast addresses and promisc flags - * @netdev: interface device structure - * - * spider_net_set_multi configures multicast addresses as needed for the - * netdev interface. It also sets up multicast, allmulti and promisc - * flags appropriately - */ -static void -spider_net_set_multi(struct net_device *netdev) -{ - struct netdev_hw_addr *ha; - u8 hash; - int i; - u32 reg; - struct spider_net_card *card = netdev_priv(netdev); - unsigned long bitmask[SPIDER_NET_MULTICAST_HASHES / BITS_PER_LONG] = - {0, }; - - spider_net_set_promisc(card); - - if (netdev->flags & IFF_ALLMULTI) { - for (i = 0; i < SPIDER_NET_MULTICAST_HASHES; i++) { - set_bit(i, bitmask); - } - goto write_hash; - } - - /* well, we know, what the broadcast hash value is: it's xfd - hash = spider_net_get_multicast_hash(netdev, netdev->broadcast); */ - set_bit(0xfd, bitmask); - - netdev_for_each_mc_addr(ha, netdev) { - hash = spider_net_get_multicast_hash(netdev, ha->addr); - set_bit(hash, bitmask); - } - -write_hash: - for (i = 0; i < SPIDER_NET_MULTICAST_HASHES / 4; i++) { - reg = 0; - if (test_bit(i * 4, bitmask)) - reg += 0x08; - reg <<= 8; - if (test_bit(i * 4 + 1, bitmask)) - reg += 0x08; - reg <<= 8; - if (test_bit(i * 4 + 2, bitmask)) - reg += 0x08; - reg <<= 8; - if (test_bit(i * 4 + 3, bitmask)) - reg += 0x08; - - spider_net_write_reg(card, SPIDER_NET_GMRMHFILnR + i * 4, reg); - } -} - -/** - * spider_net_prepare_tx_descr - fill tx descriptor with skb data - * @card: card structure - * @skb: packet to use - * - * returns 0 on success, <0 on failure. - * - * fills out the descriptor structure with skb data and len. Copies data, - * if needed (32bit DMA!) - */ -static int -spider_net_prepare_tx_descr(struct spider_net_card *card, - struct sk_buff *skb) -{ - struct spider_net_descr_chain *chain = &card->tx_chain; - struct spider_net_descr *descr; - struct spider_net_hw_descr *hwdescr; - dma_addr_t buf; - unsigned long flags; - - buf = pci_map_single(card->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(card->pdev, buf)) { - if (netif_msg_tx_err(card) && net_ratelimit()) - dev_err(&card->netdev->dev, "could not iommu-map packet (%p, %i). " - "Dropping packet\n", skb->data, skb->len); - card->spider_stats.tx_iommu_map_error++; - return -ENOMEM; - } - - spin_lock_irqsave(&chain->lock, flags); - descr = card->tx_chain.head; - if (descr->next == chain->tail->prev) { - spin_unlock_irqrestore(&chain->lock, flags); - pci_unmap_single(card->pdev, buf, skb->len, PCI_DMA_TODEVICE); - return -ENOMEM; - } - hwdescr = descr->hwdescr; - chain->head = descr->next; - - descr->skb = skb; - hwdescr->buf_addr = buf; - hwdescr->buf_size = skb->len; - hwdescr->next_descr_addr = 0; - hwdescr->data_status = 0; - - hwdescr->dmac_cmd_status = - SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_TXFRMTL; - spin_unlock_irqrestore(&chain->lock, flags); - - if (skb->ip_summed == CHECKSUM_PARTIAL) - switch (ip_hdr(skb)->protocol) { - case IPPROTO_TCP: - hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_TCP; - break; - case IPPROTO_UDP: - hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_UDP; - break; - } - - /* Chain the bus address, so that the DMA engine finds this descr. */ - wmb(); - descr->prev->hwdescr->next_descr_addr = descr->bus_addr; - - card->netdev->trans_start = jiffies; /* set netdev watchdog timer */ - return 0; -} - -static int -spider_net_set_low_watermark(struct spider_net_card *card) -{ - struct spider_net_descr *descr = card->tx_chain.tail; - struct spider_net_hw_descr *hwdescr; - unsigned long flags; - int status; - int cnt=0; - int i; - - /* Measure the length of the queue. Measurement does not - * need to be precise -- does not need a lock. */ - while (descr != card->tx_chain.head) { - status = descr->hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_NOT_IN_USE; - if (status == SPIDER_NET_DESCR_NOT_IN_USE) - break; - descr = descr->next; - cnt++; - } - - /* If TX queue is short, don't even bother with interrupts */ - if (cnt < card->tx_chain.num_desc/4) - return cnt; - - /* Set low-watermark 3/4th's of the way into the queue. */ - descr = card->tx_chain.tail; - cnt = (cnt*3)/4; - for (i=0;inext; - - /* Set the new watermark, clear the old watermark */ - spin_lock_irqsave(&card->tx_chain.lock, flags); - descr->hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_TXDESFLG; - if (card->low_watermark && card->low_watermark != descr) { - hwdescr = card->low_watermark->hwdescr; - hwdescr->dmac_cmd_status = - hwdescr->dmac_cmd_status & ~SPIDER_NET_DESCR_TXDESFLG; - } - card->low_watermark = descr; - spin_unlock_irqrestore(&card->tx_chain.lock, flags); - return cnt; -} - -/** - * spider_net_release_tx_chain - processes sent tx descriptors - * @card: adapter structure - * @brutal: if set, don't care about whether descriptor seems to be in use - * - * returns 0 if the tx ring is empty, otherwise 1. - * - * spider_net_release_tx_chain releases the tx descriptors that spider has - * finished with (if non-brutal) or simply release tx descriptors (if brutal). - * If some other context is calling this function, we return 1 so that we're - * scheduled again (if we were scheduled) and will not lose initiative. - */ -static int -spider_net_release_tx_chain(struct spider_net_card *card, int brutal) -{ - struct net_device *dev = card->netdev; - struct spider_net_descr_chain *chain = &card->tx_chain; - struct spider_net_descr *descr; - struct spider_net_hw_descr *hwdescr; - struct sk_buff *skb; - u32 buf_addr; - unsigned long flags; - int status; - - while (1) { - spin_lock_irqsave(&chain->lock, flags); - if (chain->tail == chain->head) { - spin_unlock_irqrestore(&chain->lock, flags); - return 0; - } - descr = chain->tail; - hwdescr = descr->hwdescr; - - status = spider_net_get_descr_status(hwdescr); - switch (status) { - case SPIDER_NET_DESCR_COMPLETE: - dev->stats.tx_packets++; - dev->stats.tx_bytes += descr->skb->len; - break; - - case SPIDER_NET_DESCR_CARDOWNED: - if (!brutal) { - spin_unlock_irqrestore(&chain->lock, flags); - return 1; - } - - /* fallthrough, if we release the descriptors - * brutally (then we don't care about - * SPIDER_NET_DESCR_CARDOWNED) */ - - case SPIDER_NET_DESCR_RESPONSE_ERROR: - case SPIDER_NET_DESCR_PROTECTION_ERROR: - case SPIDER_NET_DESCR_FORCE_END: - if (netif_msg_tx_err(card)) - dev_err(&card->netdev->dev, "forcing end of tx descriptor " - "with status x%02x\n", status); - dev->stats.tx_errors++; - break; - - default: - dev->stats.tx_dropped++; - if (!brutal) { - spin_unlock_irqrestore(&chain->lock, flags); - return 1; - } - } - - chain->tail = descr->next; - hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_NOT_IN_USE; - skb = descr->skb; - descr->skb = NULL; - buf_addr = hwdescr->buf_addr; - spin_unlock_irqrestore(&chain->lock, flags); - - /* unmap the skb */ - if (skb) { - pci_unmap_single(card->pdev, buf_addr, skb->len, - PCI_DMA_TODEVICE); - dev_kfree_skb(skb); - } - } - return 0; -} - -/** - * spider_net_kick_tx_dma - enables TX DMA processing - * @card: card structure - * - * This routine will start the transmit DMA running if - * it is not already running. This routine ned only be - * called when queueing a new packet to an empty tx queue. - * Writes the current tx chain head as start address - * of the tx descriptor chain and enables the transmission - * DMA engine. - */ -static inline void -spider_net_kick_tx_dma(struct spider_net_card *card) -{ - struct spider_net_descr *descr; - - if (spider_net_read_reg(card, SPIDER_NET_GDTDMACCNTR) & - SPIDER_NET_TX_DMA_EN) - goto out; - - descr = card->tx_chain.tail; - for (;;) { - if (spider_net_get_descr_status(descr->hwdescr) == - SPIDER_NET_DESCR_CARDOWNED) { - spider_net_write_reg(card, SPIDER_NET_GDTDCHA, - descr->bus_addr); - spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, - SPIDER_NET_DMA_TX_VALUE); - break; - } - if (descr == card->tx_chain.head) - break; - descr = descr->next; - } - -out: - mod_timer(&card->tx_timer, jiffies + SPIDER_NET_TX_TIMER); -} - -/** - * spider_net_xmit - transmits a frame over the device - * @skb: packet to send out - * @netdev: interface device structure - * - * returns 0 on success, !0 on failure - */ -static int -spider_net_xmit(struct sk_buff *skb, struct net_device *netdev) -{ - int cnt; - struct spider_net_card *card = netdev_priv(netdev); - - spider_net_release_tx_chain(card, 0); - - if (spider_net_prepare_tx_descr(card, skb) != 0) { - netdev->stats.tx_dropped++; - netif_stop_queue(netdev); - return NETDEV_TX_BUSY; - } - - cnt = spider_net_set_low_watermark(card); - if (cnt < 5) - spider_net_kick_tx_dma(card); - return NETDEV_TX_OK; -} - -/** - * spider_net_cleanup_tx_ring - cleans up the TX ring - * @card: card structure - * - * spider_net_cleanup_tx_ring is called by either the tx_timer - * or from the NAPI polling routine. - * This routine releases resources associted with transmitted - * packets, including updating the queue tail pointer. - */ -static void -spider_net_cleanup_tx_ring(struct spider_net_card *card) -{ - if ((spider_net_release_tx_chain(card, 0) != 0) && - (card->netdev->flags & IFF_UP)) { - spider_net_kick_tx_dma(card); - netif_wake_queue(card->netdev); - } -} - -/** - * spider_net_do_ioctl - called for device ioctls - * @netdev: interface device structure - * @ifr: request parameter structure for ioctl - * @cmd: command code for ioctl - * - * returns 0 on success, <0 on failure. Currently, we have no special ioctls. - * -EOPNOTSUPP is returned, if an unknown ioctl was requested - */ -static int -spider_net_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) -{ - switch (cmd) { - default: - return -EOPNOTSUPP; - } -} - -/** - * spider_net_pass_skb_up - takes an skb from a descriptor and passes it on - * @descr: descriptor to process - * @card: card structure - * - * Fills out skb structure and passes the data to the stack. - * The descriptor state is not changed. - */ -static void -spider_net_pass_skb_up(struct spider_net_descr *descr, - struct spider_net_card *card) -{ - struct spider_net_hw_descr *hwdescr = descr->hwdescr; - struct sk_buff *skb = descr->skb; - struct net_device *netdev = card->netdev; - u32 data_status = hwdescr->data_status; - u32 data_error = hwdescr->data_error; - - skb_put(skb, hwdescr->valid_size); - - /* the card seems to add 2 bytes of junk in front - * of the ethernet frame */ -#define SPIDER_MISALIGN 2 - skb_pull(skb, SPIDER_MISALIGN); - skb->protocol = eth_type_trans(skb, netdev); - - /* checksum offload */ - skb_checksum_none_assert(skb); - if (netdev->features & NETIF_F_RXCSUM) { - if ( ( (data_status & SPIDER_NET_DATA_STATUS_CKSUM_MASK) == - SPIDER_NET_DATA_STATUS_CKSUM_MASK) && - !(data_error & SPIDER_NET_DATA_ERR_CKSUM_MASK)) - skb->ip_summed = CHECKSUM_UNNECESSARY; - } - - if (data_status & SPIDER_NET_VLAN_PACKET) { - /* further enhancements: HW-accel VLAN */ - } - - /* update netdevice statistics */ - netdev->stats.rx_packets++; - netdev->stats.rx_bytes += skb->len; - - /* pass skb up to stack */ - netif_receive_skb(skb); -} - -static void show_rx_chain(struct spider_net_card *card) -{ - struct spider_net_descr_chain *chain = &card->rx_chain; - struct spider_net_descr *start= chain->tail; - struct spider_net_descr *descr= start; - struct spider_net_hw_descr *hwd = start->hwdescr; - struct device *dev = &card->netdev->dev; - u32 curr_desc, next_desc; - int status; - - int tot = 0; - int cnt = 0; - int off = start - chain->ring; - int cstat = hwd->dmac_cmd_status; - - dev_info(dev, "Total number of descrs=%d\n", - chain->num_desc); - dev_info(dev, "Chain tail located at descr=%d, status=0x%x\n", - off, cstat); - - curr_desc = spider_net_read_reg(card, SPIDER_NET_GDACTDPA); - next_desc = spider_net_read_reg(card, SPIDER_NET_GDACNEXTDA); - - status = cstat; - do - { - hwd = descr->hwdescr; - off = descr - chain->ring; - status = hwd->dmac_cmd_status; - - if (descr == chain->head) - dev_info(dev, "Chain head is at %d, head status=0x%x\n", - off, status); - - if (curr_desc == descr->bus_addr) - dev_info(dev, "HW curr desc (GDACTDPA) is at %d, status=0x%x\n", - off, status); - - if (next_desc == descr->bus_addr) - dev_info(dev, "HW next desc (GDACNEXTDA) is at %d, status=0x%x\n", - off, status); - - if (hwd->next_descr_addr == 0) - dev_info(dev, "chain is cut at %d\n", off); - - if (cstat != status) { - int from = (chain->num_desc + off - cnt) % chain->num_desc; - int to = (chain->num_desc + off - 1) % chain->num_desc; - dev_info(dev, "Have %d (from %d to %d) descrs " - "with stat=0x%08x\n", cnt, from, to, cstat); - cstat = status; - cnt = 0; - } - - cnt ++; - tot ++; - descr = descr->next; - } while (descr != start); - - dev_info(dev, "Last %d descrs with stat=0x%08x " - "for a total of %d descrs\n", cnt, cstat, tot); - -#ifdef DEBUG - /* Now dump the whole ring */ - descr = start; - do - { - struct spider_net_hw_descr *hwd = descr->hwdescr; - status = spider_net_get_descr_status(hwd); - cnt = descr - chain->ring; - dev_info(dev, "Descr %d stat=0x%08x skb=%p\n", - cnt, status, descr->skb); - dev_info(dev, "bus addr=%08x buf addr=%08x sz=%d\n", - descr->bus_addr, hwd->buf_addr, hwd->buf_size); - dev_info(dev, "next=%08x result sz=%d valid sz=%d\n", - hwd->next_descr_addr, hwd->result_size, - hwd->valid_size); - dev_info(dev, "dmac=%08x data stat=%08x data err=%08x\n", - hwd->dmac_cmd_status, hwd->data_status, - hwd->data_error); - dev_info(dev, "\n"); - - descr = descr->next; - } while (descr != start); -#endif - -} - -/** - * spider_net_resync_head_ptr - Advance head ptr past empty descrs - * - * If the driver fails to keep up and empty the queue, then the - * hardware wil run out of room to put incoming packets. This - * will cause the hardware to skip descrs that are full (instead - * of halting/retrying). Thus, once the driver runs, it wil need - * to "catch up" to where the hardware chain pointer is at. - */ -static void spider_net_resync_head_ptr(struct spider_net_card *card) -{ - unsigned long flags; - struct spider_net_descr_chain *chain = &card->rx_chain; - struct spider_net_descr *descr; - int i, status; - - /* Advance head pointer past any empty descrs */ - descr = chain->head; - status = spider_net_get_descr_status(descr->hwdescr); - - if (status == SPIDER_NET_DESCR_NOT_IN_USE) - return; - - spin_lock_irqsave(&chain->lock, flags); - - descr = chain->head; - status = spider_net_get_descr_status(descr->hwdescr); - for (i=0; inum_desc; i++) { - if (status != SPIDER_NET_DESCR_CARDOWNED) break; - descr = descr->next; - status = spider_net_get_descr_status(descr->hwdescr); - } - chain->head = descr; - - spin_unlock_irqrestore(&chain->lock, flags); -} - -static int spider_net_resync_tail_ptr(struct spider_net_card *card) -{ - struct spider_net_descr_chain *chain = &card->rx_chain; - struct spider_net_descr *descr; - int i, status; - - /* Advance tail pointer past any empty and reaped descrs */ - descr = chain->tail; - status = spider_net_get_descr_status(descr->hwdescr); - - for (i=0; inum_desc; i++) { - if ((status != SPIDER_NET_DESCR_CARDOWNED) && - (status != SPIDER_NET_DESCR_NOT_IN_USE)) break; - descr = descr->next; - status = spider_net_get_descr_status(descr->hwdescr); - } - chain->tail = descr; - - if ((i == chain->num_desc) || (i == 0)) - return 1; - return 0; -} - -/** - * spider_net_decode_one_descr - processes an RX descriptor - * @card: card structure - * - * Returns 1 if a packet has been sent to the stack, otherwise 0. - * - * Processes an RX descriptor by iommu-unmapping the data buffer - * and passing the packet up to the stack. This function is called - * in softirq context, e.g. either bottom half from interrupt or - * NAPI polling context. - */ -static int -spider_net_decode_one_descr(struct spider_net_card *card) -{ - struct net_device *dev = card->netdev; - struct spider_net_descr_chain *chain = &card->rx_chain; - struct spider_net_descr *descr = chain->tail; - struct spider_net_hw_descr *hwdescr = descr->hwdescr; - u32 hw_buf_addr; - int status; - - status = spider_net_get_descr_status(hwdescr); - - /* Nothing in the descriptor, or ring must be empty */ - if ((status == SPIDER_NET_DESCR_CARDOWNED) || - (status == SPIDER_NET_DESCR_NOT_IN_USE)) - return 0; - - /* descriptor definitively used -- move on tail */ - chain->tail = descr->next; - - /* unmap descriptor */ - hw_buf_addr = hwdescr->buf_addr; - hwdescr->buf_addr = 0xffffffff; - pci_unmap_single(card->pdev, hw_buf_addr, - SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); - - if ( (status == SPIDER_NET_DESCR_RESPONSE_ERROR) || - (status == SPIDER_NET_DESCR_PROTECTION_ERROR) || - (status == SPIDER_NET_DESCR_FORCE_END) ) { - if (netif_msg_rx_err(card)) - dev_err(&dev->dev, - "dropping RX descriptor with state %d\n", status); - dev->stats.rx_dropped++; - goto bad_desc; - } - - if ( (status != SPIDER_NET_DESCR_COMPLETE) && - (status != SPIDER_NET_DESCR_FRAME_END) ) { - if (netif_msg_rx_err(card)) - dev_err(&card->netdev->dev, - "RX descriptor with unknown state %d\n", status); - card->spider_stats.rx_desc_unk_state++; - goto bad_desc; - } - - /* The cases we'll throw away the packet immediately */ - if (hwdescr->data_error & SPIDER_NET_DESTROY_RX_FLAGS) { - if (netif_msg_rx_err(card)) - dev_err(&card->netdev->dev, - "error in received descriptor found, " - "data_status=x%08x, data_error=x%08x\n", - hwdescr->data_status, hwdescr->data_error); - goto bad_desc; - } - - if (hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_BAD_STATUS) { - dev_err(&card->netdev->dev, "bad status, cmd_status=x%08x\n", - hwdescr->dmac_cmd_status); - pr_err("buf_addr=x%08x\n", hw_buf_addr); - pr_err("buf_size=x%08x\n", hwdescr->buf_size); - pr_err("next_descr_addr=x%08x\n", hwdescr->next_descr_addr); - pr_err("result_size=x%08x\n", hwdescr->result_size); - pr_err("valid_size=x%08x\n", hwdescr->valid_size); - pr_err("data_status=x%08x\n", hwdescr->data_status); - pr_err("data_error=x%08x\n", hwdescr->data_error); - pr_err("which=%ld\n", descr - card->rx_chain.ring); - - card->spider_stats.rx_desc_error++; - goto bad_desc; - } - - /* Ok, we've got a packet in descr */ - spider_net_pass_skb_up(descr, card); - descr->skb = NULL; - hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; - return 1; - -bad_desc: - if (netif_msg_rx_err(card)) - show_rx_chain(card); - dev_kfree_skb_irq(descr->skb); - descr->skb = NULL; - hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; - return 0; -} - -/** - * spider_net_poll - NAPI poll function called by the stack to return packets - * @netdev: interface device structure - * @budget: number of packets we can pass to the stack at most - * - * returns 0 if no more packets available to the driver/stack. Returns 1, - * if the quota is exceeded, but the driver has still packets. - * - * spider_net_poll returns all packets from the rx descriptors to the stack - * (using netif_receive_skb). If all/enough packets are up, the driver - * reenables interrupts and returns 0. If not, 1 is returned. - */ -static int spider_net_poll(struct napi_struct *napi, int budget) -{ - struct spider_net_card *card = container_of(napi, struct spider_net_card, napi); - int packets_done = 0; - - while (packets_done < budget) { - if (!spider_net_decode_one_descr(card)) - break; - - packets_done++; - } - - if ((packets_done == 0) && (card->num_rx_ints != 0)) { - if (!spider_net_resync_tail_ptr(card)) - packets_done = budget; - spider_net_resync_head_ptr(card); - } - card->num_rx_ints = 0; - - spider_net_refill_rx_chain(card); - spider_net_enable_rxdmac(card); - - spider_net_cleanup_tx_ring(card); - - /* if all packets are in the stack, enable interrupts and return 0 */ - /* if not, return 1 */ - if (packets_done < budget) { - napi_complete(napi); - spider_net_rx_irq_on(card); - card->ignore_rx_ramfull = 0; - } - - return packets_done; -} - -/** - * spider_net_change_mtu - changes the MTU of an interface - * @netdev: interface device structure - * @new_mtu: new MTU value - * - * returns 0 on success, <0 on failure - */ -static int -spider_net_change_mtu(struct net_device *netdev, int new_mtu) -{ - /* no need to re-alloc skbs or so -- the max mtu is about 2.3k - * and mtu is outbound only anyway */ - if ( (new_mtu < SPIDER_NET_MIN_MTU ) || - (new_mtu > SPIDER_NET_MAX_MTU) ) - return -EINVAL; - netdev->mtu = new_mtu; - return 0; -} - -/** - * spider_net_set_mac - sets the MAC of an interface - * @netdev: interface device structure - * @ptr: pointer to new MAC address - * - * Returns 0 on success, <0 on failure. Currently, we don't support this - * and will always return EOPNOTSUPP. - */ -static int -spider_net_set_mac(struct net_device *netdev, void *p) -{ - struct spider_net_card *card = netdev_priv(netdev); - u32 macl, macu, regvalue; - struct sockaddr *addr = p; - - if (!is_valid_ether_addr(addr->sa_data)) - return -EADDRNOTAVAIL; - - /* switch off GMACTPE and GMACRPE */ - regvalue = spider_net_read_reg(card, SPIDER_NET_GMACOPEMD); - regvalue &= ~((1 << 5) | (1 << 6)); - spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, regvalue); - - /* write mac */ - macu = (addr->sa_data[0]<<24) + (addr->sa_data[1]<<16) + - (addr->sa_data[2]<<8) + (addr->sa_data[3]); - macl = (addr->sa_data[4]<<8) + (addr->sa_data[5]); - spider_net_write_reg(card, SPIDER_NET_GMACUNIMACU, macu); - spider_net_write_reg(card, SPIDER_NET_GMACUNIMACL, macl); - - /* switch GMACTPE and GMACRPE back on */ - regvalue = spider_net_read_reg(card, SPIDER_NET_GMACOPEMD); - regvalue |= ((1 << 5) | (1 << 6)); - spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, regvalue); - - spider_net_set_promisc(card); - - /* look up, whether we have been successful */ - if (spider_net_get_mac_address(netdev)) - return -EADDRNOTAVAIL; - if (memcmp(netdev->dev_addr,addr->sa_data,netdev->addr_len)) - return -EADDRNOTAVAIL; - - return 0; -} - -/** - * spider_net_link_reset - * @netdev: net device structure - * - * This is called when the PHY_LINK signal is asserted. For the blade this is - * not connected so we should never get here. - * - */ -static void -spider_net_link_reset(struct net_device *netdev) -{ - - struct spider_net_card *card = netdev_priv(netdev); - - del_timer_sync(&card->aneg_timer); - - /* clear interrupt, block further interrupts */ - spider_net_write_reg(card, SPIDER_NET_GMACST, - spider_net_read_reg(card, SPIDER_NET_GMACST)); - spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0); - - /* reset phy and setup aneg */ - card->aneg_count = 0; - card->medium = BCM54XX_COPPER; - spider_net_setup_aneg(card); - mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); - -} - -/** - * spider_net_handle_error_irq - handles errors raised by an interrupt - * @card: card structure - * @status_reg: interrupt status register 0 (GHIINT0STS) - * - * spider_net_handle_error_irq treats or ignores all error conditions - * found when an interrupt is presented - */ -static void -spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg, - u32 error_reg1, u32 error_reg2) -{ - u32 i; - int show_error = 1; - - /* check GHIINT0STS ************************************/ - if (status_reg) - for (i = 0; i < 32; i++) - if (status_reg & (1<netdev->dev, "PHY write queue full\n"); - show_error = 0; - break; - - /* case SPIDER_NET_GRMDADRINT: not used. print a message */ - /* case SPIDER_NET_GRMARPINT: not used. print a message */ - /* case SPIDER_NET_GRMMPINT: not used. print a message */ - - case SPIDER_NET_GDTDEN0INT: - /* someone has set TX_DMA_EN to 0 */ - show_error = 0; - break; - - case SPIDER_NET_GDDDEN0INT: /* fallthrough */ - case SPIDER_NET_GDCDEN0INT: /* fallthrough */ - case SPIDER_NET_GDBDEN0INT: /* fallthrough */ - case SPIDER_NET_GDADEN0INT: - /* someone has set RX_DMA_EN to 0 */ - show_error = 0; - break; - - /* RX interrupts */ - case SPIDER_NET_GDDFDCINT: - case SPIDER_NET_GDCFDCINT: - case SPIDER_NET_GDBFDCINT: - case SPIDER_NET_GDAFDCINT: - /* case SPIDER_NET_GDNMINT: not used. print a message */ - /* case SPIDER_NET_GCNMINT: not used. print a message */ - /* case SPIDER_NET_GBNMINT: not used. print a message */ - /* case SPIDER_NET_GANMINT: not used. print a message */ - /* case SPIDER_NET_GRFNMINT: not used. print a message */ - show_error = 0; - break; - - /* TX interrupts */ - case SPIDER_NET_GDTFDCINT: - show_error = 0; - break; - case SPIDER_NET_GTTEDINT: - show_error = 0; - break; - case SPIDER_NET_GDTDCEINT: - /* chain end. If a descriptor should be sent, kick off - * tx dma - if (card->tx_chain.tail != card->tx_chain.head) - spider_net_kick_tx_dma(card); - */ - show_error = 0; - break; - - /* case SPIDER_NET_G1TMCNTINT: not used. print a message */ - /* case SPIDER_NET_GFREECNTINT: not used. print a message */ - } - - /* check GHIINT1STS ************************************/ - if (error_reg1) - for (i = 0; i < 32; i++) - if (error_reg1 & (1<ignore_rx_ramfull == 0) { - card->ignore_rx_ramfull = 1; - spider_net_resync_head_ptr(card); - spider_net_refill_rx_chain(card); - spider_net_enable_rxdmac(card); - card->num_rx_ints ++; - napi_schedule(&card->napi); - } - show_error = 0; - break; - - /* case SPIDER_NET_GTMSHTINT: problem, print a message */ - case SPIDER_NET_GDTINVDINT: - /* allrighty. tx from previous descr ok */ - show_error = 0; - break; - - /* chain end */ - case SPIDER_NET_GDDDCEINT: /* fallthrough */ - case SPIDER_NET_GDCDCEINT: /* fallthrough */ - case SPIDER_NET_GDBDCEINT: /* fallthrough */ - case SPIDER_NET_GDADCEINT: - spider_net_resync_head_ptr(card); - spider_net_refill_rx_chain(card); - spider_net_enable_rxdmac(card); - card->num_rx_ints ++; - napi_schedule(&card->napi); - show_error = 0; - break; - - /* invalid descriptor */ - case SPIDER_NET_GDDINVDINT: /* fallthrough */ - case SPIDER_NET_GDCINVDINT: /* fallthrough */ - case SPIDER_NET_GDBINVDINT: /* fallthrough */ - case SPIDER_NET_GDAINVDINT: - /* Could happen when rx chain is full */ - spider_net_resync_head_ptr(card); - spider_net_refill_rx_chain(card); - spider_net_enable_rxdmac(card); - card->num_rx_ints ++; - napi_schedule(&card->napi); - show_error = 0; - break; - - /* case SPIDER_NET_GDTRSERINT: problem, print a message */ - /* case SPIDER_NET_GDDRSERINT: problem, print a message */ - /* case SPIDER_NET_GDCRSERINT: problem, print a message */ - /* case SPIDER_NET_GDBRSERINT: problem, print a message */ - /* case SPIDER_NET_GDARSERINT: problem, print a message */ - /* case SPIDER_NET_GDSERINT: problem, print a message */ - /* case SPIDER_NET_GDTPTERINT: problem, print a message */ - /* case SPIDER_NET_GDDPTERINT: problem, print a message */ - /* case SPIDER_NET_GDCPTERINT: problem, print a message */ - /* case SPIDER_NET_GDBPTERINT: problem, print a message */ - /* case SPIDER_NET_GDAPTERINT: problem, print a message */ - default: - show_error = 1; - break; - } - - /* check GHIINT2STS ************************************/ - if (error_reg2) - for (i = 0; i < 32; i++) - if (error_reg2 & (1<netdev->dev, "Error interrupt, GHIINT0STS = 0x%08x, " - "GHIINT1STS = 0x%08x, GHIINT2STS = 0x%08x\n", - status_reg, error_reg1, error_reg2); - - /* clear interrupt sources */ - spider_net_write_reg(card, SPIDER_NET_GHIINT1STS, error_reg1); - spider_net_write_reg(card, SPIDER_NET_GHIINT2STS, error_reg2); -} - -/** - * spider_net_interrupt - interrupt handler for spider_net - * @irq: interrupt number - * @ptr: pointer to net_device - * - * returns IRQ_HANDLED, if interrupt was for driver, or IRQ_NONE, if no - * interrupt found raised by card. - * - * This is the interrupt handler, that turns off - * interrupts for this device and makes the stack poll the driver - */ -static irqreturn_t -spider_net_interrupt(int irq, void *ptr) -{ - struct net_device *netdev = ptr; - struct spider_net_card *card = netdev_priv(netdev); - u32 status_reg, error_reg1, error_reg2; - - status_reg = spider_net_read_reg(card, SPIDER_NET_GHIINT0STS); - error_reg1 = spider_net_read_reg(card, SPIDER_NET_GHIINT1STS); - error_reg2 = spider_net_read_reg(card, SPIDER_NET_GHIINT2STS); - - if (!(status_reg & SPIDER_NET_INT0_MASK_VALUE) && - !(error_reg1 & SPIDER_NET_INT1_MASK_VALUE) && - !(error_reg2 & SPIDER_NET_INT2_MASK_VALUE)) - return IRQ_NONE; - - if (status_reg & SPIDER_NET_RXINT ) { - spider_net_rx_irq_off(card); - napi_schedule(&card->napi); - card->num_rx_ints ++; - } - if (status_reg & SPIDER_NET_TXINT) - napi_schedule(&card->napi); - - if (status_reg & SPIDER_NET_LINKINT) - spider_net_link_reset(netdev); - - if (status_reg & SPIDER_NET_ERRINT ) - spider_net_handle_error_irq(card, status_reg, - error_reg1, error_reg2); - - /* clear interrupt sources */ - spider_net_write_reg(card, SPIDER_NET_GHIINT0STS, status_reg); - - return IRQ_HANDLED; -} - -#ifdef CONFIG_NET_POLL_CONTROLLER -/** - * spider_net_poll_controller - artificial interrupt for netconsole etc. - * @netdev: interface device structure - * - * see Documentation/networking/netconsole.txt - */ -static void -spider_net_poll_controller(struct net_device *netdev) -{ - disable_irq(netdev->irq); - spider_net_interrupt(netdev->irq, netdev); - enable_irq(netdev->irq); -} -#endif /* CONFIG_NET_POLL_CONTROLLER */ - -/** - * spider_net_enable_interrupts - enable interrupts - * @card: card structure - * - * spider_net_enable_interrupt enables several interrupts - */ -static void -spider_net_enable_interrupts(struct spider_net_card *card) -{ - spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, - SPIDER_NET_INT0_MASK_VALUE); - spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, - SPIDER_NET_INT1_MASK_VALUE); - spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, - SPIDER_NET_INT2_MASK_VALUE); -} - -/** - * spider_net_disable_interrupts - disable interrupts - * @card: card structure - * - * spider_net_disable_interrupts disables all the interrupts - */ -static void -spider_net_disable_interrupts(struct spider_net_card *card) -{ - spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 0); - spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, 0); - spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, 0); - spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0); -} - -/** - * spider_net_init_card - initializes the card - * @card: card structure - * - * spider_net_init_card initializes the card so that other registers can - * be used - */ -static void -spider_net_init_card(struct spider_net_card *card) -{ - spider_net_write_reg(card, SPIDER_NET_CKRCTRL, - SPIDER_NET_CKRCTRL_STOP_VALUE); - - spider_net_write_reg(card, SPIDER_NET_CKRCTRL, - SPIDER_NET_CKRCTRL_RUN_VALUE); - - /* trigger ETOMOD signal */ - spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, - spider_net_read_reg(card, SPIDER_NET_GMACOPEMD) | 0x4); - - spider_net_disable_interrupts(card); -} - -/** - * spider_net_enable_card - enables the card by setting all kinds of regs - * @card: card structure - * - * spider_net_enable_card sets a lot of SMMIO registers to enable the device - */ -static void -spider_net_enable_card(struct spider_net_card *card) -{ - int i; - /* the following array consists of (register),(value) pairs - * that are set in this function. A register of 0 ends the list */ - u32 regs[][2] = { - { SPIDER_NET_GRESUMINTNUM, 0 }, - { SPIDER_NET_GREINTNUM, 0 }, - - /* set interrupt frame number registers */ - /* clear the single DMA engine registers first */ - { SPIDER_NET_GFAFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, - { SPIDER_NET_GFBFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, - { SPIDER_NET_GFCFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, - { SPIDER_NET_GFDFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, - /* then set, what we really need */ - { SPIDER_NET_GFFRMNUM, SPIDER_NET_FRAMENUM_VALUE }, - - /* timer counter registers and stuff */ - { SPIDER_NET_GFREECNNUM, 0 }, - { SPIDER_NET_GONETIMENUM, 0 }, - { SPIDER_NET_GTOUTFRMNUM, 0 }, - - /* RX mode setting */ - { SPIDER_NET_GRXMDSET, SPIDER_NET_RXMODE_VALUE }, - /* TX mode setting */ - { SPIDER_NET_GTXMDSET, SPIDER_NET_TXMODE_VALUE }, - /* IPSEC mode setting */ - { SPIDER_NET_GIPSECINIT, SPIDER_NET_IPSECINIT_VALUE }, - - { SPIDER_NET_GFTRESTRT, SPIDER_NET_RESTART_VALUE }, - - { SPIDER_NET_GMRWOLCTRL, 0 }, - { SPIDER_NET_GTESTMD, 0x10000000 }, - { SPIDER_NET_GTTQMSK, 0x00400040 }, - - { SPIDER_NET_GMACINTEN, 0 }, - - /* flow control stuff */ - { SPIDER_NET_GMACAPAUSE, SPIDER_NET_MACAPAUSE_VALUE }, - { SPIDER_NET_GMACTXPAUSE, SPIDER_NET_TXPAUSE_VALUE }, - - { SPIDER_NET_GMACBSTLMT, SPIDER_NET_BURSTLMT_VALUE }, - { 0, 0} - }; - - i = 0; - while (regs[i][0]) { - spider_net_write_reg(card, regs[i][0], regs[i][1]); - i++; - } - - /* clear unicast filter table entries 1 to 14 */ - for (i = 1; i <= 14; i++) { - spider_net_write_reg(card, - SPIDER_NET_GMRUAFILnR + i * 8, - 0x00080000); - spider_net_write_reg(card, - SPIDER_NET_GMRUAFILnR + i * 8 + 4, - 0x00000000); - } - - spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, 0x08080000); - - spider_net_write_reg(card, SPIDER_NET_ECMODE, SPIDER_NET_ECMODE_VALUE); - - /* set chain tail address for RX chains and - * enable DMA */ - spider_net_enable_rxchtails(card); - spider_net_enable_rxdmac(card); - - spider_net_write_reg(card, SPIDER_NET_GRXDMAEN, SPIDER_NET_WOL_VALUE); - - spider_net_write_reg(card, SPIDER_NET_GMACLENLMT, - SPIDER_NET_LENLMT_VALUE); - spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, - SPIDER_NET_OPMODE_VALUE); - - spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, - SPIDER_NET_GDTBSTA); -} - -/** - * spider_net_download_firmware - loads firmware into the adapter - * @card: card structure - * @firmware_ptr: pointer to firmware data - * - * spider_net_download_firmware loads the firmware data into the - * adapter. It assumes the length etc. to be allright. - */ -static int -spider_net_download_firmware(struct spider_net_card *card, - const void *firmware_ptr) -{ - int sequencer, i; - const u32 *fw_ptr = firmware_ptr; - - /* stop sequencers */ - spider_net_write_reg(card, SPIDER_NET_GSINIT, - SPIDER_NET_STOP_SEQ_VALUE); - - for (sequencer = 0; sequencer < SPIDER_NET_FIRMWARE_SEQS; - sequencer++) { - spider_net_write_reg(card, - SPIDER_NET_GSnPRGADR + sequencer * 8, 0); - for (i = 0; i < SPIDER_NET_FIRMWARE_SEQWORDS; i++) { - spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT + - sequencer * 8, *fw_ptr); - fw_ptr++; - } - } - - if (spider_net_read_reg(card, SPIDER_NET_GSINIT)) - return -EIO; - - spider_net_write_reg(card, SPIDER_NET_GSINIT, - SPIDER_NET_RUN_SEQ_VALUE); - - return 0; -} - -/** - * spider_net_init_firmware - reads in firmware parts - * @card: card structure - * - * Returns 0 on success, <0 on failure - * - * spider_net_init_firmware opens the sequencer firmware and does some basic - * checks. This function opens and releases the firmware structure. A call - * to download the firmware is performed before the release. - * - * Firmware format - * =============== - * spider_fw.bin is expected to be a file containing 6*1024*4 bytes, 4k being - * the program for each sequencer. Use the command - * tail -q -n +2 Seq_code1_0x088.txt Seq_code2_0x090.txt \ - * Seq_code3_0x098.txt Seq_code4_0x0A0.txt Seq_code5_0x0A8.txt \ - * Seq_code6_0x0B0.txt | xxd -r -p -c4 > spider_fw.bin - * - * to generate spider_fw.bin, if you have sequencer programs with something - * like the following contents for each sequencer: - * - * - * - * ... - * <1024th 4-BYTES-WORD FOR SEQUENCER> - */ -static int -spider_net_init_firmware(struct spider_net_card *card) -{ - struct firmware *firmware = NULL; - struct device_node *dn; - const u8 *fw_prop = NULL; - int err = -ENOENT; - int fw_size; - - if (request_firmware((const struct firmware **)&firmware, - SPIDER_NET_FIRMWARE_NAME, &card->pdev->dev) == 0) { - if ( (firmware->size != SPIDER_NET_FIRMWARE_LEN) && - netif_msg_probe(card) ) { - dev_err(&card->netdev->dev, - "Incorrect size of spidernet firmware in " \ - "filesystem. Looking in host firmware...\n"); - goto try_host_fw; - } - err = spider_net_download_firmware(card, firmware->data); - - release_firmware(firmware); - if (err) - goto try_host_fw; - - goto done; - } - -try_host_fw: - dn = pci_device_to_OF_node(card->pdev); - if (!dn) - goto out_err; - - fw_prop = of_get_property(dn, "firmware", &fw_size); - if (!fw_prop) - goto out_err; - - if ( (fw_size != SPIDER_NET_FIRMWARE_LEN) && - netif_msg_probe(card) ) { - dev_err(&card->netdev->dev, - "Incorrect size of spidernet firmware in host firmware\n"); - goto done; - } - - err = spider_net_download_firmware(card, fw_prop); - -done: - return err; -out_err: - if (netif_msg_probe(card)) - dev_err(&card->netdev->dev, - "Couldn't find spidernet firmware in filesystem " \ - "or host firmware\n"); - return err; -} - -/** - * spider_net_open - called upon ifonfig up - * @netdev: interface device structure - * - * returns 0 on success, <0 on failure - * - * spider_net_open allocates all the descriptors and memory needed for - * operation, sets up multicast list and enables interrupts - */ -int -spider_net_open(struct net_device *netdev) -{ - struct spider_net_card *card = netdev_priv(netdev); - int result; - - result = spider_net_init_firmware(card); - if (result) - goto init_firmware_failed; - - /* start probing with copper */ - card->aneg_count = 0; - card->medium = BCM54XX_COPPER; - spider_net_setup_aneg(card); - if (card->phy.def->phy_id) - mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); - - result = spider_net_init_chain(card, &card->tx_chain); - if (result) - goto alloc_tx_failed; - card->low_watermark = NULL; - - result = spider_net_init_chain(card, &card->rx_chain); - if (result) - goto alloc_rx_failed; - - /* Allocate rx skbs */ - if (spider_net_alloc_rx_skbs(card)) - goto alloc_skbs_failed; - - spider_net_set_multi(netdev); - - /* further enhancement: setup hw vlan, if needed */ - - result = -EBUSY; - if (request_irq(netdev->irq, spider_net_interrupt, - IRQF_SHARED, netdev->name, netdev)) - goto register_int_failed; - - spider_net_enable_card(card); - - netif_start_queue(netdev); - netif_carrier_on(netdev); - napi_enable(&card->napi); - - spider_net_enable_interrupts(card); - - return 0; - -register_int_failed: - spider_net_free_rx_chain_contents(card); -alloc_skbs_failed: - spider_net_free_chain(card, &card->rx_chain); -alloc_rx_failed: - spider_net_free_chain(card, &card->tx_chain); -alloc_tx_failed: - del_timer_sync(&card->aneg_timer); -init_firmware_failed: - return result; -} - -/** - * spider_net_link_phy - * @data: used for pointer to card structure - * - */ -static void spider_net_link_phy(unsigned long data) -{ - struct spider_net_card *card = (struct spider_net_card *)data; - struct mii_phy *phy = &card->phy; - - /* if link didn't come up after SPIDER_NET_ANEG_TIMEOUT tries, setup phy again */ - if (card->aneg_count > SPIDER_NET_ANEG_TIMEOUT) { - - pr_debug("%s: link is down trying to bring it up\n", - card->netdev->name); - - switch (card->medium) { - case BCM54XX_COPPER: - /* enable fiber with autonegotiation first */ - if (phy->def->ops->enable_fiber) - phy->def->ops->enable_fiber(phy, 1); - card->medium = BCM54XX_FIBER; - break; - - case BCM54XX_FIBER: - /* fiber didn't come up, try to disable fiber autoneg */ - if (phy->def->ops->enable_fiber) - phy->def->ops->enable_fiber(phy, 0); - card->medium = BCM54XX_UNKNOWN; - break; - - case BCM54XX_UNKNOWN: - /* copper, fiber with and without failed, - * retry from beginning */ - spider_net_setup_aneg(card); - card->medium = BCM54XX_COPPER; - break; - } - - card->aneg_count = 0; - mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); - return; - } - - /* link still not up, try again later */ - if (!(phy->def->ops->poll_link(phy))) { - card->aneg_count++; - mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); - return; - } - - /* link came up, get abilities */ - phy->def->ops->read_link(phy); - - spider_net_write_reg(card, SPIDER_NET_GMACST, - spider_net_read_reg(card, SPIDER_NET_GMACST)); - spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0x4); - - if (phy->speed == 1000) - spider_net_write_reg(card, SPIDER_NET_GMACMODE, 0x00000001); - else - spider_net_write_reg(card, SPIDER_NET_GMACMODE, 0); - - card->aneg_count = 0; - - pr_info("%s: link up, %i Mbps, %s-duplex %sautoneg.\n", - card->netdev->name, phy->speed, - phy->duplex == 1 ? "Full" : "Half", - phy->autoneg == 1 ? "" : "no "); -} - -/** - * spider_net_setup_phy - setup PHY - * @card: card structure - * - * returns 0 on success, <0 on failure - * - * spider_net_setup_phy is used as part of spider_net_probe. - **/ -static int -spider_net_setup_phy(struct spider_net_card *card) -{ - struct mii_phy *phy = &card->phy; - - spider_net_write_reg(card, SPIDER_NET_GDTDMASEL, - SPIDER_NET_DMASEL_VALUE); - spider_net_write_reg(card, SPIDER_NET_GPCCTRL, - SPIDER_NET_PHY_CTRL_VALUE); - - phy->dev = card->netdev; - phy->mdio_read = spider_net_read_phy; - phy->mdio_write = spider_net_write_phy; - - for (phy->mii_id = 1; phy->mii_id <= 31; phy->mii_id++) { - unsigned short id; - id = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR); - if (id != 0x0000 && id != 0xffff) { - if (!mii_phy_probe(phy, phy->mii_id)) { - pr_info("Found %s.\n", phy->def->name); - break; - } - } - } - - return 0; -} - -/** - * spider_net_workaround_rxramfull - work around firmware bug - * @card: card structure - * - * no return value - **/ -static void -spider_net_workaround_rxramfull(struct spider_net_card *card) -{ - int i, sequencer = 0; - - /* cancel reset */ - spider_net_write_reg(card, SPIDER_NET_CKRCTRL, - SPIDER_NET_CKRCTRL_RUN_VALUE); - - /* empty sequencer data */ - for (sequencer = 0; sequencer < SPIDER_NET_FIRMWARE_SEQS; - sequencer++) { - spider_net_write_reg(card, SPIDER_NET_GSnPRGADR + - sequencer * 8, 0x0); - for (i = 0; i < SPIDER_NET_FIRMWARE_SEQWORDS; i++) { - spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT + - sequencer * 8, 0x0); - } - } - - /* set sequencer operation */ - spider_net_write_reg(card, SPIDER_NET_GSINIT, 0x000000fe); - - /* reset */ - spider_net_write_reg(card, SPIDER_NET_CKRCTRL, - SPIDER_NET_CKRCTRL_STOP_VALUE); -} - -/** - * spider_net_stop - called upon ifconfig down - * @netdev: interface device structure - * - * always returns 0 - */ -int -spider_net_stop(struct net_device *netdev) -{ - struct spider_net_card *card = netdev_priv(netdev); - - napi_disable(&card->napi); - netif_carrier_off(netdev); - netif_stop_queue(netdev); - del_timer_sync(&card->tx_timer); - del_timer_sync(&card->aneg_timer); - - spider_net_disable_interrupts(card); - - free_irq(netdev->irq, netdev); - - spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, - SPIDER_NET_DMA_TX_FEND_VALUE); - - /* turn off DMA, force end */ - spider_net_disable_rxdmac(card); - - /* release chains */ - spider_net_release_tx_chain(card, 1); - spider_net_free_rx_chain_contents(card); - - spider_net_free_chain(card, &card->tx_chain); - spider_net_free_chain(card, &card->rx_chain); - - return 0; -} - -/** - * spider_net_tx_timeout_task - task scheduled by the watchdog timeout - * function (to be called not under interrupt status) - * @data: data, is interface device structure - * - * called as task when tx hangs, resets interface (if interface is up) - */ -static void -spider_net_tx_timeout_task(struct work_struct *work) -{ - struct spider_net_card *card = - container_of(work, struct spider_net_card, tx_timeout_task); - struct net_device *netdev = card->netdev; - - if (!(netdev->flags & IFF_UP)) - goto out; - - netif_device_detach(netdev); - spider_net_stop(netdev); - - spider_net_workaround_rxramfull(card); - spider_net_init_card(card); - - if (spider_net_setup_phy(card)) - goto out; - - spider_net_open(netdev); - spider_net_kick_tx_dma(card); - netif_device_attach(netdev); - -out: - atomic_dec(&card->tx_timeout_task_counter); -} - -/** - * spider_net_tx_timeout - called when the tx timeout watchdog kicks in. - * @netdev: interface device structure - * - * called, if tx hangs. Schedules a task that resets the interface - */ -static void -spider_net_tx_timeout(struct net_device *netdev) -{ - struct spider_net_card *card; - - card = netdev_priv(netdev); - atomic_inc(&card->tx_timeout_task_counter); - if (netdev->flags & IFF_UP) - schedule_work(&card->tx_timeout_task); - else - atomic_dec(&card->tx_timeout_task_counter); - card->spider_stats.tx_timeouts++; -} - -static const struct net_device_ops spider_net_ops = { - .ndo_open = spider_net_open, - .ndo_stop = spider_net_stop, - .ndo_start_xmit = spider_net_xmit, - .ndo_set_multicast_list = spider_net_set_multi, - .ndo_set_mac_address = spider_net_set_mac, - .ndo_change_mtu = spider_net_change_mtu, - .ndo_do_ioctl = spider_net_do_ioctl, - .ndo_tx_timeout = spider_net_tx_timeout, - .ndo_validate_addr = eth_validate_addr, - /* HW VLAN */ -#ifdef CONFIG_NET_POLL_CONTROLLER - /* poll controller */ - .ndo_poll_controller = spider_net_poll_controller, -#endif /* CONFIG_NET_POLL_CONTROLLER */ -}; - -/** - * spider_net_setup_netdev_ops - initialization of net_device operations - * @netdev: net_device structure - * - * fills out function pointers in the net_device structure - */ -static void -spider_net_setup_netdev_ops(struct net_device *netdev) -{ - netdev->netdev_ops = &spider_net_ops; - netdev->watchdog_timeo = SPIDER_NET_WATCHDOG_TIMEOUT; - /* ethtool ops */ - netdev->ethtool_ops = &spider_net_ethtool_ops; -} - -/** - * spider_net_setup_netdev - initialization of net_device - * @card: card structure - * - * Returns 0 on success or <0 on failure - * - * spider_net_setup_netdev initializes the net_device structure - **/ -static int -spider_net_setup_netdev(struct spider_net_card *card) -{ - int result; - struct net_device *netdev = card->netdev; - struct device_node *dn; - struct sockaddr addr; - const u8 *mac; - - SET_NETDEV_DEV(netdev, &card->pdev->dev); - - pci_set_drvdata(card->pdev, netdev); - - init_timer(&card->tx_timer); - card->tx_timer.function = - (void (*)(unsigned long)) spider_net_cleanup_tx_ring; - card->tx_timer.data = (unsigned long) card; - netdev->irq = card->pdev->irq; - - card->aneg_count = 0; - init_timer(&card->aneg_timer); - card->aneg_timer.function = spider_net_link_phy; - card->aneg_timer.data = (unsigned long) card; - - netif_napi_add(netdev, &card->napi, - spider_net_poll, SPIDER_NET_NAPI_WEIGHT); - - spider_net_setup_netdev_ops(netdev); - - netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM; - if (SPIDER_NET_RX_CSUM_DEFAULT) - netdev->features |= NETIF_F_RXCSUM; - netdev->features |= NETIF_F_IP_CSUM | NETIF_F_LLTX; - /* some time: NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | - * NETIF_F_HW_VLAN_FILTER */ - - netdev->irq = card->pdev->irq; - card->num_rx_ints = 0; - card->ignore_rx_ramfull = 0; - - dn = pci_device_to_OF_node(card->pdev); - if (!dn) - return -EIO; - - mac = of_get_property(dn, "local-mac-address", NULL); - if (!mac) - return -EIO; - memcpy(addr.sa_data, mac, ETH_ALEN); - - result = spider_net_set_mac(netdev, &addr); - if ((result) && (netif_msg_probe(card))) - dev_err(&card->netdev->dev, - "Failed to set MAC address: %i\n", result); - - result = register_netdev(netdev); - if (result) { - if (netif_msg_probe(card)) - dev_err(&card->netdev->dev, - "Couldn't register net_device: %i\n", result); - return result; - } - - if (netif_msg_probe(card)) - pr_info("Initialized device %s.\n", netdev->name); - - return 0; -} - -/** - * spider_net_alloc_card - allocates net_device and card structure - * - * returns the card structure or NULL in case of errors - * - * the card and net_device structures are linked to each other - */ -static struct spider_net_card * -spider_net_alloc_card(void) -{ - struct net_device *netdev; - struct spider_net_card *card; - size_t alloc_size; - - alloc_size = sizeof(struct spider_net_card) + - (tx_descriptors + rx_descriptors) * sizeof(struct spider_net_descr); - netdev = alloc_etherdev(alloc_size); - if (!netdev) - return NULL; - - card = netdev_priv(netdev); - card->netdev = netdev; - card->msg_enable = SPIDER_NET_DEFAULT_MSG; - INIT_WORK(&card->tx_timeout_task, spider_net_tx_timeout_task); - init_waitqueue_head(&card->waitq); - atomic_set(&card->tx_timeout_task_counter, 0); - - card->rx_chain.num_desc = rx_descriptors; - card->rx_chain.ring = card->darray; - card->tx_chain.num_desc = tx_descriptors; - card->tx_chain.ring = card->darray + rx_descriptors; - - return card; -} - -/** - * spider_net_undo_pci_setup - releases PCI ressources - * @card: card structure - * - * spider_net_undo_pci_setup releases the mapped regions - */ -static void -spider_net_undo_pci_setup(struct spider_net_card *card) -{ - iounmap(card->regs); - pci_release_regions(card->pdev); -} - -/** - * spider_net_setup_pci_dev - sets up the device in terms of PCI operations - * @pdev: PCI device - * - * Returns the card structure or NULL if any errors occur - * - * spider_net_setup_pci_dev initializes pdev and together with the - * functions called in spider_net_open configures the device so that - * data can be transferred over it - * The net_device structure is attached to the card structure, if the - * function returns without error. - **/ -static struct spider_net_card * -spider_net_setup_pci_dev(struct pci_dev *pdev) -{ - struct spider_net_card *card; - unsigned long mmio_start, mmio_len; - - if (pci_enable_device(pdev)) { - dev_err(&pdev->dev, "Couldn't enable PCI device\n"); - return NULL; - } - - if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { - dev_err(&pdev->dev, - "Couldn't find proper PCI device base address.\n"); - goto out_disable_dev; - } - - if (pci_request_regions(pdev, spider_net_driver_name)) { - dev_err(&pdev->dev, - "Couldn't obtain PCI resources, aborting.\n"); - goto out_disable_dev; - } - - pci_set_master(pdev); - - card = spider_net_alloc_card(); - if (!card) { - dev_err(&pdev->dev, - "Couldn't allocate net_device structure, aborting.\n"); - goto out_release_regions; - } - card->pdev = pdev; - - /* fetch base address and length of first resource */ - mmio_start = pci_resource_start(pdev, 0); - mmio_len = pci_resource_len(pdev, 0); - - card->netdev->mem_start = mmio_start; - card->netdev->mem_end = mmio_start + mmio_len; - card->regs = ioremap(mmio_start, mmio_len); - - if (!card->regs) { - dev_err(&pdev->dev, - "Couldn't obtain PCI resources, aborting.\n"); - goto out_release_regions; - } - - return card; - -out_release_regions: - pci_release_regions(pdev); -out_disable_dev: - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - return NULL; -} - -/** - * spider_net_probe - initialization of a device - * @pdev: PCI device - * @ent: entry in the device id list - * - * Returns 0 on success, <0 on failure - * - * spider_net_probe initializes pdev and registers a net_device - * structure for it. After that, the device can be ifconfig'ed up - **/ -static int __devinit -spider_net_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -EIO; - struct spider_net_card *card; - - card = spider_net_setup_pci_dev(pdev); - if (!card) - goto out; - - spider_net_workaround_rxramfull(card); - spider_net_init_card(card); - - err = spider_net_setup_phy(card); - if (err) - goto out_undo_pci; - - err = spider_net_setup_netdev(card); - if (err) - goto out_undo_pci; - - return 0; - -out_undo_pci: - spider_net_undo_pci_setup(card); - free_netdev(card->netdev); -out: - return err; -} - -/** - * spider_net_remove - removal of a device - * @pdev: PCI device - * - * Returns 0 on success, <0 on failure - * - * spider_net_remove is called to remove the device and unregisters the - * net_device - **/ -static void __devexit -spider_net_remove(struct pci_dev *pdev) -{ - struct net_device *netdev; - struct spider_net_card *card; - - netdev = pci_get_drvdata(pdev); - card = netdev_priv(netdev); - - wait_event(card->waitq, - atomic_read(&card->tx_timeout_task_counter) == 0); - - unregister_netdev(netdev); - - /* switch off card */ - spider_net_write_reg(card, SPIDER_NET_CKRCTRL, - SPIDER_NET_CKRCTRL_STOP_VALUE); - spider_net_write_reg(card, SPIDER_NET_CKRCTRL, - SPIDER_NET_CKRCTRL_RUN_VALUE); - - spider_net_undo_pci_setup(card); - free_netdev(netdev); -} - -static struct pci_driver spider_net_driver = { - .name = spider_net_driver_name, - .id_table = spider_net_pci_tbl, - .probe = spider_net_probe, - .remove = __devexit_p(spider_net_remove) -}; - -/** - * spider_net_init - init function when the driver is loaded - * - * spider_net_init registers the device driver - */ -static int __init spider_net_init(void) -{ - printk(KERN_INFO "Spidernet version %s.\n", VERSION); - - if (rx_descriptors < SPIDER_NET_RX_DESCRIPTORS_MIN) { - rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_MIN; - pr_info("adjusting rx descriptors to %i.\n", rx_descriptors); - } - if (rx_descriptors > SPIDER_NET_RX_DESCRIPTORS_MAX) { - rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_MAX; - pr_info("adjusting rx descriptors to %i.\n", rx_descriptors); - } - if (tx_descriptors < SPIDER_NET_TX_DESCRIPTORS_MIN) { - tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_MIN; - pr_info("adjusting tx descriptors to %i.\n", tx_descriptors); - } - if (tx_descriptors > SPIDER_NET_TX_DESCRIPTORS_MAX) { - tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_MAX; - pr_info("adjusting tx descriptors to %i.\n", tx_descriptors); - } - - return pci_register_driver(&spider_net_driver); -} - -/** - * spider_net_cleanup - exit function when driver is unloaded - * - * spider_net_cleanup unregisters the device driver - */ -static void __exit spider_net_cleanup(void) -{ - pci_unregister_driver(&spider_net_driver); -} - -module_init(spider_net_init); -module_exit(spider_net_cleanup); diff --git a/drivers/net/spider_net.h b/drivers/net/spider_net.h deleted file mode 100644 index a891ad00054b..000000000000 --- a/drivers/net/spider_net.h +++ /dev/null @@ -1,489 +0,0 @@ -/* - * Network device driver for Cell Processor-Based Blade and Celleb platform - * - * (C) Copyright IBM Corp. 2005 - * (C) Copyright 2006 TOSHIBA CORPORATION - * - * Authors : Utz Bacher - * Jens Osterkamp - * - * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _SPIDER_NET_H -#define _SPIDER_NET_H - -#define VERSION "2.0 B" - -#include "./ethernet/sun/sungem_phy.h" - -extern int spider_net_stop(struct net_device *netdev); -extern int spider_net_open(struct net_device *netdev); - -extern const struct ethtool_ops spider_net_ethtool_ops; - -extern char spider_net_driver_name[]; - -#define SPIDER_NET_MAX_FRAME 2312 -#define SPIDER_NET_MAX_MTU 2294 -#define SPIDER_NET_MIN_MTU 64 - -#define SPIDER_NET_RXBUF_ALIGN 128 - -#define SPIDER_NET_RX_DESCRIPTORS_DEFAULT 256 -#define SPIDER_NET_RX_DESCRIPTORS_MIN 16 -#define SPIDER_NET_RX_DESCRIPTORS_MAX 512 - -#define SPIDER_NET_TX_DESCRIPTORS_DEFAULT 256 -#define SPIDER_NET_TX_DESCRIPTORS_MIN 16 -#define SPIDER_NET_TX_DESCRIPTORS_MAX 512 - -#define SPIDER_NET_TX_TIMER (HZ/5) -#define SPIDER_NET_ANEG_TIMER (HZ) -#define SPIDER_NET_ANEG_TIMEOUT 5 - -#define SPIDER_NET_RX_CSUM_DEFAULT 1 - -#define SPIDER_NET_WATCHDOG_TIMEOUT 50*HZ -#define SPIDER_NET_NAPI_WEIGHT 64 - -#define SPIDER_NET_FIRMWARE_SEQS 6 -#define SPIDER_NET_FIRMWARE_SEQWORDS 1024 -#define SPIDER_NET_FIRMWARE_LEN (SPIDER_NET_FIRMWARE_SEQS * \ - SPIDER_NET_FIRMWARE_SEQWORDS * \ - sizeof(u32)) -#define SPIDER_NET_FIRMWARE_NAME "spider_fw.bin" - -/** spider_net SMMIO registers */ -#define SPIDER_NET_GHIINT0STS 0x00000000 -#define SPIDER_NET_GHIINT1STS 0x00000004 -#define SPIDER_NET_GHIINT2STS 0x00000008 -#define SPIDER_NET_GHIINT0MSK 0x00000010 -#define SPIDER_NET_GHIINT1MSK 0x00000014 -#define SPIDER_NET_GHIINT2MSK 0x00000018 - -#define SPIDER_NET_GRESUMINTNUM 0x00000020 -#define SPIDER_NET_GREINTNUM 0x00000024 - -#define SPIDER_NET_GFFRMNUM 0x00000028 -#define SPIDER_NET_GFAFRMNUM 0x0000002c -#define SPIDER_NET_GFBFRMNUM 0x00000030 -#define SPIDER_NET_GFCFRMNUM 0x00000034 -#define SPIDER_NET_GFDFRMNUM 0x00000038 - -/* clear them (don't use it) */ -#define SPIDER_NET_GFREECNNUM 0x0000003c -#define SPIDER_NET_GONETIMENUM 0x00000040 - -#define SPIDER_NET_GTOUTFRMNUM 0x00000044 - -#define SPIDER_NET_GTXMDSET 0x00000050 -#define SPIDER_NET_GPCCTRL 0x00000054 -#define SPIDER_NET_GRXMDSET 0x00000058 -#define SPIDER_NET_GIPSECINIT 0x0000005c -#define SPIDER_NET_GFTRESTRT 0x00000060 -#define SPIDER_NET_GRXDMAEN 0x00000064 -#define SPIDER_NET_GMRWOLCTRL 0x00000068 -#define SPIDER_NET_GPCWOPCMD 0x0000006c -#define SPIDER_NET_GPCROPCMD 0x00000070 -#define SPIDER_NET_GTTFRMCNT 0x00000078 -#define SPIDER_NET_GTESTMD 0x0000007c - -#define SPIDER_NET_GSINIT 0x00000080 -#define SPIDER_NET_GSnPRGADR 0x00000084 -#define SPIDER_NET_GSnPRGDAT 0x00000088 - -#define SPIDER_NET_GMACOPEMD 0x00000100 -#define SPIDER_NET_GMACLENLMT 0x00000108 -#define SPIDER_NET_GMACST 0x00000110 -#define SPIDER_NET_GMACINTEN 0x00000118 -#define SPIDER_NET_GMACPHYCTRL 0x00000120 - -#define SPIDER_NET_GMACAPAUSE 0x00000154 -#define SPIDER_NET_GMACTXPAUSE 0x00000164 - -#define SPIDER_NET_GMACMODE 0x000001b0 -#define SPIDER_NET_GMACBSTLMT 0x000001b4 - -#define SPIDER_NET_GMACUNIMACU 0x000001c0 -#define SPIDER_NET_GMACUNIMACL 0x000001c8 - -#define SPIDER_NET_GMRMHFILnR 0x00000400 -#define SPIDER_NET_MULTICAST_HASHES 256 - -#define SPIDER_NET_GMRUAFILnR 0x00000500 -#define SPIDER_NET_GMRUA0FIL15R 0x00000578 - -#define SPIDER_NET_GTTQMSK 0x00000934 - -/* RX DMA controller registers, all 0x00000a.. are for DMA controller A, - * 0x00000b.. for DMA controller B, etc. */ -#define SPIDER_NET_GDADCHA 0x00000a00 -#define SPIDER_NET_GDADMACCNTR 0x00000a04 -#define SPIDER_NET_GDACTDPA 0x00000a08 -#define SPIDER_NET_GDACTDCNT 0x00000a0c -#define SPIDER_NET_GDACDBADDR 0x00000a20 -#define SPIDER_NET_GDACDBSIZE 0x00000a24 -#define SPIDER_NET_GDACNEXTDA 0x00000a28 -#define SPIDER_NET_GDACCOMST 0x00000a2c -#define SPIDER_NET_GDAWBCOMST 0x00000a30 -#define SPIDER_NET_GDAWBRSIZE 0x00000a34 -#define SPIDER_NET_GDAWBVSIZE 0x00000a38 -#define SPIDER_NET_GDAWBTRST 0x00000a3c -#define SPIDER_NET_GDAWBTRERR 0x00000a40 - -/* TX DMA controller registers */ -#define SPIDER_NET_GDTDCHA 0x00000e00 -#define SPIDER_NET_GDTDMACCNTR 0x00000e04 -#define SPIDER_NET_GDTCDPA 0x00000e08 -#define SPIDER_NET_GDTDMASEL 0x00000e14 - -#define SPIDER_NET_ECMODE 0x00000f00 -/* clock and reset control register */ -#define SPIDER_NET_CKRCTRL 0x00000ff0 - -/** SCONFIG registers */ -#define SPIDER_NET_SCONFIG_IOACTE 0x00002810 - -/** interrupt mask registers */ -#define SPIDER_NET_INT0_MASK_VALUE 0x3f7fe2c7 -#define SPIDER_NET_INT1_MASK_VALUE 0x0000fff2 -#define SPIDER_NET_INT2_MASK_VALUE 0x000003f1 - -/* we rely on flagged descriptor interrupts */ -#define SPIDER_NET_FRAMENUM_VALUE 0x00000000 -/* set this first, then the FRAMENUM_VALUE */ -#define SPIDER_NET_GFXFRAMES_VALUE 0x00000000 - -#define SPIDER_NET_STOP_SEQ_VALUE 0x00000000 -#define SPIDER_NET_RUN_SEQ_VALUE 0x0000007e - -#define SPIDER_NET_PHY_CTRL_VALUE 0x00040040 -/* #define SPIDER_NET_PHY_CTRL_VALUE 0x01070080*/ -#define SPIDER_NET_RXMODE_VALUE 0x00000011 -/* auto retransmission in case of MAC aborts */ -#define SPIDER_NET_TXMODE_VALUE 0x00010000 -#define SPIDER_NET_RESTART_VALUE 0x00000000 -#define SPIDER_NET_WOL_VALUE 0x00001111 -#if 0 -#define SPIDER_NET_WOL_VALUE 0x00000000 -#endif -#define SPIDER_NET_IPSECINIT_VALUE 0x6f716f71 - -/* pause frames: automatic, no upper retransmission count */ -/* outside loopback mode: ETOMOD signal dont matter, not connected */ -/* ETOMOD signal is brought to PHY reset. bit 2 must be 1 in Celleb */ -#define SPIDER_NET_OPMODE_VALUE 0x00000067 -/*#define SPIDER_NET_OPMODE_VALUE 0x001b0062*/ -#define SPIDER_NET_LENLMT_VALUE 0x00000908 - -#define SPIDER_NET_MACAPAUSE_VALUE 0x00000800 /* about 1 ms */ -#define SPIDER_NET_TXPAUSE_VALUE 0x00000000 - -#define SPIDER_NET_MACMODE_VALUE 0x00000001 -#define SPIDER_NET_BURSTLMT_VALUE 0x00000200 /* about 16 us */ - -/* DMAC control register GDMACCNTR - * - * 1(0) enable r/tx dma - * 0000000 fixed to 0 - * - * 000000 fixed to 0 - * 0(1) en/disable descr writeback on force end - * 0(1) force end - * - * 000000 fixed to 0 - * 00 burst alignment: 128 bytes - * 11 burst alignment: 1024 bytes - * - * 00000 fixed to 0 - * 0 descr writeback size 32 bytes - * 0(1) descr chain end interrupt enable - * 0(1) descr status writeback enable */ - -/* to set RX_DMA_EN */ -#define SPIDER_NET_DMA_RX_VALUE 0x80000000 -#define SPIDER_NET_DMA_RX_FEND_VALUE 0x00030003 -/* to set TX_DMA_EN */ -#define SPIDER_NET_TX_DMA_EN 0x80000000 -#define SPIDER_NET_GDTBSTA 0x00000300 -#define SPIDER_NET_GDTDCEIDIS 0x00000002 -#define SPIDER_NET_DMA_TX_VALUE SPIDER_NET_TX_DMA_EN | \ - SPIDER_NET_GDTDCEIDIS | \ - SPIDER_NET_GDTBSTA - -#define SPIDER_NET_DMA_TX_FEND_VALUE 0x00030003 - -/* SPIDER_NET_UA_DESCR_VALUE is OR'ed with the unicast address */ -#define SPIDER_NET_UA_DESCR_VALUE 0x00080000 -#define SPIDER_NET_PROMISC_VALUE 0x00080000 -#define SPIDER_NET_NONPROMISC_VALUE 0x00000000 - -#define SPIDER_NET_DMASEL_VALUE 0x00000001 - -#define SPIDER_NET_ECMODE_VALUE 0x00000000 - -#define SPIDER_NET_CKRCTRL_RUN_VALUE 0x1fff010f -#define SPIDER_NET_CKRCTRL_STOP_VALUE 0x0000010f - -#define SPIDER_NET_SBIMSTATE_VALUE 0x00000000 -#define SPIDER_NET_SBTMSTATE_VALUE 0x00000000 - -/* SPIDER_NET_GHIINT0STS bits, in reverse order so that they can be used - * with 1 << SPIDER_NET_... */ -enum spider_net_int0_status { - SPIDER_NET_GPHYINT = 0, - SPIDER_NET_GMAC2INT, - SPIDER_NET_GMAC1INT, - SPIDER_NET_GIPSINT, - SPIDER_NET_GFIFOINT, - SPIDER_NET_GDMACINT, - SPIDER_NET_GSYSINT, - SPIDER_NET_GPWOPCMPINT, - SPIDER_NET_GPROPCMPINT, - SPIDER_NET_GPWFFINT, - SPIDER_NET_GRMDADRINT, - SPIDER_NET_GRMARPINT, - SPIDER_NET_GRMMPINT, - SPIDER_NET_GDTDEN0INT, - SPIDER_NET_GDDDEN0INT, - SPIDER_NET_GDCDEN0INT, - SPIDER_NET_GDBDEN0INT, - SPIDER_NET_GDADEN0INT, - SPIDER_NET_GDTFDCINT, - SPIDER_NET_GDDFDCINT, - SPIDER_NET_GDCFDCINT, - SPIDER_NET_GDBFDCINT, - SPIDER_NET_GDAFDCINT, - SPIDER_NET_GTTEDINT, - SPIDER_NET_GDTDCEINT, - SPIDER_NET_GRFDNMINT, - SPIDER_NET_GRFCNMINT, - SPIDER_NET_GRFBNMINT, - SPIDER_NET_GRFANMINT, - SPIDER_NET_GRFNMINT, - SPIDER_NET_G1TMCNTINT, - SPIDER_NET_GFREECNTINT -}; -/* GHIINT1STS bits */ -enum spider_net_int1_status { - SPIDER_NET_GTMFLLINT = 0, - SPIDER_NET_GRMFLLINT, - SPIDER_NET_GTMSHTINT, - SPIDER_NET_GDTINVDINT, - SPIDER_NET_GRFDFLLINT, - SPIDER_NET_GDDDCEINT, - SPIDER_NET_GDDINVDINT, - SPIDER_NET_GRFCFLLINT, - SPIDER_NET_GDCDCEINT, - SPIDER_NET_GDCINVDINT, - SPIDER_NET_GRFBFLLINT, - SPIDER_NET_GDBDCEINT, - SPIDER_NET_GDBINVDINT, - SPIDER_NET_GRFAFLLINT, - SPIDER_NET_GDADCEINT, - SPIDER_NET_GDAINVDINT, - SPIDER_NET_GDTRSERINT, - SPIDER_NET_GDDRSERINT, - SPIDER_NET_GDCRSERINT, - SPIDER_NET_GDBRSERINT, - SPIDER_NET_GDARSERINT, - SPIDER_NET_GDSERINT, - SPIDER_NET_GDTPTERINT, - SPIDER_NET_GDDPTERINT, - SPIDER_NET_GDCPTERINT, - SPIDER_NET_GDBPTERINT, - SPIDER_NET_GDAPTERINT -}; -/* GHIINT2STS bits */ -enum spider_net_int2_status { - SPIDER_NET_GPROPERINT = 0, - SPIDER_NET_GMCTCRSNGINT, - SPIDER_NET_GMCTLCOLINT, - SPIDER_NET_GMCTTMOTINT, - SPIDER_NET_GMCRCAERINT, - SPIDER_NET_GMCRCALERINT, - SPIDER_NET_GMCRALNERINT, - SPIDER_NET_GMCROVRINT, - SPIDER_NET_GMCRRNTINT, - SPIDER_NET_GMCRRXERINT, - SPIDER_NET_GTITCSERINT, - SPIDER_NET_GTIFMTERINT, - SPIDER_NET_GTIPKTRVKINT, - SPIDER_NET_GTISPINGINT, - SPIDER_NET_GTISADNGINT, - SPIDER_NET_GTISPDNGINT, - SPIDER_NET_GRIFMTERINT, - SPIDER_NET_GRIPKTRVKINT, - SPIDER_NET_GRISPINGINT, - SPIDER_NET_GRISADNGINT, - SPIDER_NET_GRISPDNGINT -}; - -#define SPIDER_NET_TXINT (1 << SPIDER_NET_GDTFDCINT) - -/* We rely on flagged descriptor interrupts */ -#define SPIDER_NET_RXINT ( (1 << SPIDER_NET_GDAFDCINT) ) - -#define SPIDER_NET_LINKINT ( 1 << SPIDER_NET_GMAC2INT ) - -#define SPIDER_NET_ERRINT ( 0xffffffff & \ - (~SPIDER_NET_TXINT) & \ - (~SPIDER_NET_RXINT) & \ - (~SPIDER_NET_LINKINT) ) - -#define SPIDER_NET_GPREXEC 0x80000000 -#define SPIDER_NET_GPRDAT_MASK 0x0000ffff - -#define SPIDER_NET_DMAC_NOINTR_COMPLETE 0x00800000 -#define SPIDER_NET_DMAC_TXFRMTL 0x00040000 -#define SPIDER_NET_DMAC_TCP 0x00020000 -#define SPIDER_NET_DMAC_UDP 0x00030000 -#define SPIDER_NET_TXDCEST 0x08000000 - -#define SPIDER_NET_DESCR_RXFDIS 0x00000001 -#define SPIDER_NET_DESCR_RXDCEIS 0x00000002 -#define SPIDER_NET_DESCR_RXDEN0IS 0x00000004 -#define SPIDER_NET_DESCR_RXINVDIS 0x00000008 -#define SPIDER_NET_DESCR_RXRERRIS 0x00000010 -#define SPIDER_NET_DESCR_RXFDCIMS 0x00000100 -#define SPIDER_NET_DESCR_RXDCEIMS 0x00000200 -#define SPIDER_NET_DESCR_RXDEN0IMS 0x00000400 -#define SPIDER_NET_DESCR_RXINVDIMS 0x00000800 -#define SPIDER_NET_DESCR_RXRERRMIS 0x00001000 -#define SPIDER_NET_DESCR_UNUSED 0x077fe0e0 - -#define SPIDER_NET_DESCR_IND_PROC_MASK 0xF0000000 -#define SPIDER_NET_DESCR_COMPLETE 0x00000000 /* used in rx and tx */ -#define SPIDER_NET_DESCR_RESPONSE_ERROR 0x10000000 /* used in rx and tx */ -#define SPIDER_NET_DESCR_PROTECTION_ERROR 0x20000000 /* used in rx and tx */ -#define SPIDER_NET_DESCR_FRAME_END 0x40000000 /* used in rx */ -#define SPIDER_NET_DESCR_FORCE_END 0x50000000 /* used in rx and tx */ -#define SPIDER_NET_DESCR_CARDOWNED 0xA0000000 /* used in rx and tx */ -#define SPIDER_NET_DESCR_NOT_IN_USE 0xF0000000 -#define SPIDER_NET_DESCR_TXDESFLG 0x00800000 - -#define SPIDER_NET_DESCR_BAD_STATUS (SPIDER_NET_DESCR_RXDEN0IS | \ - SPIDER_NET_DESCR_RXRERRIS | \ - SPIDER_NET_DESCR_RXDEN0IMS | \ - SPIDER_NET_DESCR_RXINVDIMS | \ - SPIDER_NET_DESCR_RXRERRMIS | \ - SPIDER_NET_DESCR_UNUSED) - -/* Descriptor, as defined by the hardware */ -struct spider_net_hw_descr { - u32 buf_addr; - u32 buf_size; - u32 next_descr_addr; - u32 dmac_cmd_status; - u32 result_size; - u32 valid_size; /* all zeroes for tx */ - u32 data_status; - u32 data_error; /* all zeroes for tx */ -} __attribute__((aligned(32))); - -struct spider_net_descr { - struct spider_net_hw_descr *hwdescr; - struct sk_buff *skb; - u32 bus_addr; - struct spider_net_descr *next; - struct spider_net_descr *prev; -}; - -struct spider_net_descr_chain { - spinlock_t lock; - struct spider_net_descr *head; - struct spider_net_descr *tail; - struct spider_net_descr *ring; - int num_desc; - struct spider_net_hw_descr *hwring; - dma_addr_t dma_addr; -}; - -/* descriptor data_status bits */ -#define SPIDER_NET_RX_IPCHK 29 -#define SPIDER_NET_RX_TCPCHK 28 -#define SPIDER_NET_VLAN_PACKET 21 -#define SPIDER_NET_DATA_STATUS_CKSUM_MASK ( (1 << SPIDER_NET_RX_IPCHK) | \ - (1 << SPIDER_NET_RX_TCPCHK) ) - -/* descriptor data_error bits */ -#define SPIDER_NET_RX_IPCHKERR 27 -#define SPIDER_NET_RX_RXTCPCHKERR 28 - -#define SPIDER_NET_DATA_ERR_CKSUM_MASK (1 << SPIDER_NET_RX_IPCHKERR) - -/* the cases we don't pass the packet to the stack. - * 701b8000 would be correct, but every packets gets that flag */ -#define SPIDER_NET_DESTROY_RX_FLAGS 0x700b8000 - -#define SPIDER_NET_DEFAULT_MSG ( NETIF_MSG_DRV | \ - NETIF_MSG_PROBE | \ - NETIF_MSG_LINK | \ - NETIF_MSG_TIMER | \ - NETIF_MSG_IFDOWN | \ - NETIF_MSG_IFUP | \ - NETIF_MSG_RX_ERR | \ - NETIF_MSG_TX_ERR | \ - NETIF_MSG_TX_QUEUED | \ - NETIF_MSG_INTR | \ - NETIF_MSG_TX_DONE | \ - NETIF_MSG_RX_STATUS | \ - NETIF_MSG_PKTDATA | \ - NETIF_MSG_HW | \ - NETIF_MSG_WOL ) - -struct spider_net_extra_stats { - unsigned long rx_desc_error; - unsigned long tx_timeouts; - unsigned long alloc_rx_skb_error; - unsigned long rx_iommu_map_error; - unsigned long tx_iommu_map_error; - unsigned long rx_desc_unk_state; -}; - -struct spider_net_card { - struct net_device *netdev; - struct pci_dev *pdev; - struct mii_phy phy; - - struct napi_struct napi; - - int medium; - - void __iomem *regs; - - struct spider_net_descr_chain tx_chain; - struct spider_net_descr_chain rx_chain; - struct spider_net_descr *low_watermark; - - int aneg_count; - struct timer_list aneg_timer; - struct timer_list tx_timer; - struct work_struct tx_timeout_task; - atomic_t tx_timeout_task_counter; - wait_queue_head_t waitq; - int num_rx_ints; - int ignore_rx_ramfull; - - /* for ethtool */ - int msg_enable; - struct spider_net_extra_stats spider_stats; - - /* Must be last item in struct */ - struct spider_net_descr darray[0]; -}; - -#endif diff --git a/drivers/net/spider_net_ethtool.c b/drivers/net/spider_net_ethtool.c deleted file mode 100644 index 9c288cd7d171..000000000000 --- a/drivers/net/spider_net_ethtool.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Network device driver for Cell Processor-Based Blade - * - * (C) Copyright IBM Corp. 2005 - * - * Authors : Utz Bacher - * Jens Osterkamp - * - * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include - -#include "spider_net.h" - - -static struct { - const char str[ETH_GSTRING_LEN]; -} ethtool_stats_keys[] = { - { "tx_packets" }, - { "tx_bytes" }, - { "rx_packets" }, - { "rx_bytes" }, - { "tx_errors" }, - { "tx_dropped" }, - { "rx_dropped" }, - { "rx_descriptor_error" }, - { "tx_timeouts" }, - { "alloc_rx_skb_error" }, - { "rx_iommu_map_error" }, - { "tx_iommu_map_error" }, - { "rx_desc_unk_state" }, -}; - -static int -spider_net_ethtool_get_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) -{ - struct spider_net_card *card; - card = netdev_priv(netdev); - - cmd->supported = (SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE); - cmd->advertising = (ADVERTISED_1000baseT_Full | - ADVERTISED_FIBRE); - cmd->port = PORT_FIBRE; - ethtool_cmd_speed_set(cmd, card->phy.speed); - cmd->duplex = DUPLEX_FULL; - - return 0; -} - -static void -spider_net_ethtool_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *drvinfo) -{ - struct spider_net_card *card; - card = netdev_priv(netdev); - - /* clear and fill out info */ - memset(drvinfo, 0, sizeof(struct ethtool_drvinfo)); - strncpy(drvinfo->driver, spider_net_driver_name, 32); - strncpy(drvinfo->version, VERSION, 32); - strcpy(drvinfo->fw_version, "no information"); - strncpy(drvinfo->bus_info, pci_name(card->pdev), 32); -} - -static void -spider_net_ethtool_get_wol(struct net_device *netdev, - struct ethtool_wolinfo *wolinfo) -{ - /* no support for wol */ - wolinfo->supported = 0; - wolinfo->wolopts = 0; -} - -static u32 -spider_net_ethtool_get_msglevel(struct net_device *netdev) -{ - struct spider_net_card *card; - card = netdev_priv(netdev); - return card->msg_enable; -} - -static void -spider_net_ethtool_set_msglevel(struct net_device *netdev, - u32 level) -{ - struct spider_net_card *card; - card = netdev_priv(netdev); - card->msg_enable = level; -} - -static int -spider_net_ethtool_nway_reset(struct net_device *netdev) -{ - if (netif_running(netdev)) { - spider_net_stop(netdev); - spider_net_open(netdev); - } - return 0; -} - -static void -spider_net_ethtool_get_ringparam(struct net_device *netdev, - struct ethtool_ringparam *ering) -{ - struct spider_net_card *card = netdev_priv(netdev); - - ering->tx_max_pending = SPIDER_NET_TX_DESCRIPTORS_MAX; - ering->tx_pending = card->tx_chain.num_desc; - ering->rx_max_pending = SPIDER_NET_RX_DESCRIPTORS_MAX; - ering->rx_pending = card->rx_chain.num_desc; -} - -static int spider_net_get_sset_count(struct net_device *netdev, int sset) -{ - switch (sset) { - case ETH_SS_STATS: - return ARRAY_SIZE(ethtool_stats_keys); - default: - return -EOPNOTSUPP; - } -} - -static void spider_net_get_ethtool_stats(struct net_device *netdev, - struct ethtool_stats *stats, u64 *data) -{ - struct spider_net_card *card = netdev_priv(netdev); - - data[0] = netdev->stats.tx_packets; - data[1] = netdev->stats.tx_bytes; - data[2] = netdev->stats.rx_packets; - data[3] = netdev->stats.rx_bytes; - data[4] = netdev->stats.tx_errors; - data[5] = netdev->stats.tx_dropped; - data[6] = netdev->stats.rx_dropped; - data[7] = card->spider_stats.rx_desc_error; - data[8] = card->spider_stats.tx_timeouts; - data[9] = card->spider_stats.alloc_rx_skb_error; - data[10] = card->spider_stats.rx_iommu_map_error; - data[11] = card->spider_stats.tx_iommu_map_error; - data[12] = card->spider_stats.rx_desc_unk_state; -} - -static void spider_net_get_strings(struct net_device *netdev, u32 stringset, - u8 *data) -{ - memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); -} - -const struct ethtool_ops spider_net_ethtool_ops = { - .get_settings = spider_net_ethtool_get_settings, - .get_drvinfo = spider_net_ethtool_get_drvinfo, - .get_wol = spider_net_ethtool_get_wol, - .get_msglevel = spider_net_ethtool_get_msglevel, - .set_msglevel = spider_net_ethtool_set_msglevel, - .get_link = ethtool_op_get_link, - .nway_reset = spider_net_ethtool_nway_reset, - .get_ringparam = spider_net_ethtool_get_ringparam, - .get_strings = spider_net_get_strings, - .get_sset_count = spider_net_get_sset_count, - .get_ethtool_stats = spider_net_get_ethtool_stats, -}; - diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c deleted file mode 100644 index 4a55a162dfe6..000000000000 --- a/drivers/net/tc35815.c +++ /dev/null @@ -1,2228 +0,0 @@ -/* - * tc35815.c: A TOSHIBA TC35815CF PCI 10/100Mbps ethernet driver for linux. - * - * Based on skelton.c by Donald Becker. - * - * This driver is a replacement of older and less maintained version. - * This is a header of the older version: - * ---------- - * Copyright 2001 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * ahennessy@mvista.com - * Copyright (C) 2000-2001 Toshiba Corporation - * static const char *version = - * "tc35815.c:v0.00 26/07/2000 by Toshiba Corporation\n"; - * ---------- - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright TOSHIBA CORPORATION 2004-2005 - * All Rights Reserved. - */ - -#define DRV_VERSION "1.39" -static const char *version = "tc35815.c:v" DRV_VERSION "\n"; -#define MODNAME "tc35815" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum tc35815_chiptype { - TC35815CF = 0, - TC35815_NWU, - TC35815_TX4939, -}; - -/* indexed by tc35815_chiptype, above */ -static const struct { - const char *name; -} chip_info[] __devinitdata = { - { "TOSHIBA TC35815CF 10/100BaseTX" }, - { "TOSHIBA TC35815 with Wake on LAN" }, - { "TOSHIBA TC35815/TX4939" }, -}; - -static DEFINE_PCI_DEVICE_TABLE(tc35815_pci_tbl) = { - {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815CF), .driver_data = TC35815CF }, - {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815_NWU), .driver_data = TC35815_NWU }, - {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939), .driver_data = TC35815_TX4939 }, - {0,} -}; -MODULE_DEVICE_TABLE(pci, tc35815_pci_tbl); - -/* see MODULE_PARM_DESC */ -static struct tc35815_options { - int speed; - int duplex; -} options; - -/* - * Registers - */ -struct tc35815_regs { - __u32 DMA_Ctl; /* 0x00 */ - __u32 TxFrmPtr; - __u32 TxThrsh; - __u32 TxPollCtr; - __u32 BLFrmPtr; - __u32 RxFragSize; - __u32 Int_En; - __u32 FDA_Bas; - __u32 FDA_Lim; /* 0x20 */ - __u32 Int_Src; - __u32 unused0[2]; - __u32 PauseCnt; - __u32 RemPauCnt; - __u32 TxCtlFrmStat; - __u32 unused1; - __u32 MAC_Ctl; /* 0x40 */ - __u32 CAM_Ctl; - __u32 Tx_Ctl; - __u32 Tx_Stat; - __u32 Rx_Ctl; - __u32 Rx_Stat; - __u32 MD_Data; - __u32 MD_CA; - __u32 CAM_Adr; /* 0x60 */ - __u32 CAM_Data; - __u32 CAM_Ena; - __u32 PROM_Ctl; - __u32 PROM_Data; - __u32 Algn_Cnt; - __u32 CRC_Cnt; - __u32 Miss_Cnt; -}; - -/* - * Bit assignments - */ -/* DMA_Ctl bit assign ------------------------------------------------------- */ -#define DMA_RxAlign 0x00c00000 /* 1:Reception Alignment */ -#define DMA_RxAlign_1 0x00400000 -#define DMA_RxAlign_2 0x00800000 -#define DMA_RxAlign_3 0x00c00000 -#define DMA_M66EnStat 0x00080000 /* 1:66MHz Enable State */ -#define DMA_IntMask 0x00040000 /* 1:Interrupt mask */ -#define DMA_SWIntReq 0x00020000 /* 1:Software Interrupt request */ -#define DMA_TxWakeUp 0x00010000 /* 1:Transmit Wake Up */ -#define DMA_RxBigE 0x00008000 /* 1:Receive Big Endian */ -#define DMA_TxBigE 0x00004000 /* 1:Transmit Big Endian */ -#define DMA_TestMode 0x00002000 /* 1:Test Mode */ -#define DMA_PowrMgmnt 0x00001000 /* 1:Power Management */ -#define DMA_DmBurst_Mask 0x000001fc /* DMA Burst size */ - -/* RxFragSize bit assign ---------------------------------------------------- */ -#define RxFrag_EnPack 0x00008000 /* 1:Enable Packing */ -#define RxFrag_MinFragMask 0x00000ffc /* Minimum Fragment */ - -/* MAC_Ctl bit assign ------------------------------------------------------- */ -#define MAC_Link10 0x00008000 /* 1:Link Status 10Mbits */ -#define MAC_EnMissRoll 0x00002000 /* 1:Enable Missed Roll */ -#define MAC_MissRoll 0x00000400 /* 1:Missed Roll */ -#define MAC_Loop10 0x00000080 /* 1:Loop 10 Mbps */ -#define MAC_Conn_Auto 0x00000000 /*00:Connection mode (Automatic) */ -#define MAC_Conn_10M 0x00000020 /*01: (10Mbps endec)*/ -#define MAC_Conn_Mll 0x00000040 /*10: (Mll clock) */ -#define MAC_MacLoop 0x00000010 /* 1:MAC Loopback */ -#define MAC_FullDup 0x00000008 /* 1:Full Duplex 0:Half Duplex */ -#define MAC_Reset 0x00000004 /* 1:Software Reset */ -#define MAC_HaltImm 0x00000002 /* 1:Halt Immediate */ -#define MAC_HaltReq 0x00000001 /* 1:Halt request */ - -/* PROM_Ctl bit assign ------------------------------------------------------ */ -#define PROM_Busy 0x00008000 /* 1:Busy (Start Operation) */ -#define PROM_Read 0x00004000 /*10:Read operation */ -#define PROM_Write 0x00002000 /*01:Write operation */ -#define PROM_Erase 0x00006000 /*11:Erase operation */ - /*00:Enable or Disable Writting, */ - /* as specified in PROM_Addr. */ -#define PROM_Addr_Ena 0x00000030 /*11xxxx:PROM Write enable */ - /*00xxxx: disable */ - -/* CAM_Ctl bit assign ------------------------------------------------------- */ -#define CAM_CompEn 0x00000010 /* 1:CAM Compare Enable */ -#define CAM_NegCAM 0x00000008 /* 1:Reject packets CAM recognizes,*/ - /* accept other */ -#define CAM_BroadAcc 0x00000004 /* 1:Broadcast assept */ -#define CAM_GroupAcc 0x00000002 /* 1:Multicast assept */ -#define CAM_StationAcc 0x00000001 /* 1:unicast accept */ - -/* CAM_Ena bit assign ------------------------------------------------------- */ -#define CAM_ENTRY_MAX 21 /* CAM Data entry max count */ -#define CAM_Ena_Mask ((1<chiptype != TC35815CF) - -/* Tuning parameters */ -#define DMA_BURST_SIZE 32 -#define TX_THRESHOLD 1024 -/* used threshold with packet max byte for low pci transfer ability.*/ -#define TX_THRESHOLD_MAX 1536 -/* setting threshold max value when overrun error occurred this count. */ -#define TX_THRESHOLD_KEEP_LIMIT 10 - -/* 16 + RX_BUF_NUM * 8 + RX_FD_NUM * 16 + TX_FD_NUM * 32 <= PAGE_SIZE*FD_PAGE_NUM */ -#define FD_PAGE_NUM 4 -#define RX_BUF_NUM 128 /* < 256 */ -#define RX_FD_NUM 256 /* >= 32 */ -#define TX_FD_NUM 128 -#if RX_CTL_CMD & Rx_LongEn -#define RX_BUF_SIZE PAGE_SIZE -#elif RX_CTL_CMD & Rx_StripCRC -#define RX_BUF_SIZE \ - L1_CACHE_ALIGN(ETH_FRAME_LEN + VLAN_HLEN + NET_IP_ALIGN) -#else -#define RX_BUF_SIZE \ - L1_CACHE_ALIGN(ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN + NET_IP_ALIGN) -#endif -#define RX_FD_RESERVE (2 / 2) /* max 2 BD per RxFD */ -#define NAPI_WEIGHT 16 - -struct TxFD { - struct FDesc fd; - struct BDesc bd; - struct BDesc unused; -}; - -struct RxFD { - struct FDesc fd; - struct BDesc bd[0]; /* variable length */ -}; - -struct FrFD { - struct FDesc fd; - struct BDesc bd[RX_BUF_NUM]; -}; - - -#define tc_readl(addr) ioread32(addr) -#define tc_writel(d, addr) iowrite32(d, addr) - -#define TC35815_TX_TIMEOUT msecs_to_jiffies(400) - -/* Information that need to be kept for each controller. */ -struct tc35815_local { - struct pci_dev *pci_dev; - - struct net_device *dev; - struct napi_struct napi; - - /* statistics */ - struct { - int max_tx_qlen; - int tx_ints; - int rx_ints; - int tx_underrun; - } lstats; - - /* Tx control lock. This protects the transmit buffer ring - * state along with the "tx full" state of the driver. This - * means all netif_queue flow control actions are protected - * by this lock as well. - */ - spinlock_t lock; - spinlock_t rx_lock; - - struct mii_bus *mii_bus; - struct phy_device *phy_dev; - int duplex; - int speed; - int link; - struct work_struct restart_work; - - /* - * Transmitting: Batch Mode. - * 1 BD in 1 TxFD. - * Receiving: Non-Packing Mode. - * 1 circular FD for Free Buffer List. - * RX_BUF_NUM BD in Free Buffer FD. - * One Free Buffer BD has ETH_FRAME_LEN data buffer. - */ - void *fd_buf; /* for TxFD, RxFD, FrFD */ - dma_addr_t fd_buf_dma; - struct TxFD *tfd_base; - unsigned int tfd_start; - unsigned int tfd_end; - struct RxFD *rfd_base; - struct RxFD *rfd_limit; - struct RxFD *rfd_cur; - struct FrFD *fbl_ptr; - unsigned int fbl_count; - struct { - struct sk_buff *skb; - dma_addr_t skb_dma; - } tx_skbs[TX_FD_NUM], rx_skbs[RX_BUF_NUM]; - u32 msg_enable; - enum tc35815_chiptype chiptype; -}; - -static inline dma_addr_t fd_virt_to_bus(struct tc35815_local *lp, void *virt) -{ - return lp->fd_buf_dma + ((u8 *)virt - (u8 *)lp->fd_buf); -} -#ifdef DEBUG -static inline void *fd_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus) -{ - return (void *)((u8 *)lp->fd_buf + (bus - lp->fd_buf_dma)); -} -#endif -static struct sk_buff *alloc_rxbuf_skb(struct net_device *dev, - struct pci_dev *hwdev, - dma_addr_t *dma_handle) -{ - struct sk_buff *skb; - skb = dev_alloc_skb(RX_BUF_SIZE); - if (!skb) - return NULL; - *dma_handle = pci_map_single(hwdev, skb->data, RX_BUF_SIZE, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(hwdev, *dma_handle)) { - dev_kfree_skb_any(skb); - return NULL; - } - skb_reserve(skb, 2); /* make IP header 4byte aligned */ - return skb; -} - -static void free_rxbuf_skb(struct pci_dev *hwdev, struct sk_buff *skb, dma_addr_t dma_handle) -{ - pci_unmap_single(hwdev, dma_handle, RX_BUF_SIZE, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(skb); -} - -/* Index to functions, as function prototypes. */ - -static int tc35815_open(struct net_device *dev); -static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev); -static irqreturn_t tc35815_interrupt(int irq, void *dev_id); -static int tc35815_rx(struct net_device *dev, int limit); -static int tc35815_poll(struct napi_struct *napi, int budget); -static void tc35815_txdone(struct net_device *dev); -static int tc35815_close(struct net_device *dev); -static struct net_device_stats *tc35815_get_stats(struct net_device *dev); -static void tc35815_set_multicast_list(struct net_device *dev); -static void tc35815_tx_timeout(struct net_device *dev); -static int tc35815_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -#ifdef CONFIG_NET_POLL_CONTROLLER -static void tc35815_poll_controller(struct net_device *dev); -#endif -static const struct ethtool_ops tc35815_ethtool_ops; - -/* Example routines you must write ;->. */ -static void tc35815_chip_reset(struct net_device *dev); -static void tc35815_chip_init(struct net_device *dev); - -#ifdef DEBUG -static void panic_queues(struct net_device *dev); -#endif - -static void tc35815_restart_work(struct work_struct *work); - -static int tc_mdio_read(struct mii_bus *bus, int mii_id, int regnum) -{ - struct net_device *dev = bus->priv; - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - unsigned long timeout = jiffies + HZ; - - tc_writel(MD_CA_Busy | (mii_id << 5) | (regnum & 0x1f), &tr->MD_CA); - udelay(12); /* it takes 32 x 400ns at least */ - while (tc_readl(&tr->MD_CA) & MD_CA_Busy) { - if (time_after(jiffies, timeout)) - return -EIO; - cpu_relax(); - } - return tc_readl(&tr->MD_Data) & 0xffff; -} - -static int tc_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 val) -{ - struct net_device *dev = bus->priv; - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - unsigned long timeout = jiffies + HZ; - - tc_writel(val, &tr->MD_Data); - tc_writel(MD_CA_Busy | MD_CA_Wr | (mii_id << 5) | (regnum & 0x1f), - &tr->MD_CA); - udelay(12); /* it takes 32 x 400ns at least */ - while (tc_readl(&tr->MD_CA) & MD_CA_Busy) { - if (time_after(jiffies, timeout)) - return -EIO; - cpu_relax(); - } - return 0; -} - -static void tc_handle_link_change(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - struct phy_device *phydev = lp->phy_dev; - unsigned long flags; - int status_change = 0; - - spin_lock_irqsave(&lp->lock, flags); - if (phydev->link && - (lp->speed != phydev->speed || lp->duplex != phydev->duplex)) { - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - u32 reg; - - reg = tc_readl(&tr->MAC_Ctl); - reg |= MAC_HaltReq; - tc_writel(reg, &tr->MAC_Ctl); - if (phydev->duplex == DUPLEX_FULL) - reg |= MAC_FullDup; - else - reg &= ~MAC_FullDup; - tc_writel(reg, &tr->MAC_Ctl); - reg &= ~MAC_HaltReq; - tc_writel(reg, &tr->MAC_Ctl); - - /* - * TX4939 PCFG.SPEEDn bit will be changed on - * NETDEV_CHANGE event. - */ - /* - * WORKAROUND: enable LostCrS only if half duplex - * operation. - * (TX4939 does not have EnLCarr) - */ - if (phydev->duplex == DUPLEX_HALF && - lp->chiptype != TC35815_TX4939) - tc_writel(tc_readl(&tr->Tx_Ctl) | Tx_EnLCarr, - &tr->Tx_Ctl); - - lp->speed = phydev->speed; - lp->duplex = phydev->duplex; - status_change = 1; - } - - if (phydev->link != lp->link) { - if (phydev->link) { - /* delayed promiscuous enabling */ - if (dev->flags & IFF_PROMISC) - tc35815_set_multicast_list(dev); - } else { - lp->speed = 0; - lp->duplex = -1; - } - lp->link = phydev->link; - - status_change = 1; - } - spin_unlock_irqrestore(&lp->lock, flags); - - if (status_change && netif_msg_link(lp)) { - phy_print_status(phydev); - pr_debug("%s: MII BMCR %04x BMSR %04x LPA %04x\n", - dev->name, - phy_read(phydev, MII_BMCR), - phy_read(phydev, MII_BMSR), - phy_read(phydev, MII_LPA)); - } -} - -static int tc_mii_probe(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - struct phy_device *phydev = NULL; - int phy_addr; - u32 dropmask; - - /* find the first phy */ - for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - if (lp->mii_bus->phy_map[phy_addr]) { - if (phydev) { - printk(KERN_ERR "%s: multiple PHYs found\n", - dev->name); - return -EINVAL; - } - phydev = lp->mii_bus->phy_map[phy_addr]; - break; - } - } - - if (!phydev) { - printk(KERN_ERR "%s: no PHY found\n", dev->name); - return -ENODEV; - } - - /* attach the mac to the phy */ - phydev = phy_connect(dev, dev_name(&phydev->dev), - &tc_handle_link_change, 0, - lp->chiptype == TC35815_TX4939 ? - PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII); - if (IS_ERR(phydev)) { - printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); - return PTR_ERR(phydev); - } - printk(KERN_INFO "%s: attached PHY driver [%s] " - "(mii_bus:phy_addr=%s, id=%x)\n", - dev->name, phydev->drv->name, dev_name(&phydev->dev), - phydev->phy_id); - - /* mask with MAC supported features */ - phydev->supported &= PHY_BASIC_FEATURES; - dropmask = 0; - if (options.speed == 10) - dropmask |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; - else if (options.speed == 100) - dropmask |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; - if (options.duplex == 1) - dropmask |= SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full; - else if (options.duplex == 2) - dropmask |= SUPPORTED_10baseT_Half | SUPPORTED_100baseT_Half; - phydev->supported &= ~dropmask; - phydev->advertising = phydev->supported; - - lp->link = 0; - lp->speed = 0; - lp->duplex = -1; - lp->phy_dev = phydev; - - return 0; -} - -static int tc_mii_init(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - int err; - int i; - - lp->mii_bus = mdiobus_alloc(); - if (lp->mii_bus == NULL) { - err = -ENOMEM; - goto err_out; - } - - lp->mii_bus->name = "tc35815_mii_bus"; - lp->mii_bus->read = tc_mdio_read; - lp->mii_bus->write = tc_mdio_write; - snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%x", - (lp->pci_dev->bus->number << 8) | lp->pci_dev->devfn); - lp->mii_bus->priv = dev; - lp->mii_bus->parent = &lp->pci_dev->dev; - lp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!lp->mii_bus->irq) { - err = -ENOMEM; - goto err_out_free_mii_bus; - } - - for (i = 0; i < PHY_MAX_ADDR; i++) - lp->mii_bus->irq[i] = PHY_POLL; - - err = mdiobus_register(lp->mii_bus); - if (err) - goto err_out_free_mdio_irq; - err = tc_mii_probe(dev); - if (err) - goto err_out_unregister_bus; - return 0; - -err_out_unregister_bus: - mdiobus_unregister(lp->mii_bus); -err_out_free_mdio_irq: - kfree(lp->mii_bus->irq); -err_out_free_mii_bus: - mdiobus_free(lp->mii_bus); -err_out: - return err; -} - -#ifdef CONFIG_CPU_TX49XX -/* - * Find a platform_device providing a MAC address. The platform code - * should provide a "tc35815-mac" device with a MAC address in its - * platform_data. - */ -static int __devinit tc35815_mac_match(struct device *dev, void *data) -{ - struct platform_device *plat_dev = to_platform_device(dev); - struct pci_dev *pci_dev = data; - unsigned int id = pci_dev->irq; - return !strcmp(plat_dev->name, "tc35815-mac") && plat_dev->id == id; -} - -static int __devinit tc35815_read_plat_dev_addr(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - struct device *pd = bus_find_device(&platform_bus_type, NULL, - lp->pci_dev, tc35815_mac_match); - if (pd) { - if (pd->platform_data) - memcpy(dev->dev_addr, pd->platform_data, ETH_ALEN); - put_device(pd); - return is_valid_ether_addr(dev->dev_addr) ? 0 : -ENODEV; - } - return -ENODEV; -} -#else -static int __devinit tc35815_read_plat_dev_addr(struct net_device *dev) -{ - return -ENODEV; -} -#endif - -static int __devinit tc35815_init_dev_addr(struct net_device *dev) -{ - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - int i; - - while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) - ; - for (i = 0; i < 6; i += 2) { - unsigned short data; - tc_writel(PROM_Busy | PROM_Read | (i / 2 + 2), &tr->PROM_Ctl); - while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) - ; - data = tc_readl(&tr->PROM_Data); - dev->dev_addr[i] = data & 0xff; - dev->dev_addr[i+1] = data >> 8; - } - if (!is_valid_ether_addr(dev->dev_addr)) - return tc35815_read_plat_dev_addr(dev); - return 0; -} - -static const struct net_device_ops tc35815_netdev_ops = { - .ndo_open = tc35815_open, - .ndo_stop = tc35815_close, - .ndo_start_xmit = tc35815_send_packet, - .ndo_get_stats = tc35815_get_stats, - .ndo_set_multicast_list = tc35815_set_multicast_list, - .ndo_tx_timeout = tc35815_tx_timeout, - .ndo_do_ioctl = tc35815_ioctl, - .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = tc35815_poll_controller, -#endif -}; - -static int __devinit tc35815_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - void __iomem *ioaddr = NULL; - struct net_device *dev; - struct tc35815_local *lp; - int rc; - - static int printed_version; - if (!printed_version++) { - printk(version); - dev_printk(KERN_DEBUG, &pdev->dev, - "speed:%d duplex:%d\n", - options.speed, options.duplex); - } - - if (!pdev->irq) { - dev_warn(&pdev->dev, "no IRQ assigned.\n"); - return -ENODEV; - } - - /* dev zeroed in alloc_etherdev */ - dev = alloc_etherdev(sizeof(*lp)); - if (dev == NULL) { - dev_err(&pdev->dev, "unable to alloc new ethernet\n"); - return -ENOMEM; - } - SET_NETDEV_DEV(dev, &pdev->dev); - lp = netdev_priv(dev); - lp->dev = dev; - - /* enable device (incl. PCI PM wakeup), and bus-mastering */ - rc = pcim_enable_device(pdev); - if (rc) - goto err_out; - rc = pcim_iomap_regions(pdev, 1 << 1, MODNAME); - if (rc) - goto err_out; - pci_set_master(pdev); - ioaddr = pcim_iomap_table(pdev)[1]; - - /* Initialize the device structure. */ - dev->netdev_ops = &tc35815_netdev_ops; - dev->ethtool_ops = &tc35815_ethtool_ops; - dev->watchdog_timeo = TC35815_TX_TIMEOUT; - netif_napi_add(dev, &lp->napi, tc35815_poll, NAPI_WEIGHT); - - dev->irq = pdev->irq; - dev->base_addr = (unsigned long)ioaddr; - - INIT_WORK(&lp->restart_work, tc35815_restart_work); - spin_lock_init(&lp->lock); - spin_lock_init(&lp->rx_lock); - lp->pci_dev = pdev; - lp->chiptype = ent->driver_data; - - lp->msg_enable = NETIF_MSG_TX_ERR | NETIF_MSG_HW | NETIF_MSG_DRV | NETIF_MSG_LINK; - pci_set_drvdata(pdev, dev); - - /* Soft reset the chip. */ - tc35815_chip_reset(dev); - - /* Retrieve the ethernet address. */ - if (tc35815_init_dev_addr(dev)) { - dev_warn(&pdev->dev, "not valid ether addr\n"); - random_ether_addr(dev->dev_addr); - } - - rc = register_netdev(dev); - if (rc) - goto err_out; - - memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); - printk(KERN_INFO "%s: %s at 0x%lx, %pM, IRQ %d\n", - dev->name, - chip_info[ent->driver_data].name, - dev->base_addr, - dev->dev_addr, - dev->irq); - - rc = tc_mii_init(dev); - if (rc) - goto err_out_unregister; - - return 0; - -err_out_unregister: - unregister_netdev(dev); -err_out: - free_netdev(dev); - return rc; -} - - -static void __devexit tc35815_remove_one(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct tc35815_local *lp = netdev_priv(dev); - - phy_disconnect(lp->phy_dev); - mdiobus_unregister(lp->mii_bus); - kfree(lp->mii_bus->irq); - mdiobus_free(lp->mii_bus); - unregister_netdev(dev); - free_netdev(dev); - pci_set_drvdata(pdev, NULL); -} - -static int -tc35815_init_queues(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - int i; - unsigned long fd_addr; - - if (!lp->fd_buf) { - BUG_ON(sizeof(struct FDesc) + - sizeof(struct BDesc) * RX_BUF_NUM + - sizeof(struct FDesc) * RX_FD_NUM + - sizeof(struct TxFD) * TX_FD_NUM > - PAGE_SIZE * FD_PAGE_NUM); - - lp->fd_buf = pci_alloc_consistent(lp->pci_dev, - PAGE_SIZE * FD_PAGE_NUM, - &lp->fd_buf_dma); - if (!lp->fd_buf) - return -ENOMEM; - for (i = 0; i < RX_BUF_NUM; i++) { - lp->rx_skbs[i].skb = - alloc_rxbuf_skb(dev, lp->pci_dev, - &lp->rx_skbs[i].skb_dma); - if (!lp->rx_skbs[i].skb) { - while (--i >= 0) { - free_rxbuf_skb(lp->pci_dev, - lp->rx_skbs[i].skb, - lp->rx_skbs[i].skb_dma); - lp->rx_skbs[i].skb = NULL; - } - pci_free_consistent(lp->pci_dev, - PAGE_SIZE * FD_PAGE_NUM, - lp->fd_buf, - lp->fd_buf_dma); - lp->fd_buf = NULL; - return -ENOMEM; - } - } - printk(KERN_DEBUG "%s: FD buf %p DataBuf", - dev->name, lp->fd_buf); - printk("\n"); - } else { - for (i = 0; i < FD_PAGE_NUM; i++) - clear_page((void *)((unsigned long)lp->fd_buf + - i * PAGE_SIZE)); - } - fd_addr = (unsigned long)lp->fd_buf; - - /* Free Descriptors (for Receive) */ - lp->rfd_base = (struct RxFD *)fd_addr; - fd_addr += sizeof(struct RxFD) * RX_FD_NUM; - for (i = 0; i < RX_FD_NUM; i++) - lp->rfd_base[i].fd.FDCtl = cpu_to_le32(FD_CownsFD); - lp->rfd_cur = lp->rfd_base; - lp->rfd_limit = (struct RxFD *)fd_addr - (RX_FD_RESERVE + 1); - - /* Transmit Descriptors */ - lp->tfd_base = (struct TxFD *)fd_addr; - fd_addr += sizeof(struct TxFD) * TX_FD_NUM; - for (i = 0; i < TX_FD_NUM; i++) { - lp->tfd_base[i].fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, &lp->tfd_base[i+1])); - lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); - lp->tfd_base[i].fd.FDCtl = cpu_to_le32(0); - } - lp->tfd_base[TX_FD_NUM-1].fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, &lp->tfd_base[0])); - lp->tfd_start = 0; - lp->tfd_end = 0; - - /* Buffer List (for Receive) */ - lp->fbl_ptr = (struct FrFD *)fd_addr; - lp->fbl_ptr->fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, lp->fbl_ptr)); - lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_NUM | FD_CownsFD); - /* - * move all allocated skbs to head of rx_skbs[] array. - * fbl_count mighe not be RX_BUF_NUM if alloc_rxbuf_skb() in - * tc35815_rx() had failed. - */ - lp->fbl_count = 0; - for (i = 0; i < RX_BUF_NUM; i++) { - if (lp->rx_skbs[i].skb) { - if (i != lp->fbl_count) { - lp->rx_skbs[lp->fbl_count].skb = - lp->rx_skbs[i].skb; - lp->rx_skbs[lp->fbl_count].skb_dma = - lp->rx_skbs[i].skb_dma; - } - lp->fbl_count++; - } - } - for (i = 0; i < RX_BUF_NUM; i++) { - if (i >= lp->fbl_count) { - lp->fbl_ptr->bd[i].BuffData = 0; - lp->fbl_ptr->bd[i].BDCtl = 0; - continue; - } - lp->fbl_ptr->bd[i].BuffData = - cpu_to_le32(lp->rx_skbs[i].skb_dma); - /* BDID is index of FrFD.bd[] */ - lp->fbl_ptr->bd[i].BDCtl = - cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) | - RX_BUF_SIZE); - } - - printk(KERN_DEBUG "%s: TxFD %p RxFD %p FrFD %p\n", - dev->name, lp->tfd_base, lp->rfd_base, lp->fbl_ptr); - return 0; -} - -static void -tc35815_clear_queues(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - int i; - - for (i = 0; i < TX_FD_NUM; i++) { - u32 fdsystem = le32_to_cpu(lp->tfd_base[i].fd.FDSystem); - struct sk_buff *skb = - fdsystem != 0xffffffff ? - lp->tx_skbs[fdsystem].skb : NULL; -#ifdef DEBUG - if (lp->tx_skbs[i].skb != skb) { - printk("%s: tx_skbs mismatch(%d).\n", dev->name, i); - panic_queues(dev); - } -#else - BUG_ON(lp->tx_skbs[i].skb != skb); -#endif - if (skb) { - pci_unmap_single(lp->pci_dev, lp->tx_skbs[i].skb_dma, skb->len, PCI_DMA_TODEVICE); - lp->tx_skbs[i].skb = NULL; - lp->tx_skbs[i].skb_dma = 0; - dev_kfree_skb_any(skb); - } - lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); - } - - tc35815_init_queues(dev); -} - -static void -tc35815_free_queues(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - int i; - - if (lp->tfd_base) { - for (i = 0; i < TX_FD_NUM; i++) { - u32 fdsystem = le32_to_cpu(lp->tfd_base[i].fd.FDSystem); - struct sk_buff *skb = - fdsystem != 0xffffffff ? - lp->tx_skbs[fdsystem].skb : NULL; -#ifdef DEBUG - if (lp->tx_skbs[i].skb != skb) { - printk("%s: tx_skbs mismatch(%d).\n", dev->name, i); - panic_queues(dev); - } -#else - BUG_ON(lp->tx_skbs[i].skb != skb); -#endif - if (skb) { - dev_kfree_skb(skb); - pci_unmap_single(lp->pci_dev, lp->tx_skbs[i].skb_dma, skb->len, PCI_DMA_TODEVICE); - lp->tx_skbs[i].skb = NULL; - lp->tx_skbs[i].skb_dma = 0; - } - lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); - } - } - - lp->rfd_base = NULL; - lp->rfd_limit = NULL; - lp->rfd_cur = NULL; - lp->fbl_ptr = NULL; - - for (i = 0; i < RX_BUF_NUM; i++) { - if (lp->rx_skbs[i].skb) { - free_rxbuf_skb(lp->pci_dev, lp->rx_skbs[i].skb, - lp->rx_skbs[i].skb_dma); - lp->rx_skbs[i].skb = NULL; - } - } - if (lp->fd_buf) { - pci_free_consistent(lp->pci_dev, PAGE_SIZE * FD_PAGE_NUM, - lp->fd_buf, lp->fd_buf_dma); - lp->fd_buf = NULL; - } -} - -static void -dump_txfd(struct TxFD *fd) -{ - printk("TxFD(%p): %08x %08x %08x %08x\n", fd, - le32_to_cpu(fd->fd.FDNext), - le32_to_cpu(fd->fd.FDSystem), - le32_to_cpu(fd->fd.FDStat), - le32_to_cpu(fd->fd.FDCtl)); - printk("BD: "); - printk(" %08x %08x", - le32_to_cpu(fd->bd.BuffData), - le32_to_cpu(fd->bd.BDCtl)); - printk("\n"); -} - -static int -dump_rxfd(struct RxFD *fd) -{ - int i, bd_count = (le32_to_cpu(fd->fd.FDCtl) & FD_BDCnt_MASK) >> FD_BDCnt_SHIFT; - if (bd_count > 8) - bd_count = 8; - printk("RxFD(%p): %08x %08x %08x %08x\n", fd, - le32_to_cpu(fd->fd.FDNext), - le32_to_cpu(fd->fd.FDSystem), - le32_to_cpu(fd->fd.FDStat), - le32_to_cpu(fd->fd.FDCtl)); - if (le32_to_cpu(fd->fd.FDCtl) & FD_CownsFD) - return 0; - printk("BD: "); - for (i = 0; i < bd_count; i++) - printk(" %08x %08x", - le32_to_cpu(fd->bd[i].BuffData), - le32_to_cpu(fd->bd[i].BDCtl)); - printk("\n"); - return bd_count; -} - -#ifdef DEBUG -static void -dump_frfd(struct FrFD *fd) -{ - int i; - printk("FrFD(%p): %08x %08x %08x %08x\n", fd, - le32_to_cpu(fd->fd.FDNext), - le32_to_cpu(fd->fd.FDSystem), - le32_to_cpu(fd->fd.FDStat), - le32_to_cpu(fd->fd.FDCtl)); - printk("BD: "); - for (i = 0; i < RX_BUF_NUM; i++) - printk(" %08x %08x", - le32_to_cpu(fd->bd[i].BuffData), - le32_to_cpu(fd->bd[i].BDCtl)); - printk("\n"); -} - -static void -panic_queues(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - int i; - - printk("TxFD base %p, start %u, end %u\n", - lp->tfd_base, lp->tfd_start, lp->tfd_end); - printk("RxFD base %p limit %p cur %p\n", - lp->rfd_base, lp->rfd_limit, lp->rfd_cur); - printk("FrFD %p\n", lp->fbl_ptr); - for (i = 0; i < TX_FD_NUM; i++) - dump_txfd(&lp->tfd_base[i]); - for (i = 0; i < RX_FD_NUM; i++) { - int bd_count = dump_rxfd(&lp->rfd_base[i]); - i += (bd_count + 1) / 2; /* skip BDs */ - } - dump_frfd(lp->fbl_ptr); - panic("%s: Illegal queue state.", dev->name); -} -#endif - -static void print_eth(const u8 *add) -{ - printk(KERN_DEBUG "print_eth(%p)\n", add); - printk(KERN_DEBUG " %pM => %pM : %02x%02x\n", - add + 6, add, add[12], add[13]); -} - -static int tc35815_tx_full(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - return (lp->tfd_start + 1) % TX_FD_NUM == lp->tfd_end; -} - -static void tc35815_restart(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - - if (lp->phy_dev) { - int timeout; - - phy_write(lp->phy_dev, MII_BMCR, BMCR_RESET); - timeout = 100; - while (--timeout) { - if (!(phy_read(lp->phy_dev, MII_BMCR) & BMCR_RESET)) - break; - udelay(1); - } - if (!timeout) - printk(KERN_ERR "%s: BMCR reset failed.\n", dev->name); - } - - spin_lock_bh(&lp->rx_lock); - spin_lock_irq(&lp->lock); - tc35815_chip_reset(dev); - tc35815_clear_queues(dev); - tc35815_chip_init(dev); - /* Reconfigure CAM again since tc35815_chip_init() initialize it. */ - tc35815_set_multicast_list(dev); - spin_unlock_irq(&lp->lock); - spin_unlock_bh(&lp->rx_lock); - - netif_wake_queue(dev); -} - -static void tc35815_restart_work(struct work_struct *work) -{ - struct tc35815_local *lp = - container_of(work, struct tc35815_local, restart_work); - struct net_device *dev = lp->dev; - - tc35815_restart(dev); -} - -static void tc35815_schedule_restart(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - unsigned long flags; - - /* disable interrupts */ - spin_lock_irqsave(&lp->lock, flags); - tc_writel(0, &tr->Int_En); - tc_writel(tc_readl(&tr->DMA_Ctl) | DMA_IntMask, &tr->DMA_Ctl); - schedule_work(&lp->restart_work); - spin_unlock_irqrestore(&lp->lock, flags); -} - -static void tc35815_tx_timeout(struct net_device *dev) -{ - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - - printk(KERN_WARNING "%s: transmit timed out, status %#x\n", - dev->name, tc_readl(&tr->Tx_Stat)); - - /* Try to restart the adaptor. */ - tc35815_schedule_restart(dev); - dev->stats.tx_errors++; -} - -/* - * Open/initialize the controller. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ -static int -tc35815_open(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - - /* - * This is used if the interrupt line can turned off (shared). - * See 3c503.c for an example of selecting the IRQ at config-time. - */ - if (request_irq(dev->irq, tc35815_interrupt, IRQF_SHARED, - dev->name, dev)) - return -EAGAIN; - - tc35815_chip_reset(dev); - - if (tc35815_init_queues(dev) != 0) { - free_irq(dev->irq, dev); - return -EAGAIN; - } - - napi_enable(&lp->napi); - - /* Reset the hardware here. Don't forget to set the station address. */ - spin_lock_irq(&lp->lock); - tc35815_chip_init(dev); - spin_unlock_irq(&lp->lock); - - netif_carrier_off(dev); - /* schedule a link state check */ - phy_start(lp->phy_dev); - - /* We are now ready to accept transmit requeusts from - * the queueing layer of the networking. - */ - netif_start_queue(dev); - - return 0; -} - -/* This will only be invoked if your driver is _not_ in XOFF state. - * What this means is that you need not check it, and that this - * invariant will hold if you make sure that the netif_*_queue() - * calls are done at the proper times. - */ -static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - struct TxFD *txfd; - unsigned long flags; - - /* If some error occurs while trying to transmit this - * packet, you should return '1' from this function. - * In such a case you _may not_ do anything to the - * SKB, it is still owned by the network queueing - * layer when an error is returned. This means you - * may not modify any SKB fields, you may not free - * the SKB, etc. - */ - - /* This is the most common case for modern hardware. - * The spinlock protects this code from the TX complete - * hardware interrupt handler. Queue flow control is - * thus managed under this lock as well. - */ - spin_lock_irqsave(&lp->lock, flags); - - /* failsafe... (handle txdone now if half of FDs are used) */ - if ((lp->tfd_start + TX_FD_NUM - lp->tfd_end) % TX_FD_NUM > - TX_FD_NUM / 2) - tc35815_txdone(dev); - - if (netif_msg_pktdata(lp)) - print_eth(skb->data); -#ifdef DEBUG - if (lp->tx_skbs[lp->tfd_start].skb) { - printk("%s: tx_skbs conflict.\n", dev->name); - panic_queues(dev); - } -#else - BUG_ON(lp->tx_skbs[lp->tfd_start].skb); -#endif - lp->tx_skbs[lp->tfd_start].skb = skb; - lp->tx_skbs[lp->tfd_start].skb_dma = pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); - - /*add to ring */ - txfd = &lp->tfd_base[lp->tfd_start]; - txfd->bd.BuffData = cpu_to_le32(lp->tx_skbs[lp->tfd_start].skb_dma); - txfd->bd.BDCtl = cpu_to_le32(skb->len); - txfd->fd.FDSystem = cpu_to_le32(lp->tfd_start); - txfd->fd.FDCtl = cpu_to_le32(FD_CownsFD | (1 << FD_BDCnt_SHIFT)); - - if (lp->tfd_start == lp->tfd_end) { - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - /* Start DMA Transmitter. */ - txfd->fd.FDNext |= cpu_to_le32(FD_Next_EOL); - txfd->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); - if (netif_msg_tx_queued(lp)) { - printk("%s: starting TxFD.\n", dev->name); - dump_txfd(txfd); - } - tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr); - } else { - txfd->fd.FDNext &= cpu_to_le32(~FD_Next_EOL); - if (netif_msg_tx_queued(lp)) { - printk("%s: queueing TxFD.\n", dev->name); - dump_txfd(txfd); - } - } - lp->tfd_start = (lp->tfd_start + 1) % TX_FD_NUM; - - /* If we just used up the very last entry in the - * TX ring on this device, tell the queueing - * layer to send no more. - */ - if (tc35815_tx_full(dev)) { - if (netif_msg_tx_queued(lp)) - printk(KERN_WARNING "%s: TxFD Exhausted.\n", dev->name); - netif_stop_queue(dev); - } - - /* When the TX completion hw interrupt arrives, this - * is when the transmit statistics are updated. - */ - - spin_unlock_irqrestore(&lp->lock, flags); - return NETDEV_TX_OK; -} - -#define FATAL_ERROR_INT \ - (Int_IntPCI | Int_DmParErr | Int_IntNRAbt) -static void tc35815_fatal_error_interrupt(struct net_device *dev, u32 status) -{ - static int count; - printk(KERN_WARNING "%s: Fatal Error Intterrupt (%#x):", - dev->name, status); - if (status & Int_IntPCI) - printk(" IntPCI"); - if (status & Int_DmParErr) - printk(" DmParErr"); - if (status & Int_IntNRAbt) - printk(" IntNRAbt"); - printk("\n"); - if (count++ > 100) - panic("%s: Too many fatal errors.", dev->name); - printk(KERN_WARNING "%s: Resetting ...\n", dev->name); - /* Try to restart the adaptor. */ - tc35815_schedule_restart(dev); -} - -static int tc35815_do_interrupt(struct net_device *dev, u32 status, int limit) -{ - struct tc35815_local *lp = netdev_priv(dev); - int ret = -1; - - /* Fatal errors... */ - if (status & FATAL_ERROR_INT) { - tc35815_fatal_error_interrupt(dev, status); - return 0; - } - /* recoverable errors */ - if (status & Int_IntFDAEx) { - if (netif_msg_rx_err(lp)) - dev_warn(&dev->dev, - "Free Descriptor Area Exhausted (%#x).\n", - status); - dev->stats.rx_dropped++; - ret = 0; - } - if (status & Int_IntBLEx) { - if (netif_msg_rx_err(lp)) - dev_warn(&dev->dev, - "Buffer List Exhausted (%#x).\n", - status); - dev->stats.rx_dropped++; - ret = 0; - } - if (status & Int_IntExBD) { - if (netif_msg_rx_err(lp)) - dev_warn(&dev->dev, - "Excessive Buffer Descriptiors (%#x).\n", - status); - dev->stats.rx_length_errors++; - ret = 0; - } - - /* normal notification */ - if (status & Int_IntMacRx) { - /* Got a packet(s). */ - ret = tc35815_rx(dev, limit); - lp->lstats.rx_ints++; - } - if (status & Int_IntMacTx) { - /* Transmit complete. */ - lp->lstats.tx_ints++; - spin_lock_irq(&lp->lock); - tc35815_txdone(dev); - spin_unlock_irq(&lp->lock); - if (ret < 0) - ret = 0; - } - return ret; -} - -/* - * The typical workload of the driver: - * Handle the network interface interrupts. - */ -static irqreturn_t tc35815_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct tc35815_local *lp = netdev_priv(dev); - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - u32 dmactl = tc_readl(&tr->DMA_Ctl); - - if (!(dmactl & DMA_IntMask)) { - /* disable interrupts */ - tc_writel(dmactl | DMA_IntMask, &tr->DMA_Ctl); - if (napi_schedule_prep(&lp->napi)) - __napi_schedule(&lp->napi); - else { - printk(KERN_ERR "%s: interrupt taken in poll\n", - dev->name); - BUG(); - } - (void)tc_readl(&tr->Int_Src); /* flush */ - return IRQ_HANDLED; - } - return IRQ_NONE; -} - -#ifdef CONFIG_NET_POLL_CONTROLLER -static void tc35815_poll_controller(struct net_device *dev) -{ - disable_irq(dev->irq); - tc35815_interrupt(dev->irq, dev); - enable_irq(dev->irq); -} -#endif - -/* We have a good packet(s), get it/them out of the buffers. */ -static int -tc35815_rx(struct net_device *dev, int limit) -{ - struct tc35815_local *lp = netdev_priv(dev); - unsigned int fdctl; - int i; - int received = 0; - - while (!((fdctl = le32_to_cpu(lp->rfd_cur->fd.FDCtl)) & FD_CownsFD)) { - int status = le32_to_cpu(lp->rfd_cur->fd.FDStat); - int pkt_len = fdctl & FD_FDLength_MASK; - int bd_count = (fdctl & FD_BDCnt_MASK) >> FD_BDCnt_SHIFT; -#ifdef DEBUG - struct RxFD *next_rfd; -#endif -#if (RX_CTL_CMD & Rx_StripCRC) == 0 - pkt_len -= ETH_FCS_LEN; -#endif - - if (netif_msg_rx_status(lp)) - dump_rxfd(lp->rfd_cur); - if (status & Rx_Good) { - struct sk_buff *skb; - unsigned char *data; - int cur_bd; - - if (--limit < 0) - break; - BUG_ON(bd_count > 1); - cur_bd = (le32_to_cpu(lp->rfd_cur->bd[0].BDCtl) - & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT; -#ifdef DEBUG - if (cur_bd >= RX_BUF_NUM) { - printk("%s: invalid BDID.\n", dev->name); - panic_queues(dev); - } - BUG_ON(lp->rx_skbs[cur_bd].skb_dma != - (le32_to_cpu(lp->rfd_cur->bd[0].BuffData) & ~3)); - if (!lp->rx_skbs[cur_bd].skb) { - printk("%s: NULL skb.\n", dev->name); - panic_queues(dev); - } -#else - BUG_ON(cur_bd >= RX_BUF_NUM); -#endif - skb = lp->rx_skbs[cur_bd].skb; - prefetch(skb->data); - lp->rx_skbs[cur_bd].skb = NULL; - pci_unmap_single(lp->pci_dev, - lp->rx_skbs[cur_bd].skb_dma, - RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - if (!HAVE_DMA_RXALIGN(lp) && NET_IP_ALIGN) - memmove(skb->data, skb->data - NET_IP_ALIGN, - pkt_len); - data = skb_put(skb, pkt_len); - if (netif_msg_pktdata(lp)) - print_eth(data); - skb->protocol = eth_type_trans(skb, dev); - netif_receive_skb(skb); - received++; - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } else { - dev->stats.rx_errors++; - if (netif_msg_rx_err(lp)) - dev_info(&dev->dev, "Rx error (status %x)\n", - status & Rx_Stat_Mask); - /* WORKAROUND: LongErr and CRCErr means Overflow. */ - if ((status & Rx_LongErr) && (status & Rx_CRCErr)) { - status &= ~(Rx_LongErr|Rx_CRCErr); - status |= Rx_Over; - } - if (status & Rx_LongErr) - dev->stats.rx_length_errors++; - if (status & Rx_Over) - dev->stats.rx_fifo_errors++; - if (status & Rx_CRCErr) - dev->stats.rx_crc_errors++; - if (status & Rx_Align) - dev->stats.rx_frame_errors++; - } - - if (bd_count > 0) { - /* put Free Buffer back to controller */ - int bdctl = le32_to_cpu(lp->rfd_cur->bd[bd_count - 1].BDCtl); - unsigned char id = - (bdctl & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT; -#ifdef DEBUG - if (id >= RX_BUF_NUM) { - printk("%s: invalid BDID.\n", dev->name); - panic_queues(dev); - } -#else - BUG_ON(id >= RX_BUF_NUM); -#endif - /* free old buffers */ - lp->fbl_count--; - while (lp->fbl_count < RX_BUF_NUM) - { - unsigned char curid = - (id + 1 + lp->fbl_count) % RX_BUF_NUM; - struct BDesc *bd = &lp->fbl_ptr->bd[curid]; -#ifdef DEBUG - bdctl = le32_to_cpu(bd->BDCtl); - if (bdctl & BD_CownsBD) { - printk("%s: Freeing invalid BD.\n", - dev->name); - panic_queues(dev); - } -#endif - /* pass BD to controller */ - if (!lp->rx_skbs[curid].skb) { - lp->rx_skbs[curid].skb = - alloc_rxbuf_skb(dev, - lp->pci_dev, - &lp->rx_skbs[curid].skb_dma); - if (!lp->rx_skbs[curid].skb) - break; /* try on next reception */ - bd->BuffData = cpu_to_le32(lp->rx_skbs[curid].skb_dma); - } - /* Note: BDLength was modified by chip. */ - bd->BDCtl = cpu_to_le32(BD_CownsBD | - (curid << BD_RxBDID_SHIFT) | - RX_BUF_SIZE); - lp->fbl_count++; - } - } - - /* put RxFD back to controller */ -#ifdef DEBUG - next_rfd = fd_bus_to_virt(lp, - le32_to_cpu(lp->rfd_cur->fd.FDNext)); - if (next_rfd < lp->rfd_base || next_rfd > lp->rfd_limit) { - printk("%s: RxFD FDNext invalid.\n", dev->name); - panic_queues(dev); - } -#endif - for (i = 0; i < (bd_count + 1) / 2 + 1; i++) { - /* pass FD to controller */ -#ifdef DEBUG - lp->rfd_cur->fd.FDNext = cpu_to_le32(0xdeaddead); -#else - lp->rfd_cur->fd.FDNext = cpu_to_le32(FD_Next_EOL); -#endif - lp->rfd_cur->fd.FDCtl = cpu_to_le32(FD_CownsFD); - lp->rfd_cur++; - } - if (lp->rfd_cur > lp->rfd_limit) - lp->rfd_cur = lp->rfd_base; -#ifdef DEBUG - if (lp->rfd_cur != next_rfd) - printk("rfd_cur = %p, next_rfd %p\n", - lp->rfd_cur, next_rfd); -#endif - } - - return received; -} - -static int tc35815_poll(struct napi_struct *napi, int budget) -{ - struct tc35815_local *lp = container_of(napi, struct tc35815_local, napi); - struct net_device *dev = lp->dev; - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - int received = 0, handled; - u32 status; - - spin_lock(&lp->rx_lock); - status = tc_readl(&tr->Int_Src); - do { - /* BLEx, FDAEx will be cleared later */ - tc_writel(status & ~(Int_BLEx | Int_FDAEx), - &tr->Int_Src); /* write to clear */ - - handled = tc35815_do_interrupt(dev, status, budget - received); - if (status & (Int_BLEx | Int_FDAEx)) - tc_writel(status & (Int_BLEx | Int_FDAEx), - &tr->Int_Src); - if (handled >= 0) { - received += handled; - if (received >= budget) - break; - } - status = tc_readl(&tr->Int_Src); - } while (status); - spin_unlock(&lp->rx_lock); - - if (received < budget) { - napi_complete(napi); - /* enable interrupts */ - tc_writel(tc_readl(&tr->DMA_Ctl) & ~DMA_IntMask, &tr->DMA_Ctl); - } - return received; -} - -#define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_NCarr|Tx_LateColl|Tx_TxPar|Tx_SQErr) - -static void -tc35815_check_tx_stat(struct net_device *dev, int status) -{ - struct tc35815_local *lp = netdev_priv(dev); - const char *msg = NULL; - - /* count collisions */ - if (status & Tx_ExColl) - dev->stats.collisions += 16; - if (status & Tx_TxColl_MASK) - dev->stats.collisions += status & Tx_TxColl_MASK; - - /* TX4939 does not have NCarr */ - if (lp->chiptype == TC35815_TX4939) - status &= ~Tx_NCarr; - /* WORKAROUND: ignore LostCrS in full duplex operation */ - if (!lp->link || lp->duplex == DUPLEX_FULL) - status &= ~Tx_NCarr; - - if (!(status & TX_STA_ERR)) { - /* no error. */ - dev->stats.tx_packets++; - return; - } - - dev->stats.tx_errors++; - if (status & Tx_ExColl) { - dev->stats.tx_aborted_errors++; - msg = "Excessive Collision."; - } - if (status & Tx_Under) { - dev->stats.tx_fifo_errors++; - msg = "Tx FIFO Underrun."; - if (lp->lstats.tx_underrun < TX_THRESHOLD_KEEP_LIMIT) { - lp->lstats.tx_underrun++; - if (lp->lstats.tx_underrun >= TX_THRESHOLD_KEEP_LIMIT) { - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - tc_writel(TX_THRESHOLD_MAX, &tr->TxThrsh); - msg = "Tx FIFO Underrun.Change Tx threshold to max."; - } - } - } - if (status & Tx_Defer) { - dev->stats.tx_fifo_errors++; - msg = "Excessive Deferral."; - } - if (status & Tx_NCarr) { - dev->stats.tx_carrier_errors++; - msg = "Lost Carrier Sense."; - } - if (status & Tx_LateColl) { - dev->stats.tx_aborted_errors++; - msg = "Late Collision."; - } - if (status & Tx_TxPar) { - dev->stats.tx_fifo_errors++; - msg = "Transmit Parity Error."; - } - if (status & Tx_SQErr) { - dev->stats.tx_heartbeat_errors++; - msg = "Signal Quality Error."; - } - if (msg && netif_msg_tx_err(lp)) - printk(KERN_WARNING "%s: %s (%#x)\n", dev->name, msg, status); -} - -/* This handles TX complete events posted by the device - * via interrupts. - */ -static void -tc35815_txdone(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - struct TxFD *txfd; - unsigned int fdctl; - - txfd = &lp->tfd_base[lp->tfd_end]; - while (lp->tfd_start != lp->tfd_end && - !((fdctl = le32_to_cpu(txfd->fd.FDCtl)) & FD_CownsFD)) { - int status = le32_to_cpu(txfd->fd.FDStat); - struct sk_buff *skb; - unsigned long fdnext = le32_to_cpu(txfd->fd.FDNext); - u32 fdsystem = le32_to_cpu(txfd->fd.FDSystem); - - if (netif_msg_tx_done(lp)) { - printk("%s: complete TxFD.\n", dev->name); - dump_txfd(txfd); - } - tc35815_check_tx_stat(dev, status); - - skb = fdsystem != 0xffffffff ? - lp->tx_skbs[fdsystem].skb : NULL; -#ifdef DEBUG - if (lp->tx_skbs[lp->tfd_end].skb != skb) { - printk("%s: tx_skbs mismatch.\n", dev->name); - panic_queues(dev); - } -#else - BUG_ON(lp->tx_skbs[lp->tfd_end].skb != skb); -#endif - if (skb) { - dev->stats.tx_bytes += skb->len; - pci_unmap_single(lp->pci_dev, lp->tx_skbs[lp->tfd_end].skb_dma, skb->len, PCI_DMA_TODEVICE); - lp->tx_skbs[lp->tfd_end].skb = NULL; - lp->tx_skbs[lp->tfd_end].skb_dma = 0; - dev_kfree_skb_any(skb); - } - txfd->fd.FDSystem = cpu_to_le32(0xffffffff); - - lp->tfd_end = (lp->tfd_end + 1) % TX_FD_NUM; - txfd = &lp->tfd_base[lp->tfd_end]; -#ifdef DEBUG - if ((fdnext & ~FD_Next_EOL) != fd_virt_to_bus(lp, txfd)) { - printk("%s: TxFD FDNext invalid.\n", dev->name); - panic_queues(dev); - } -#endif - if (fdnext & FD_Next_EOL) { - /* DMA Transmitter has been stopping... */ - if (lp->tfd_end != lp->tfd_start) { - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - int head = (lp->tfd_start + TX_FD_NUM - 1) % TX_FD_NUM; - struct TxFD *txhead = &lp->tfd_base[head]; - int qlen = (lp->tfd_start + TX_FD_NUM - - lp->tfd_end) % TX_FD_NUM; - -#ifdef DEBUG - if (!(le32_to_cpu(txfd->fd.FDCtl) & FD_CownsFD)) { - printk("%s: TxFD FDCtl invalid.\n", dev->name); - panic_queues(dev); - } -#endif - /* log max queue length */ - if (lp->lstats.max_tx_qlen < qlen) - lp->lstats.max_tx_qlen = qlen; - - - /* start DMA Transmitter again */ - txhead->fd.FDNext |= cpu_to_le32(FD_Next_EOL); - txhead->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); - if (netif_msg_tx_queued(lp)) { - printk("%s: start TxFD on queue.\n", - dev->name); - dump_txfd(txfd); - } - tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr); - } - break; - } - } - - /* If we had stopped the queue due to a "tx full" - * condition, and space has now been made available, - * wake up the queue. - */ - if (netif_queue_stopped(dev) && !tc35815_tx_full(dev)) - netif_wake_queue(dev); -} - -/* The inverse routine to tc35815_open(). */ -static int -tc35815_close(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - - netif_stop_queue(dev); - napi_disable(&lp->napi); - if (lp->phy_dev) - phy_stop(lp->phy_dev); - cancel_work_sync(&lp->restart_work); - - /* Flush the Tx and disable Rx here. */ - tc35815_chip_reset(dev); - free_irq(dev->irq, dev); - - tc35815_free_queues(dev); - - return 0; - -} - -/* - * Get the current statistics. - * This may be called with the card open or closed. - */ -static struct net_device_stats *tc35815_get_stats(struct net_device *dev) -{ - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - if (netif_running(dev)) - /* Update the statistics from the device registers. */ - dev->stats.rx_missed_errors += tc_readl(&tr->Miss_Cnt); - - return &dev->stats; -} - -static void tc35815_set_cam_entry(struct net_device *dev, int index, unsigned char *addr) -{ - struct tc35815_local *lp = netdev_priv(dev); - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - int cam_index = index * 6; - u32 cam_data; - u32 saved_addr; - - saved_addr = tc_readl(&tr->CAM_Adr); - - if (netif_msg_hw(lp)) - printk(KERN_DEBUG "%s: CAM %d: %pM\n", - dev->name, index, addr); - if (index & 1) { - /* read modify write */ - tc_writel(cam_index - 2, &tr->CAM_Adr); - cam_data = tc_readl(&tr->CAM_Data) & 0xffff0000; - cam_data |= addr[0] << 8 | addr[1]; - tc_writel(cam_data, &tr->CAM_Data); - /* write whole word */ - tc_writel(cam_index + 2, &tr->CAM_Adr); - cam_data = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; - tc_writel(cam_data, &tr->CAM_Data); - } else { - /* write whole word */ - tc_writel(cam_index, &tr->CAM_Adr); - cam_data = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; - tc_writel(cam_data, &tr->CAM_Data); - /* read modify write */ - tc_writel(cam_index + 4, &tr->CAM_Adr); - cam_data = tc_readl(&tr->CAM_Data) & 0x0000ffff; - cam_data |= addr[4] << 24 | (addr[5] << 16); - tc_writel(cam_data, &tr->CAM_Data); - } - - tc_writel(saved_addr, &tr->CAM_Adr); -} - - -/* - * Set or clear the multicast filter for this adaptor. - * num_addrs == -1 Promiscuous mode, receive all packets - * num_addrs == 0 Normal mode, clear multicast list - * num_addrs > 0 Multicast mode, receive normal and MC packets, - * and do best-effort filtering. - */ -static void -tc35815_set_multicast_list(struct net_device *dev) -{ - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - - if (dev->flags & IFF_PROMISC) { - /* With some (all?) 100MHalf HUB, controller will hang - * if we enabled promiscuous mode before linkup... */ - struct tc35815_local *lp = netdev_priv(dev); - - if (!lp->link) - return; - /* Enable promiscuous mode */ - tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc | CAM_StationAcc, &tr->CAM_Ctl); - } else if ((dev->flags & IFF_ALLMULTI) || - netdev_mc_count(dev) > CAM_ENTRY_MAX - 3) { - /* CAM 0, 1, 20 are reserved. */ - /* Disable promiscuous mode, use normal mode. */ - tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc, &tr->CAM_Ctl); - } else if (!netdev_mc_empty(dev)) { - struct netdev_hw_addr *ha; - int i; - int ena_bits = CAM_Ena_Bit(CAM_ENTRY_SOURCE); - - tc_writel(0, &tr->CAM_Ctl); - /* Walk the address list, and load the filter */ - i = 0; - netdev_for_each_mc_addr(ha, dev) { - /* entry 0,1 is reserved. */ - tc35815_set_cam_entry(dev, i + 2, ha->addr); - ena_bits |= CAM_Ena_Bit(i + 2); - i++; - } - tc_writel(ena_bits, &tr->CAM_Ena); - tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl); - } else { - tc_writel(CAM_Ena_Bit(CAM_ENTRY_SOURCE), &tr->CAM_Ena); - tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl); - } -} - -static void tc35815_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - struct tc35815_local *lp = netdev_priv(dev); - strcpy(info->driver, MODNAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, pci_name(lp->pci_dev)); -} - -static int tc35815_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct tc35815_local *lp = netdev_priv(dev); - - if (!lp->phy_dev) - return -ENODEV; - return phy_ethtool_gset(lp->phy_dev, cmd); -} - -static int tc35815_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct tc35815_local *lp = netdev_priv(dev); - - if (!lp->phy_dev) - return -ENODEV; - return phy_ethtool_sset(lp->phy_dev, cmd); -} - -static u32 tc35815_get_msglevel(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - return lp->msg_enable; -} - -static void tc35815_set_msglevel(struct net_device *dev, u32 datum) -{ - struct tc35815_local *lp = netdev_priv(dev); - lp->msg_enable = datum; -} - -static int tc35815_get_sset_count(struct net_device *dev, int sset) -{ - struct tc35815_local *lp = netdev_priv(dev); - - switch (sset) { - case ETH_SS_STATS: - return sizeof(lp->lstats) / sizeof(int); - default: - return -EOPNOTSUPP; - } -} - -static void tc35815_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) -{ - struct tc35815_local *lp = netdev_priv(dev); - data[0] = lp->lstats.max_tx_qlen; - data[1] = lp->lstats.tx_ints; - data[2] = lp->lstats.rx_ints; - data[3] = lp->lstats.tx_underrun; -} - -static struct { - const char str[ETH_GSTRING_LEN]; -} ethtool_stats_keys[] = { - { "max_tx_qlen" }, - { "tx_ints" }, - { "rx_ints" }, - { "tx_underrun" }, -}; - -static void tc35815_get_strings(struct net_device *dev, u32 stringset, u8 *data) -{ - memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); -} - -static const struct ethtool_ops tc35815_ethtool_ops = { - .get_drvinfo = tc35815_get_drvinfo, - .get_settings = tc35815_get_settings, - .set_settings = tc35815_set_settings, - .get_link = ethtool_op_get_link, - .get_msglevel = tc35815_get_msglevel, - .set_msglevel = tc35815_set_msglevel, - .get_strings = tc35815_get_strings, - .get_sset_count = tc35815_get_sset_count, - .get_ethtool_stats = tc35815_get_ethtool_stats, -}; - -static int tc35815_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct tc35815_local *lp = netdev_priv(dev); - - if (!netif_running(dev)) - return -EINVAL; - if (!lp->phy_dev) - return -ENODEV; - return phy_mii_ioctl(lp->phy_dev, rq, cmd); -} - -static void tc35815_chip_reset(struct net_device *dev) -{ - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - int i; - /* reset the controller */ - tc_writel(MAC_Reset, &tr->MAC_Ctl); - udelay(4); /* 3200ns */ - i = 0; - while (tc_readl(&tr->MAC_Ctl) & MAC_Reset) { - if (i++ > 100) { - printk(KERN_ERR "%s: MAC reset failed.\n", dev->name); - break; - } - mdelay(1); - } - tc_writel(0, &tr->MAC_Ctl); - - /* initialize registers to default value */ - tc_writel(0, &tr->DMA_Ctl); - tc_writel(0, &tr->TxThrsh); - tc_writel(0, &tr->TxPollCtr); - tc_writel(0, &tr->RxFragSize); - tc_writel(0, &tr->Int_En); - tc_writel(0, &tr->FDA_Bas); - tc_writel(0, &tr->FDA_Lim); - tc_writel(0xffffffff, &tr->Int_Src); /* Write 1 to clear */ - tc_writel(0, &tr->CAM_Ctl); - tc_writel(0, &tr->Tx_Ctl); - tc_writel(0, &tr->Rx_Ctl); - tc_writel(0, &tr->CAM_Ena); - (void)tc_readl(&tr->Miss_Cnt); /* Read to clear */ - - /* initialize internal SRAM */ - tc_writel(DMA_TestMode, &tr->DMA_Ctl); - for (i = 0; i < 0x1000; i += 4) { - tc_writel(i, &tr->CAM_Adr); - tc_writel(0, &tr->CAM_Data); - } - tc_writel(0, &tr->DMA_Ctl); -} - -static void tc35815_chip_init(struct net_device *dev) -{ - struct tc35815_local *lp = netdev_priv(dev); - struct tc35815_regs __iomem *tr = - (struct tc35815_regs __iomem *)dev->base_addr; - unsigned long txctl = TX_CTL_CMD; - - /* load station address to CAM */ - tc35815_set_cam_entry(dev, CAM_ENTRY_SOURCE, dev->dev_addr); - - /* Enable CAM (broadcast and unicast) */ - tc_writel(CAM_Ena_Bit(CAM_ENTRY_SOURCE), &tr->CAM_Ena); - tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl); - - /* Use DMA_RxAlign_2 to make IP header 4-byte aligned. */ - if (HAVE_DMA_RXALIGN(lp)) - tc_writel(DMA_BURST_SIZE | DMA_RxAlign_2, &tr->DMA_Ctl); - else - tc_writel(DMA_BURST_SIZE, &tr->DMA_Ctl); - tc_writel(0, &tr->TxPollCtr); /* Batch mode */ - tc_writel(TX_THRESHOLD, &tr->TxThrsh); - tc_writel(INT_EN_CMD, &tr->Int_En); - - /* set queues */ - tc_writel(fd_virt_to_bus(lp, lp->rfd_base), &tr->FDA_Bas); - tc_writel((unsigned long)lp->rfd_limit - (unsigned long)lp->rfd_base, - &tr->FDA_Lim); - /* - * Activation method: - * First, enable the MAC Transmitter and the DMA Receive circuits. - * Then enable the DMA Transmitter and the MAC Receive circuits. - */ - tc_writel(fd_virt_to_bus(lp, lp->fbl_ptr), &tr->BLFrmPtr); /* start DMA receiver */ - tc_writel(RX_CTL_CMD, &tr->Rx_Ctl); /* start MAC receiver */ - - /* start MAC transmitter */ - /* TX4939 does not have EnLCarr */ - if (lp->chiptype == TC35815_TX4939) - txctl &= ~Tx_EnLCarr; - /* WORKAROUND: ignore LostCrS in full duplex operation */ - if (!lp->phy_dev || !lp->link || lp->duplex == DUPLEX_FULL) - txctl &= ~Tx_EnLCarr; - tc_writel(txctl, &tr->Tx_Ctl); -} - -#ifdef CONFIG_PM -static int tc35815_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct tc35815_local *lp = netdev_priv(dev); - unsigned long flags; - - pci_save_state(pdev); - if (!netif_running(dev)) - return 0; - netif_device_detach(dev); - if (lp->phy_dev) - phy_stop(lp->phy_dev); - spin_lock_irqsave(&lp->lock, flags); - tc35815_chip_reset(dev); - spin_unlock_irqrestore(&lp->lock, flags); - pci_set_power_state(pdev, PCI_D3hot); - return 0; -} - -static int tc35815_resume(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct tc35815_local *lp = netdev_priv(dev); - - pci_restore_state(pdev); - if (!netif_running(dev)) - return 0; - pci_set_power_state(pdev, PCI_D0); - tc35815_restart(dev); - netif_carrier_off(dev); - if (lp->phy_dev) - phy_start(lp->phy_dev); - netif_device_attach(dev); - return 0; -} -#endif /* CONFIG_PM */ - -static struct pci_driver tc35815_pci_driver = { - .name = MODNAME, - .id_table = tc35815_pci_tbl, - .probe = tc35815_init_one, - .remove = __devexit_p(tc35815_remove_one), -#ifdef CONFIG_PM - .suspend = tc35815_suspend, - .resume = tc35815_resume, -#endif -}; - -module_param_named(speed, options.speed, int, 0); -MODULE_PARM_DESC(speed, "0:auto, 10:10Mbps, 100:100Mbps"); -module_param_named(duplex, options.duplex, int, 0); -MODULE_PARM_DESC(duplex, "0:auto, 1:half, 2:full"); - -static int __init tc35815_init_module(void) -{ - return pci_register_driver(&tc35815_pci_driver); -} - -static void __exit tc35815_cleanup_module(void) -{ - pci_unregister_driver(&tc35815_pci_driver); -} - -module_init(tc35815_init_module); -module_exit(tc35815_cleanup_module); - -MODULE_DESCRIPTION("TOSHIBA TC35815 PCI 10M/100M Ethernet driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b