From: Jeff Garzik <jeff@garzik.org>
To: MOKUNO Masakazu <mokuno@sm.sony.co.jp>
Cc: netdev@vger.kernel.org,
Geoff Levand <geoffrey.levand@am.sony.com>,
Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Subject: Re: [PATCH]: ps3: gigabit ethernet driver for PS3
Date: Wed, 13 Jun 2007 16:27:32 -0400 [thread overview]
Message-ID: <46705334.5060001@garzik.org> (raw)
In-Reply-To: <20070613154156.71DB.MOKUNO@sm.sony.co.jp>
MOKUNO Masakazu wrote:
> Hi Jeff,
>
> The following patch adds support for the gigabit ethernet device of PS3.
> It was sent out before as RFC, now I submit it for 2.6.23.
>
> Signed-off-by: Masakazu Mokuno <mokuno@sm.sony.co.jp>
> Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
> ---
> drivers/net/Kconfig | 10
> drivers/net/Makefile | 2
> drivers/net/gelic_net.c | 1564 ++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/net/gelic_net.h | 233 +++++++
> 4 files changed, 1809 insertions(+)
a MAINTAINERS entry would be nice
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -2264,6 +2264,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 Gigabit Ethernet device on the
> + PS3 game console.
> +
> + 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
> --- 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 += gelic_net.o
> obj-$(CONFIG_TC35815) += tc35815.o
> obj-$(CONFIG_SKGE) += skge.o
> obj-$(CONFIG_SKY2) += sky2.o
How about ps3_gige for the driver name. Ditto DaveM's comments about
cleanups here.
> --- /dev/null
> +++ b/drivers/net/gelic_net.c
> @@ -0,0 +1,1564 @@
> +/*
> + * PS3 Platfom gelic network driver.
> + *
> + * Copyright (C) 2007 Sony Computer Entertainment Inc.
> + * Copyright 2007 Sony Corporation
> + *
> + * this file is based on: spider_net.c
> + *
> + * 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.
> + */
> +
> +#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 "gelic_net.h"
Please run this patch through scripts/checkpatch.pl (newly added in
latest 2.6.22-rcX-gitY)
> +#define GELIC_NET_DRV_NAME "Gelic Network Driver"
> +#define GELIC_NET_DRV_VERSION "1.0"
please follow other net drivers and use the more simple DRV_NAME and
DRV_VERSION
> +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;
> + wmb();
does the wmb() actually do anything useful here?
> + * 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 = (GELIC_NET_MAX_MTU + GELIC_NET_RXBUF_ALIGN - 1) &
> + (~(GELIC_NET_RXBUF_ALIGN - 1));
use 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);
net_device allocation is already rounded. and combined with the above
code snippet, it appears you're aligning twice
> + 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_BIDIRECTIONAL);
> + wmb();
> + 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_BIDIRECTIONAL);
> + 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)
> + if ((ret = gelic_net_prepare_rx_descr(card, descr)))
> + goto rewind;
please avoid combining two C statements into a single line.
ret = gelic_net_...()
if (ret)
goto rewind
is more readable
> + 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_BIDIRECTIONAL);
> + dev_kfree_skb_any(skb);
> + } else {
> + dma_unmap_single(ctodev(card), descr->buf_addr,
> + descr->buf_size, DMA_BIDIRECTIONAL);
> + }
> +
> + 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_queue_stopped(card->netdev))
> + netif_wake_queue(card->netdev);
shouldn't need to test netif_queued_stopped() before calling
netif_wake_queue(), as netif_wake_queue() essentially already does this
> + * 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);
> + }
this seems not to handle the promisc case
> +/**
> + * 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 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)
> + printk("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 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 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);
> +}
do we really need all these three-C-statement functions?
> +/**
> + * 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_BIDIRECTIONAL);
> +
> + 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)
> + dev_err(ctodev(card),"descr is not free!\n"); /* XXX will be removed */
> +
> + buf[1] = dma_map_single(ctodev(card), skb->data + GELIC_NET_VLAN_POS,
> + skb->len - GELIC_NET_VLAN_POS,
> + DMA_BIDIRECTIONAL);
> +
> + 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_BIDIRECTIONAL);
> + 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;
skb will never be NULL here
> + 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:
> + 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_BIDIRECTIONAL);
why BIDIRECTIONAL?
> + skb->dev = netdev;
shouldn't be needed anymore with netdev_alloc_skb()
> + 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;
> + }
remove all braces around single-line C statements
> + 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;
no RX filter to change?
> +/**
> + * 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;
next prev parent reply other threads:[~2007-06-13 20:27 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-06-13 6:58 [PATCH]: ps3: gigabit ethernet driver for PS3 MOKUNO Masakazu
2007-06-13 7:03 ` David Miller
2007-06-13 8:48 ` MOKUNO Masakazu
2007-06-13 20:27 ` Jeff Garzik [this message]
2007-06-14 12:51 ` MOKUNO Masakazu
2007-06-15 17:16 ` Geoff Levand
2007-06-15 18:18 ` Dan Williams
2007-06-15 19:03 ` Geoff Levand
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=46705334.5060001@garzik.org \
--to=jeff@garzik.org \
--cc=Geert.Uytterhoeven@sonycom.com \
--cc=geoffrey.levand@am.sony.com \
--cc=mokuno@sm.sony.co.jp \
--cc=netdev@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.