diff options
-rw-r--r-- | MAINTAINERS | 7 | ||||
-rw-r--r-- | drivers/net/Kconfig | 10 | ||||
-rw-r--r-- | drivers/net/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ps3_gelic_net.c | 1576 | ||||
-rw-r--r-- | drivers/net/ps3_gelic_net.h | 239 |
5 files changed, 1834 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 2c1dfb27161..0223d6d191d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2930,6 +2930,13 @@ M: mikpe@it.uu.se L: linux-ide@vger.kernel.org S: Maintained +PS3 NETWORK SUPPORT +P: Masakazu Mokuno +M: mokuno@sm.sony.co.jp +L: netdev@vger.kernel.org +L: cbe-oss-dev@ozlabs.org +S: Supported + PS3 PLATFORM SUPPORT P: Geoff Levand M: geoffrey.levand@am.sony.com diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index a64c2fb8ac2..ec846842c48 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2200,6 +2200,16 @@ config TSI108_ETH To compile this driver as a module, choose M here: the module will be called tsi108_eth. +config GELIC_NET + tristate "PS3 Gigabit Ethernet driver" + depends on PPC_PS3 + 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 GIANFAR tristate "Gianfar Ethernet" depends on 85xx || 83xx || PPC_86xx diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 77bd8222ab4..1bbcbedad04 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -60,6 +60,8 @@ obj-$(CONFIG_TIGON3) += tg3.o obj-$(CONFIG_BNX2) += bnx2.o spidernet-y += spider_net.o spider_net_ethtool.o obj-$(CONFIG_SPIDER_NET) += spidernet.o sungem_phy.o +obj-$(CONFIG_GELIC_NET) += ps3_gelic.o +ps3_gelic-objs += ps3_gelic_net.o obj-$(CONFIG_TC35815) += tc35815.o obj-$(CONFIG_SKGE) += skge.o obj-$(CONFIG_SKY2) += sky2.o diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c new file mode 100644 index 00000000000..08d25066f05 --- /dev/null +++ b/drivers/net/ps3_gelic_net.c @@ -0,0 +1,1576 @@ +/* + * 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/kernel.h> +#include <linux/module.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" + +#define DRV_NAME "Gelic Network Driver" +#define DRV_VERSION "1.0" + +MODULE_AUTHOR("SCE Inc."); +MODULE_DESCRIPTION("Gelic Network driver"); +MODULE_LICENSE("GPL"); + +static inline struct device *ctodev(struct gelic_net_card *card) +{ + return &card->dev->core; +} +static inline unsigned int bus_id(struct gelic_net_card *card) +{ + return card->dev->bus_id; +} +static inline unsigned int dev_id(struct gelic_net_card *card) +{ + return card->dev->dev_id; +} + +/* set irq_mask */ +static int gelic_net_set_irq_mask(struct gelic_net_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), + "lv1_net_set_interrupt_mask failed %d\n", status); + return status; +} +static inline void gelic_net_rx_irq_on(struct gelic_net_card *card) +{ + gelic_net_set_irq_mask(card, card->ghiintmask | GELIC_NET_RXINT); +} +static inline void gelic_net_rx_irq_off(struct gelic_net_card *card) +{ + gelic_net_set_irq_mask(card, card->ghiintmask & ~GELIC_NET_RXINT); +} +/** + * gelic_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 enum gelic_net_descr_status +gelic_net_get_descr_status(struct gelic_net_descr *descr) +{ + u32 cmd_status; + + cmd_status = descr->dmac_cmd_status; + cmd_status >>= GELIC_NET_DESCR_IND_PROC_SHIFT; + return cmd_status; +} + +/** + * gelic_net_set_descr_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_net_set_descr_status(struct gelic_net_descr *descr, + enum gelic_net_descr_status status) +{ + u32 cmd_status; + + /* read the status */ + cmd_status = descr->dmac_cmd_status; + /* clean the upper 4 bits */ + cmd_status &= GELIC_NET_DESCR_IND_PROC_MASKO; + /* add the status to it */ + cmd_status |= ((u32)status) << GELIC_NET_DESCR_IND_PROC_SHIFT; + /* and write it back */ + descr->dmac_cmd_status = cmd_status; + /* + * 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_net_free_chain - free descriptor chain + * @card: card structure + * @descr_in: address of desc + */ +static void gelic_net_free_chain(struct gelic_net_card *card, + struct gelic_net_descr *descr_in) +{ + struct gelic_net_descr *descr; + + for (descr = descr_in; descr && descr->bus_addr; descr = descr->next) { + dma_unmap_single(ctodev(card), descr->bus_addr, + GELIC_NET_DESCR_SIZE, DMA_BIDIRECTIONAL); + descr->bus_addr = 0; + } +} + +/** + * gelic_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 gelic_net_init_chain(struct gelic_net_card *card, + struct gelic_net_descr_chain *chain, + struct gelic_net_descr *start_descr, int no) +{ + int i; + struct gelic_net_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_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE); + descr->bus_addr = + dma_map_single(ctodev(card), descr, + GELIC_NET_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 = 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_NET_DESCR_SIZE, + DMA_BIDIRECTIONAL); + return -ENOMEM; +} + +/** + * gelic_net_prepare_rx_descr - reinitializes a rx descriptor + * @card: card structure + * @descr: descriptor to re-init + * + * return 0 on succes, <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_net_prepare_rx_descr(struct gelic_net_card *card, + struct gelic_net_descr *descr) +{ + int offset; + unsigned int bufsize; + + if (gelic_net_get_descr_status(descr) != GELIC_NET_DESCR_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 = netdev_alloc_skb(card->netdev, + 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 = 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 = 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_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE); + return -ENOMEM; + } else { + gelic_net_set_descr_status(descr, GELIC_NET_DESCR_CARDOWNED); + return 0; + } +} + +/** + * gelic_net_release_rx_chain - free all skb of rx descr + * @card: card structure + * + */ +static void gelic_net_release_rx_chain(struct gelic_net_card *card) +{ + struct gelic_net_descr *descr = card->rx_chain.head; + + do { + if (descr->skb) { + dma_unmap_single(ctodev(card), + descr->buf_addr, + descr->skb->len, + DMA_FROM_DEVICE); + descr->buf_addr = 0; + dev_kfree_skb_any(descr->skb); + descr->skb = NULL; + descr->dmac_cmd_status = GELIC_NET_DESCR_NOT_IN_USE; + } + descr = descr->next; + } while (descr != card->rx_chain.head); +} + +/** + * gelic_net_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_net_fill_rx_chain(struct gelic_net_card *card) +{ + struct gelic_net_descr *descr = card->rx_chain.head; + int ret; + + do { + if (!descr->skb) { + ret = gelic_net_prepare_rx_descr(card, descr); + if (ret) + goto rewind; + } + descr = descr->next; + } while (descr != card->rx_chain.head); + + return 0; +rewind: + gelic_net_release_rx_chain(card); + return ret; +} + +/** + * gelic_net_alloc_rx_skbs - allocates rx skbs in rx descriptor chains + * @card: card structure + * + * returns 0 on success, <0 on failure + */ +static int gelic_net_alloc_rx_skbs(struct gelic_net_card *card) +{ + struct gelic_net_descr_chain *chain; + int ret; + chain = &card->rx_chain; + ret = gelic_net_fill_rx_chain(card); + chain->head = card->rx_top->prev; /* point to the last */ + return ret; +} + +/** + * gelic_net_release_tx_descr - processes a used tx descriptor + * @card: card structure + * @descr: descriptor to release + * + * releases a used tx descriptor (unmapping, freeing of skb) + */ +static void gelic_net_release_tx_descr(struct gelic_net_card *card, + struct gelic_net_descr *descr) +{ + struct sk_buff *skb; + + + if (descr->data_status & (1 << GELIC_NET_TXDESC_TAIL)) { + /* 2nd descriptor */ + skb = descr->skb; + dma_unmap_single(ctodev(card), descr->buf_addr, skb->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + } else { + dma_unmap_single(ctodev(card), descr->buf_addr, + descr->buf_size, DMA_TO_DEVICE); + } + + 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 */ + descr->dmac_cmd_status = GELIC_NET_DMAC_CMDSTAT_NOT_IN_USE; +} + +/** + * gelic_net_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_net_release_tx_chain(struct gelic_net_card *card, int stop) +{ + struct gelic_net_descr_chain *tx_chain; + enum gelic_net_descr_status status; + 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_net_get_descr_status(tx_chain->tail); + switch (status) { + case GELIC_NET_DESCR_RESPONSE_ERROR: + case GELIC_NET_DESCR_PROTECTION_ERROR: + case GELIC_NET_DESCR_FORCE_END: + if (printk_ratelimit()) + dev_info(ctodev(card), + "%s: forcing end of tx descriptor " \ + "with status %x\n", + __func__, status); + card->netdev_stats.tx_dropped++; + break; + + case GELIC_NET_DESCR_COMPLETE: + card->netdev_stats.tx_packets++; + card->netdev_stats.tx_bytes += + tx_chain->tail->skb->len; + break; + + case GELIC_NET_DESCR_CARDOWNED: + /* pending tx request */ + default: + /* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */ + goto out; + } + gelic_net_release_tx_descr(card, tx_chain->tail); + release = 1; + } +out: + if (!stop && release) + netif_wake_queue(card->netdev); +} + +/** + * 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 + */ +static void gelic_net_set_multi(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + struct dev_mc_list *mc; + 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 > GELIC_NET_MC_COUNT_MAX) { /* list 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 address */ + for (mc = netdev->mc_list; mc; mc = mc->next) { + addr = 0; + p = mc->dmi_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_enable_rxdmac - enables the receive DMA controller + * @card: card structure + * + * gelic_net_enable_rxdmac enables the DMA controller by setting RX_DMA_EN + * in the GDADMACCNTR register + */ +static inline void gelic_net_enable_rxdmac(struct gelic_net_card *card) +{ + int status; + + status = lv1_net_start_rx_dma(bus_id(card), dev_id(card), + card->rx_chain.tail->bus_addr, 0); + if (status) + dev_info(ctodev(card), + "lv1_net_start_rx_dma failed, status=%d\n", status); +} + +/** + * gelic_net_disable_rxdmac - disables the receive DMA controller + * @card: card structure + * + * gelic_net_disable_rxdmac terminates processing on the DMA controller by + * turing off DMA and issueing a force end + */ +static inline void gelic_net_disable_rxdmac(struct gelic_net_card *card) +{ + int status; + + /* this hvc blocks until the DMA in progress really stopped */ + status = lv1_net_stop_rx_dma(bus_id(card), dev_id(card), 0); + if (status) + dev_err(ctodev(card), + "lv1_net_stop_rx_dma faild, %d\n", status); +} + +/** + * gelic_net_disable_txdmac - disables the transmit DMA controller + * @card: card structure + * + * gelic_net_disable_txdmac terminates processing on the DMA controller by + * turing off DMA and issueing a force end + */ +static inline void gelic_net_disable_txdmac(struct gelic_net_card *card) +{ + int status; + + /* this hvc blocks until the DMA in progress really stopped */ + status = lv1_net_stop_tx_dma(bus_id(card), dev_id(card), 0); + if (status) + dev_err(ctodev(card), + "lv1_net_stop_tx_dma faild, status=%d\n", status); +} + +/** + * gelic_net_stop - called upon ifconfig down + * @netdev: interface device structure + * + * always returns 0 + */ +static int gelic_net_stop(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + + netif_poll_disable(netdev); + netif_stop_queue(netdev); + + /* turn off DMA, force end */ + gelic_net_disable_rxdmac(card); + gelic_net_disable_txdmac(card); + + gelic_net_set_irq_mask(card, 0); + + /* disconnect event port */ + free_irq(card->netdev->irq, card->netdev); + ps3_sb_event_receive_port_destroy(card->dev, card->netdev->irq); + card->netdev->irq = NO_IRQ; + + netif_carrier_off(netdev); + + /* release chains */ + gelic_net_release_tx_chain(card, 1); + gelic_net_release_rx_chain(card); + + gelic_net_free_chain(card, card->tx_top); + gelic_net_free_chain(card, card->rx_top); + + return 0; +} + +/** + * gelic_net_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_net_descr * +gelic_net_get_next_tx_descr(struct gelic_net_card *card) +{ + if (!card->tx_chain.head) + return NULL; + /* see if we can two consecutive free descrs */ + if (card->tx_chain.tail != card->tx_chain.head->next && + gelic_net_get_descr_status(card->tx_chain.head) == + GELIC_NET_DESCR_NOT_IN_USE && + card->tx_chain.tail != card->tx_chain.head->next->next && + gelic_net_get_descr_status(card->tx_chain.head->next) == + GELIC_NET_DESCR_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 + * @middle: middle of frame + * + * 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_net_set_txdescr_cmdstat(struct gelic_net_descr *descr, + struct sk_buff *skb, int middle) +{ + u32 eofr; + + if (middle) + eofr = 0; + else + eofr = GELIC_NET_DMAC_CMDSTAT_END_FRAME; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + descr->dmac_cmd_status = GELIC_NET_DMAC_CMDSTAT_NOCS | eofr; + 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 = + GELIC_NET_DMAC_CMDSTAT_TCPCS | eofr; + else if (ip_hdr(skb)->protocol == IPPROTO_UDP) + descr->dmac_cmd_status = + GELIC_NET_DMAC_CMDSTAT_UDPCS | eofr; + else /* + * the stack should checksum non-tcp and non-udp + * packets on his own: NETIF_F_IP_CSUM + */ + descr->dmac_cmd_status = + GELIC_NET_DMAC_CMDSTAT_NOCS | eofr; + } + } +} + +/** + * gelic_net_prepare_tx_descr_v - get dma address of skb_data + * @card: card structure + * @descr: descriptor structure + * @skb: packet to use + * + * returns 0 on success, <0 on failure. + * + */ +static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card, + struct gelic_net_descr *descr, + struct sk_buff *skb) +{ + dma_addr_t buf[2]; + unsigned int vlan_len; + + if (skb->len < GELIC_NET_VLAN_POS) + return -EINVAL; + + memcpy(&descr->vlan, skb->data, GELIC_NET_VLAN_POS); + if (card->vlan_index != -1) { + descr->vlan.h_vlan_proto = htons(ETH_P_8021Q); /* vlan 0x8100*/ + descr->vlan.h_vlan_TCI = htons(card->vlan_id[card->vlan_index]); + vlan_len = GELIC_NET_VLAN_POS + VLAN_HLEN; /* VLAN_HLEN=4 */ + } else + vlan_len = GELIC_NET_VLAN_POS; /* no vlan tag */ + + /* first descr */ + buf[0] = dma_map_single(ctodev(card), &descr->vlan, + vlan_len, DMA_TO_DEVICE); + + if (!buf[0]) { + dev_err(ctodev(card), + "dma map 1 failed (%p, %i). Dropping packet\n", + skb->data, vlan_len); + return -ENOMEM; + } + + descr->buf_addr = buf[0]; + descr->buf_size = vlan_len; + descr->skb = skb; /* not used */ + descr->data_status = 0; + gelic_net_set_txdescr_cmdstat(descr, skb, 1); /* not the frame end */ + + /* second descr */ + card->tx_chain.head = card->tx_chain.head->next; + descr->next_descr_addr = descr->next->bus_addr; + descr = descr->next; + if (gelic_net_get_descr_status(descr) != GELIC_NET_DESCR_NOT_IN_USE) + /* XXX will be removed */ + dev_err(ctodev(card), "descr is not free!\n"); + + buf[1] = dma_map_single(ctodev(card), skb->data + GELIC_NET_VLAN_POS, + skb->len - GELIC_NET_VLAN_POS, + DMA_TO_DEVICE); + + if (!buf[1]) { + dev_err(ctodev(card), + "dma map 2 failed (%p, %i). Dropping packet\n", + skb->data + GELIC_NET_VLAN_POS, + skb->len - GELIC_NET_VLAN_POS); + dma_unmap_single(ctodev(card), buf[0], vlan_len, + DMA_TO_DEVICE); + return -ENOMEM; + } + + descr->buf_addr = buf[1]; + descr->buf_size = skb->len - GELIC_NET_VLAN_POS; + descr->skb = skb; + descr->data_status = 0; + descr->next_descr_addr = 0; /* terminate hw descr */ + gelic_net_set_txdescr_cmdstat(descr, skb, 0); + + return 0; +} + +/** + * gelic_net_kick_txdma - enables TX DMA processing + * @card: card structure + * @descr: descriptor address to enable TX processing at + * + */ +static int gelic_net_kick_txdma(struct gelic_net_card *card, + struct gelic_net_descr *descr) +{ + int status = -ENXIO; + int count = 10; + + if (card->tx_dma_progress) + return 0; + + if (gelic_net_get_descr_status(descr) == GELIC_NET_DESCR_CARDOWNED) { + card->tx_dma_progress = 1; + /* sometimes we need retry here */ + while (count--) { + status = lv1_net_start_tx_dma(bus_id(card), + dev_id(card), + descr->bus_addr, 0); + if (!status) + break; + } + if (!count) + dev_info(ctodev(card), "lv1_net_start_txdma failed," \ + "status=%d %#lx\n", + status, card->irq_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 + */ +static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + struct gelic_net_descr *descr = NULL; + int result; + unsigned long flags; + + spin_lock_irqsave(&card->tx_dma_lock, flags); + + gelic_net_release_tx_chain(card, 0); + if (!skb) + goto kick; + descr = gelic_net_get_next_tx_descr(card); + if (!descr) { + netif_stop_queue(netdev); + spin_unlock_irqrestore(&card->tx_dma_lock, flags); + return NETDEV_TX_BUSY; + } + result = gelic_net_prepare_tx_descr_v(card, descr, skb); + + if (result) + goto error; + + card->tx_chain.head = card->tx_chain.head->next; + + if (descr->prev) + descr->prev->next_descr_addr = descr->bus_addr; +kick: + /* + * as hardware descriptor is modified in the above lines, + * ensure that the hardware sees it + */ + wmb(); + if (gelic_net_kick_txdma(card, card->tx_chain.tail)) + goto error; + + netdev->trans_start = jiffies; + spin_unlock_irqrestore(&card->tx_dma_lock, flags); + return NETDEV_TX_OK; + +error: + card->netdev_stats.tx_dropped++; + spin_unlock_irqrestore(&card->tx_dma_lock, flags); + return NETDEV_TX_LOCKED; +} + +/** + * gelic_net_pass_skb_up - takes an skb from a descriptor and passes it on + * @descr: descriptor to process + * @card: card structure + * + * 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_net_descr *descr, + struct gelic_net_card *card) +{ + struct sk_buff *skb; + struct net_device *netdev; + u32 data_status, data_error; + + data_status = descr->data_status; + data_error = descr->data_error; + netdev = card->netdev; + /* unmap skb buffer */ + skb = descr->skb; + dma_unmap_single(ctodev(card), descr->buf_addr, GELIC_NET_MAX_MTU, + DMA_FROM_DEVICE); + + skb_put(skb, descr->valid_size? descr->valid_size : descr->result_size); + if (!descr->valid_size) + dev_info(ctodev(card), "buffer full %x %x %x\n", + descr->result_size, descr->buf_size, + 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 (card->rx_csum) { + if ((data_status & GELIC_NET_DATA_STATUS_CHK_MASK) && + (!(data_error & GELIC_NET_DATA_ERROR_CHK_MASK))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + } else + skb->ip_summed = CHECKSUM_NONE; + + /* update netdevice statistics */ + card->netdev_stats.rx_packets++; + card->netdev_stats.rx_bytes += skb->len; + + /* pass skb up to stack */ + netif_receive_skb(skb); +} + +/** + * gelic_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 + */ +static int gelic_net_decode_one_descr(struct gelic_net_card *card) +{ + enum gelic_net_descr_status status; + struct gelic_net_descr_chain *chain = &card->rx_chain; + struct gelic_net_descr *descr = chain->tail; + int dmac_chain_ended; + + status = gelic_net_get_descr_status(descr); + /* is this descriptor terminated with next_descr == NULL? */ + dmac_chain_ended = + descr->dmac_cmd_status & GELIC_NET_DMAC_CMDSTAT_RXDCEIS; + + if (status == GELIC_NET_DESCR_CARDOWNED) + return 0; + + if (status == GELIC_NET_DESCR_NOT_IN_USE) { + dev_dbg(ctodev(card), "dormant descr? %p\n", descr); + return 0; + } + + if ((status == GELIC_NET_DESCR_RESPONSE_ERROR) || + (status == GELIC_NET_DESCR_PROTECTION_ERROR) || + (status == GELIC_NET_DESCR_FORCE_END)) { + dev_info(ctodev(card), "dropping RX descriptor with state %x\n", + status); + card->netdev_stats.rx_dropped++; + goto refill; + } + + if ((status != GELIC_NET_DESCR_COMPLETE) && + (status != GELIC_NET_DESCR_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); /* 1: skb_up sccess */ + +refill: + descr->next_descr_addr = 0; /* unlink the descr */ + + /* change the descriptor state: */ + gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE); + + /* refill one desc + * FIXME: this can fail, but for now, just leave this + * descriptor without skb + */ + gelic_net_prepare_rx_descr(card, descr); + chain->head = descr; + chain->tail = descr->next; + descr->prev->next_descr_addr = descr->bus_addr; + + if (dmac_chain_ended) { + gelic_net_enable_rxdmac(card); + dev_dbg(ctodev(card), "reenable rx dma\n"); + } + + return 1; +} + +/** + * gelic_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. + * + */ +static int gelic_net_poll(struct net_device *netdev, int *budget) +{ + struct gelic_net_card *card = netdev_priv(netdev); + int packets_to_do, packets_done = 0; + int no_more_packets = 0; + + packets_to_do = min(*budget, netdev->quota); + + while (packets_to_do) { + if (gelic_net_decode_one_descr(card)) { + packets_done++; + packets_to_do--; + } else { + /* no more packets for the stack */ + no_more_packets = 1; + break; + } + } + netdev->quota -= packets_done; + *budget -= packets_done; + if (no_more_packets) { + netif_rx_complete(netdev); + gelic_net_rx_irq_on(card); + return 0; + } else + return 1; +} + +/** + * gelic_net_get_stats - get interface statistics + * @netdev: interface device structure + * + * returns the interface statistics residing in the gelic_net_card struct + */ +static struct net_device_stats *gelic_net_get_stats(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + + return &card->netdev_stats; +} + +/** + * 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 + */ +static 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_net_interrupt - event handler for gelic_net + */ +static irqreturn_t gelic_net_interrupt(int irq, void *ptr) +{ + unsigned long flags; + struct net_device *netdev = ptr; + struct gelic_net_card *card = netdev_priv(netdev); + u64 status; + + status = card->irq_status; + + if (!status) + return IRQ_NONE; + + if (status & GELIC_NET_RXINT) { + gelic_net_rx_irq_off(card); + netif_rx_schedule(netdev); + } + + if (status & GELIC_NET_TXINT) { + spin_lock_irqsave(&card->tx_dma_lock, flags); + card->tx_dma_progress = 0; + spin_unlock_irqrestore(&card->tx_dma_lock, flags); + /* start pending DMA */ + gelic_net_xmit(NULL, netdev); + } + 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 + */ +static void gelic_net_poll_controller(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + + gelic_net_set_irq_mask(card, 0); + gelic_net_interrupt(netdev->irq, netdev); + gelic_net_set_irq_mask(card, card->ghiintmask); +} +#endif /* CONFIG_NET_POLL_CONTROLLER */ + +/** + * gelic_net_open_device - open device and map dma region + * @card: card structure + */ +static int gelic_net_open_device(struct gelic_net_card *card) +{ + int result; + + result = ps3_sb_event_receive_port_setup(card->dev, PS3_BINDING_CPU_ANY, + &card->netdev->irq); + + if (result) { + dev_info(ctodev(card), + "%s:%d: gelic_net_open_device failed (%d)\n", + __func__, __LINE__, result); + result = -EPERM; + goto fail_alloc_irq; + } + + result = request_irq(card->netdev->irq, gelic_net_interrupt, + IRQF_DISABLED, "gelic network", card->netdev); + + if (result) { + dev_info(ctodev(card), "%s:%d: request_irq failed (%d)\n", + __func__, __LINE__, result); + goto fail_request_irq; + } + + return 0; + +fail_request_irq: + ps3_sb_event_receive_port_destroy(card->dev, card->netdev->irq); + card->netdev->irq = NO_IRQ; +fail_alloc_irq: + return result; +} + + +/** + * gelic_net_open - called upon ifonfig 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 + */ +static int gelic_net_open(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + + dev_dbg(ctodev(card), " -> %s:%d\n", __func__, __LINE__); + + gelic_net_open_device(card); + + if (gelic_net_init_chain(card, &card->tx_chain, + card->descr, GELIC_NET_TX_DESCRIPTORS)) + goto alloc_tx_fa |