2007-07-05 15:11:16 +04:00
|
|
|
/*
|
|
|
|
* 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 cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 11:04:11 +03:00
|
|
|
#include <linux/slab.h>
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
#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"
|
2008-02-07 13:58:57 +03:00
|
|
|
#include "ps3_gelic_wireless.h"
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
#define DRV_NAME "Gelic Network Driver"
|
2008-02-07 13:58:57 +03:00
|
|
|
#define DRV_VERSION "2.0"
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
MODULE_AUTHOR("SCE Inc.");
|
|
|
|
MODULE_DESCRIPTION("Gelic Network driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
|
|
|
|
static inline void gelic_card_enable_rxdmac(struct gelic_card *card);
|
|
|
|
static inline void gelic_card_disable_rxdmac(struct gelic_card *card);
|
|
|
|
static inline void gelic_card_disable_txdmac(struct gelic_card *card);
|
|
|
|
static inline void gelic_card_reset_chain(struct gelic_card *card,
|
|
|
|
struct gelic_descr_chain *chain,
|
|
|
|
struct gelic_descr *start_descr);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
/* set irq_mask */
|
2008-02-07 13:58:42 +03:00
|
|
|
int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = lv1_net_set_interrupt_mask(bus_id(card), dev_id(card),
|
|
|
|
mask, 0);
|
|
|
|
if (status)
|
|
|
|
dev_info(ctodev(card),
|
2008-02-07 13:58:42 +03:00
|
|
|
"%s failed %d\n", __func__, status);
|
2007-07-05 15:11:16 +04:00
|
|
|
return status;
|
|
|
|
}
|
2008-02-07 13:58:42 +03:00
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
static inline void gelic_card_rx_irq_on(struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
card->irq_mask |= GELIC_CARD_RXINT;
|
|
|
|
gelic_card_set_irq_mask(card, card->irq_mask);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
2008-02-07 13:58:08 +03:00
|
|
|
static inline void gelic_card_rx_irq_off(struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
card->irq_mask &= ~GELIC_CARD_RXINT;
|
|
|
|
gelic_card_set_irq_mask(card, card->irq_mask);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
2008-02-07 13:58:32 +03:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
static void gelic_card_get_ether_port_status(struct gelic_card *card,
|
|
|
|
int inform)
|
2008-02-07 13:58:32 +03:00
|
|
|
{
|
|
|
|
u64 v2;
|
|
|
|
struct net_device *ether_netdev;
|
|
|
|
|
|
|
|
lv1_net_control(bus_id(card), dev_id(card),
|
|
|
|
GELIC_LV1_GET_ETH_PORT_STATUS,
|
2009-12-01 15:15:53 +03:00
|
|
|
GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0,
|
2008-02-07 13:58:32 +03:00
|
|
|
&card->ether_port_status, &v2);
|
|
|
|
|
|
|
|
if (inform) {
|
2009-12-01 15:15:53 +03:00
|
|
|
ether_netdev = card->netdev[GELIC_PORT_ETHERNET_0];
|
2008-02-07 13:58:32 +03:00
|
|
|
if (card->ether_port_status & GELIC_LV1_ETHER_LINK_UP)
|
|
|
|
netif_carrier_on(ether_netdev);
|
|
|
|
else
|
|
|
|
netif_carrier_off(ether_netdev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-01 15:15:58 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
void gelic_card_up(struct gelic_card *card)
|
|
|
|
{
|
|
|
|
pr_debug("%s: called\n", __func__);
|
2008-05-22 11:00:03 +04:00
|
|
|
mutex_lock(&card->updown_lock);
|
2008-02-07 13:58:42 +03:00
|
|
|
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);
|
|
|
|
}
|
2008-05-22 11:00:03 +04:00
|
|
|
mutex_unlock(&card->updown_lock);
|
2008-02-07 13:58:42 +03:00
|
|
|
pr_debug("%s: done\n", __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gelic_card_down(struct gelic_card *card)
|
|
|
|
{
|
|
|
|
u64 mask;
|
|
|
|
pr_debug("%s: called\n", __func__);
|
2008-05-22 11:00:03 +04:00
|
|
|
mutex_lock(&card->updown_lock);
|
2008-02-07 13:58:42 +03:00
|
|
|
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);
|
|
|
|
}
|
2008-05-22 11:00:03 +04:00
|
|
|
mutex_unlock(&card->updown_lock);
|
2008-02-07 13:58:42 +03:00
|
|
|
pr_debug("%s: done\n", __func__);
|
|
|
|
}
|
2008-02-07 13:58:32 +03:00
|
|
|
|
2007-07-05 15:11:16 +04:00
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_descr_get_status -- returns the status of a descriptor
|
2007-07-05 15:11:16 +04:00
|
|
|
* @descr: descriptor to look at
|
|
|
|
*
|
|
|
|
* returns the status as in the dmac_cmd_status field of the descriptor
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static enum gelic_descr_dma_status
|
|
|
|
gelic_descr_get_status(struct gelic_descr *descr)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
return be32_to_cpu(descr->dmac_cmd_status) & GELIC_DESCR_DMA_STAT_MASK;
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_descr_set_status -- sets the status of a descriptor
|
2007-07-05 15:11:16 +04:00
|
|
|
* @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
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static void gelic_descr_set_status(struct gelic_descr *descr,
|
|
|
|
enum gelic_descr_dma_status status)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
descr->dmac_cmd_status = cpu_to_be32(status |
|
2008-02-07 13:58:42 +03:00
|
|
|
(be32_to_cpu(descr->dmac_cmd_status) &
|
|
|
|
~GELIC_DESCR_DMA_STAT_MASK));
|
2007-07-05 15:11:16 +04:00
|
|
|
/*
|
|
|
|
* 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();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_free_chain - free descriptor chain
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
* @descr_in: address of desc
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static void gelic_card_free_chain(struct gelic_card *card,
|
|
|
|
struct gelic_descr *descr_in)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_descr *descr;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
for (descr = descr_in; descr && descr->bus_addr; descr = descr->next) {
|
|
|
|
dma_unmap_single(ctodev(card), descr->bus_addr,
|
2008-02-07 13:58:08 +03:00
|
|
|
GELIC_DESCR_SIZE, DMA_BIDIRECTIONAL);
|
2007-07-05 15:11:16 +04:00
|
|
|
descr->bus_addr = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_init_chain - links descriptor chain
|
2007-07-05 15:11:16 +04:00
|
|
|
* @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
|
|
|
|
*/
|
2009-06-10 08:38:58 +04:00
|
|
|
static int __devinit gelic_card_init_chain(struct gelic_card *card,
|
|
|
|
struct gelic_descr_chain *chain,
|
|
|
|
struct gelic_descr *start_descr,
|
|
|
|
int no)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
int i;
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_descr *descr;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
descr = start_descr;
|
|
|
|
memset(descr, 0, sizeof(*descr) * no);
|
|
|
|
|
|
|
|
/* set up the hardware pointers in each descriptor */
|
|
|
|
for (i = 0; i < no; i++, descr++) {
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE);
|
2007-07-05 15:11:16 +04:00
|
|
|
descr->bus_addr =
|
|
|
|
dma_map_single(ctodev(card), descr,
|
2008-02-07 13:58:08 +03:00
|
|
|
GELIC_DESCR_SIZE,
|
2007-07-05 15:11:16 +04:00
|
|
|
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++) {
|
2008-02-07 13:57:54 +03:00
|
|
|
descr->next_descr_addr = cpu_to_be32(descr->next->bus_addr);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2008-02-07 13:58:08 +03:00
|
|
|
GELIC_DESCR_SIZE,
|
2007-07-05 15:11:16 +04:00
|
|
|
DMA_BIDIRECTIONAL);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
2007-07-05 15:11:16 +04:00
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_descr_prepare_rx - reinitializes a rx descriptor
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
* @descr: descriptor to re-init
|
|
|
|
*
|
tree-wide: fix assorted typos all over the place
That is "success", "unknown", "through", "performance", "[re|un]mapping"
, "access", "default", "reasonable", "[con]currently", "temperature"
, "channel", "[un]used", "application", "example","hierarchy", "therefore"
, "[over|under]flow", "contiguous", "threshold", "enough" and others.
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2009-11-14 18:09:05 +03:00
|
|
|
* return 0 on success, <0 on failure
|
2007-07-05 15:11:16 +04:00
|
|
|
*
|
|
|
|
* allocates a new rx skb, iommu-maps it and attaches it to the descriptor.
|
|
|
|
* Activate the descriptor state-wise
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static int gelic_descr_prepare_rx(struct gelic_card *card,
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_descr *descr)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
int offset;
|
|
|
|
unsigned int bufsize;
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
if (gelic_descr_get_status(descr) != GELIC_DESCR_DMA_NOT_IN_USE)
|
2010-03-24 10:57:31 +03:00
|
|
|
dev_info(ctodev(card), "%s: ERROR status\n", __func__);
|
2007-07-05 15:11:16 +04:00
|
|
|
/* 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 */
|
2008-02-07 13:58:42 +03:00
|
|
|
descr->skb = dev_alloc_skb(bufsize + GELIC_NET_RXBUF_ALIGN - 1);
|
2007-07-05 15:11:16 +04:00
|
|
|
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;
|
|
|
|
}
|
2008-02-07 13:57:54 +03:00
|
|
|
descr->buf_size = cpu_to_be32(bufsize);
|
2007-07-05 15:11:16 +04:00
|
|
|
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 */
|
2008-02-07 13:57:54 +03:00
|
|
|
descr->buf_addr = cpu_to_be32(dma_map_single(ctodev(card),
|
|
|
|
descr->skb->data,
|
|
|
|
GELIC_NET_MAX_MTU,
|
|
|
|
DMA_FROM_DEVICE));
|
2007-07-05 15:11:16 +04:00
|
|
|
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__);
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE);
|
2007-07-05 15:11:16 +04:00
|
|
|
return -ENOMEM;
|
|
|
|
} else {
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_set_status(descr, GELIC_DESCR_DMA_CARDOWNED);
|
2007-07-05 15:11:16 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_release_rx_chain - free all skb of rx descr
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
*
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static void gelic_card_release_rx_chain(struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_descr *descr = card->rx_chain.head;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
do {
|
|
|
|
if (descr->skb) {
|
|
|
|
dma_unmap_single(ctodev(card),
|
2008-02-07 13:57:54 +03:00
|
|
|
be32_to_cpu(descr->buf_addr),
|
2007-07-05 15:11:16 +04:00
|
|
|
descr->skb->len,
|
|
|
|
DMA_FROM_DEVICE);
|
|
|
|
descr->buf_addr = 0;
|
|
|
|
dev_kfree_skb_any(descr->skb);
|
|
|
|
descr->skb = NULL;
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_set_status(descr,
|
|
|
|
GELIC_DESCR_DMA_NOT_IN_USE);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
descr = descr->next;
|
|
|
|
} while (descr != card->rx_chain.head);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_fill_rx_chain - fills descriptors/skbs in the rx chains
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
*
|
|
|
|
* fills all descriptors in the rx chain: allocates skbs
|
|
|
|
* and iommu-maps them.
|
2008-02-07 13:58:08 +03:00
|
|
|
* returns 0 on success, < 0 on failure
|
2007-07-05 15:11:16 +04:00
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static int gelic_card_fill_rx_chain(struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_descr *descr = card->rx_chain.head;
|
2007-07-05 15:11:16 +04:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (!descr->skb) {
|
2008-02-07 13:58:08 +03:00
|
|
|
ret = gelic_descr_prepare_rx(card, descr);
|
2007-07-05 15:11:16 +04:00
|
|
|
if (ret)
|
|
|
|
goto rewind;
|
|
|
|
}
|
|
|
|
descr = descr->next;
|
|
|
|
} while (descr != card->rx_chain.head);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
rewind:
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_card_release_rx_chain(card);
|
2007-07-05 15:11:16 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_alloc_rx_skbs - allocates rx skbs in rx descriptor chains
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
*
|
2008-02-07 13:58:08 +03:00
|
|
|
* returns 0 on success, < 0 on failure
|
2007-07-05 15:11:16 +04:00
|
|
|
*/
|
2009-06-10 08:38:58 +04:00
|
|
|
static int __devinit gelic_card_alloc_rx_skbs(struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_descr_chain *chain;
|
2007-07-05 15:11:16 +04:00
|
|
|
int ret;
|
|
|
|
chain = &card->rx_chain;
|
2008-02-07 13:58:08 +03:00
|
|
|
ret = gelic_card_fill_rx_chain(card);
|
2008-02-07 13:58:42 +03:00
|
|
|
chain->tail = card->rx_top->prev; /* point to the last */
|
2007-07-05 15:11:16 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_descr_release_tx - processes a used tx descriptor
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
* @descr: descriptor to release
|
|
|
|
*
|
|
|
|
* releases a used tx descriptor (unmapping, freeing of skb)
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static void gelic_descr_release_tx(struct gelic_card *card,
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_descr *descr)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2007-08-31 17:22:32 +04:00
|
|
|
struct sk_buff *skb = descr->skb;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
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);
|
2007-08-31 17:22:32 +04:00
|
|
|
dev_kfree_skb_any(skb);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
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 */
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
static void gelic_card_stop_queues(struct gelic_card *card)
|
|
|
|
{
|
2009-12-01 15:15:53 +03:00
|
|
|
netif_stop_queue(card->netdev[GELIC_PORT_ETHERNET_0]);
|
2008-02-07 13:58:42 +03:00
|
|
|
|
|
|
|
if (card->netdev[GELIC_PORT_WIRELESS])
|
|
|
|
netif_stop_queue(card->netdev[GELIC_PORT_WIRELESS]);
|
|
|
|
}
|
|
|
|
static void gelic_card_wake_queues(struct gelic_card *card)
|
|
|
|
{
|
2009-12-01 15:15:53 +03:00
|
|
|
netif_wake_queue(card->netdev[GELIC_PORT_ETHERNET_0]);
|
2008-02-07 13:58:42 +03:00
|
|
|
|
|
|
|
if (card->netdev[GELIC_PORT_WIRELESS])
|
|
|
|
netif_wake_queue(card->netdev[GELIC_PORT_WIRELESS]);
|
|
|
|
}
|
2007-07-05 15:11:16 +04:00
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_release_tx_chain - processes sent tx descriptors
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: adapter structure
|
|
|
|
* @stop: net_stop sequence
|
|
|
|
*
|
|
|
|
* releases the tx descriptors that gelic has finished with
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static void gelic_card_release_tx_chain(struct gelic_card *card, int stop)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_descr_chain *tx_chain;
|
|
|
|
enum gelic_descr_dma_status status;
|
2008-02-07 13:58:42 +03:00
|
|
|
struct net_device *netdev;
|
2007-07-05 15:11:16 +04:00
|
|
|
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) {
|
2008-02-07 13:58:08 +03:00
|
|
|
status = gelic_descr_get_status(tx_chain->tail);
|
2008-02-07 13:58:42 +03:00
|
|
|
netdev = tx_chain->tail->skb->dev;
|
2007-07-05 15:11:16 +04:00
|
|
|
switch (status) {
|
2008-02-07 13:58:08 +03:00
|
|
|
case GELIC_DESCR_DMA_RESPONSE_ERROR:
|
|
|
|
case GELIC_DESCR_DMA_PROTECTION_ERROR:
|
|
|
|
case GELIC_DESCR_DMA_FORCE_END:
|
2007-07-05 15:11:16 +04:00
|
|
|
if (printk_ratelimit())
|
|
|
|
dev_info(ctodev(card),
|
|
|
|
"%s: forcing end of tx descriptor " \
|
|
|
|
"with status %x\n",
|
|
|
|
__func__, status);
|
2008-02-07 13:58:42 +03:00
|
|
|
netdev->stats.tx_dropped++;
|
2007-07-05 15:11:16 +04:00
|
|
|
break;
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
case GELIC_DESCR_DMA_COMPLETE:
|
2007-07-20 12:24:56 +04:00
|
|
|
if (tx_chain->tail->skb) {
|
2008-02-07 13:58:42 +03:00
|
|
|
netdev->stats.tx_packets++;
|
|
|
|
netdev->stats.tx_bytes +=
|
2007-07-20 12:24:56 +04:00
|
|
|
tx_chain->tail->skb->len;
|
|
|
|
}
|
2007-07-05 15:11:16 +04:00
|
|
|
break;
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
case GELIC_DESCR_DMA_CARDOWNED:
|
2007-07-05 15:11:16 +04:00
|
|
|
/* pending tx request */
|
|
|
|
default:
|
2008-02-07 13:58:08 +03:00
|
|
|
/* any other value (== GELIC_DESCR_DMA_NOT_IN_USE) */
|
2007-07-20 12:24:56 +04:00
|
|
|
if (!stop)
|
|
|
|
goto out;
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_release_tx(card, tx_chain->tail);
|
2007-07-20 12:24:56 +04:00
|
|
|
release ++;
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
out:
|
2007-08-31 17:22:32 +04:00
|
|
|
if (!stop && release)
|
2008-02-07 13:58:42 +03:00
|
|
|
gelic_card_wake_queues(card);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
void gelic_net_set_multi(struct net_device *netdev)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card = netdev_card(netdev);
|
2010-04-02 01:22:57 +04:00
|
|
|
struct netdev_hw_addr *ha;
|
2007-07-05 15:11:16 +04:00
|
|
|
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);
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
if ((netdev->flags & IFF_ALLMULTI) ||
|
2010-02-08 07:30:35 +03:00
|
|
|
(netdev_mc_count(netdev) > GELIC_NET_MC_COUNT_MAX)) {
|
2007-07-05 15:11:16 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
/* set multicast addresses */
|
2010-04-02 01:22:57 +04:00
|
|
|
netdev_for_each_mc_addr(ha, netdev) {
|
2007-07-05 15:11:16 +04:00
|
|
|
addr = 0;
|
2010-04-02 01:22:57 +04:00
|
|
|
p = ha->addr;
|
2007-07-05 15:11:16 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_enable_rxdmac - enables the receive DMA controller
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
*
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_enable_rxdmac enables the DMA controller by setting RX_DMA_EN
|
2007-07-05 15:11:16 +04:00
|
|
|
* in the GDADMACCNTR register
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static inline void gelic_card_enable_rxdmac(struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
#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
|
2007-07-05 15:11:16 +04:00
|
|
|
status = lv1_net_start_rx_dma(bus_id(card), dev_id(card),
|
2008-02-07 13:58:42 +03:00
|
|
|
card->rx_chain.head->bus_addr, 0);
|
2007-07-05 15:11:16 +04:00
|
|
|
if (status)
|
|
|
|
dev_info(ctodev(card),
|
|
|
|
"lv1_net_start_rx_dma failed, status=%d\n", status);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_disable_rxdmac - disables the receive DMA controller
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
*
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_disable_rxdmac terminates processing on the DMA controller by
|
2007-07-05 15:11:16 +04:00
|
|
|
* turing off DMA and issueing a force end
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static inline void gelic_card_disable_rxdmac(struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
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),
|
2010-08-07 14:30:03 +04:00
|
|
|
"lv1_net_stop_rx_dma failed, %d\n", status);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_disable_txdmac - disables the transmit DMA controller
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
*
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_disable_txdmac terminates processing on the DMA controller by
|
2007-07-05 15:11:16 +04:00
|
|
|
* turing off DMA and issueing a force end
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static inline void gelic_card_disable_txdmac(struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
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),
|
2010-08-07 14:30:03 +04:00
|
|
|
"lv1_net_stop_tx_dma failed, status=%d\n", status);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gelic_net_stop - called upon ifconfig down
|
|
|
|
* @netdev: interface device structure
|
|
|
|
*
|
|
|
|
* always returns 0
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
int gelic_net_stop(struct net_device *netdev)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
pr_debug("%s: start\n", __func__);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
netif_stop_queue(netdev);
|
2007-07-05 15:11:16 +04:00
|
|
|
netif_carrier_off(netdev);
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
card = netdev_card(netdev);
|
|
|
|
gelic_card_down(card);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
pr_debug("%s: done\n", __func__);
|
2007-07-05 15:11:16 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_get_next_tx_descr - returns the next available tx descriptor
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: device structure to get descriptor from
|
|
|
|
*
|
|
|
|
* returns the address of the next descriptor, or NULL if not available.
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static struct gelic_descr *
|
|
|
|
gelic_card_get_next_tx_descr(struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
if (!card->tx_chain.head)
|
|
|
|
return NULL;
|
2007-08-31 17:22:32 +04:00
|
|
|
/* see if the next descriptor is free */
|
2007-07-05 15:11:16 +04:00
|
|
|
if (card->tx_chain.tail != card->tx_chain.head->next &&
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_get_status(card->tx_chain.head) ==
|
|
|
|
GELIC_DESCR_DMA_NOT_IN_USE)
|
2007-07-05 15:11:16 +04:00
|
|
|
return card->tx_chain.head;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:42 +03:00
|
|
|
* gelic_net_set_txdescr_cmdstat - sets the tx descriptor command field
|
2007-07-05 15:11:16 +04:00
|
|
|
* @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.
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static void gelic_descr_set_tx_cmdstat(struct gelic_descr *descr,
|
|
|
|
struct sk_buff *skb)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
if (skb->ip_summed != CHECKSUM_PARTIAL)
|
2008-02-07 13:57:54 +03:00
|
|
|
descr->dmac_cmd_status =
|
2008-02-07 13:58:08 +03:00
|
|
|
cpu_to_be32(GELIC_DESCR_DMA_CMD_NO_CHKSUM |
|
|
|
|
GELIC_DESCR_TX_DMA_FRAME_TAIL);
|
2007-07-05 15:11:16 +04:00
|
|
|
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 =
|
2008-02-07 13:58:08 +03:00
|
|
|
cpu_to_be32(GELIC_DESCR_DMA_CMD_TCP_CHKSUM |
|
|
|
|
GELIC_DESCR_TX_DMA_FRAME_TAIL);
|
2007-08-31 17:22:32 +04:00
|
|
|
|
2007-07-05 15:11:16 +04:00
|
|
|
else if (ip_hdr(skb)->protocol == IPPROTO_UDP)
|
|
|
|
descr->dmac_cmd_status =
|
2008-02-07 13:58:08 +03:00
|
|
|
cpu_to_be32(GELIC_DESCR_DMA_CMD_UDP_CHKSUM |
|
|
|
|
GELIC_DESCR_TX_DMA_FRAME_TAIL);
|
2007-07-05 15:11:16 +04:00
|
|
|
else /*
|
|
|
|
* the stack should checksum non-tcp and non-udp
|
|
|
|
* packets on his own: NETIF_F_IP_CSUM
|
|
|
|
*/
|
|
|
|
descr->dmac_cmd_status =
|
2008-02-07 13:58:08 +03:00
|
|
|
cpu_to_be32(GELIC_DESCR_DMA_CMD_NO_CHKSUM |
|
|
|
|
GELIC_DESCR_TX_DMA_FRAME_TAIL);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-31 17:22:32 +04:00
|
|
|
static inline struct sk_buff *gelic_put_vlan_tag(struct sk_buff *skb,
|
|
|
|
unsigned short tag)
|
|
|
|
{
|
|
|
|
struct vlan_ethhdr *veth;
|
|
|
|
static unsigned int c;
|
|
|
|
|
|
|
|
if (skb_headroom(skb) < VLAN_HLEN) {
|
|
|
|
struct sk_buff *sk_tmp = skb;
|
|
|
|
pr_debug("%s: hd=%d c=%ud\n", __func__, skb_headroom(skb), c);
|
|
|
|
skb = skb_realloc_headroom(sk_tmp, VLAN_HLEN);
|
|
|
|
if (!skb)
|
|
|
|
return NULL;
|
|
|
|
dev_kfree_skb_any(sk_tmp);
|
|
|
|
}
|
|
|
|
veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN);
|
|
|
|
|
|
|
|
/* Move the mac addresses to the top of buffer */
|
|
|
|
memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN);
|
|
|
|
|
2009-02-01 11:45:17 +03:00
|
|
|
veth->h_vlan_proto = cpu_to_be16(ETH_P_8021Q);
|
2007-08-31 17:22:32 +04:00
|
|
|
veth->h_vlan_TCI = htons(tag);
|
|
|
|
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
2007-07-05 15:11:16 +04:00
|
|
|
/**
|
2008-02-07 13:58:42 +03:00
|
|
|
* gelic_descr_prepare_tx - setup a descriptor for sending packets
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
* @descr: descriptor structure
|
|
|
|
* @skb: packet to use
|
|
|
|
*
|
|
|
|
* returns 0 on success, <0 on failure.
|
|
|
|
*
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static int gelic_descr_prepare_tx(struct gelic_card *card,
|
|
|
|
struct gelic_descr *descr,
|
|
|
|
struct sk_buff *skb)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2007-08-31 17:22:32 +04:00
|
|
|
dma_addr_t buf;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
if (card->vlan_required) {
|
2007-08-31 17:22:32 +04:00
|
|
|
struct sk_buff *skb_tmp;
|
2008-02-07 13:58:42 +03:00
|
|
|
enum gelic_port_type type;
|
|
|
|
|
|
|
|
type = netdev_port(skb->dev)->type;
|
2007-08-31 17:22:32 +04:00
|
|
|
skb_tmp = gelic_put_vlan_tag(skb,
|
2008-02-07 13:58:42 +03:00
|
|
|
card->vlan[type].tx);
|
2007-08-31 17:22:32 +04:00
|
|
|
if (!skb_tmp)
|
|
|
|
return -ENOMEM;
|
|
|
|
skb = skb_tmp;
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
|
2007-08-31 17:22:32 +04:00
|
|
|
buf = dma_map_single(ctodev(card), skb->data, skb->len, DMA_TO_DEVICE);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2007-08-31 17:22:32 +04:00
|
|
|
if (!buf) {
|
2007-07-05 15:11:16 +04:00
|
|
|
dev_err(ctodev(card),
|
|
|
|
"dma map 2 failed (%p, %i). Dropping packet\n",
|
2007-08-31 17:22:32 +04:00
|
|
|
skb->data, skb->len);
|
2007-07-05 15:11:16 +04:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:57:54 +03:00
|
|
|
descr->buf_addr = cpu_to_be32(buf);
|
|
|
|
descr->buf_size = cpu_to_be32(skb->len);
|
2007-08-31 17:22:32 +04:00
|
|
|
descr->skb = skb;
|
2007-07-05 15:11:16 +04:00
|
|
|
descr->data_status = 0;
|
2007-08-31 17:22:32 +04:00
|
|
|
descr->next_descr_addr = 0; /* terminate hw descr */
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_set_tx_cmdstat(descr, skb);
|
2007-07-20 12:24:56 +04:00
|
|
|
|
|
|
|
/* bump free descriptor pointer */
|
2007-08-31 17:22:32 +04:00
|
|
|
card->tx_chain.head = descr->next;
|
2007-07-05 15:11:16 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_kick_txdma - enables TX DMA processing
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
* @descr: descriptor address to enable TX processing at
|
|
|
|
*
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static int gelic_card_kick_txdma(struct gelic_card *card,
|
|
|
|
struct gelic_descr *descr)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2007-07-20 12:24:56 +04:00
|
|
|
int status = 0;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
if (card->tx_dma_progress)
|
|
|
|
return 0;
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
if (gelic_descr_get_status(descr) == GELIC_DESCR_DMA_CARDOWNED) {
|
2007-07-05 15:11:16 +04:00
|
|
|
card->tx_dma_progress = 1;
|
2007-08-31 17:25:09 +04:00
|
|
|
status = lv1_net_start_tx_dma(bus_id(card), dev_id(card),
|
|
|
|
descr->bus_addr, 0);
|
|
|
|
if (status)
|
2007-07-05 15:11:16 +04:00
|
|
|
dev_info(ctodev(card), "lv1_net_start_txdma failed," \
|
2007-08-31 17:25:09 +04:00
|
|
|
"status=%d\n", status);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
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
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card = netdev_card(netdev);
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_descr *descr;
|
2007-07-05 15:11:16 +04:00
|
|
|
int result;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
spin_lock_irqsave(&card->tx_lock, flags);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_card_release_tx_chain(card, 0);
|
2007-07-20 12:24:56 +04:00
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
descr = gelic_card_get_next_tx_descr(card);
|
2007-07-05 15:11:16 +04:00
|
|
|
if (!descr) {
|
2007-07-20 12:24:56 +04:00
|
|
|
/*
|
|
|
|
* no more descriptors free
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
gelic_card_stop_queues(card);
|
|
|
|
spin_unlock_irqrestore(&card->tx_lock, flags);
|
2007-07-05 15:11:16 +04:00
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
result = gelic_descr_prepare_tx(card, descr, skb);
|
2007-07-20 12:24:56 +04:00
|
|
|
if (result) {
|
|
|
|
/*
|
|
|
|
* DMA map failed. As chanses are that failure
|
|
|
|
* would continue, just release skb and return
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
netdev->stats.tx_dropped++;
|
2007-07-20 12:24:56 +04:00
|
|
|
dev_kfree_skb_any(skb);
|
2008-02-07 13:58:42 +03:00
|
|
|
spin_unlock_irqrestore(&card->tx_lock, flags);
|
2007-07-20 12:24:56 +04:00
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* link this prepared descriptor to previous one
|
|
|
|
* to achieve high performance
|
|
|
|
*/
|
2008-02-07 13:57:54 +03:00
|
|
|
descr->prev->next_descr_addr = cpu_to_be32(descr->bus_addr);
|
2007-07-05 15:11:16 +04:00
|
|
|
/*
|
|
|
|
* as hardware descriptor is modified in the above lines,
|
|
|
|
* ensure that the hardware sees it
|
|
|
|
*/
|
|
|
|
wmb();
|
2008-02-07 13:58:08 +03:00
|
|
|
if (gelic_card_kick_txdma(card, descr)) {
|
2007-07-20 12:24:56 +04:00
|
|
|
/*
|
|
|
|
* kick failed.
|
|
|
|
* release descriptors which were just prepared
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
netdev->stats.tx_dropped++;
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_release_tx(card, descr);
|
|
|
|
gelic_descr_release_tx(card, descr->next);
|
2007-07-20 12:24:56 +04:00
|
|
|
card->tx_chain.tail = descr->next->next;
|
|
|
|
dev_info(ctodev(card), "%s: kick failure\n", __func__);
|
|
|
|
}
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
spin_unlock_irqrestore(&card->tx_lock, flags);
|
2007-07-05 15:11:16 +04:00
|
|
|
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
|
2008-02-07 13:58:42 +03:00
|
|
|
* @netdev: net_device structure to be passed packet
|
2007-07-05 15:11:16 +04:00
|
|
|
*
|
|
|
|
* iommu-unmaps the skb, fills out skb structure and passes the data to the
|
|
|
|
* stack. The descriptor state is not changed.
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static void gelic_net_pass_skb_up(struct gelic_descr *descr,
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card,
|
|
|
|
struct net_device *netdev)
|
|
|
|
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
struct sk_buff *skb = descr->skb;
|
2007-07-05 15:11:16 +04:00
|
|
|
u32 data_status, data_error;
|
|
|
|
|
2008-02-07 13:57:54 +03:00
|
|
|
data_status = be32_to_cpu(descr->data_status);
|
|
|
|
data_error = be32_to_cpu(descr->data_error);
|
2007-07-05 15:11:16 +04:00
|
|
|
/* unmap skb buffer */
|
2008-02-07 13:58:42 +03:00
|
|
|
dma_unmap_single(ctodev(card), be32_to_cpu(descr->buf_addr),
|
|
|
|
GELIC_NET_MAX_MTU,
|
2007-07-05 15:11:16 +04:00
|
|
|
DMA_FROM_DEVICE);
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
skb_put(skb, be32_to_cpu(descr->valid_size)?
|
2008-02-07 13:57:54 +03:00
|
|
|
be32_to_cpu(descr->valid_size) :
|
|
|
|
be32_to_cpu(descr->result_size));
|
2007-07-05 15:11:16 +04:00
|
|
|
if (!descr->valid_size)
|
|
|
|
dev_info(ctodev(card), "buffer full %x %x %x\n",
|
2008-02-07 13:57:54 +03:00
|
|
|
be32_to_cpu(descr->result_size),
|
|
|
|
be32_to_cpu(descr->buf_size),
|
|
|
|
be32_to_cpu(descr->dmac_cmd_status));
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
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) {
|
2008-02-07 13:58:08 +03:00
|
|
|
if ((data_status & GELIC_DESCR_DATA_STATUS_CHK_MASK) &&
|
|
|
|
(!(data_error & GELIC_DESCR_DATA_ERROR_CHK_MASK)))
|
2007-07-05 15:11:16 +04:00
|
|
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
|
|
else
|
2010-09-03 00:07:41 +04:00
|
|
|
skb_checksum_none_assert(skb);
|
2007-07-05 15:11:16 +04:00
|
|
|
} else
|
2010-09-03 00:07:41 +04:00
|
|
|
skb_checksum_none_assert(skb);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
/* update netdevice statistics */
|
2008-02-07 13:58:42 +03:00
|
|
|
netdev->stats.rx_packets++;
|
|
|
|
netdev->stats.rx_bytes += skb->len;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
/* pass skb up to stack */
|
|
|
|
netif_receive_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_decode_one_descr - processes an rx descriptor
|
2007-07-05 15:11:16 +04:00
|
|
|
* @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
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static int gelic_card_decode_one_descr(struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
enum gelic_descr_dma_status status;
|
|
|
|
struct gelic_descr_chain *chain = &card->rx_chain;
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_descr *descr = chain->head;
|
|
|
|
struct net_device *netdev = NULL;
|
2007-07-05 15:11:16 +04:00
|
|
|
int dmac_chain_ended;
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
status = gelic_descr_get_status(descr);
|
2007-07-05 15:11:16 +04:00
|
|
|
/* is this descriptor terminated with next_descr == NULL? */
|
|
|
|
dmac_chain_ended =
|
2008-02-07 13:57:54 +03:00
|
|
|
be32_to_cpu(descr->dmac_cmd_status) &
|
2008-02-07 13:58:08 +03:00
|
|
|
GELIC_DESCR_RX_DMA_CHAIN_END;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
if (status == GELIC_DESCR_DMA_CARDOWNED)
|
2007-07-05 15:11:16 +04:00
|
|
|
return 0;
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
if (status == GELIC_DESCR_DMA_NOT_IN_USE) {
|
2007-07-05 15:11:16 +04:00
|
|
|
dev_dbg(ctodev(card), "dormant descr? %p\n", descr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
/* 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
|
2009-12-01 15:15:53 +03:00
|
|
|
netdev = card->netdev[GELIC_PORT_ETHERNET_0];
|
2008-02-07 13:58:42 +03:00
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
if ((status == GELIC_DESCR_DMA_RESPONSE_ERROR) ||
|
|
|
|
(status == GELIC_DESCR_DMA_PROTECTION_ERROR) ||
|
|
|
|
(status == GELIC_DESCR_DMA_FORCE_END)) {
|
2007-07-05 15:11:16 +04:00
|
|
|
dev_info(ctodev(card), "dropping RX descriptor with state %x\n",
|
|
|
|
status);
|
2008-02-07 13:58:42 +03:00
|
|
|
netdev->stats.rx_dropped++;
|
2007-07-05 15:11:16 +04:00
|
|
|
goto refill;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
if (status == GELIC_DESCR_DMA_BUFFER_FULL) {
|
2007-07-20 12:39:25 +04:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* descriptoers any other than FRAME_END here should
|
|
|
|
* be treated as error.
|
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
if (status != GELIC_DESCR_DMA_FRAME_END) {
|
2007-07-05 15:11:16 +04:00
|
|
|
dev_dbg(ctodev(card), "RX descriptor with state %x\n",
|
|
|
|
status);
|
|
|
|
goto refill;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ok, we've got a packet in descr */
|
2008-02-07 13:58:42 +03:00
|
|
|
gelic_net_pass_skb_up(descr, card, netdev);
|
2007-07-05 15:11:16 +04:00
|
|
|
refill:
|
2007-07-20 12:39:25 +04:00
|
|
|
/*
|
|
|
|
* So that always DMAC can see the end
|
|
|
|
* of the descriptor chain to avoid
|
|
|
|
* from unwanted DMAC overrun.
|
|
|
|
*/
|
|
|
|
descr->next_descr_addr = 0;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
/* change the descriptor state: */
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2007-07-20 12:39:25 +04:00
|
|
|
/*
|
|
|
|
* this call can fail, but for now, just leave this
|
|
|
|
* decriptor without skb
|
2007-07-05 15:11:16 +04:00
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_descr_prepare_rx(card, descr);
|
2007-07-20 12:39:25 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
chain->tail = descr;
|
|
|
|
chain->head = descr->next;
|
2007-07-20 12:39:25 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set this descriptor the end of the chain.
|
|
|
|
*/
|
2008-02-07 13:57:54 +03:00
|
|
|
descr->prev->next_descr_addr = cpu_to_be32(descr->bus_addr);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2007-07-20 12:39:25 +04:00
|
|
|
/*
|
|
|
|
* If dmac chain was met, DMAC stopped.
|
|
|
|
* thus re-enable it
|
|
|
|
*/
|
2007-07-05 15:11:16 +04:00
|
|
|
if (dmac_chain_ended) {
|
2007-07-20 12:35:54 +04:00
|
|
|
card->rx_dma_restart_required = 1;
|
|
|
|
dev_dbg(ctodev(card), "reenable rx dma scheduled\n");
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gelic_net_poll - NAPI poll function called by the stack to return packets
|
2008-02-07 13:58:42 +03:00
|
|
|
* @napi: napi structure
|
2007-07-05 15:11:16 +04:00
|
|
|
* @budget: number of packets we can pass to the stack at most
|
|
|
|
*
|
2008-02-07 13:58:42 +03:00
|
|
|
* returns the number of the processed packets
|
2007-07-05 15:11:16 +04:00
|
|
|
*
|
|
|
|
*/
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 03:41:36 +04:00
|
|
|
static int gelic_net_poll(struct napi_struct *napi, int budget)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_card *card = container_of(napi, struct gelic_card, napi);
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 03:41:36 +04:00
|
|
|
int packets_done = 0;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 03:41:36 +04:00
|
|
|
while (packets_done < budget) {
|
2008-02-07 13:58:08 +03:00
|
|
|
if (!gelic_card_decode_one_descr(card))
|
2007-07-05 15:11:16 +04:00
|
|
|
break;
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 03:41:36 +04:00
|
|
|
|
|
|
|
packets_done++;
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 03:41:36 +04:00
|
|
|
|
|
|
|
if (packets_done < budget) {
|
2008-02-07 13:58:42 +03:00
|
|
|
napi_complete(napi);
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_card_rx_irq_on(card);
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 03:41:36 +04:00
|
|
|
}
|
|
|
|
return packets_done;
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
int gelic_net_change_mtu(struct net_device *netdev, int new_mtu)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_card_interrupt - event handler for gelic_net
|
2007-07-05 15:11:16 +04:00
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
static irqreturn_t gelic_card_interrupt(int irq, void *ptr)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card = ptr;
|
2007-07-05 15:11:16 +04:00
|
|
|
u64 status;
|
|
|
|
|
|
|
|
status = card->irq_status;
|
|
|
|
|
|
|
|
if (!status)
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
status &= card->irq_mask;
|
|
|
|
|
2007-07-20 12:35:54 +04:00
|
|
|
if (card->rx_dma_restart_required) {
|
|
|
|
card->rx_dma_restart_required = 0;
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_card_enable_rxdmac(card);
|
2007-07-20 12:35:54 +04:00
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
if (status & GELIC_CARD_RXINT) {
|
|
|
|
gelic_card_rx_irq_off(card);
|
2008-02-07 13:58:42 +03:00
|
|
|
napi_schedule(&card->napi);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
if (status & GELIC_CARD_TXINT) {
|
2008-02-07 13:58:42 +03:00
|
|
|
spin_lock_irqsave(&card->tx_lock, flags);
|
2007-07-05 15:11:16 +04:00
|
|
|
card->tx_dma_progress = 0;
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_card_release_tx_chain(card, 0);
|
2007-07-20 12:24:56 +04:00
|
|
|
/* kick outstanding tx descriptor if any */
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_card_kick_txdma(card, card->tx_chain.tail);
|
2008-02-07 13:58:42 +03:00
|
|
|
spin_unlock_irqrestore(&card->tx_lock, flags);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
2008-02-07 13:58:32 +03:00
|
|
|
|
|
|
|
/* ether port status changed */
|
|
|
|
if (status & GELIC_CARD_PORT_STATUS_CHANGED)
|
|
|
|
gelic_card_get_ether_port_status(card, 1);
|
2008-02-07 13:58:42 +03:00
|
|
|
|
2008-02-07 13:58:57 +03:00
|
|
|
#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
|
|
|
|
|
2007-07-05 15:11:16 +04:00
|
|
|
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
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
void gelic_net_poll_controller(struct net_device *netdev)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card = netdev_card(netdev);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
gelic_card_set_irq_mask(card, 0);
|
|
|
|
gelic_card_interrupt(netdev->irq, netdev);
|
2008-02-07 13:58:42 +03:00
|
|
|
gelic_card_set_irq_mask(card, card->irq_mask);
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_POLL_CONTROLLER */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
int gelic_net_open(struct net_device *netdev)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card = netdev_card(netdev);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
dev_dbg(ctodev(card), " -> %s %p\n", __func__, netdev);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
gelic_card_up(card);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
netif_start_queue(netdev);
|
2008-02-07 13:58:32 +03:00
|
|
|
gelic_card_get_ether_port_status(card, 1);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
dev_dbg(ctodev(card), " <- %s\n", __func__);
|
2007-07-05 15:11:16 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
void gelic_net_get_drvinfo(struct net_device *netdev,
|
|
|
|
struct ethtool_drvinfo *info)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1);
|
|
|
|
strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1);
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
static int gelic_ether_get_settings(struct net_device *netdev,
|
|
|
|
struct ethtool_cmd *cmd)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card = netdev_card(netdev);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:32 +03:00
|
|
|
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:
|
|
|
|
cmd->speed = SPEED_10;
|
|
|
|
break;
|
|
|
|
case GELIC_LV1_ETHER_SPEED_100:
|
|
|
|
cmd->speed = SPEED_100;
|
|
|
|
break;
|
|
|
|
case GELIC_LV1_ETHER_SPEED_1000:
|
|
|
|
cmd->speed = SPEED_1000;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pr_info("%s: speed unknown\n", __func__);
|
|
|
|
cmd->speed = SPEED_10;
|
|
|
|
break;
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
2008-02-07 13:58:32 +03:00
|
|
|
|
2007-07-05 15:11:16 +04:00
|
|
|
cmd->supported = SUPPORTED_TP | SUPPORTED_Autoneg |
|
|
|
|
SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
|
|
|
|
SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
|
2009-12-01 15:15:58 +03:00
|
|
|
SUPPORTED_1000baseT_Full;
|
2007-07-05 15:11:16 +04:00
|
|
|
cmd->advertising = cmd->supported;
|
2009-12-01 15:15:58 +03:00
|
|
|
if (card->link_mode & GELIC_LV1_ETHER_AUTO_NEG) {
|
|
|
|
cmd->autoneg = AUTONEG_ENABLE;
|
|
|
|
} else {
|
|
|
|
cmd->autoneg = AUTONEG_DISABLE;
|
|
|
|
cmd->advertising &= ~ADVERTISED_Autoneg;
|
|
|
|
}
|
2007-07-05 15:11:16 +04:00
|
|
|
cmd->port = PORT_TP;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-12-01 15:15:58 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
u32 gelic_net_get_rx_csum(struct net_device *netdev)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card = netdev_card(netdev);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
return card->rx_csum;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
int gelic_net_set_rx_csum(struct net_device *netdev, u32 data)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card = netdev_card(netdev);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
card->rx_csum = data;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-03-27 03:39:31 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-09-02 12:03:33 +04:00
|
|
|
static const struct ethtool_ops gelic_ether_ethtool_ops = {
|
2007-07-05 15:11:16 +04:00
|
|
|
.get_drvinfo = gelic_net_get_drvinfo,
|
2008-02-07 13:58:08 +03:00
|
|
|
.get_settings = gelic_ether_get_settings,
|
2009-12-01 15:15:58 +03:00
|
|
|
.set_settings = gelic_ether_set_settings,
|
2008-02-07 13:58:20 +03:00
|
|
|
.get_link = ethtool_op_get_link,
|
|
|
|
.get_tx_csum = ethtool_op_get_tx_csum,
|
|
|
|
.set_tx_csum = ethtool_op_set_tx_csum,
|
2007-07-05 15:11:16 +04:00
|
|
|
.get_rx_csum = gelic_net_get_rx_csum,
|
|
|
|
.set_rx_csum = gelic_net_set_rx_csum,
|
2008-03-27 03:39:31 +03:00
|
|
|
.get_wol = gelic_net_get_wol,
|
|
|
|
.set_wol = gelic_net_set_wol,
|
2007-07-05 15:11:16 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_card *card =
|
|
|
|
container_of(work, struct gelic_card, tx_timeout_task);
|
2009-12-01 15:15:53 +03:00
|
|
|
struct net_device *netdev = card->netdev[GELIC_PORT_ETHERNET_0];
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2010-03-24 10:57:31 +03:00
|
|
|
dev_info(ctodev(card), "%s:Timed out. Restarting...\n", __func__);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
void gelic_net_tx_timeout(struct net_device *netdev)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_card *card;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
card = netdev_card(netdev);
|
2007-07-05 15:11:16 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-01-16 01:47:29 +03:00
|
|
|
static const struct net_device_ops gelic_netdevice_ops = {
|
|
|
|
.ndo_open = gelic_net_open,
|
|
|
|
.ndo_stop = gelic_net_stop,
|
|
|
|
.ndo_start_xmit = gelic_net_xmit,
|
|
|
|
.ndo_set_multicast_list = gelic_net_set_multi,
|
|
|
|
.ndo_change_mtu = gelic_net_change_mtu,
|
|
|
|
.ndo_tx_timeout = gelic_net_tx_timeout,
|
2009-07-09 21:54:35 +04:00
|
|
|
.ndo_set_mac_address = eth_mac_addr,
|
2009-01-16 01:47:29 +03:00
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
.ndo_poll_controller = gelic_net_poll_controller,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2007-07-05 15:11:16 +04:00
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_ether_setup_netdev_ops - initialization of net_device operations
|
2007-07-05 15:11:16 +04:00
|
|
|
* @netdev: net_device structure
|
|
|
|
*
|
|
|
|
* fills out function pointers in the net_device structure
|
|
|
|
*/
|
2009-06-10 08:38:58 +04:00
|
|
|
static void __devinit gelic_ether_setup_netdev_ops(struct net_device *netdev,
|
|
|
|
struct napi_struct *napi)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT;
|
2008-02-07 13:58:42 +03:00
|
|
|
/* NAPI */
|
|
|
|
netif_napi_add(netdev, napi,
|
|
|
|
gelic_net_poll, GELIC_NET_NAPI_WEIGHT);
|
|
|
|
netdev->ethtool_ops = &gelic_ether_ethtool_ops;
|
2009-01-16 01:47:29 +03:00
|
|
|
netdev->netdev_ops = &gelic_netdevice_ops;
|
2007-07-05 15:11:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:42 +03:00
|
|
|
* gelic_ether_setup_netdev - initialization of net_device
|
|
|
|
* @netdev: net_device structure
|
2007-07-05 15:11:16 +04:00
|
|
|
* @card: card structure
|
|
|
|
*
|
|
|
|
* Returns 0 on success or <0 on failure
|
|
|
|
*
|
2008-02-07 13:58:42 +03:00
|
|
|
* gelic_ether_setup_netdev initializes the net_device structure
|
|
|
|
* and register it.
|
2007-07-05 15:11:16 +04:00
|
|
|
**/
|
2009-06-10 08:38:58 +04:00
|
|
|
int __devinit gelic_net_setup_netdev(struct net_device *netdev,
|
|
|
|
struct gelic_card *card)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
u64 v1, v2;
|
|
|
|
|
|
|
|
netdev->features = NETIF_F_IP_CSUM;
|
|
|
|
|
|
|
|
status = lv1_net_control(bus_id(card), dev_id(card),
|
2008-02-07 13:58:08 +03:00
|
|
|
GELIC_LV1_GET_MAC_ADDRESS,
|
2007-07-05 15:11:16 +04:00
|
|
|
0, 0, 0, &v1, &v2);
|
2008-02-07 13:58:42 +03:00
|
|
|
v1 <<= 16;
|
2007-07-05 15:11:16 +04:00
|
|
|
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;
|
|
|
|
}
|
2008-02-07 13:58:42 +03:00
|
|
|
memcpy(netdev->dev_addr, &v1, ETH_ALEN);
|
2007-08-31 17:22:32 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
if (card->vlan_required) {
|
2007-08-31 17:22:32 +04:00
|
|
|
netdev->hard_header_len += VLAN_HLEN;
|
2008-02-07 13:58:42 +03:00
|
|
|
/*
|
|
|
|
* As vlan is internally used,
|
|
|
|
* we can not receive vlan packets
|
|
|
|
*/
|
|
|
|
netdev->features |= NETIF_F_VLAN_CHALLENGED;
|
2007-08-31 17:22:32 +04:00
|
|
|
}
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
status = register_netdev(netdev);
|
|
|
|
if (status) {
|
2008-02-07 13:58:42 +03:00
|
|
|
dev_err(ctodev(card), "%s:Couldn't register %s %d\n",
|
|
|
|
__func__, netdev->name, status);
|
2007-07-05 15:11:16 +04:00
|
|
|
return status;
|
|
|
|
}
|
2008-10-28 01:59:26 +03:00
|
|
|
dev_info(ctodev(card), "%s: MAC addr %pM\n",
|
|
|
|
netdev->name, netdev->dev_addr);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-02-07 13:58:08 +03:00
|
|
|
* gelic_alloc_card_net - allocates net_device and card structure
|
2007-07-05 15:11:16 +04:00
|
|
|
*
|
|
|
|
* returns the card structure or NULL in case of errors
|
|
|
|
*
|
|
|
|
* the card and net_device structures are linked to each other
|
|
|
|
*/
|
2008-02-07 13:58:42 +03:00
|
|
|
#define GELIC_ALIGN (32)
|
2009-06-10 08:38:58 +04:00
|
|
|
static struct gelic_card * __devinit gelic_alloc_card_net(struct net_device **netdev)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:08 +03:00
|
|
|
struct gelic_card *card;
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_port *port;
|
|
|
|
void *p;
|
2007-07-05 15:11:16 +04:00
|
|
|
size_t alloc_size;
|
|
|
|
/*
|
2008-02-07 13:58:42 +03:00
|
|
|
* gelic requires dma descriptor is 32 bytes aligned and
|
|
|
|
* the hypervisor requires irq_status is 8 bytes aligned.
|
2007-07-05 15:11:16 +04:00
|
|
|
*/
|
2008-02-07 13:58:08 +03:00
|
|
|
BUILD_BUG_ON(offsetof(struct gelic_card, irq_status) % 8);
|
|
|
|
BUILD_BUG_ON(offsetof(struct gelic_card, descr) % 32);
|
2008-02-07 13:58:42 +03:00
|
|
|
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;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
p = kzalloc(alloc_size, GFP_KERNEL);
|
|
|
|
if (!p)
|
2007-07-05 15:11:16 +04:00
|
|
|
return NULL;
|
2008-02-07 13:58:42 +03:00
|
|
|
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;
|
2009-12-01 15:15:53 +03:00
|
|
|
port->type = GELIC_PORT_ETHERNET_0;
|
2008-02-07 13:58:42 +03:00
|
|
|
|
|
|
|
/* gelic_card */
|
2009-12-01 15:15:53 +03:00
|
|
|
card->netdev[GELIC_PORT_ETHERNET_0] = *netdev;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
INIT_WORK(&card->tx_timeout_task, gelic_net_tx_timeout_task);
|
|
|
|
init_waitqueue_head(&card->waitq);
|
|
|
|
atomic_set(&card->tx_timeout_task_counter, 0);
|
2008-05-22 11:00:03 +04:00
|
|
|
mutex_init(&card->updown_lock);
|
2008-02-07 13:58:42 +03:00
|
|
|
atomic_set(&card->users, 0);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
return card;
|
|
|
|
}
|
|
|
|
|
2009-06-10 08:38:58 +04:00
|
|
|
static void __devinit gelic_card_get_vlan_info(struct gelic_card *card)
|
2008-02-07 13:58:42 +03:00
|
|
|
{
|
|
|
|
u64 v1, v2;
|
|
|
|
int status;
|
|
|
|
unsigned int i;
|
|
|
|
struct {
|
|
|
|
int tx;
|
|
|
|
int rx;
|
|
|
|
} vlan_id_ix[2] = {
|
2009-12-01 15:15:53 +03:00
|
|
|
[GELIC_PORT_ETHERNET_0] = {
|
|
|
|
.tx = GELIC_LV1_VLAN_TX_ETHERNET_0,
|
|
|
|
.rx = GELIC_LV1_VLAN_RX_ETHERNET_0
|
2008-02-07 13:58:42 +03:00
|
|
|
},
|
|
|
|
[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);
|
|
|
|
}
|
|
|
|
|
2009-12-01 15:15:53 +03:00
|
|
|
if (card->vlan[GELIC_PORT_ETHERNET_0].tx) {
|
2008-02-07 13:58:42 +03:00
|
|
|
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");
|
|
|
|
}
|
2007-07-05 15:11:16 +04:00
|
|
|
/**
|
|
|
|
* ps3_gelic_driver_probe - add a device to the control of this driver
|
|
|
|
*/
|
2009-06-10 08:38:58 +04:00
|
|
|
static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2008-02-07 13:58:42 +03:00
|
|
|
struct gelic_card *card;
|
|
|
|
struct net_device *netdev;
|
2007-07-05 15:11:16 +04:00
|
|
|
int result;
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
pr_debug("%s: called\n", __func__);
|
2007-07-05 15:11:16 +04:00
|
|
|
result = ps3_open_hv_device(dev);
|
|
|
|
|
|
|
|
if (result) {
|
2008-02-07 13:58:42 +03:00
|
|
|
dev_dbg(&dev->core, "%s:ps3_open_hv_device failed\n",
|
|
|
|
__func__);
|
2007-07-05 15:11:16 +04:00
|
|
|
goto fail_open;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = ps3_dma_region_create(dev->d_region);
|
|
|
|
|
|
|
|
if (result) {
|
2008-02-07 13:58:42 +03:00
|
|
|
dev_dbg(&dev->core, "%s:ps3_dma_region_create failed(%d)\n",
|
|
|
|
__func__, result);
|
2007-07-05 15:11:16 +04:00
|
|
|
BUG_ON("check region type");
|
|
|
|
goto fail_dma_region;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
/* 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;
|
|
|
|
}
|
2009-06-10 08:38:54 +04:00
|
|
|
ps3_system_bus_set_drvdata(dev, card);
|
2008-02-07 13:58:42 +03:00
|
|
|
card->dev = dev;
|
|
|
|
|
|
|
|
/* get internal vlan info */
|
|
|
|
gelic_card_get_vlan_info(card);
|
|
|
|
|
2009-12-01 15:15:58 +03:00
|
|
|
card->link_mode = GELIC_LV1_ETHER_AUTO_NEG;
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
/* setup interrupt */
|
2007-07-05 15:11:16 +04:00
|
|
|
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,
|
2008-02-07 13:58:42 +03:00
|
|
|
"%s:set_interrupt_status_indicator failed: %s\n",
|
|
|
|
__func__, ps3_result(result));
|
2007-07-05 15:11:16 +04:00
|
|
|
result = -EIO;
|
|
|
|
goto fail_status_indicator;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
result = ps3_sb_event_receive_port_setup(dev, PS3_BINDING_CPU_ANY,
|
|
|
|
&card->irq);
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
dev_info(ctodev(card),
|
|
|
|
"%s:gelic_net_open_device failed (%d)\n",
|
|
|
|
__func__, result);
|
|
|
|
result = -EPERM;
|
|
|
|
goto fail_alloc_irq;
|
|
|
|
}
|
|
|
|
result = request_irq(card->irq, gelic_card_interrupt,
|
|
|
|
IRQF_DISABLED, netdev->name, card);
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
dev_info(ctodev(card), "%s:request_irq failed (%d)\n",
|
|
|
|
__func__, result);
|
|
|
|
goto fail_request_irq;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* setup card structure */
|
|
|
|
card->irq_mask = GELIC_CARD_RXINT | GELIC_CARD_TXINT |
|
|
|
|
GELIC_CARD_PORT_STATUS_CHANGED;
|
|
|
|
card->rx_csum = GELIC_CARD_RX_CSUM_DEFAULT;
|
|
|
|
|
|
|
|
|
|
|
|
if (gelic_card_init_chain(card, &card->tx_chain,
|
|
|
|
card->descr, GELIC_NET_TX_DESCRIPTORS))
|
|
|
|
goto fail_alloc_tx;
|
|
|
|
if (gelic_card_init_chain(card, &card->rx_chain,
|
|
|
|
card->descr + GELIC_NET_TX_DESCRIPTORS,
|
|
|
|
GELIC_NET_RX_DESCRIPTORS))
|
|
|
|
goto fail_alloc_rx;
|
|
|
|
|
|
|
|
/* head of chain */
|
|
|
|
card->tx_top = card->tx_chain.head;
|
|
|
|
card->rx_top = card->rx_chain.head;
|
|
|
|
dev_dbg(ctodev(card), "descr rx %p, tx %p, size %#lx, num %#x\n",
|
|
|
|
card->rx_top, card->tx_top, sizeof(struct gelic_descr),
|
|
|
|
GELIC_NET_RX_DESCRIPTORS);
|
|
|
|
/* allocate rx skbs */
|
|
|
|
if (gelic_card_alloc_rx_skbs(card))
|
|
|
|
goto fail_alloc_skbs;
|
|
|
|
|
|
|
|
spin_lock_init(&card->tx_lock);
|
|
|
|
card->tx_dma_progress = 0;
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
/* 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);
|
2007-07-05 15:11:16 +04:00
|
|
|
if (result) {
|
2008-02-07 13:58:42 +03:00
|
|
|
dev_dbg(&dev->core, "%s: setup_netdev failed %d",
|
|
|
|
__func__, result);
|
2007-07-05 15:11:16 +04:00
|
|
|
goto fail_setup_netdev;
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:57 +03:00
|
|
|
#ifdef CONFIG_GELIC_WIRELESS
|
|
|
|
if (gelic_wl_driver_probe(card)) {
|
|
|
|
dev_dbg(&dev->core, "%s: WL init failed\n", __func__);
|
|
|
|
goto fail_setup_netdev;
|
|
|
|
}
|
|
|
|
#endif
|
2008-02-07 13:58:42 +03:00
|
|
|
pr_debug("%s: done\n", __func__);
|
2007-07-05 15:11:16 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail_setup_netdev:
|
2008-02-07 13:58:42 +03:00
|
|
|
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:
|
2007-07-05 15:11:16 +04:00
|
|
|
lv1_net_set_interrupt_status_indicator(bus_id(card),
|
2008-02-07 13:58:42 +03:00
|
|
|
bus_id(card),
|
|
|
|
0, 0);
|
2007-07-05 15:11:16 +04:00
|
|
|
fail_status_indicator:
|
2009-06-10 08:38:54 +04:00
|
|
|
ps3_system_bus_set_drvdata(dev, NULL);
|
2008-02-07 13:58:42 +03:00
|
|
|
kfree(netdev_card(netdev)->unalign);
|
|
|
|
free_netdev(netdev);
|
|
|
|
fail_alloc_card:
|
2007-07-05 15:11:16 +04:00
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev)
|
2007-07-05 15:11:16 +04:00
|
|
|
{
|
2009-06-10 08:38:54 +04:00
|
|
|
struct gelic_card *card = ps3_system_bus_get_drvdata(dev);
|
2008-02-07 13:58:42 +03:00
|
|
|
struct net_device *netdev0;
|
|
|
|
pr_debug("%s: called\n", __func__);
|
|
|
|
|
2009-12-01 15:15:58 +03:00
|
|
|
/* set auto-negotiation */
|
|
|
|
gelic_card_set_link_mode(card, GELIC_LV1_ETHER_AUTO_NEG);
|
|
|
|
|
2008-02-07 13:58:57 +03:00
|
|
|
#ifdef CONFIG_GELIC_WIRELESS
|
|
|
|
gelic_wl_driver_remove(card);
|
|
|
|
#endif
|
2008-02-07 13:58:42 +03:00
|
|
|
/* 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);
|
|
|
|
|
2009-12-01 15:15:53 +03:00
|
|
|
netdev0 = card->netdev[GELIC_PORT_ETHERNET_0];
|
2008-02-07 13:58:42 +03:00
|
|
|
/* disconnect event port */
|
|
|
|
free_irq(card->irq, card);
|
|
|
|
netdev0->irq = NO_IRQ;
|
|
|
|
ps3_sb_event_receive_port_destroy(card->dev, card->irq);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
unregister_netdev(netdev0);
|
|
|
|
kfree(netdev_card(netdev0)->unalign);
|
|
|
|
free_netdev(netdev0);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
2009-06-10 08:38:54 +04:00
|
|
|
ps3_system_bus_set_drvdata(dev, NULL);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
ps3_dma_region_free(dev->d_region);
|
|
|
|
|
|
|
|
ps3_close_hv_device(dev);
|
|
|
|
|
2008-02-07 13:58:42 +03:00
|
|
|
pr_debug("%s: done\n", __func__);
|
2007-07-05 15:11:16 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-02-07 13:58:08 +03:00
|
|
|
module_init(ps3_gelic_driver_init);
|
|
|
|
module_exit(ps3_gelic_driver_exit);
|
2007-07-05 15:11:16 +04:00
|
|
|
|
|
|
|
MODULE_ALIAS(PS3_MODULE_ALIAS_GELIC);
|
|
|
|
|