diff options
Diffstat (limited to 'drivers/net/ethernet/toshiba')
| -rw-r--r-- | drivers/net/ethernet/toshiba/Kconfig | 57 | ||||
| -rw-r--r-- | drivers/net/ethernet/toshiba/Makefile | 10 | ||||
| -rw-r--r-- | drivers/net/ethernet/toshiba/ps3_gelic_net.c | 1894 | ||||
| -rw-r--r-- | drivers/net/ethernet/toshiba/ps3_gelic_net.h | 384 | ||||
| -rw-r--r-- | drivers/net/ethernet/toshiba/ps3_gelic_wireless.c | 2680 | ||||
| -rw-r--r-- | drivers/net/ethernet/toshiba/ps3_gelic_wireless.h | 326 | ||||
| -rw-r--r-- | drivers/net/ethernet/toshiba/spider_net.c | 2604 | ||||
| -rw-r--r-- | drivers/net/ethernet/toshiba/spider_net.h | 489 | ||||
| -rw-r--r-- | drivers/net/ethernet/toshiba/spider_net_ethtool.c | 181 | ||||
| -rw-r--r-- | drivers/net/ethernet/toshiba/tc35815.c | 2208 | 
10 files changed, 10833 insertions, 0 deletions
diff --git a/drivers/net/ethernet/toshiba/Kconfig b/drivers/net/ethernet/toshiba/Kconfig new file mode 100644 index 00000000000..74acb5cf609 --- /dev/null +++ b/drivers/net/ethernet/toshiba/Kconfig @@ -0,0 +1,57 @@ +# +# Toshiba network device configuration +# + +config NET_VENDOR_TOSHIBA +	bool "Toshiba devices" +	default y +	depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB || MIPS) || PPC_PS3 +	---help--- +	  If you have a network (Ethernet) card belonging to this class, say Y +	  and read the Ethernet-HOWTO, available from +	  <http://www.tldp.org/docs.html#howto>. + +	  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 00000000000..a5069008435 --- /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 +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 00000000000..bb799280466 --- /dev/null +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c @@ -0,0 +1,1894 @@ +/* + *  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 <utz.bacher@de.ibm.com> + *           Jens Osterkamp <Jens.Osterkamp@de.ibm.com> + * + * 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 <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/if_vlan.h> + +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/tcp.h> + +#include <linux/dma-mapping.h> +#include <net/checksum.h> +#include <asm/firmware.h> +#include <asm/ps3.h> +#include <asm/lv1call.h> + +#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"); + + +/* 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 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 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; +} + +/** + * 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 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)); +	if (status) +		dev_err(ctodev(card), +			"lv1_net_stop_tx_dma failed, status=%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 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 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)); +	if (status) +		dev_err(ctodev(card), +			"lv1_net_stop_rx_dma failed, %d\n", status); +} + +/** + * 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_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; +} + +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_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 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_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 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_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 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) +{ +	strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); +	strlcpy(info->version, DRV_VERSION, sizeof(info->version)); +} + +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_rx_mode = 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 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, NAPI_POLL_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 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 *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 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 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__); + +	udbg_shutdown_ps3gelic(); + +	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, +			     0, 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; + + +	result = gelic_card_init_chain(card, &card->tx_chain, +				       card->descr, GELIC_NET_TX_DESCRIPTORS); +	if (result) +		goto fail_alloc_tx; +	result = gelic_card_init_chain(card, &card->rx_chain, +				       card->descr + GELIC_NET_TX_DESCRIPTORS, +				       GELIC_NET_RX_DESCRIPTORS); +	if (result) +		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 */ +	result = gelic_card_alloc_rx_skbs(card); +	if (result) +		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 +	result = gelic_wl_driver_probe(card); +	if (result) { +		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 00000000000..8505196be9f --- /dev/null +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.h @@ -0,0 +1,384 @@ +/* + *  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 <utz.bacher@de.ibm.com> + *           Jens Osterkamp <Jens.Osterkamp@de.ibm.com> + * + * 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_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; +} + +#ifdef CONFIG_PPC_EARLY_DEBUG_PS3GELIC +void udbg_shutdown_ps3gelic(void); +#else +static inline void udbg_shutdown_ps3gelic(void) {} +#endif + +int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask); +/* shared netdev ops */ +void gelic_card_up(struct gelic_card *card); +void gelic_card_down(struct gelic_card *card); +int gelic_net_open(struct net_device *netdev); +int gelic_net_stop(struct net_device *netdev); +int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev); +void gelic_net_set_multi(struct net_device *netdev); +void gelic_net_tx_timeout(struct net_device *netdev); +int gelic_net_change_mtu(struct net_device *netdev, int new_mtu); +int gelic_net_setup_netdev(struct net_device *netdev, struct gelic_card *card); + +/* shared ethtool ops */ +void gelic_net_get_drvinfo(struct net_device *netdev, +			   struct ethtool_drvinfo *info); +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 00000000000..d568af1eb4f --- /dev/null +++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c @@ -0,0 +1,2680 @@ +/* + *  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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/if_vlan.h> + +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/wireless.h> +#include <linux/ieee80211.h> +#include <linux/if_arp.h> +#include <linux/ctype.h> +#include <linux/string.h> +#include <net/iw_handler.h> + +#include <linux/dma-mapping.h> +#include <net/checksum.h> +#include <asm/firmware.h> +#include <asm/ps3.h> +#include <asm/lv1call.h> + +#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_VENDOR_SPECIFIC; + +	/* 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_VENDOR_SPECIFIC: +			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 (ether_addr_equal(&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) +			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 (ether_addr_equal(&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 *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_rx_mode = 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 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 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 00000000000..11f443d8e4e --- /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 <linux/wireless.h> +#include <net/iw_handler.h> + + +/* 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) + +int gelic_wl_driver_probe(struct gelic_card *card); +int gelic_wl_driver_remove(struct gelic_card *card); +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 00000000000..0282d016185 --- /dev/null +++ b/drivers/net/ethernet/toshiba/spider_net.c @@ -0,0 +1,2604 @@ +/* + * Network device driver for Cell Processor-Based Blade and Celleb platform + * + * (C) Copyright IBM Corp. 2005 + * (C) Copyright 2006 TOSHIBA CORPORATION + * + * Authors : Utz Bacher <utz.bacher@de.ibm.com> + *           Jens Osterkamp <Jens.Osterkamp@de.ibm.com> + * + * 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 <linux/compiler.h> +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/firmware.h> +#include <linux/if_vlan.h> +#include <linux/in.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/gfp.h> +#include <linux/ioport.h> +#include <linux/ip.h> +#include <linux/kernel.h> +#include <linux/mii.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/device.h> +#include <linux/pci.h> +#include <linux/skbuff.h> +#include <linux/tcp.h> +#include <linux/types.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include <linux/bitops.h> +#include <asm/pci-bridge.h> +#include <net/checksum.h> + +#include "spider_net.h" + +MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com> and Jens Osterkamp " \ +	      "<Jens.Osterkamp@de.ibm.com>"); +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; + +	sungem_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;i<cnt; i++) +		descr = descr->next; + +	/* 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_consume_skb_any(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; i<chain->num_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; i<chain->num_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<<i)) +				switch (i) +	{ +	/* let error_reg1 and error_reg2 evaluation decide, what to do +	case SPIDER_NET_PHYINT: +	case SPIDER_NET_GMAC2INT: +	case SPIDER_NET_GMAC1INT: +	case SPIDER_NET_GFIFOINT: +	case SPIDER_NET_DMACINT: +	case SPIDER_NET_GSYSINT: +		break; */ + +	case SPIDER_NET_GIPSINT: +		show_error = 0; +		break; + +	case SPIDER_NET_GPWOPCMPINT: +		/* PHY write operation completed */ +		show_error = 0; +		break; +	case SPIDER_NET_GPROPCMPINT: +		/* PHY read operation completed */ +		/* we don't use semaphores, as we poll for the completion +		 * of the read operation in spider_net_read_phy. Should take +		 * about 50 us */ +		show_error = 0; +		break; +	case SPIDER_NET_GPWFFINT: +		/* PHY command queue full */ +		if (netif_msg_intr(card)) +			dev_err(&card->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<<i)) +				switch (i) +	{ +	case SPIDER_NET_GTMFLLINT: +		/* TX RAM full may happen on a usual case. +		 * Logging is not needed. */ +		show_error = 0; +		break; +	case SPIDER_NET_GRFDFLLINT: /* fallthrough */ +	case SPIDER_NET_GRFCFLLINT: /* fallthrough */ +	case SPIDER_NET_GRFBFLLINT: /* fallthrough */ +	case SPIDER_NET_GRFAFLLINT: /* fallthrough */ +	case SPIDER_NET_GRMFLLINT: +		/* Could happen when rx chain is full */ +		if (card->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<<i)) +				switch (i) +	{ +	/* there is nothing we can (want  to) do at this time. Log a +	 * message, we can switch on and off the specific values later on +	case SPIDER_NET_GPROPERINT: +	case SPIDER_NET_GMCTCRSNGINT: +	case SPIDER_NET_GMCTLCOLINT: +	case SPIDER_NET_GMCTTMOTINT: +	case SPIDER_NET_GMCRCAERINT: +	case SPIDER_NET_GMCRCALERINT: +	case SPIDER_NET_GMCRALNERINT: +	case SPIDER_NET_GMCROVRINT: +	case SPIDER_NET_GMCRRNTINT: +	case SPIDER_NET_GMCRRXERINT: +	case SPIDER_NET_GTITCSERINT: +	case SPIDER_NET_GTIFMTERINT: +	case SPIDER_NET_GTIPKTRVKINT: +	case SPIDER_NET_GTISPINGINT: +	case SPIDER_NET_GTISADNGINT: +	case SPIDER_NET_GTISPDNGINT: +	case SPIDER_NET_GRIFMTERINT: +	case SPIDER_NET_GRIPKTRVKINT: +	case SPIDER_NET_GRISPINGINT: +	case SPIDER_NET_GRISADNGINT: +	case SPIDER_NET_GRISPDNGINT: +		break; +	*/ +		default: +			break; +	} + +	if ((show_error) && (netif_msg_intr(card)) && net_ratelimit()) +		dev_err(&card->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: + *    <ONE LINE COMMENT> + *    <FIRST 4-BYTES-WORD FOR SEQUENCER> + *    <SECOND 4-BYTES-WORD FOR 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 */ +	result = spider_net_alloc_rx_skbs(card); +	if (result) +		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 (!sungem_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_rx_mode	= 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_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | +	 *		NETIF_F_HW_VLAN_CTAG_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); +	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 +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 +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		= 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 00000000000..9b6af0845a1 --- /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 <utz.bacher@de.ibm.com> + *           Jens Osterkamp <Jens.Osterkamp@de.ibm.com> + * + * 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 <linux/sungem_phy.h> + +int spider_net_stop(struct net_device *netdev); +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 00000000000..ffe519382e1 --- /dev/null +++ b/drivers/net/ethernet/toshiba/spider_net_ethtool.c @@ -0,0 +1,181 @@ +/* + * Network device driver for Cell Processor-Based Blade + * + * (C) Copyright IBM Corp. 2005 + * + * Authors : Utz Bacher <utz.bacher@de.ibm.com> + *           Jens Osterkamp <Jens.Osterkamp@de.ibm.com> + * + * 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 <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/pci.h> + +#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 */ +	strlcpy(drvinfo->driver, spider_net_driver_name, +		sizeof(drvinfo->driver)); +	strlcpy(drvinfo->version, VERSION, sizeof(drvinfo->version)); +	strlcpy(drvinfo->fw_version, "no information", +		sizeof(drvinfo->fw_version)); +	strlcpy(drvinfo->bus_info, pci_name(card->pdev), +		sizeof(drvinfo->bus_info)); +} + +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 00000000000..fef5573dbfc --- /dev/null +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -0,0 +1,2208 @@ +/* + * 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: + *	-----<snip>----- + *	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"; + *	-----<snip>----- + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/if_vlan.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/phy.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +#include <linux/prefetch.h> +#include <asm/io.h> +#include <asm/byteorder.h> + +enum tc35815_chiptype { +	TC35815CF = 0, +	TC35815_NWU, +	TC35815_TX4939, +}; + +/* indexed by tc35815_chiptype, above */ +static const struct { +	const char *name; +} chip_info[] = { +	{ "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<<CAM_ENTRY_MAX)-1) /* CAM Enable bits (Max 21bits)  */ +#define CAM_Ena_Bit(index)	(1 << (index)) +#define CAM_ENTRY_DESTINATION	0 +#define CAM_ENTRY_SOURCE	1 +#define CAM_ENTRY_MACCTL	20 + +/* Tx_Ctl bit assign -------------------------------------------------------- */ +#define Tx_En		       0x00000001 /* 1:Transmit enable		     */ +#define Tx_TxHalt	       0x00000002 /* 1:Transmit Halt Request	     */ +#define Tx_NoPad	       0x00000004 /* 1:Suppress Padding		     */ +#define Tx_NoCRC	       0x00000008 /* 1:Suppress Padding		     */ +#define Tx_FBack	       0x00000010 /* 1:Fast Back-off		     */ +#define Tx_EnUnder	       0x00000100 /* 1:Enable Underrun		     */ +#define Tx_EnExDefer	       0x00000200 /* 1:Enable Excessive Deferral     */ +#define Tx_EnLCarr	       0x00000400 /* 1:Enable Lost Carrier	     */ +#define Tx_EnExColl	       0x00000800 /* 1:Enable Excessive Collision    */ +#define Tx_EnLateColl	       0x00001000 /* 1:Enable Late Collision	     */ +#define Tx_EnTxPar	       0x00002000 /* 1:Enable Transmit Parity	     */ +#define Tx_EnComp	       0x00004000 /* 1:Enable Completion	     */ + +/* Tx_Stat bit assign ------------------------------------------------------- */ +#define Tx_TxColl_MASK	       0x0000000F /* Tx Collision Count		     */ +#define Tx_ExColl	       0x00000010 /* Excessive Collision	     */ +#define Tx_TXDefer	       0x00000020 /* Transmit Defered		     */ +#define Tx_Paused	       0x00000040 /* Transmit Paused		     */ +#define Tx_IntTx	       0x00000080 /* Interrupt on Tx		     */ +#define Tx_Under	       0x00000100 /* Underrun			     */ +#define Tx_Defer	       0x00000200 /* Deferral			     */ +#define Tx_NCarr	       0x00000400 /* No Carrier			     */ +#define Tx_10Stat	       0x00000800 /* 10Mbps Status		     */ +#define Tx_LateColl	       0x00001000 /* Late Collision		     */ +#define Tx_TxPar	       0x00002000 /* Tx Parity Error		     */ +#define Tx_Comp		       0x00004000 /* Completion			     */ +#define Tx_Halted	       0x00008000 /* Tx Halted			     */ +#define Tx_SQErr	       0x00010000 /* Signal Quality Error(SQE)	     */ + +/* Rx_Ctl bit assign -------------------------------------------------------- */ +#define Rx_EnGood	       0x00004000 /* 1:Enable Good		     */ +#define Rx_EnRxPar	       0x00002000 /* 1:Enable Receive Parity	     */ +#define Rx_EnLongErr	       0x00000800 /* 1:Enable Long Error	     */ +#define Rx_EnOver	       0x00000400 /* 1:Enable OverFlow		     */ +#define Rx_EnCRCErr	       0x00000200 /* 1:Enable CRC Error		     */ +#define Rx_EnAlign	       0x00000100 /* 1:Enable Alignment		     */ +#define Rx_IgnoreCRC	       0x00000040 /* 1:Ignore CRC Value		     */ +#define Rx_StripCRC	       0x00000010 /* 1:Strip CRC Value		     */ +#define Rx_ShortEn	       0x00000008 /* 1:Short Enable		     */ +#define Rx_LongEn	       0x00000004 /* 1:Long Enable		     */ +#define Rx_RxHalt	       0x00000002 /* 1:Receive Halt Request	     */ +#define Rx_RxEn		       0x00000001 /* 1:Receive Intrrupt Enable	     */ + +/* Rx_Stat bit assign ------------------------------------------------------- */ +#define Rx_Halted	       0x00008000 /* Rx Halted			     */ +#define Rx_Good		       0x00004000 /* Rx Good			     */ +#define Rx_RxPar	       0x00002000 /* Rx Parity Error		     */ +#define Rx_TypePkt	       0x00001000 /* Rx Type Packet		     */ +#define Rx_LongErr	       0x00000800 /* Rx Long Error		     */ +#define Rx_Over		       0x00000400 /* Rx Overflow		     */ +#define Rx_CRCErr	       0x00000200 /* Rx CRC Error		     */ +#define Rx_Align	       0x00000100 /* Rx Alignment Error		     */ +#define Rx_10Stat	       0x00000080 /* Rx 10Mbps Status		     */ +#define Rx_IntRx	       0x00000040 /* Rx Interrupt		     */ +#define Rx_CtlRecd	       0x00000020 /* Rx Control Receive		     */ +#define Rx_InLenErr	       0x00000010 /* Rx In Range Frame Length Error  */ + +#define Rx_Stat_Mask	       0x0000FFF0 /* Rx All Status Mask		     */ + +/* Int_En bit assign -------------------------------------------------------- */ +#define Int_NRAbtEn	       0x00000800 /* 1:Non-recoverable Abort Enable  */ +#define Int_TxCtlCmpEn	       0x00000400 /* 1:Transmit Ctl Complete Enable  */ +#define Int_DmParErrEn	       0x00000200 /* 1:DMA Parity Error Enable	     */ +#define Int_DParDEn	       0x00000100 /* 1:Data Parity Error Enable	     */ +#define Int_EarNotEn	       0x00000080 /* 1:Early Notify Enable	     */ +#define Int_DParErrEn	       0x00000040 /* 1:Detected Parity Error Enable  */ +#define Int_SSysErrEn	       0x00000020 /* 1:Signalled System Error Enable */ +#define Int_RMasAbtEn	       0x00000010 /* 1:Received Master Abort Enable  */ +#define Int_RTargAbtEn	       0x00000008 /* 1:Received Target Abort Enable  */ +#define Int_STargAbtEn	       0x00000004 /* 1:Signalled Target Abort Enable */ +#define Int_BLExEn	       0x00000002 /* 1:Buffer List Exhausted Enable  */ +#define Int_FDAExEn	       0x00000001 /* 1:Free Descriptor Area	     */ +					  /*		   Exhausted Enable  */ + +/* Int_Src bit assign ------------------------------------------------------- */ +#define Int_NRabt	       0x00004000 /* 1:Non Recoverable error	     */ +#define Int_DmParErrStat       0x00002000 /* 1:DMA Parity Error & Clear	     */ +#define Int_BLEx	       0x00001000 /* 1:Buffer List Empty & Clear     */ +#define Int_FDAEx	       0x00000800 /* 1:FDA Empty & Clear	     */ +#define Int_IntNRAbt	       0x00000400 /* 1:Non Recoverable Abort	     */ +#define Int_IntCmp	       0x00000200 /* 1:MAC control packet complete   */ +#define Int_IntExBD	       0x00000100 /* 1:Interrupt Extra BD & Clear    */ +#define Int_DmParErr	       0x00000080 /* 1:DMA Parity Error & Clear	     */ +#define Int_IntEarNot	       0x00000040 /* 1:Receive Data write & Clear    */ +#define Int_SWInt	       0x00000020 /* 1:Software request & Clear	     */ +#define Int_IntBLEx	       0x00000010 /* 1:Buffer List Empty & Clear     */ +#define Int_IntFDAEx	       0x00000008 /* 1:FDA Empty & Clear	     */ +#define Int_IntPCI	       0x00000004 /* 1:PCI controller & Clear	     */ +#define Int_IntMacRx	       0x00000002 /* 1:Rx controller & Clear	     */ +#define Int_IntMacTx	       0x00000001 /* 1:Tx controller & Clear	     */ + +/* MD_CA bit assign --------------------------------------------------------- */ +#define MD_CA_PreSup	       0x00001000 /* 1:Preamble Suppress		     */ +#define MD_CA_Busy	       0x00000800 /* 1:Busy (Start Operation)	     */ +#define MD_CA_Wr	       0x00000400 /* 1:Write 0:Read		     */ + + +/* + * Descriptors + */ + +/* Frame descripter */ +struct FDesc { +	volatile __u32 FDNext; +	volatile __u32 FDSystem; +	volatile __u32 FDStat; +	volatile __u32 FDCtl; +}; + +/* Buffer descripter */ +struct BDesc { +	volatile __u32 BuffData; +	volatile __u32 BDCtl; +}; + +#define FD_ALIGN	16 + +/* Frame Descripter bit assign ---------------------------------------------- */ +#define FD_FDLength_MASK       0x0000FFFF /* Length MASK		     */ +#define FD_BDCnt_MASK	       0x001F0000 /* BD count MASK in FD	     */ +#define FD_FrmOpt_MASK	       0x7C000000 /* Frame option MASK		     */ +#define FD_FrmOpt_BigEndian    0x40000000 /* Tx/Rx */ +#define FD_FrmOpt_IntTx	       0x20000000 /* Tx only */ +#define FD_FrmOpt_NoCRC	       0x10000000 /* Tx only */ +#define FD_FrmOpt_NoPadding    0x08000000 /* Tx only */ +#define FD_FrmOpt_Packing      0x04000000 /* Rx only */ +#define FD_CownsFD	       0x80000000 /* FD Controller owner bit	     */ +#define FD_Next_EOL	       0x00000001 /* FD EOL indicator		     */ +#define FD_BDCnt_SHIFT	       16 + +/* Buffer Descripter bit assign --------------------------------------------- */ +#define BD_BuffLength_MASK     0x0000FFFF /* Receive Data Size		     */ +#define BD_RxBDID_MASK	       0x00FF0000 /* BD ID Number MASK		     */ +#define BD_RxBDSeqN_MASK       0x7F000000 /* Rx BD Sequence Number	     */ +#define BD_CownsBD	       0x80000000 /* BD Controller owner bit	     */ +#define BD_RxBDID_SHIFT	       16 +#define BD_RxBDSeqN_SHIFT      24 + + +/* Some useful constants. */ + +#define TX_CTL_CMD	(Tx_EnTxPar | Tx_EnLateColl | \ +	Tx_EnExColl | Tx_EnLCarr | Tx_EnExDefer | Tx_EnUnder | \ +	Tx_En)	/* maybe  0x7b01 */ +/* Do not use Rx_StripCRC -- it causes trouble on BLEx/FDAEx condition */ +#define RX_CTL_CMD	(Rx_EnGood | Rx_EnRxPar | Rx_EnLongErr | Rx_EnOver \ +	| Rx_EnCRCErr | Rx_EnAlign | Rx_RxEn) /* maybe 0x6f01 */ +#define INT_EN_CMD  (Int_NRAbtEn | \ +	Int_DmParErrEn | Int_DParDEn | Int_DParErrEn | \ +	Int_SSysErrEn  | Int_RMasAbtEn | Int_RTargAbtEn | \ +	Int_STargAbtEn | \ +	Int_BLExEn  | Int_FDAExEn) /* maybe 0xb7f*/ +#define DMA_CTL_CMD	DMA_BURST_SIZE +#define HAVE_DMA_RXALIGN(lp)	likely((lp)->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 = netdev_alloc_skb(dev, 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, +			     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 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 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 tc35815_read_plat_dev_addr(struct net_device *dev) +{ +	return -ENODEV; +} +#endif + +static int 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_rx_mode	= 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 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) +		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"); +		eth_hw_addr_random(dev); +	} + +	rc = register_netdev(dev); +	if (rc) +		goto err_out; + +	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 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); +} + +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); +	int ret; + +	if (lp->phy_dev) { +		ret = phy_init_hw(lp->phy_dev); +		if (ret) +			printk(KERN_ERR "%s: PHY init 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; + +	if (budget <= 0) +		return received; + +	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); + +	strlcpy(info->driver, MODNAME, sizeof(info->driver)); +	strlcpy(info->version, DRV_VERSION, sizeof(info->version)); +	strlcpy(info->bus_info, pci_name(lp->pci_dev), sizeof(info->bus_info)); +} + +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		= 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"); + +module_pci_driver(tc35815_pci_driver); +MODULE_DESCRIPTION("TOSHIBA TC35815 PCI 10M/100M Ethernet driver"); +MODULE_LICENSE("GPL");  | 
