/*
* 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/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/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/slab.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <asm/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);
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 struct pci_device_id 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)
{
u32 value;
value = readl(card->regs + reg);
value = le32_to_cpu(value);
return value;
}
/**
* 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)
{
value = cpu_to_le32(value);
writel(value, card->regs + reg);
}
/** 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_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_descr *descr)
{
return descr->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;
for (descr = chain->tail; !descr->bus_addr; descr = descr->next) {
pci_unmap_single(card->pdev, descr->bus_addr,
SPIDER_NET_DESCR_SIZE, PCI_DMA_BIDIRECTIONAL);
descr->bus_addr = 0;
}
}
/**
* spider_net_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
spider_net_init_chain(struct spider_net_card *card,
struct spider_net_descr_chain *chain,
struct spider_net_descr *start_descr,
int no)
{
int i;
struct spider_net_descr *descr;
dma_addr_t buf;
descr = start_descr;
memset(descr, 0, sizeof(*descr) * no);
/* set up the hardware pointers in each descriptor */
for (i=0; i<no; i++, descr++) {
descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE;
buf = pci_map_single(card->pdev, descr,
SPIDER_NET_DESCR_SIZE,
PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(buf))
goto iommu_error;
descr->bus_addr = buf;
descr->next = descr + 1;
descr->prev = descr - 1;
}
/* do actual circular list */
(descr-1)->next = start_descr;
start_descr->prev = descr-1;
spin_lock_init(&chain->lock);
chain->head = start_descr;
chain->tail = start_descr;
return 0;
iommu_error:
descr = start_descr;
for (i=0; i < no; i++, descr++)
if (descr->bus_addr)
pci_unmap_single(card->pdev, descr->bus_addr,
SPIDER_NET_DESCR_SIZE,
PCI_DMA_BIDIRECTIONAL);
return -ENOMEM;
}
/**
* 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) {
dev_kfree_skb(descr->skb);
pci_unmap_single(card->pdev, descr->buf_addr,
SPIDER_NET_MAX_FRAME,
PCI_DMA_BIDIRECTIONAL);
}
descr = descr->next;
} while (descr != card->rx_chain.head);
}
/**
* spider_net_prepare_rx_descr - reinitializes a rx descriptor
* @card: card structure
* @descr: descriptor to re-init
*
* return 0 on succes, <0 on failure
*
|