* Re: TESTING ROUTING CODE !!
From: Mulyadi Santosa @ 2009-07-16 18:34 UTC (permalink / raw)
To: Leena M.; +Cc: Kent Tu, kernelnewbies@nl.linux.org, netdev@vger.kernel.org
In-Reply-To: <e49baca60907161113s411e7d4ck472a9141881f56a1@mail.gmail.com>
On Fri, Jul 17, 2009 at 1:13 AM, Leena M.<kernmonk@gmail.com> wrote:
> Any specific VM do you recommend , How about Xen ?
>
> Regards,
> Leena
Please don't top post...
Since you talked about routing protocol, I think User Mode Linux would
do just fine. It runs completely in user space and can be easily
hooked with gdb.
--
regards,
Mulyadi Santosa
Freelance Linux trainer
blog: the-hydra.blogspot.com
^ permalink raw reply
* NULL pointer dereference in br_device_event, 2.6.31-rc3 stock kernel.
From: Ben Greear @ 2009-07-16 18:25 UTC (permalink / raw)
To: NetDev
I just saw this crash while shutting down the system. This was on a stock 2.6.31-rc3 kernel,
pulled yesterday afternoon.
Kernel config is here:
http://www.candelatech.com/oss/i7_config.txt
This is the same system that reports the disable_lro issue in the ixgbe driver,
but I don't think the problems are related. This is the first time I saw this,
and I've rebooted several times (though mostly using my hacked kernel), so seems
to be some sort of a race.
Jul 16 11:10:24 localhost acpid: exiting
Jul 16 11:10:25 localhost ntpd[1953]: ntpd exiting on signal 15
Jul 16 11:10:25 localhost kernel: Bridge firewalling registered
Jul 16 11:10:25 localhost kernel: BUG: unable to handle kernel NULL pointer dereference at 0000000000000160
Jul 16 11:10:25 localhost kernel: IP: [<ffffffffa0257666>] br_device_event+0xc6/0x124 [bridge]
Jul 16 11:10:25 localhost kernel: PGD 1b3483067 PUD 1b5195067 PMD 0
Jul 16 11:10:25 localhost kernel: Oops: 0000 [#1] PREEMPT SMP
Jul 16 11:10:25 localhost kernel: last sysfs file: /sys/module/stp/initstate
Jul 16 11:10:25 localhost kernel: CPU 7
Jul 16 11:10:25 localhost kernel: Modules linked in: bridge(+) veth 8021q garp fuse arc4 michael_mic macvlan pktgen sco stp llc bnep l2cap bluetooth nfs lockd
fscache nfs_acl auth_rpcgss sunrpc ipv6 dm_multipath uinput ixgbe i2c_i801 i2c_core e1000e pcspkr dca mdio iTCO_wdt iTCO_vendor_support ata_generic pata_acpi
[last unloaded: nf_nat]
Jul 16 11:10:25 localhost kernel: Pid: 6109, comm: modprobe Not tainted 2.6.31-rc3 #1 X8STi
Jul 16 11:10:25 localhost kernel: RIP: 0010:[<ffffffffa0257666>] [<ffffffffa0257666>] br_device_event+0xc6/0x124 [bridge]
Jul 16 11:10:25 localhost kernel: RSP: 0018:ffff8801b356be38 EFLAGS: 00010246
Jul 16 11:10:25 localhost kernel: RAX: 0000000000000000 RBX: 0000000000000001 RCX: 0000000000000000
Jul 16 11:10:25 localhost kernel: RDX: ffff8801b933a000 RSI: 0000000000000001 RDI: ffffffffa0260490
Jul 16 11:10:25 localhost kernel: RBP: ffff8801b356be58 R08: 0000000000000000 R09: ffff8801b356bc88
Jul 16 11:10:25 localhost kernel: R10: ffff8801bfc04200 R11: ffffffffa01a0f70 R12: ffff8801b9006f6c
Jul 16 11:10:25 localhost kernel: R13: ffff8801b93471b0 R14: ffff8801b933a000 R15: 0000000000000000
Jul 16 11:10:25 localhost kernel: FS: 00007f1cc039e6f0(0000) GS:ffff8800280ff000(0000) knlGS:0000000000000000
Jul 16 11:10:25 localhost kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
Jul 16 11:10:25 localhost kernel: CR2: 0000000000000160 CR3: 00000001b0c9e000 CR4: 00000000000006e0
Jul 16 11:10:25 localhost kernel: DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
Jul 16 11:10:25 localhost kernel: DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Jul 16 11:10:25 localhost kernel: Process modprobe (pid: 6109, threadinfo ffff8801b356a000, task ffff8801ab952de0)
Jul 16 11:10:25 localhost kernel: Stack:
Jul 16 11:10:25 localhost kernel: ffffffffa0260490 ffffffff818dad60 ffff8801b933a000 0000000000000000
Jul 16 11:10:25 localhost kernel: <0> ffff8801b356bea8 ffffffff81345d16 ffffffffa0264000 ffffffff818dadd0
Jul 16 11:10:25 localhost kernel: <0> ffff8801b7c32b48 0000000000000000 ffffffffa0264000 0000000000000000
Jul 16 11:10:25 localhost kernel: Call Trace:
Jul 16 11:10:25 localhost kernel: [<ffffffff81345d16>] register_netdevice_notifier+0x91/0x185
Jul 16 11:10:25 localhost kernel: [<ffffffffa0264000>] ? br_init+0x0/0xcf [bridge]
Jul 16 11:10:25 localhost kernel: [<ffffffffa0264000>] ? br_init+0x0/0xcf [bridge]
Jul 16 11:10:25 localhost kernel: [<ffffffffa0264066>] br_init+0x66/0xcf [bridge]
Jul 16 11:10:25 localhost kernel: [<ffffffff8100a05c>] do_one_initcall+0x56/0x136
Jul 16 11:10:25 localhost kernel: [<ffffffff81066eef>] ? up_read+0x9/0xb
Jul 16 11:10:25 localhost kernel: [<ffffffff810678ba>] ? __blocking_notifier_call_chain+0x56/0x62
Jul 16 11:10:25 localhost kernel: [<ffffffff81077f95>] sys_init_module+0xd1/0x208
Jul 16 11:10:25 localhost kernel: [<ffffffff81011b42>] system_call_fastpath+0x16/0x1b
Jul 16 11:10:25 localhost kernel: Code: 4c 89 e7 e8 a7 fc 18 e1 49 8b 44 24 18 f6 80 60 01 00 00 01 74 2e 4c 89 ef e8 bd 13 00 00 eb 24 f6 42 48 04 75 33 49 8b
44 24 18 <f6> 80 60 01 00 00 01 74 25 4c 89 e7 e8 73 fc 18 e1 4c 89 ef e8
Jul 16 11:10:25 localhost kernel: RIP [<ffffffffa0257666>] br_device_event+0xc6/0x124 [bridge]
Jul 16 11:10:25 localhost kernel: RSP <ffff8801b356be38>
Jul 16 11:10:25 localhost kernel: CR2: 0000000000000160
Jul 16 11:10:25 localhost kernel: ---[ end trace 71049551bce4d406 ]---
Jul 16 11:10:25 localhost bluetoothd[1916]: Can't remove GN bridge
Jul 16 11:10:25 localhost bluetoothd[1916]: Can't remove GN bridge
Jul 16 11:10:25 localhost bluetoothd[1916]: Stopping SDP server
Jul 16 11:10:25 localhost bluetoothd[1916]: Exit
...
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* Re: TESTING ROUTING CODE !!
From: Leena M. @ 2009-07-16 18:13 UTC (permalink / raw)
To: Kent Tu; +Cc: kernelnewbies@nl.linux.org, netdev@vger.kernel.org
In-Reply-To: <A66C29B8-4F62-4180-8D93-DF3A46F77D5D@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 414 bytes --]
Any specific VM do you recommend , How about Xen ?
Regards,
Leena
On Thu, Jul 16, 2009 at 11:36 PM, Kent Tu <kachin2@gmail.com> wrote:
> I'd recommend you to use virtual machines.
>
>
>
>
> On Jul 16, 2009, at 10:39 AM, "Leena M." <kernmonk@gmail.com> wrote:
>
> How can we test routing and other networking code
>> given a single machine with plenty of ram and processing power ?
>>
>> Regards,
>> Leena
>>
>
[-- Attachment #2: Type: text/html, Size: 957 bytes --]
^ permalink raw reply
* Re: TESTING ROUTING CODE !!
From: Kent Tu @ 2009-07-16 18:06 UTC (permalink / raw)
To: Leena M.; +Cc: kernelnewbies@nl.linux.org, netdev@vger.kernel.org
In-Reply-To: <e49baca60907161039s7ceb493bje3b1a5c943ce5f33@mail.gmail.com>
I'd recommend you to use virtual machines.
On Jul 16, 2009, at 10:39 AM, "Leena M." <kernmonk@gmail.com> wrote:
> How can we test routing and other networking code
> given a single machine with plenty of ram and processing power ?
>
> Regards,
> Leena
^ permalink raw reply
* Re: TESTING ROUTING CODE !!
From: Mulyadi Santosa @ 2009-07-16 17:56 UTC (permalink / raw)
To: Leena M.; +Cc: kernelnewbies, netdev
In-Reply-To: <e49baca60907161039s7ceb493bje3b1a5c943ce5f33@mail.gmail.com>
On Fri, Jul 17, 2009 at 12:39 AM, Leena M.<kernmonk@gmail.com> wrote:
> How can we test routing and other networking code
> given a single machine with plenty of ram and processing power ?
Uhm, use virtual machine such as User Mode Linux or Qemu/KVM?
--
regards,
Mulyadi Santosa
Freelance Linux trainer
blog: the-hydra.blogspot.com
^ permalink raw reply
* Re: Determine veth peer device?
From: Ben Greear @ 2009-07-16 17:45 UTC (permalink / raw)
To: NetDev
In-Reply-To: <4A5E6C5E.1070808@candelatech.com>
On 07/15/2009 04:55 PM, Ben Greear wrote:
> Is there a way to determine the peer device for a
> veth interface in user-space?
>
> I didn't see anything obvious printed in 'ip link show'
>
> [root@i7-1qc-1 ~]# ip link show dev veth0
> 7: veth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast
> state UP qlen 1000
> link/ether 72:2e:4d:74:5b:a6 brd ff:ff:ff:ff:ff:ff
For the record, can get it via ethtool ioctl:
[root@i7-1qc-1 ~]# ethtool -S veth1
NIC statistics:
peer_ifindex: 7
Take it easy,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* Re: [PATCH] Add mac driver for w90p910
From: Trilok Soni @ 2009-07-16 17:42 UTC (permalink / raw)
To: Wan ZongShun; +Cc: David S. Miller, linux-netdev, linux-arm-kernel, Eric.miao
In-Reply-To: <4A5F2329.9090901@gmail.com>
Hi Wan,
> diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile
> index 811a3cc..303171f 100644
> --- a/drivers/net/arm/Makefile
> +++ b/drivers/net/arm/Makefile
> @@ -11,3 +11,4 @@ obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
> obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
> obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
> obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
> +obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o
> diff --git a/drivers/net/arm/w90p910_ether.c b/drivers/net/arm/w90p910_ether.c
> new file mode 100644
> index 0000000..616fb79
> --- /dev/null
> +++ b/drivers/net/arm/w90p910_ether.c
> @@ -0,0 +1,1105 @@
> +/*
> + * Copyright (c) 2008-2009 Nuvoton technology corporation.
> + *
> + * Wan ZongShun <mcuos.com@gmail.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;version 2 of the License.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/mii.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/ethtool.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +
> +#define DRV_MODULE_NAME "w90p910-emc"
> +#define DRV_MODULE_VERSION "0.1"
> +
> +/* Ethernet MAC Registers */
> +#define REG_CAMCMR 0x00
> +#define REG_CAMEN 0x04
> +#define REG_CAMM_BASE 0x08
> +#define REG_CAML_BASE 0x0c
> +#define REG_TXDLSA 0x88
> +#define REG_RXDLSA 0x8C
> +#define REG_MCMDR 0x90
> +#define REG_MIID 0x94
> +#define REG_MIIDA 0x98
> +#define REG_FFTCR 0x9C
> +#define REG_TSDR 0xa0
> +#define REG_RSDR 0xa4
> +#define REG_DMARFC 0xa8
> +#define REG_MIEN 0xac
> +#define REG_MISTA 0xb0
> +#define REG_CTXDSA 0xcc
> +#define REG_CTXBSA 0xd0
> +#define REG_CRXDSA 0xd4
> +#define REG_CRXBSA 0xd8
> +
> +/* mac controller bit */
> +#define MCMDR_RXON 0x01
> +#define MCMDR_ACP (0x01 << 3)
> +#define MCMDR_SPCRC (0x01 << 5)
> +#define MCMDR_TXON (0x01 << 8)
> +#define MCMDR_FDUP (0x01 << 18)
> +#define MCMDR_ENMDC (0x01 << 19)
> +#define MCMDR_OPMOD (0x01 << 20)
> +#define SWR (0x01 << 24)
> +
> +/* cam command regiser */
> +#define CAMCMR_AUP 0x01
> +#define CAMCMR_AMP (0x01 << 1)
> +#define CAMCMR_ABP (0x01 << 2)
> +#define CAMCMR_CCAM (0x01 << 3)
> +#define CAMCMR_ECMP (0x01 << 4)
> +#define CAM0EN 0x01
> +
> +/* mac mii controller bit */
> +#define MDCCR (0x0a << 20)
> +#define PHYAD (0x01 << 8)
> +#define PHYWR (0x01 << 16)
> +#define PHYBUSY (0x01 << 17)
> +#define PHYPRESP (0x01 << 18)
> +#define CAM_ENTRY_SIZE 0x08
> +
> +/* rx and tx status */
> +#define TXDS_TXCP (0x01 << 19)
> +#define RXDS_CRCE (0x01 << 17)
> +#define RXDS_PTLE (0x01 << 19)
> +#define RXDS_RXGD (0x01 << 20)
> +#define RXDS_ALIE (0x01 << 21)
> +#define RXDS_RP (0x01 << 22)
> +
> +/* mac interrupt status*/
> +#define MISTA_EXDEF (0x01 << 19)
> +#define MISTA_TXBERR (0x01 << 24)
> +#define MISTA_TDU (0x01 << 23)
> +#define MISTA_RDU (0x01 << 10)
> +#define MISTA_RXBERR (0x01 << 11)
> +
> +#define ENSTART 0x01
> +#define ENRXINTR 0x01
> +#define ENRXGD (0x01 << 4)
> +#define ENRXBERR (0x01 << 11)
> +#define ENTXINTR (0x01 << 16)
> +#define ENTXCP (0x01 << 18)
> +#define ENTXABT (0x01 << 21)
> +#define ENTXBERR (0x01 << 24)
> +#define ENMDC (0x01 << 19)
> +#define PHYBUSY (0x01 << 17)
> +#define MDCCR_VAL 0xa00000
> +
> +/* rx and tx owner bit */
> +#define RX_OWEN_DMA (0x01 << 31)
> +#define RX_OWEN_CPU (~(0x03 << 30))
> +#define TX_OWEN_DMA (0x01 << 31)
> +#define TX_OWEN_CPU (~(0x01 << 31))
> +
> +/* tx frame desc controller bit */
> +#define MACTXINTEN 0x04
> +#define CRCMODE 0x02
> +#define PADDINGMODE 0x01
> +
> +/* fftcr controller bit */
> +#define TXTHD (0x03 << 8)
> +#define BLENGTH (0x01 << 20)
> +
> +/* global setting for driver */
> +#define RX_DESC_SIZE 50
> +#define TX_DESC_SIZE 10
> +#define MAX_RBUFF_SZ 0x600
> +#define MAX_TBUFF_SZ 0x600
> +#define TX_TIMEOUT 50
> +#define DELAY 1000
> +#define CAM0 0x0
> +
> +static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg);
> +
> +struct w90p910_rxbd {
> + unsigned int sl;
> + unsigned int buffer;
> + unsigned int reserved;
> + unsigned int next;
> +};
> +
> +struct w90p910_txbd {
> + unsigned int mode;
> + unsigned int buffer;
> + unsigned int sl;
> + unsigned int next;
> +};
> +
> +struct recv_pdesc {
> + struct w90p910_rxbd desclist[RX_DESC_SIZE];
> + char recv_buf[RX_DESC_SIZE][MAX_RBUFF_SZ];
> +};
> +
> +struct tran_pdesc {
> + struct w90p910_txbd desclist[TX_DESC_SIZE];
> + char tran_buf[RX_DESC_SIZE][MAX_TBUFF_SZ];
> +};
> +
> +struct w90p910_ether {
> + struct recv_pdesc *rdesc;
> + struct recv_pdesc *rdesc_phys;
> + struct tran_pdesc *tdesc;
> + struct tran_pdesc *tdesc_phys;
> + struct net_device_stats stats;
> + struct platform_device *pdev;
> + struct sk_buff *skb;
> + struct clk *clk;
> + struct clk *rmiiclk;
> + struct mii_if_info mii;
> + struct timer_list check_timer;
> + void __iomem *reg;
> + unsigned int rxirq;
> + unsigned int txirq;
> + unsigned int cur_tx;
> + unsigned int cur_rx;
> + unsigned int finish_tx;
> + unsigned int rx_packets;
> + unsigned int rx_bytes;
> + unsigned int start_tx_ptr;
> + unsigned int start_rx_ptr;
> + unsigned int linkflag;
> + spinlock_t lock;
describe what lock protects.
> +};
> +
> +
> +static void w90p910_init_desc(struct net_device *dev)
> +{
> + struct w90p910_ether *ether;
> + struct w90p910_txbd *tdesc, *tdesc_phys;
> + struct w90p910_rxbd *rdesc, *rdesc_phys;
> + unsigned int i, j;
> +
> + ether = netdev_priv(dev);
> +
> + ether->tdesc = (struct tran_pdesc *)
> + dma_alloc_coherent(NULL, sizeof(struct tran_pdesc),
> + (dma_addr_t *) ðer->tdesc_phys, GFP_KERNEL);
no-need of casting I think.
> +
> + ether->rdesc = (struct recv_pdesc *)
> + dma_alloc_coherent(NULL, sizeof(struct recv_pdesc),
> + (dma_addr_t *) ðer->rdesc_phys, GFP_KERNEL);
Ditto.
> +
> + for (i = 0; i < TX_DESC_SIZE; i++) {
> + tdesc = &(ether->tdesc->desclist[i]);
> +
> + j = ((i + 1) / TX_DESC_SIZE);
> +
> + if (j != 0) {
> + tdesc_phys = &(ether->tdesc_phys->desclist[0]);
> + ether->start_tx_ptr = (unsigned int)tdesc_phys;
> + tdesc->next = (unsigned int)ether->start_tx_ptr;
> + } else {
> + tdesc_phys = &(ether->tdesc_phys->desclist[i+1]);
> + tdesc->next = (unsigned int)tdesc_phys;
> + }
> +
> + tdesc->buffer = (unsigned int)ether->tdesc_phys->tran_buf[i];
> + tdesc->sl = 0;
> + tdesc->mode = 0;
> + }
> +
> + for (i = 0; i < RX_DESC_SIZE; i++) {
> + rdesc = &(ether->rdesc->desclist[i]);
> +
> + j = ((i + 1) / RX_DESC_SIZE);
> +
> + if (j != 0) {
> + rdesc_phys = &(ether->rdesc_phys->desclist[0]);
> + ether->start_rx_ptr = (unsigned int)rdesc_phys;
> + rdesc->next = (unsigned int)ether->start_rx_ptr;
> + } else {
> + rdesc_phys = &(ether->rdesc_phys->desclist[i+1]);
> + rdesc->next = (unsigned int)rdesc_phys;
> + }
> +
> + rdesc->sl = RX_OWEN_DMA;
> + rdesc->buffer = (unsigned int)ether->rdesc_phys->recv_buf[i];
> + }
> +}
> +
> +static void w90p910_set_fifo_threshold(struct net_device *dev)
> +{
> + struct w90p910_ether *ether = netdev_priv(dev);
> + unsigned int val;
> +
> + val = TXTHD | BLENGTH;
> + __raw_writel(val, ether->reg + REG_FFTCR);
> +}
> +
> +static void w90p910_return_default_idle(struct net_device *dev)
> +{
> + struct w90p910_ether *ether = netdev_priv(dev);
> + unsigned int val;
> +
> + val = __raw_readl(ether->reg + REG_MCMDR);
> + val |= SWR;
> + __raw_writel(val, ether->reg + REG_MCMDR);
> +}
> +
> +static void w90p910_trigger_rx(struct net_device *dev)
> +{
> + struct w90p910_ether *ether = netdev_priv(dev);
> +
> + __raw_writel(ENSTART, ether->reg + REG_RSDR);
> +}
> +
> +static void w90p910_trigger_tx(struct net_device *dev)
> +{
> + struct w90p910_ether *ether = netdev_priv(dev);
> +
> + __raw_writel(ENSTART, ether->reg + REG_TSDR);
> +}
> +
> +static void w90p910_enable_mac_interrupt(struct net_device *dev)
> +{
> + struct w90p910_ether *ether = netdev_priv(dev);
> + unsigned int val;
> +
> + val = ENTXINTR | ENRXINTR | ENRXGD | ENTXCP;
> + val |= ENTXBERR | ENRXBERR | ENTXABT;
> +
> + __raw_writel(val, ether->reg + REG_MIEN);
> +}
> +
> +static int set_mac_address(struct net_device *dev, void *addr)
> +{
how about naming it as w90p910_set_mac_address for consistency.
> + struct sockaddr *address = addr;
> +
> + if (!is_valid_ether_addr(address->sa_data))
> + return -EADDRNOTAVAIL;
> +
> + memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
> + w90p910_write_cam(dev, CAM0, dev->dev_addr);
> +
> + return 0;
> +}
> +
> +
> +static int w90p910_ether_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct w90p910_ether *ether = netdev_priv(dev);
> +
> + if (!(w90p910_send_frame(dev, skb->data, skb->len))) {
> + ether->skb = skb;
> + dev_kfree_skb_irq(skb);
> + return 0;
> + }
> + return -1;
better error code?
> +}
> +
> +static irqreturn_t w90p910_tx_interrupt(int irq, void *dev_id)
> +{
> + struct w90p910_ether *ether;
> + struct w90p910_txbd *txbd;
> + struct platform_device *pdev;
> + struct tran_pdesc *tran_pdesc;
> + struct net_device *dev;
> + unsigned int cur_entry, entry, status;
> +
> + dev = (struct net_device *)dev_id;
un-necessary casting.
> + ether = netdev_priv(dev);
> + pdev = ether->pdev;
> +
> + spin_lock(ðer->lock);
> +
> + w90p910_get_and_clear_int(dev, &status);
> +
> + cur_entry = __raw_readl(ether->reg + REG_CTXDSA);
> +
> + tran_pdesc = ether->tdesc_phys;
> + entry = (unsigned int)(&tran_pdesc->desclist[ether->finish_tx]);
> +
> + while (entry != cur_entry) {
> + txbd = ðer->tdesc->desclist[ether->finish_tx];
> +
> + ether->finish_tx = (ether->finish_tx + 1) % TX_DESC_SIZE;
> +
> + if (txbd->sl & TXDS_TXCP) {
> + ether->stats.tx_packets++;
> + ether->stats.tx_bytes += txbd->sl & 0xFFFF;
> + } else {
> + ether->stats.tx_errors++;
> + }
> +
> + txbd->sl = 0x0;
> + txbd->mode = 0x0;
> +
> + if (netif_queue_stopped(dev))
> + netif_wake_queue(dev);
> +
> + entry = (unsigned int)(&tran_pdesc->desclist[ether->finish_tx]);
> + }
> +
> + if (status & MISTA_EXDEF) {
> + dev_err(&pdev->dev, "emc defer exceed interrupt\n");
> + } else if (status & MISTA_TXBERR) {
> + dev_err(&pdev->dev, "emc bus error interrupt\n");
> + w90p910_reset_mac(dev);
> + } else if (status & MISTA_TDU) {
> + if (netif_queue_stopped(dev))
> + netif_wake_queue(dev);
> + }
indentation doesn't look correct.
> +
> + spin_unlock(ðer->lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +
> +static irqreturn_t w90p910_rx_interrupt(int irq, void *dev_id)
> +{
> + struct net_device *dev;
> + struct w90p910_ether *ether;
> + struct platform_device *pdev;
> + unsigned int status;
> +
> + dev = (struct net_device *)dev_id;
No need of casting.
> + ether = netdev_priv(dev);
> + pdev = ether->pdev;
> +
> + spin_lock(ðer->lock);
> +
> + w90p910_get_and_clear_int(dev, &status);
> +
> + if (status & MISTA_RDU) {
> + netdev_rx(dev);
> +
> + w90p910_trigger_rx(dev);
> +
> + spin_unlock(ðer->lock);
> + return IRQ_HANDLED;
> + } else if (status & MISTA_RXBERR) {
> + dev_err(&pdev->dev, "emc rx bus error\n");
> + w90p910_reset_mac(dev);
> + }
indentation problem.
> +
> + netdev_rx(dev);
> + spin_unlock(ðer->lock);
> + return IRQ_HANDLED;
> +}
> +
> +static int w90p910_ether_open(struct net_device *dev)
> +{
> + struct w90p910_ether *ether;
> + struct platform_device *pdev;
> +
> + ether = netdev_priv(dev);
> + pdev = ether->pdev;
> +
> + w90p910_reset_mac(dev);
> + w90p910_set_fifo_threshold(dev);
> + w90p910_set_curdest(dev);
> + w90p910_enable_cam(dev);
> + w90p910_enable_cam_command(dev);
> + w90p910_enable_mac_interrupt(dev);
> + w90p910_set_global_maccmd(dev);
> + w90p910_enable_rx(dev, 1);
> +
> + ether->rx_packets = 0x0;
> + ether->rx_bytes = 0x0;
> +
> + if (request_irq(ether->txirq, w90p910_tx_interrupt,
> + 0x0, pdev->name, dev)) {
> + dev_err(&pdev->dev, "register irq tx failed\n");
> + return -EAGAIN;
> + }
> +
> + if (request_irq(ether->rxirq, w90p910_rx_interrupt,
> + 0x0, pdev->name, dev)) {
> + dev_err(&pdev->dev, "register irq rx failed\n");
> + return -EAGAIN;
error path problem. You should do free_irq(ether->txirq);
> + }
> +
> + mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000));
> + netif_start_queue(dev);
> + w90p910_trigger_rx(dev);
> +
> + dev_info(&pdev->dev, "%s is OPENED\n", dev->name);
> +
> + return 0;
> +}
> +
> +
> +static int __devinit w90p910_ether_probe(struct platform_device *pdev)
> +{
> + struct w90p910_ether *ether;
> + struct net_device *dev;
> + struct resource *res;
> + int error;
> +
> + dev = alloc_etherdev(sizeof(struct w90p910_ether));
> + if (!dev)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (res == NULL) {
> + dev_err(&pdev->dev, "failed to get I/O memory\n");
> + error = -ENXIO;
> + goto failed_free;
> + }
> +
> + res = request_mem_region(res->start, resource_size(res), pdev->name);
> + if (res == NULL) {
> + dev_err(&pdev->dev, "failed to request I/O memory\n");
> + error = -EBUSY;
> + goto failed_free;
> + }
> +
> + ether = netdev_priv(dev);
> +
> + ether->reg = ioremap(res->start, resource_size(res));
> + if (ether->reg == NULL) {
> + dev_err(&pdev->dev, "failed to remap I/O memory\n");
> + error = -ENXIO;
> + goto failed_free_mem;
> + }
> +
> + ether->txirq = platform_get_irq(pdev, 0);
> + if (ether->txirq < 0) {
> + dev_err(&pdev->dev, "failed to get ether tx irq\n");
> + error = -ENXIO;
> + goto failed_free_io;
> + }
> +
> + ether->rxirq = platform_get_irq(pdev, 1);
> + if (ether->rxirq < 0) {
> + dev_err(&pdev->dev, "failed to get ether rx irq\n");
> + error = -ENXIO;
> + goto failed_free_txirq;
> + }
> +
> + platform_set_drvdata(pdev, dev);
> +
> + ether->clk = clk_get(&pdev->dev, NULL);
> + if (IS_ERR(ether->clk)) {
> + dev_err(&pdev->dev, "failed to get ether clock\n");
> + error = PTR_ERR(ether->clk);
> + goto failed_free_rxirq;
> + }
> +
> + ether->rmiiclk = clk_get(&pdev->dev, "RMII");
> + if (IS_ERR(ether->rmiiclk)) {
> + dev_err(&pdev->dev, "failed to get ether clock\n");
> + error = PTR_ERR(ether->rmiiclk);
> + goto failed_put_clk;
> + }
> +
> + ether->pdev = pdev;
> +
> + w90p910_ether_setup(dev);
> +
> + error = register_netdev(dev);
> + if (error != 0) {
> + dev_err(&pdev->dev, "Regiter EMC w90p910 FAILED\n");
> + error = -ENODEV;
> + goto failed_put_rmiiclk;
> + }
> +
> + return 0;
> +failed_put_rmiiclk:
> + clk_put(ether->rmiiclk);
> +failed_put_clk:
> + clk_put(ether->clk);
> +failed_free_rxirq:
> + free_irq(ether->rxirq, pdev);
> + platform_set_drvdata(pdev, NULL);
> +failed_free_txirq:
> + free_irq(ether->txirq, pdev);
> +failed_free_io:
> + iounmap(ether->reg);
> +failed_free_mem:
> + release_mem_region(res->start, resource_size(res));
> +failed_free:
> + free_netdev(dev);
> + return error;
> +}
> +
> +static int __devexit w90p910_ether_remove(struct platform_device *pdev)
> +{
> + struct net_device *dev = platform_get_drvdata(pdev);
> + struct w90p910_ether *ether = netdev_priv(dev);
> +
> + unregister_netdev(dev);
> + clk_put(ether->rmiiclk);
> + clk_put(ether->clk);
> + del_timer_sync(ðer->check_timer);
> + platform_set_drvdata(pdev, NULL);
> + free_netdev(dev);
I don't see free_irq for two irqs.
> + return 0;
> +}
--
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni
-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
^ permalink raw reply
* Re: ixgbe: schedule while atomic bug during dev_disable_lro 2.6.31-rc3
From: Ben Greear @ 2009-07-16 17:39 UTC (permalink / raw)
To: Ben Hutchings; +Cc: NetDev
In-Reply-To: <4A5E6B37.2080605@candelatech.com>
On 07/15/2009 04:50 PM, Ben Greear wrote:
> On 07/15/2009 04:35 PM, Ben Hutchings wrote:
>> On Wed, 2009-07-15 at 16:00 -0700, Ben Greear wrote:
>>> I just got a fancy new 10G NIC and tried it out in a (patched
>>> elsewhere, but stock ixgbe driver) 2.6.31-rc3) kernel.
>>
>> And what exactly are those patches?
>
> various and sundry, but so far I only see this problem with ixgbe
> (the 1G NICs don't complain at least).
>
> I'll build an un-patched kernel and see if I can reproduce them.
I see the same problem on a stock kernel. I'm running a pre-empt kernel..think
that might have some bearing on the problem?
Full dmesg here:
http://www.candelatech.com/oss/i7_dmesg.txt
Kernel config file here:
http://www.candelatech.com/oss/i7_config.txt
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* Re: [PATCH] imwc3200: move iwmc3200 SDIO ids to sdio_ids.h
From: Marcel Holtmann @ 2009-07-16 16:57 UTC (permalink / raw)
To: Tomas Winkler
Cc: Zhu, Yi, drzeus-list@drzeus.cx, netdev@vger.kernel.org,
linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <1ba2fa240907160804g18b1a215w3161c9682db7b35@mail.gmail.com>
Hi Dave,
> > > Subject: RE: [PATCH] imwc3200: move iwmc3200 SDIO ids to sdio_ids.h
> >
> > BTW, subject should be "iwmc3200".
>
> Thanks. I'll address all comments although I'm not sure how to split
> the patch since adding SDIO_VENDOR_ID_INTEL to sdio_ids.h it affects
> all the drivers anyhow, so I'm suggesting to route it through netdev.
I think it is best that you take the whole patch via net-next-2.6 to
avoid breakage during the next merge window.
Regards
Marcel
^ permalink raw reply
* Re: [PATCH 2/8] ar9170: Remove unused DECLARE_MAC_BUF
From: Luis R. Rodriguez @ 2009-07-16 16:39 UTC (permalink / raw)
To: Tobias Klauser
Cc: chunkeey, linville, johannes, yi.zhu, linux-wireless, netdev
In-Reply-To: <1247734381-20718-1-git-send-email-klto@zhaw.ch>
On Thu, Jul 16, 2009 at 1:53 AM, Tobias Klauser<klto@zhaw.ch> wrote:
> Is not needed anymore because of the %pM printk conversion specifier.
>
> Signed-off-by: Tobias Klauser <klto@zhaw.ch>
> ---
> drivers/net/wireless/ath/ar9170/main.c | 1 -
> 1 files changed, 0 insertions(+), 1 deletions(-)
This is already patched by Johannes through the patch titled:
[PATCH] wireless: remove print_mac uses
Please see and resubmit if there is anything missing:
http://marc.info/?l=linux-wireless&m=124767211929848&w=2
Luis
^ permalink raw reply
* [PATCH] cdc-eem: bad crc checking
From: Vincent CUISSARD @ 2009-07-16 16:08 UTC (permalink / raw)
To: netdev
When the driver received an EEM packet with CRC option enabled, driver must
compute and check the CRC of the Ethernet data. Previous version computes CRC
on Ethernet data plus the original CRC value. Skbuff is correctly trimed but
the old length is used when CRC is computed.
Signed-off-by: Vincent CUISSARD <vincent.cuissard@gmail.com>
---
drivers/net/usb/cdc_eem.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c
index 80e0177..b9e7162 100644
--- a/drivers/net/usb/cdc_eem.c
+++ b/drivers/net/usb/cdc_eem.c
@@ -311,7 +311,7 @@ static int eem_rx_fixup(struct usbnet *dev, struct
sk_buff *skb)
* bmCRC = 0 : CRC = 0xDEADBEEF
*/
if (header & BIT(14))
- crc2 = ~crc32_le(~0, skb2->data, len);
+ crc2 = ~crc32_le(~0, skb2->data, skb2->len);
else
crc2 = 0xdeadbeef;
^ permalink raw reply related
* Re: [RFC] [PATCH 0/7] IPsec: convert to ahash
From: Herbert Xu @ 2009-07-16 15:46 UTC (permalink / raw)
To: Steffen Klassert; +Cc: linux-crypto, netdev
In-Reply-To: <20090716111548.GP20288@secunet.com>
On Thu, Jul 16, 2009 at 01:15:48PM +0200, Steffen Klassert wrote:
>
> Since the calls to the hash algorithms can now return asynchronous, I'd like
> to avoid multiple calls to the hash update functions. I'd rather like to do
> all the hashing with one call to crypto_ahash_digest(). As it is, this
> requires chaining of all the involved scatterlists. Since we still can't use
> sg_chain() to chain up the lists, I added an additional scatterlist entry to
> the scatterlist of the assoc data (esp) to be able to chain later in the
> crypto layer. To keep compatibility I set the termination bit at the first
> entry and remove it later in authenc. In fact to rely on this additional
> entry and just to remove the termintation bit later makes me a bit nervous
> and I'm not sure whether this is acceptable, so better ideas are very welcome.
My suggestion would be to optimise for the common case, where
assoc is a single-entry list. So just put some space aside in
the request context for a two-entry sg list and copy the assoc
sg entry into it and chain it with the rest.
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: Achieved 10Gbit/s bidirectional routing
From: Bill Fink @ 2009-07-16 15:38 UTC (permalink / raw)
To: Jesper Dangaard Brouer
Cc: netdev@vger.kernel.org, David S. Miller, Robert Olsson,
Waskiewicz Jr, Peter P, Ronciak, John, jesse.brandeburg,
Stephen Hemminger, Linux Kernel Mailing List
In-Reply-To: <1247737144.30876.53.camel@localhost.localdomain>
On Thu, 16 Jul 2009, Jesper Dangaard Brouer wrote:
> On Wed, 2009-07-15 at 23:22 -0400, Bill Fink wrote:
> > On Wed, 15 Jul 2009, Jesper Dangaard Brouer wrote:
> >
> > > I'm giving a talk at LinuxCon, about 10Gbit/s routing on standard
> > > hardware running Linux.
> > >
> > > http://linuxcon.linuxfoundation.org/meetings/1585
> > > https://events.linuxfoundation.org/lc09o17
> > >
> > > I'm getting some really good 10Gbit/s bidirectional routing results
> > > with Intels latest 82599 chip. (I got two pre-release engineering
> > > samples directly from Intel, thanks Peter)
> > >
> > > Using a Core i7-920, and tuning the memory according to the RAMs
> > > X.M.P. settings DDR3-1600MHz, notice this also increases the QPI to
> > > 6.4GT/s. (Motherboard P6T6 WS revolution)
> > >
> > > With big 1514 bytes packets, I can basically do 10Gbit/s wirespeed
> > > bidirectional routing.
> > >
> > > Notice bidirectional routing means that we actually has to move approx
> > > 40Gbit/s through memory and in-and-out of the interfaces.
> > >
> > > Formatted quick view using 'ifstat -b'
> > >
> > > eth31-in eth31-out eth32-in eth32-out
> > > 9.57 + 9.52 + 9.51 + 9.60 = 38.20 Gbit/s
> > > 9.60 + 9.55 + 9.52 + 9.62 = 38.29 Gbit/s
> > > 9.61 + 9.53 + 9.52 + 9.62 = 38.28 Gbit/s
> > > 9.61 + 9.53 + 9.54 + 9.62 = 38.30 Gbit/s
> > >
> > > [Adding an extra NIC]
> > >
> > > Another observation is that I'm hitting some kind of bottleneck on the
> > > PCI-express switch. Adding an extra NIC in a PCIe slot connected to
> > > the same PCIe switch, does not scale beyond 40Gbit/s collective
> > > throughput.
>
> Correcting my self, according to Bill's info below.
>
> It does not scale when adding an extra NIC to the same NVIDIA NF200 PCIe
> switch chip (reason explained below by Bill)
>
>
> > > But, I happened to have a special motherboard ASUS P6T6 WS revolution,
> > > which has an additional PCIe switch chip NVIDIA's NF200.
> > >
> > > Connecting two dual port 10GbE NICs via two different PCI-express
> > > switch chips, makes things scale again! I have achieved a collective
> > > throughput of 66.25 Gbit/s. This results is also influenced by my
> > > pktgen machines cannot keep up, and I'm getting closer to the memory
> > > bandwidth limits.
> > >
> > > FYI: I found a really good reference explaining the PCI-express
> > > architecture, written by Intel:
> > >
> > > http://download.intel.com/design/intarch/papers/321071.pdf
> > >
> > > I'm not sure how to explain the PCI-express chip bottleneck I'm
> > > seeing, but my guess is that I'm limited by the number of outstanding
> > > packets/DMA-transfers and the latency for the DMA operations.
> > >
> > > Does any one have datasheets on the X58 and NVIDIA's NF200 PCI-express
> > > chips, that can tell me the number of outstanding transfers they
> > > support?
> >
> > We've achieved 70 Gbps aggregate unidirectional TCP performance from
> > one P6T6 based system to another. We figured out in our case that
> > we were being limited by the interconnect between the Intel X58 and
> > Nvidia N200 chips. The first 2 PCIe 2.0 slots are directly off the
> > Intel X58 and get the full 40 Gbps throughput from the dual-port
> > Myricom 10-GigE NICs we have installed in them. But the other
> > 3 PCIe 2.0 slots are on the Nvidia N200 chip, and I discovered
> > through googling that the link between the X58 and N200 chips
> > only operates at PCIe x16 _1.0_ speed, which limits the possible
> > aggregate throughput of the last 3 PCIe 2.0 slots to only 32 Gbps.
>
> This definitly explains the bottlenecks I have seen! Thanks!
>
> Yes, it seems to scale when installing the two NICs in the first two
> slots, both connected to the X58. If overclocking the RAM and CPU a
> bit, I can match my pktgen machines speed which gives a collective
> throughput of 67.95 Gbit/s.
>
> eth33 eth34 eth31 eth32
> in out in out in out in out
> 7.54 + 9.58 + 9.56 + 7.56 + 7.33 + 9.53 + 9.50 + 7.35 = 67.95 Gbit/s
>
> Now I just need a faster generator machine, to find the next bottleneck ;-)
>
>
> > This was clearly seen in our nuttcp testing:
> >
> > [root@i7raid-1 ~]# ./nuttcp-6.2.6 -In2 -xc0/0 -p5001 192.168.1.11 & ./nuttcp-6.2.6 -In3 -xc0/0 -p5002 192.168.2.11 & ./nuttcp-6.2.6 -In4 -xc1/1 -p5003 192.168.3.11 & ./nuttcp-6.2.6 -In5 -xc1/1 -p5004 192.168.4.11 & ./nuttcp-6.2.6 -In6 -xc2/2 -p5005 192.168.5.11 & ./nuttcp-6.2.6 -In7 -xc2/2 -p5006 192.168.6.11 & ./nuttcp-6.2.6 -In8 -xc3/3 -p5007 192.168.7.11 & ./nuttcp-6.2.6 -In9 -xc3/3 -p5008 192.168.8.11
> > n2: 11505.2648 MB / 10.09 sec = 9566.2298 Mbps 37 %TX 55 %RX 0 retrans 0.10 msRTT
> > n3: 11727.4489 MB / 10.02 sec = 9815.7570 Mbps 39 %TX 44 %RX 0 retrans 0.10 msRTT
> > n4: 11770.1250 MB / 10.07 sec = 9803.9901 Mbps 39 %TX 51 %RX 0 retrans 0.10 msRTT
> > n5: 11837.9320 MB / 10.05 sec = 9876.5725 Mbps 39 %TX 47 %RX 0 retrans 0.10 msRTT
> > n6: 9096.8125 MB / 10.09 sec = 7559.3310 Mbps 30 %TX 32 %RX 0 retrans 0.10 msRTT
> > n7: 9100.1211 MB / 10.10 sec = 7559.7790 Mbps 30 %TX 44 %RX 0 retrans 0.10 msRTT
> > n8: 9095.6179 MB / 10.10 sec = 7557.9983 Mbps 31 %TX 33 %RX 0 retrans 0.10 msRTT
> > n9: 9075.5472 MB / 10.08 sec = 7551.0234 Mbps 31 %TX 33 %RX 0 retrans 0.11 msRTT
> >
> > This used 4 dual-port Myricom 10-GigE NICs. We also tested with
> > a fifth dual-port 10-GigE NIC, but the aggregate throughput stayed
> > at about 70 Gbps, due to the performance bottleneck between the
> > X58 and N200 chips.
>
> This is also very excellent results!
>
> Thanks a lot Bill !!!
We also achieved nearly 80 Gbps in bidirectional TCP tests (40 Gbps
simultaneously in each direction):
[root@i7raid-1 ~]# ./nuttcp-6.2.6 -In2 -xc0/0 -p5001 192.168.1.11 & ./nuttcp-6.2.6 -In3 -r -xc0/0 -p5002 192.168.2.11 & ./nuttcp-6.2.6 -In4 -xc1/1 -p5003 192.168.3.11 & ./nuttcp-6.2.6 -In5 -r -xc1/1 -p5004 192.168.4.11 & ./nuttcp-6.2.6 -In6 -xc2/2 -p5005 192.168.5.11 & ./nuttcp-6.2.6 -In7 -r -xc2/2 -p5006 192.168.6.11 & ./nuttcp-6.2.6 -In8 -xc3/3 -p5007 192.168.7.11 & ./nuttcp-6.2.6 -In9 -r -xc3/3 -p5008 192.168.8.11
n2: 11542.6250 MB / 10.07 sec = 9619.9920 Mbps 44 %TX 51 %RX 0 retrans 0.12 msRTT
n3: 11543.7143 MB / 10.06 sec = 9622.2153 Mbps 41 %TX 49 %RX 0 retrans 0.15 msRTT
n4: 11622.8125 MB / 10.05 sec = 9701.0296 Mbps 43 %TX 51 %RX 0 retrans 0.10 msRTT
n5: 11523.6875 MB / 10.03 sec = 9638.8883 Mbps 43 %TX 50 %RX 0 retrans 0.15 msRTT
n6: 11608.0141 MB / 10.04 sec = 9695.7388 Mbps 43 %TX 50 %RX 0 retrans 0.10 msRTT
n7: 11580.1250 MB / 10.04 sec = 9679.3910 Mbps 43 %TX 50 %RX 0 retrans 0.13 msRTT
n8: 11608.0000 MB / 10.06 sec = 9678.7596 Mbps 42 %TX 50 %RX 0 retrans 0.10 msRTT
n9: 11553.3750 MB / 10.05 sec = 9643.7296 Mbps 45 %TX 50 %RX 0 retrans 0.11 msRTT
This was using 2 dual-port 10-GigE NICs in the first two PCIe 2.0 slots.
We are using an Intel i7 965 quad-core 3.2 GHz Nehalem processor
(overclocked to 3.4 GHz) and 2000 MHz DDR3 memory. Adding an additional
dual-port 10-GigE NIC on the Nvidia N200 chip does only marginally
better, as it appears we are basically CPU limited at this point for
this test (the sum of the TX and RX CPU utilization for each pair of
10-GigE interfaces is about 93%).
-Bill
^ permalink raw reply
* net: Micrel KS8851 SPI network driver
From: Ben Dooks @ 2009-07-16 15:24 UTC (permalink / raw)
To: netdev
[-- Attachment #1: ks8851-driver.patch --]
[-- Type: text/plain, Size: 46760 bytes --]
Network driver for the SPI version of the Micrel KS8851
network chip.
Signed-off-by: Ben Dooks <ben@simtec.co.uk>
---
drivers/net/Kconfig | 6
drivers/net/Makefile | 1
drivers/net/ks8851.c | 1322 +++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/net/ks8851.h | 296 +++++++++++
4 files changed, 1625 insertions(+)
Index: b/drivers/net/Kconfig
===================================================================
--- a/drivers/net/Kconfig 2009-07-15 13:02:56.000000000 +0100
+++ b/drivers/net/Kconfig 2009-07-15 13:03:01.000000000 +0100
@@ -1729,6 +1729,12 @@ config KS8842
help
This platform driver is for Micrel KSZ8842 chip.
+config KS8851
+ tristate "Micrel KS8851 SPI"
+ depends on SPI
+ help
+ SPI driver for Micrel KS8851 SPI attached network chip.
+
config VIA_RHINE
tristate "VIA Rhine support"
depends on NET_PCI && PCI
Index: b/drivers/net/Makefile
===================================================================
--- a/drivers/net/Makefile 2009-07-15 13:02:56.000000000 +0100
+++ b/drivers/net/Makefile 2009-07-15 13:03:01.000000000 +0100
@@ -88,6 +88,7 @@ obj-$(CONFIG_SKGE) += skge.o
obj-$(CONFIG_SKY2) += sky2.o
obj-$(CONFIG_SKFP) += skfp/
obj-$(CONFIG_KS8842) += ks8842.o
+obj-$(CONFIG_KS8851) += ks8851.o
obj-$(CONFIG_VIA_RHINE) += via-rhine.o
obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o
obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
Index: b/drivers/net/ks8851.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/net/ks8851.c 2009-07-16 15:57:46.000000000 +0100
@@ -0,0 +1,1322 @@
+/* drivers/net/ks8651.c
+ *
+ * Copyright 2009 Simtec Electronics
+ * http://www.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define DEBUG
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/cache.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+
+#include <linux/spi/spi.h>
+
+#include "ks8851.h"
+
+/**
+ * struct ks8851_rxctrl - KS8851 driver rx control
+ * @mchash: Multicast hash-table data.
+ * @rxcr1: KS_RXCR1 register setting
+ * @rxcr2: KS_RXCR2 register setting
+ *
+ * Representation of the settings needs to control the receive filtering
+ * such as the multicast hash-filter and the receive register settings. This
+ * is used to make the job of working out if the receive settings change and
+ * then issuing the new settings to the worker that will send the necessary
+ * commands.
+ */
+struct ks8851_rxctrl {
+ u16 mchash[4];
+ u16 rxcr1;
+ u16 rxcr2;
+};
+
+/**
+ * union ks8851_tx_hdr - tx header data
+ * @txb: The header as bytes
+ * @txw: The header as 16bit, little-endian words
+ *
+ * A dual representation of the tx header data to allow
+ * access to individual bytes, and to allow 16bit accesses
+ * with 16bit alignment.
+ */
+union ks8851_tx_hdr {
+ u8 txb[6];
+ __le16 txw[3];
+};
+
+/**
+ * struct ks8851_net - KS8851 driver private data
+ * @netdev: The network device we're bound to
+ * @spidev: The spi device we're bound to.
+ * @lock: Lock to ensure that the device is not accessed when busy.
+ * @statelock: Lock on this structure for tx list.
+ * @mii: The MII state information for the mii calls.
+ * @rxctrl: RX settings for @rxctrl_work.
+ * @tx_work: Work queue for tx packets
+ * @irq_work: Work queue for servicing interrupts
+ * @rxctrl_work: Work queue for updating RX mode and multicast lists
+ * @txq: Queue of packets for transmission.
+ * @spi_msg1: pre-setup SPI transfer with one message, @spi_xfer1.
+ * @spi_msg2: pre-setup SPI transfer with two messages, @spi_xfer2.
+ * @txh: Space for generating packet TX header in DMA-able data
+ * @rxd: Space for receiving SPI data, in DMA-able space.
+ * @txd: Space for transmitting SPI data, in DMA-able space.
+ * @msg_enable: The message flags controlling driver output (see ethtool).
+ * @fid: Incrementing frame id tag.
+ * @rc_ier: Cached copy of KS_IER.
+ * @rc_rxqcr: Cached copy of KS_RXQCR.
+ *
+ * The @lock ensures that the chip is protected when certain operations are
+ * in progress. When the read or write packet transfer is in progress, most
+ * of the chip registers are not ccessible until the transfer is finished and
+ * the DMA has been de-asserted.
+ *
+ * The @statelock is used to protect information in the structure which may
+ * need to be accessed via several sources, such as the network driver layer
+ * or one of the work queues.
+ *
+ * We align the buffers we may use for rx/tx to ensure that if the SPI driver
+ * wants to DMA map them, it will not have any problems with data the driver
+ * modifies.
+ */
+struct ks8851_net {
+ struct net_device *netdev;
+ struct spi_device *spidev;
+ struct mutex lock;
+ spinlock_t statelock;
+
+ union ks8851_tx_hdr txh ____cacheline_aligned;
+ u8 rxd[8];
+ u8 txd[8];
+
+ u32 msg_enable ____cacheline_aligned;
+ u16 tx_space;
+ u8 fid;
+
+ u16 rc_ier;
+ u16 rc_rxqcr;
+
+ struct mii_if_info mii;
+ struct ks8851_rxctrl rxctrl;
+
+ struct work_struct tx_work;
+ struct work_struct irq_work;
+ struct work_struct rxctrl_work;
+
+ struct sk_buff_head txq;
+
+ struct spi_message spi_msg1;
+ struct spi_message spi_msg2;
+ struct spi_transfer spi_xfer1;
+ struct spi_transfer spi_xfer2[2];
+};
+
+static int msg_enable;
+
+#define ks_info(_ks, _msg...) dev_info(&(_ks)->spidev->dev, _msg)
+#define ks_warn(_ks, _msg...) dev_warn(&(_ks)->spidev->dev, _msg)
+#define ks_dbg(_ks, _msg...) dev_dbg(&(_ks)->spidev->dev, _msg)
+#define ks_err(_ks, _msg...) dev_err(&(_ks)->spidev->dev, _msg)
+
+/* shift for byte-enable data */
+#define BYTE_EN(_x) ((_x) << 2)
+
+/* turn register number and byte-enable mask into data for start of packet */
+#define MK_OP(_byteen, _reg) (BYTE_EN(_byteen) | (_reg) << (8+2) | (_reg) >> 6)
+
+/* SPI register read/write calls.
+ *
+ * All these calls issue SPI transactions to access the chip's registers. They
+ * all require that the necessary lock is held to prevent accesses when the
+ * chip is busy transfering packet data (RX/TX FIFO accesses).
+ */
+
+/**
+ * ks8851_wrreg16 - write 16bit register value to chip
+ * @ks: The chip state
+ * @reg: The register address
+ * @val: The value to write
+ *
+ * Issue a write to put the value @val into the register specified in @reg.
+ */
+static void ks8851_wrreg16(struct ks8851_net *ks, unsigned reg, unsigned val)
+{
+ struct spi_transfer *xfer = &ks->spi_xfer1;
+ struct spi_message *msg = &ks->spi_msg1;
+ __le16 txb[2];
+ int ret;
+
+ txb[0] = cpu_to_le16(MK_OP(reg & 2 ? 0xC : 0x03, reg) | KS_SPIOP_WR);
+ txb[1] = cpu_to_le16(val);
+
+ xfer->tx_buf = txb;
+ xfer->rx_buf = NULL;
+ xfer->len = 4;
+
+ ret = spi_sync(ks->spidev, msg);
+ if (ret < 0)
+ ks_err(ks, "spi_sync() failed\n");
+}
+
+/**
+ * ks8851_rx_1msg - select whether to use one or two messages for spi read
+ * @ks: The device structure
+ *
+ * Return whether to generate a single message with a tx and rx buffer
+ * supplied to spi_sync(), or alternatively send the tx and rx buffers
+ * as separate messages.
+ *
+ * Depending on the hardware in use, a single message may be more efficient
+ * on interrupts or work done by the driver.
+ *
+ * This currently always returns true until we add some per-device data passed
+ * from the platform code to specify which mode is better.
+ */
+static inline bool ks8851_rx_1msg(struct ks8851_net *ks)
+{
+ return true;
+}
+
+/**
+ * ks8851_rdreg - issue read register command and return the data
+ * @ks: The device state
+ * @op: The register address and byte enables in message format.
+ * @rxb: The RX buffer to return the result into
+ * @rxl: The length of data expected.
+ *
+ * This is the low level read call that issues the necessary spi message(s)
+ * to read data from the register specified in @op.
+ */
+static void ks8851_rdreg(struct ks8851_net *ks, unsigned op,
+ u8 *rxb, unsigned rxl)
+{
+ struct spi_transfer *xfer;
+ struct spi_message *msg;
+ __le16 *txb = (__le16 *)ks->txd;
+ u8 *trx = ks->rxd;
+ int ret;
+
+ txb[0] = cpu_to_le16(op | KS_SPIOP_RD);
+
+ if (ks8851_rx_1msg(ks)) {
+ msg = &ks->spi_msg1;
+ xfer = &ks->spi_xfer1;
+
+ xfer->tx_buf = txb;
+ xfer->rx_buf = trx;
+ xfer->len = rxl + 2;
+ } else {
+ msg = &ks->spi_msg2;
+ xfer = ks->spi_xfer2;
+
+ xfer->tx_buf = txb;
+ xfer->rx_buf = NULL;
+ xfer->len = 2;
+
+ xfer++;
+ xfer->tx_buf = NULL;
+ xfer->rx_buf = trx;
+ xfer->len = rxl;
+ }
+
+ ret = spi_sync(ks->spidev, msg);
+ if (ret < 0)
+ ks_err(ks, "read: spi_sync() failed\n");
+ else if (ks8851_rx_1msg(ks))
+ memcpy(rxb, trx + 2, rxl);
+ else
+ memcpy(rxb, trx, rxl);
+}
+
+/**
+ * ks8851_rdreg8 - read 8 bit register from device
+ * @ks: The chip information
+ * @reg: The register address
+ *
+ * Read a 8bit register from the chip, returning the result
+*/
+static unsigned ks8851_rdreg8(struct ks8851_net *ks, unsigned reg)
+{
+ u8 rxb[1];
+
+ ks8851_rdreg(ks, MK_OP(1 << (reg & 3), reg), rxb, 1);
+ return rxb[0];
+}
+
+/**
+ * ks8851_rdreg16 - read 16 bit register from device
+ * @ks: The chip information
+ * @reg: The register address
+ *
+ * Read a 16bit register from the chip, returning the result
+*/
+static unsigned ks8851_rdreg16(struct ks8851_net *ks, unsigned reg)
+{
+ __le16 rx = 0;
+
+ ks8851_rdreg(ks, MK_OP(reg & 2 ? 0xC : 0x3, reg), (u8 *)&rx, 2);
+ return le16_to_cpu(rx);
+}
+
+/**
+ * ks8851_rdreg32 - read 32 bit register from device
+ * @ks: The chip information
+ * @reg: The register address
+ *
+ * Read a 32bit register from the chip.
+ *
+ * Note, this read requires the address be aligned to 4 bytes.
+*/
+static unsigned ks8851_rdreg32(struct ks8851_net *ks, unsigned reg)
+{
+ __le32 rx = 0;
+
+ WARN_ON(reg & 3);
+
+ ks8851_rdreg(ks, MK_OP(0xf, reg), (u8 *)&rx, 4);
+ return le32_to_cpu(rx);
+}
+
+/**
+ * ks8851_soft_reset - issue one of the soft reset to the device
+ * @ks: The device state.
+ * @op: The bit(s) to set in the GRR
+ *
+ * Issue the relevant soft-reset command to the device's GRR register
+ * specified by @op.
+ *
+ * Note, the delays are in there as a caution to ensure that the reset
+ * has time to take effect and then complete. Since the datasheet does
+ * not currently specify the exact sequence, we have chosen something
+ * that seems to work with our device.
+ */
+static void ks8851_soft_reset(struct ks8851_net *ks, unsigned op)
+{
+ ks8851_wrreg16(ks, KS_GRR, op);
+ mdelay(1); /* wait a short time to effect reset */
+ ks8851_wrreg16(ks, KS_GRR, 0);
+ mdelay(1); /* wait for condition to clear */
+}
+
+/**
+ * ks8851_write_mac_addr - write mac address to device registers
+ * @dev: The network device
+ *
+ * Update the KS8851 MAC address registers from the address in @dev.
+ *
+ * This call assumes that the chip is not running, so there is no need to
+ * shutdown the RXQ process whilst setting this.
+*/
+static int ks8851_write_mac_addr(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ u16 *mcp = (u16 *)dev->dev_addr;
+
+ mutex_lock(&ks->lock);
+
+ ks8851_wrreg16(ks, KS_MARL, mcp[0]);
+ ks8851_wrreg16(ks, KS_MARM, mcp[1]);
+ ks8851_wrreg16(ks, KS_MARH, mcp[2]);
+
+ mutex_unlock(&ks->lock);
+
+ return 0;
+}
+
+/**
+ * ks8851_init_mac - initialise the mac address
+ * @ks: The device structure
+ *
+ * Get or create the initial mac address for the device and then set that
+ * into the station address register. Currently we assume that the device
+ * does not have a valid mac address in it, and so we use random_ether_addr()
+ * to create a new one.
+ *
+ * In future, the driver should check to see if the device has an EEPROM
+ * attached and whether that has a valid ethernet address in it.
+ */
+static void ks8851_init_mac(struct ks8851_net *ks)
+{
+ struct net_device *dev = ks->netdev;
+
+ random_ether_addr(dev->dev_addr);
+ ks8851_write_mac_addr(dev);
+}
+
+/**
+ * ks8851_irq - device interrupt handler
+ * @irq: Interrupt number passed from the IRQ hnalder.
+ * @pw: The private word passed to register_irq(), our struct ks8851_net.
+ *
+ * Disable the interrupt from happening again until we've processed the
+ * current status by scheduling ks8851_irq_work().
+ */
+static irqreturn_t ks8851_irq(int irq, void *pw)
+{
+ struct ks8851_net *ks = pw;
+
+ disable_irq_nosync(irq);
+ schedule_work(&ks->irq_work);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8851_rdfifo - read data from the receive fifo
+ * @ks: The device state.
+ * @buff: The buffer address
+ * @len: The length of the data to read
+ *
+ * Issue an RXQ FIFO read command and read the @len ammount of data from
+ * the FIFO into the buffer specified by @buff.
+ */
+static void ks8851_rdfifo(struct ks8851_net *ks, u8 *buff, unsigned len)
+{
+ struct spi_transfer *xfer = ks->spi_xfer2;
+ struct spi_message *msg = &ks->spi_msg2;
+ u8 txb[1];
+ int ret;
+
+ if (netif_msg_rx_status(ks))
+ ks_dbg(ks, "%s: %d@%p\n", __func__, len, buff);
+
+ /* set the operation we're issuing */
+ txb[0] = KS_SPIOP_RXFIFO;
+
+ xfer->tx_buf = txb;
+ xfer->rx_buf = NULL;
+ xfer->len = 1;
+
+ xfer++;
+ xfer->rx_buf = buff;
+ xfer->tx_buf = NULL;
+ xfer->len = len;
+
+ ret = spi_sync(ks->spidev, msg);
+ if (ret < 0)
+ ks_err(ks, "%s: spi_sync() failed\n", __func__);
+}
+
+/**
+ * ks8851_dbg_dumpkkt - dump initial packet contents to debug
+ * @ks: The device state
+ * @rxpkt: The data for the received packet
+ *
+ * Dump the initial data from the packet to dev_dbg().
+*/
+static void ks8851_dbg_dumpkkt(struct ks8851_net *ks, u8 *rxpkt)
+{
+ ks_dbg(ks, "pkt %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
+ rxpkt[4], rxpkt[5], rxpkt[6], rxpkt[7],
+ rxpkt[8], rxpkt[9], rxpkt[10], rxpkt[11],
+ rxpkt[12], rxpkt[13], rxpkt[14], rxpkt[15]);
+}
+
+/**
+ * ks8851_rx_pkts - receive packets from the host
+ * @ks: The device information.
+ *
+ * This is called from the IRQ work queue when the system detects that there
+ * are packets in the receive queue. Find out how many packets there are and
+ * read them from the FIFO.
+ */
+static void ks8851_rx_pkts(struct ks8851_net *ks)
+{
+ struct sk_buff *skb;
+ unsigned rxfc;
+ unsigned rxlen;
+ unsigned rxstat;
+ u32 rxh;
+ u8 *rxpkt;
+
+ rxfc = ks8851_rdreg8(ks, KS_RXFC);
+
+ if (netif_msg_rx_status(ks))
+ ks_dbg(ks, "%s: %d packets\n", __func__, rxfc);
+
+ /* Currently we're issuing a read per packet, but we could possibly
+ * improve the code by issuing a single read, getting the receive
+ * header, allocating the packet and then reading the packet data
+ * out in one go.
+ *
+ * This form of operation would require us to hold the SPI bus'
+ * chipselect low during the entie transaction to avoid any
+ * reset to the data stream comming from the chip.
+ */
+
+ for (; rxfc != 0; rxfc--) {
+ rxh = ks8851_rdreg32(ks, KS_RXFHSR);
+ rxstat = rxh & 0xffff;
+ rxlen = rxh >> 16;
+
+ if (netif_msg_rx_status(ks))
+ ks_dbg(ks, "rx: stat 0x%04x, len 0x%04x\n",
+ rxstat, rxlen);
+
+ /* the length of the packet includes the 32bit CRC */
+
+ /* set dma read address */
+ ks8851_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI | 0x00);
+
+ /* start the packet dma process, and set auto-dequeue rx */
+ ks8851_wrreg16(ks, KS_RXQCR,
+ ks->rc_rxqcr | RXQCR_SDA | RXQCR_ADRFE);
+
+ if (rxlen > 0) {
+ skb = netdev_alloc_skb(ks->netdev, rxlen + 2 + 8);
+ if (!skb) {
+ /* todo - dump frame and move on */
+ }
+
+ /* two bytes to ensure ip is aligned, and four bytes
+ * for the status header and 4 bytes of garbage */
+ skb_reserve(skb, 2 + 4 + 4);
+
+ rxpkt = skb_put(skb, rxlen - 4) - 8;
+
+ /* align the packet length to 4 bytes, and add 4 bytes
+ * as we're getting the rx status header as well */
+ ks8851_rdfifo(ks, rxpkt, ALIGN(rxlen, 4) + 8);
+
+ if (netif_msg_pktdata(ks))
+ ks8851_dbg_dumpkkt(ks, rxpkt);
+
+ skb->protocol = eth_type_trans(skb, ks->netdev);
+ netif_rx(skb);
+
+ ks->netdev->stats.rx_packets++;
+ ks->netdev->stats.rx_bytes += rxlen - 4;
+ }
+
+ ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+ }
+}
+
+/**
+ * ks8851_irq_work - work queue handler for dealing with interrupt requests
+ * @work: The work structure that was scheduled by schedule_work()
+ *
+ * This is the handler invoked when the ks8851_irq() is called to find out
+ * what happened, as we cannot allow ourselves to sleep whilst waiting for
+ * anything other process has the chip's lock.
+ *
+ * Read the interrupt status, work out what needs to be done and then clear
+ * any of the interrupts that are not needed.
+ */
+static void ks8851_irq_work(struct work_struct *work)
+{
+ struct ks8851_net *ks = container_of(work, struct ks8851_net, irq_work);
+ unsigned status;
+ unsigned handled = 0;
+
+ mutex_lock(&ks->lock);
+
+ status = ks8851_rdreg16(ks, KS_ISR);
+
+ if (netif_msg_intr(ks))
+ dev_dbg(&ks->spidev->dev, "%s: status 0x%04x\n",
+ __func__, status);
+
+ if (status & IRQ_LCI) {
+ /* should do something about checking link status */
+ handled |= IRQ_LCI;
+ }
+
+ if (status & IRQ_LDI) {
+ u16 pmecr = ks8851_rdreg16(ks, KS_PMECR);
+ pmecr &= ~PMECR_WKEVT_MASK;
+ ks8851_wrreg16(ks, KS_PMECR, pmecr | PMECR_WKEVT_LINK);
+
+ handled |= IRQ_LDI;
+ }
+
+ if (status & IRQ_RXPSI)
+ handled |= IRQ_RXPSI;
+
+ if (status & IRQ_TXI) {
+ handled |= IRQ_TXI;
+
+ /* no lock here, tx queue should have been stopped */
+
+ /* update our idea of how much tx space is available to the
+ * system */
+ ks->tx_space = ks8851_rdreg16(ks, KS_TXMIR);
+
+ if (netif_msg_intr(ks))
+ ks_dbg(ks, "%s: txspace %d\n", __func__, ks->tx_space);
+ }
+
+ if (status & IRQ_RXI)
+ handled |= IRQ_RXI;
+
+ if (status & IRQ_SPIBEI) {
+ dev_err(&ks->spidev->dev, "%s: spi bus error\n", __func__);
+ handled |= IRQ_SPIBEI;
+ }
+
+ ks8851_wrreg16(ks, KS_ISR, handled);
+
+ if (status & IRQ_RXI) {
+ /* the datasheet says to disable the rx interrupt during
+ * packet read-out, however we're masking the interrupt
+ * from the device so do not bother masking just the RX
+ * from the device. */
+
+ ks8851_rx_pkts(ks);
+ }
+
+ /* if something stopped the rx process, probably due to wanting
+ * to change the rx settings, then do something about restarting
+ * it. */
+ if (status & IRQ_RXPSI) {
+ struct ks8851_rxctrl *rxc = &ks->rxctrl;
+
+ /* update the multicast hash table */
+ ks8851_wrreg16(ks, KS_MAHTR0, rxc->mchash[0]);
+ ks8851_wrreg16(ks, KS_MAHTR1, rxc->mchash[1]);
+ ks8851_wrreg16(ks, KS_MAHTR2, rxc->mchash[2]);
+ ks8851_wrreg16(ks, KS_MAHTR3, rxc->mchash[3]);
+
+ ks8851_wrreg16(ks, KS_RXCR2, rxc->rxcr2);
+ ks8851_wrreg16(ks, KS_RXCR1, rxc->rxcr1);
+ }
+
+ mutex_unlock(&ks->lock);
+
+ if (status & IRQ_TXI)
+ netif_wake_queue(ks->netdev);
+
+ enable_irq(ks->netdev->irq);
+}
+
+/**
+ * calc_txlen - calculate size of message to send packet
+ * @len: Lenght of data
+ *
+ * Returns the size of the TXFIFO message needed to send
+ * this packet.
+ */
+static inline unsigned calc_txlen(unsigned len)
+{
+ return ALIGN(len + 4, 4);
+}
+
+/**
+ * ks8851_wrpkt - write packet to TX FIFO
+ * @ks: The device state.
+ * @txp: The sk_buff to transmit.
+ * @irq: IRQ on completion of the packet.
+ *
+ * Send the @txp to the chip. This means creating the relevant packet header
+ * specifying the length of the packet and the other information the chip
+ * needs, such as IRQ on completion. Send the header and the packet data to
+ * the device.
+ */
+static void ks8851_wrpkt(struct ks8851_net *ks, struct sk_buff *txp, bool irq)
+{
+ struct spi_transfer *xfer = ks->spi_xfer2;
+ struct spi_message *msg = &ks->spi_msg2;
+ unsigned fid = 0;
+ int ret;
+
+ if (netif_msg_tx_queued(ks))
+ dev_dbg(&ks->spidev->dev, "%s: skb %p, %d@%p, irq %d\n",
+ __func__, txp, txp->len, txp->data, irq);
+
+ fid = ks->fid++;
+ fid &= TXFR_TXFID_MASK;
+
+ if (irq)
+ fid |= TXFR_TXIC; /* irq on completion */
+
+ /* start header at txb[1] to align txw entries */
+ ks->txh.txb[1] = KS_SPIOP_TXFIFO;
+ ks->txh.txw[1] = cpu_to_le16(fid);
+ ks->txh.txw[2] = cpu_to_le16(txp->len);
+
+ xfer->tx_buf = &ks->txh.txb[1];
+ xfer->rx_buf = NULL;
+ xfer->len = 5;
+
+ xfer++;
+ xfer->tx_buf = txp->data;
+ xfer->rx_buf = NULL;
+ xfer->len = ALIGN(txp->len, 4);
+
+ ret = spi_sync(ks->spidev, msg);
+ if (ret < 0)
+ ks_err(ks, "%s: spi_sync() failed\n", __func__);
+}
+
+/**
+ * ks8851_done_tx - update and then free skbuff after transmitting
+ * @ks: The device state
+ * @txb: The buffer transmitted
+ */
+static void ks8851_done_tx(struct ks8851_net *ks, struct sk_buff *txb)
+{
+ struct net_device *dev = ks->netdev;
+
+ dev->stats.tx_bytes += txb->len;
+ dev->stats.tx_packets++;
+
+ dev_kfree_skb(txb);
+}
+
+/**
+ * ks8851_tx_work - process tx packet(s)
+ * @work: The work strucutre what was scheduled.
+ *
+ * This is called when a number of packets have been scheduled for
+ * transmission and need to be sent to the device.
+ */
+static void ks8851_tx_work(struct work_struct *work)
+{
+ struct ks8851_net *ks = container_of(work, struct ks8851_net, tx_work);
+ struct sk_buff *txb;
+ bool last = false;
+
+ mutex_lock(&ks->lock);
+
+ while (!last) {
+ txb = skb_dequeue(&ks->txq);
+ last = skb_queue_empty(&ks->txq);
+
+ ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA);
+ ks8851_wrpkt(ks, txb, last);
+ ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+ ks8851_wrreg16(ks, KS_TXQCR, TXQCR_METFE);
+
+ ks8851_done_tx(ks, txb);
+ }
+
+ mutex_unlock(&ks->lock);
+}
+
+/**
+ * ks8851_set_powermode - set power mode of the device
+ * @ks: The device state
+ * @pwrmode: The power mode value to write to KS_PMECR.
+ *
+ * Change the power mode of the chip.
+ */
+static void ks8851_set_powermode(struct ks8851_net *ks, unsigned pwrmode)
+{
+ unsigned pmecr;
+
+ if (netif_msg_hw(ks))
+ ks_dbg(ks, "setting power mode %d\n", pwrmode);
+
+ pmecr = ks8851_rdreg16(ks, KS_PMECR);
+ pmecr &= ~PMECR_PM_MASK;
+ pmecr |= pwrmode;
+
+ ks8851_wrreg16(ks, KS_PMECR, pmecr);
+}
+
+/**
+ * ks8851_net_open - open network device
+ * @dev: The network device being opened.
+ *
+ * Called when the network device is marked active, such as a user executing
+ * 'ifconfig up' on the device.
+ */
+static int ks8851_net_open(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+
+ /* lock the card, even if we may not actually be doing anything
+ * else at the moment */
+ mutex_lock(&ks->lock);
+
+ if (netif_msg_ifup(ks))
+ ks_dbg(ks, "opening %s\n", dev->name);
+
+ /* bring chip out of any power saving mode it was in */
+ ks8851_set_powermode(ks, PMECR_PM_NORMAL);
+
+ /* issue a soft reset to the RX/TX QMU to put it into a known
+ * state. */
+ ks8851_soft_reset(ks, GRR_QMU);
+
+ /* setup transmission parameters */
+
+ ks8851_wrreg16(ks, KS_TXCR, (TXCR_TXE | /* enable transmit process */
+ TXCR_TXPE | /* pad to min length */
+ TXCR_TXCRC | /* add CRC */
+ TXCR_TXFCE)); /* enable flow control */
+
+ /* auto-increment tx data, reset tx pointer */
+ ks8851_wrreg16(ks, KS_TXFDPR, TXFDPR_TXFPAI);
+
+ /* setup receiver control */
+
+ ks8851_wrreg16(ks, KS_RXCR1, (RXCR1_RXPAFMA | /* from mac filter */
+ RXCR1_RXFCE | /* enable flow control */
+ RXCR1_RXBE | /* broadcast enable */
+ RXCR1_RXUE | /* unicast enable */
+ RXCR1_RXE)); /* enable rx block */
+
+ /* transfer entire frames out in one go */
+ ks8851_wrreg16(ks, KS_RXCR2, RXCR2_SRDBL_FRAME);
+
+ /* set receive counter timeouts */
+ ks8851_wrreg16(ks, KS_RXDTTR, 1000); /* 1ms after first frame to IRQ */
+ ks8851_wrreg16(ks, KS_RXDBCTR, 4096); /* >4Kbytes in buffer to IRQ */
+ ks8851_wrreg16(ks, KS_RXFCTR, 10); /* 10 frames to IRQ */
+
+ ks->rc_rxqcr = (RXQCR_RXFCTE | /* IRQ on frame count exceeded */
+ RXQCR_RXDBCTE | /* IRQ on byte count exceeded */
+ RXQCR_RXDTTE); /* IRQ on time exceeded */
+
+ ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+
+ /* clear then enable interrupts */
+
+#define STD_IRQ (IRQ_LCI | /* Link Change */ \
+ IRQ_TXI | /* TX done */ \
+ IRQ_RXI | /* RX done */ \
+ IRQ_SPIBEI | /* SPI bus error */ \
+ IRQ_TXPSI | /* TX process stop */ \
+ IRQ_RXPSI) /* RX process stop */
+
+ ks->rc_ier = STD_IRQ;
+ ks8851_wrreg16(ks, KS_ISR, STD_IRQ);
+ ks8851_wrreg16(ks, KS_IER, STD_IRQ);
+
+ netif_start_queue(ks->netdev);
+
+ if (netif_msg_ifup(ks))
+ ks_dbg(ks, "network device %s up\n", dev->name);
+
+ mutex_unlock(&ks->lock);
+ return 0;
+}
+
+/**
+ * ks8851_net_stop - close network device
+ * @dev: The device being closed.
+ *
+ * Called to close down a network device which has been active. Cancell any
+ * work, shutdown the RX and TX process and then place the chip into a low
+ * power state whilst it is not being used.
+ */
+static int ks8851_net_stop(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+
+ if (netif_msg_ifdown(ks))
+ ks_info(ks, "%s: shutting down\n", dev->name);
+
+ netif_stop_queue(dev);
+
+ mutex_lock(&ks->lock);
+
+ /* stop any outstanding work */
+ flush_work(&ks->irq_work);
+ flush_work(&ks->tx_work);
+ flush_work(&ks->rxctrl_work);
+
+ /* turn off the IRQs and ack any outstanding */
+ ks8851_wrreg16(ks, KS_IER, 0x0000);
+ ks8851_wrreg16(ks, KS_ISR, 0xffff);
+
+ /* shutdown RX process */
+ ks8851_wrreg16(ks, KS_RXCR1, 0x0000);
+
+ /* shutdown TX process */
+ ks8851_wrreg16(ks, KS_TXCR, 0x0000);
+
+ /* set powermode to soft power down to save power */
+ ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN);
+
+ /* ensure any queued tx buffers are dumped */
+ while (!skb_queue_empty(&ks->txq)) {
+ struct sk_buff *txb = skb_dequeue(&ks->txq);
+
+ if (netif_msg_ifdown(ks))
+ ks_dbg(ks, "%s: freeing txb %p\n", __func__, txb);
+
+ dev_kfree_skb(txb);
+ }
+
+ mutex_unlock(&ks->lock);
+ return 0;
+}
+
+/**
+ * ks8851_start_xmit - transmit packet
+ * @skb: The buffer to transmit
+ * @dev: The device used to transmit the packet.
+ *
+ * Called by the network layer to transmit the @skb. Queue the packet for
+ * the device and schedule the necessary work to transmit the packet when
+ * it is free.
+ *
+ * We do this to firstly avoid sleeping with the network device locked,
+ * and secondly so we can round up more than one packet to transmit which
+ * means we can try and avoid generating too many transmit done interrupts.
+ */
+static int ks8851_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ unsigned needed = calc_txlen(skb->len);
+ int ret = NETDEV_TX_OK;
+
+ if (netif_msg_tx_queued(ks))
+ ks_dbg(ks, "%s: skb %p, %d@%p\n", __func__,
+ skb, skb->len, skb->data);
+
+ spin_lock(&ks->statelock);
+
+ if (needed > ks->tx_space) {
+ netif_stop_queue(dev);
+ ret = NETDEV_TX_BUSY;
+ } else {
+ ks->tx_space -= needed;
+ skb_queue_tail(&ks->txq, skb);
+ }
+
+ spin_unlock(&ks->statelock);
+ schedule_work(&ks->tx_work);
+
+ return ret;
+}
+
+/**
+ * ks8851_rxctrl_work - work handler to change rx mode
+ * @work: The work structure this belongs to.
+ *
+ * Lock the device and issue the necessary changes to the receive mode from
+ * the network device layer. This is done so that we can do this without
+ * having to sleep whilst holding the network device lock.
+ *
+ * Since the recommendation from Micrel is that the RXQ is shutdown whilst the
+ * receive parameters are programmed, we issue a write to disable the RXQ and
+ * then wait for the interrupt handler to be triggered once the RXQ shutdown is
+ * complete. The interrupt handler then writes the new values into the chip.
+ */
+static void ks8851_rxctrl_work(struct work_struct *work)
+{
+ struct ks8851_net *ks = container_of(work, struct ks8851_net, rxctrl_work);
+
+ mutex_lock(&ks->lock);
+
+ /* need to shutdown RXQ before modifying filter parameters */
+ ks8851_wrreg16(ks, KS_RXCR1, 0x00);
+
+ mutex_unlock(&ks->lock);
+}
+
+static void ks8851_set_rx_mode(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ struct ks8851_rxctrl rxctrl;
+
+ memset(&rxctrl, 0, sizeof(rxctrl));
+
+ if (dev->flags & IFF_PROMISC) {
+ /* interface to receive everything */
+
+ rxctrl.rxcr1 = RXCR1_RXAE | RXCR1_RXINVF;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* accept all multicast packets */
+
+ rxctrl.rxcr1 = (RXCR1_RXME | RXCR1_RXAE |
+ RXCR1_RXPAFMA | RXCR1_RXMAFMA);
+ } else if (dev->flags & IFF_MULTICAST && dev->mc_count > 0) {
+ struct dev_mc_list *mcptr = dev->mc_list;
+ u32 crc;
+ int i;
+
+ /* accept some multicast */
+
+ for (i = dev->mc_count; i > 0; i--) {
+ crc = ether_crc(ETH_ALEN, mcptr->dmi_addr);
+ crc >>= (32 - 6); /* get top six bits */
+
+ rxctrl.mchash[crc >> 4] |= (1 << (crc & 0xf));
+ mcptr = mcptr->next;
+ }
+
+ rxctrl.rxcr1 = RXCR1_RXME | RXCR1_RXAE | RXCR1_RXPAFMA;
+ } else {
+ /* just accept broadcast / unicast */
+ rxctrl.rxcr1 = RXCR1_RXPAFMA;
+ }
+
+ rxctrl.rxcr1 |= (RXCR1_RXUE | /* unicast enable */
+ RXCR1_RXBE | /* broadcast enable */
+ RXCR1_RXE | /* RX process enable */
+ RXCR1_RXFCE); /* enable flow control */
+
+ rxctrl.rxcr2 |= RXCR2_SRDBL_FRAME;
+
+ /* schedule work to do the actual set of the data if needed */
+
+ spin_lock(&ks->statelock);
+
+ if (memcmp(&rxctrl, &ks->rxctrl, sizeof(rxctrl)) != 0) {
+ memcpy(&ks->rxctrl, &rxctrl, sizeof(ks->rxctrl));
+ schedule_work(&ks->rxctrl_work);
+ }
+
+ spin_unlock(&ks->statelock);
+}
+
+static int ks8851_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!is_valid_ether_addr(sa->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
+ return ks8851_write_mac_addr(dev);
+}
+
+static int ks8851_net_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ return generic_mii_ioctl(&ks->mii, if_mii(req), cmd, NULL);
+}
+
+static const struct net_device_ops ks8851_netdev_ops = {
+ .ndo_open = ks8851_net_open,
+ .ndo_stop = ks8851_net_stop,
+ .ndo_do_ioctl = ks8851_net_ioctl,
+ .ndo_start_xmit = ks8851_start_xmit,
+ .ndo_set_mac_address = ks8851_set_mac_address,
+ .ndo_set_rx_mode = ks8851_set_rx_mode,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/* ethtool support */
+
+static void ks8851_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *di)
+{
+ strlcpy(di->driver, "KS8851", sizeof(di->driver));
+ strlcpy(di->version, "1.00", sizeof(di->version));
+ strlcpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info));
+}
+
+static u32 ks8851_get_msglevel(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ return ks->msg_enable;
+}
+
+static void ks8851_set_msglevel(struct net_device *dev, u32 to)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ ks->msg_enable = to;
+}
+
+static int ks8851_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ return mii_ethtool_gset(&ks->mii, cmd);
+}
+
+static int ks8851_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ return mii_ethtool_sset(&ks->mii, cmd);
+}
+
+static u32 ks8851_get_link(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ return mii_link_ok(&ks->mii);
+}
+
+static int ks8851_nway_reset(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ return mii_nway_restart(&ks->mii);
+}
+
+static const struct ethtool_ops ks8851_ethtool_ops = {
+ .get_drvinfo = ks8851_get_drvinfo,
+ .get_msglevel = ks8851_get_msglevel,
+ .set_msglevel = ks8851_set_msglevel,
+ .get_settings = ks8851_get_settings,
+ .set_settings = ks8851_set_settings,
+ .get_link = ks8851_get_link,
+ .nway_reset = ks8851_nway_reset,
+};
+
+/* MII interface controls */
+
+/**
+ * ks8851_phy_reg - convert MII register into a KS8851 register
+ * @reg: MII register number.
+ *
+ * Return the KS8851 register number for the corresponding MII PHY register
+ * if possible. Return zero if the MII register has no direct mapping to the
+ * KS8851 register set.
+ */
+static int ks8851_phy_reg(int reg)
+{
+ switch (reg) {
+ case MII_BMCR:
+ return KS_P1MBCR;
+ case MII_BMSR:
+ return KS_P1MBSR;
+ case MII_PHYSID1:
+ return KS_PHY1ILR;
+ case MII_PHYSID2:
+ return KS_PHY1IHR;
+ case MII_ADVERTISE:
+ return KS_P1ANAR;
+ case MII_LPA:
+ return KS_P1ANLPR;
+ }
+
+ return 0x0;
+}
+
+/**
+ * ks8851_phy_read - MII interface PHY register read.
+ * @dev: The network device the PHY is on.
+ * @phy_addr: Address of PHY (ignored as we only have one)
+ * @reg: The register to read.
+ *
+ * This call reads data from the PHY register specified in @reg. Since the
+ * device does not support all the MII registers, the non-existant values
+ * are always returned as zero.
+ *
+ * We return zero for unsupported registers as the MII code does not check
+ * the value returned for any error status, and simply returns it to the
+ * caller. The mii-tool that the driver was tested with takes any -ve error
+ * as real PHY capabilities, thus displaying incorrect data to the user.
+ */
+static int ks8851_phy_read(struct net_device *dev, int phy_addr, int reg)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ int ksreg;
+ int result;
+
+ ksreg = ks8851_phy_reg(reg);
+ if (!ksreg)
+ return 0x0; /* no error return allowed, so use zero */
+
+ mutex_lock(&ks->lock);
+ result = ks8851_rdreg16(ks, ksreg);
+ mutex_unlock(&ks->lock);
+
+ return result;
+}
+
+static void ks8851_phy_write(struct net_device *dev,
+ int phy, int reg, int value)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ int ksreg;
+
+ ksreg = ks8851_phy_reg(reg);
+ if (ksreg) {
+ mutex_lock(&ks->lock);
+ ks8851_wrreg16(ks, ksreg, value);
+ mutex_unlock(&ks->lock);
+ }
+}
+
+/**
+ * ks8851_read_selftest - read the selftest memory info.
+ * @ks: The device state
+ *
+ * Read and check the TX/RX memory selftest information.
+ */
+static int ks8851_read_selftest(struct ks8851_net *ks)
+{
+ unsigned both_done = MBIR_TXMBF | MBIR_RXMBF;
+ int ret = 0;
+ unsigned rd;
+
+ rd = ks8851_rdreg16(ks, KS_MBIR);
+
+ if ((rd & both_done) != both_done) {
+ ks_warn(ks, "Memory selftest not finished\n");
+ return 0;
+ }
+
+ if (rd & MBIR_TXMBFA) {
+ ks_err(ks, "TX memory selftest fail\n");
+ ret |= 1;
+ }
+
+ if (rd & MBIR_RXMBFA) {
+ ks_err(ks, "RX memory selftest fail\n");
+ ret |= 2;
+ }
+
+ return 0;
+}
+
+/* driver bus management functions */
+
+static int __devinit ks8851_probe(struct spi_device *spi)
+{
+ struct net_device *ndev;
+ struct ks8851_net *ks;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(struct ks8851_net));
+ if (!ndev) {
+ dev_err(&spi->dev, "failed to alloc ethernet device\n");
+ return -ENOMEM;
+ }
+
+ spi->bits_per_word = 8;
+
+ ks = netdev_priv(ndev);
+
+ ks->netdev = ndev;
+ ks->spidev = spi;
+ ks->tx_space = 6144;
+
+ mutex_init(&ks->lock);
+ spin_lock_init(&ks->statelock);
+
+ INIT_WORK(&ks->tx_work, ks8851_tx_work);
+ INIT_WORK(&ks->irq_work, ks8851_irq_work);
+ INIT_WORK(&ks->rxctrl_work, ks8851_rxctrl_work);
+
+ /* initialise pre-made spi transfer messages */
+
+ spi_message_init(&ks->spi_msg1);
+ spi_message_add_tail(&ks->spi_xfer1, &ks->spi_msg1);
+
+ spi_message_init(&ks->spi_msg2);
+ spi_message_add_tail(&ks->spi_xfer2[0], &ks->spi_msg2);
+ spi_message_add_tail(&ks->spi_xfer2[1], &ks->spi_msg2);
+
+ /* setup mii state */
+ ks->mii.dev = ndev;
+ ks->mii.phy_id = 1,
+ ks->mii.phy_id_mask = 1;
+ ks->mii.reg_num_mask = 0xf;
+ ks->mii.mdio_read = ks8851_phy_read;
+ ks->mii.mdio_write = ks8851_phy_write;
+
+ dev_info(&spi->dev, "message enable is %d\n", msg_enable);
+
+ /* set the default message enable */
+ ks->msg_enable = netif_msg_init(msg_enable, (NETIF_MSG_DRV |
+ NETIF_MSG_PROBE |
+ NETIF_MSG_LINK));
+
+ skb_queue_head_init(&ks->txq);
+
+ SET_ETHTOOL_OPS(ndev, &ks8851_ethtool_ops);
+ SET_NETDEV_DEV(ndev, &spi->dev);
+
+ dev_set_drvdata(&spi->dev, ks);
+
+ ndev->if_port = IF_PORT_100BASET;
+ ndev->netdev_ops = &ks8851_netdev_ops;
+ ndev->irq = spi->irq;
+
+ /* simple check for a valid chip being connected to the bus */
+
+ if ((ks8851_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) {
+ dev_err(&spi->dev, "failed to read device ID\n");
+ ret = -ENODEV;
+ goto err_id;
+ }
+
+ ks8851_read_selftest(ks);
+ ks8851_init_mac(ks);
+
+ ret = request_irq(spi->irq, ks8851_irq, IRQF_TRIGGER_LOW,
+ ndev->name, ks);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to get irq\n");
+ goto err_irq;
+ }
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ dev_err(&spi->dev, "failed to register network device\n");
+ goto err_netdev;
+ }
+
+ dev_info(&spi->dev, "revision %d, MAC %pM, IRQ %d\n",
+ CIDER_REV_GET(ks8851_rdreg16(ks, KS_CIDER)),
+ ndev->dev_addr, ndev->irq);
+
+ return 0;
+
+
+err_netdev:
+ free_irq(ndev->irq, ndev);
+
+err_id:
+err_irq:
+ free_netdev(ndev);
+ return ret;
+}
+
+static int __devexit ks8851_remove(struct spi_device *spi)
+{
+ struct ks8851_net *priv = dev_get_drvdata(&spi->dev);
+
+ if (netif_msg_drv(priv))
+ dev_info(&spi->dev, "remove");
+
+ unregister_netdev(priv->netdev);
+ free_irq(spi->irq, priv);
+ free_netdev(priv->netdev);
+
+ return 0;
+}
+
+static struct spi_driver ks8851_driver = {
+ .driver = {
+ .name = "ks8851",
+ .owner = THIS_MODULE,
+ },
+ .probe = ks8851_probe,
+ .remove = __devexit_p(ks8851_remove),
+};
+
+static int __init ks8851_init(void)
+{
+ return spi_register_driver(&ks8851_driver);
+}
+
+static void __exit ks8851_exit(void)
+{
+ spi_unregister_driver(&ks8851_driver);
+}
+
+module_init(ks8851_init);
+module_exit(ks8851_exit);
+
+MODULE_DESCRIPTION("KS8851 Network driver");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
+
+module_param_named(message, msg_enable, int, 0);
+MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)");
Index: b/drivers/net/ks8851.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/net/ks8851.h 2009-07-16 10:49:18.000000000 +0100
@@ -0,0 +1,296 @@
+/* drivers/net/ks8851.h
+ *
+ * Copyright 2009 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * KS8851 register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define KS_CCR 0x08
+#define CCR_EEPROM (1 << 9)
+#define CCR_SPI (1 << 8)
+#define CCR_32PIN (1 << 0)
+
+/* MAC address registers */
+#define KS_MARL 0x10
+#define KS_MARM 0x12
+#define KS_MARH 0x14
+
+#define KS_OBCR 0x20
+#define OBCR_ODS_16mA (1 << 6)
+
+#define KS_EEPCR 0x22
+#define EEPCR_EESA (1 << 4)
+#define EEPCR_EESB (1 << 3)
+#define EEPCR_EEDO (1 << 2)
+#define EEPCR_EESCK (1 << 1)
+#define EEPCR_EECS (1 << 0)
+
+#define KS_MBIR 0x24
+#define MBIR_TXMBF (1 << 12)
+#define MBIR_TXMBFA (1 << 11)
+#define MBIR_RXMBF (1 << 4)
+#define MBIR_RXMBFA (1 << 3)
+
+#define KS_GRR 0x26
+#define GRR_QMU (1 << 1)
+#define GRR_GSR (1 << 0)
+
+#define KS_WFCR 0x2A
+#define WFCR_MPRXE (1 << 7)
+#define WFCR_WF3E (1 << 3)
+#define WFCR_WF2E (1 << 2)
+#define WFCR_WF1E (1 << 1)
+#define WFCR_WF0E (1 << 0)
+
+#define KS_WF0CRC0 0x30
+#define KS_WF0CRC1 0x32
+#define KS_WF0BM0 0x34
+#define KS_WF0BM1 0x36
+#define KS_WF0BM2 0x38
+#define KS_WF0BM3 0x3A
+
+#define KS_WF1CRC0 0x40
+#define KS_WF1CRC1 0x42
+#define KS_WF1BM0 0x44
+#define KS_WF1BM1 0x46
+#define KS_WF1BM2 0x48
+#define KS_WF1BM3 0x4A
+
+#define KS_WF2CRC0 0x50
+#define KS_WF2CRC1 0x52
+#define KS_WF2BM0 0x54
+#define KS_WF2BM1 0x56
+#define KS_WF2BM2 0x58
+#define KS_WF2BM3 0x5A
+
+#define KS_WF3CRC0 0x60
+#define KS_WF3CRC1 0x62
+#define KS_WF3BM0 0x64
+#define KS_WF3BM1 0x66
+#define KS_WF3BM2 0x68
+#define KS_WF3BM3 0x6A
+
+#define KS_TXCR 0x70
+#define TXCR_TCGICMP (1 << 8)
+#define TXCR_TCGUDP (1 << 7)
+#define TXCR_TCGTCP (1 << 6)
+#define TXCR_TCGIP (1 << 5)
+#define TXCR_FTXQ (1 << 4)
+#define TXCR_TXFCE (1 << 3)
+#define TXCR_TXPE (1 << 2)
+#define TXCR_TXCRC (1 << 1)
+#define TXCR_TXE (1 << 0)
+
+#define KS_TXSR 0x72
+#define TXSR_TXLC (1 << 13)
+#define TXSR_TXMC (1 << 12)
+#define TXSR_TXFID_MASK (0x3f << 0)
+#define TXSR_TXFID_SHIFT (0)
+#define TXSR_TXFID_GET(_v) (((_v) >> 0) & 0x3f)
+
+#define KS_RXCR1 0x74
+#define RXCR1_FRXQ (1 << 15)
+#define RXCR1_RXUDPFCC (1 << 14)
+#define RXCR1_RXTCPFCC (1 << 13)
+#define RXCR1_RXIPFCC (1 << 12)
+#define RXCR1_RXPAFMA (1 << 11)
+#define RXCR1_RXFCE (1 << 10)
+#define RXCR1_RXEFE (1 << 9)
+#define RXCR1_RXMAFMA (1 << 8)
+#define RXCR1_RXBE (1 << 7)
+#define RXCR1_RXME (1 << 6)
+#define RXCR1_RXUE (1 << 5)
+#define RXCR1_RXAE (1 << 4)
+#define RXCR1_RXINVF (1 << 1)
+#define RXCR1_RXE (1 << 0)
+
+#define KS_RXCR2 0x76
+#define RXCR2_SRDBL_MASK (0x7 << 5)
+#define RXCR2_SRDBL_SHIFT (5)
+#define RXCR2_SRDBL_4B (0x0 << 5)
+#define RXCR2_SRDBL_8B (0x1 << 5)
+#define RXCR2_SRDBL_16B (0x2 << 5)
+#define RXCR2_SRDBL_32B (0x3 << 5)
+#define RXCR2_SRDBL_FRAME (0x4 << 5)
+#define RXCR2_IUFFP (1 << 4)
+#define RXCR2_RXIUFCEZ (1 << 3)
+#define RXCR2_UDPLFE (1 << 2)
+#define RXCR2_RXICMPFCC (1 << 1)
+#define RXCR2_RXSAF (1 << 0)
+
+#define KS_TXMIR 0x78
+
+#define KS_RXFHSR 0x7C
+#define RXFSHR_RXFV (1 << 15)
+#define RXFSHR_RXICMPFCS (1 << 13)
+#define RXFSHR_RXIPFCS (1 << 12)
+#define RXFSHR_RXTCPFCS (1 << 11)
+#define RXFSHR_RXUDPFCS (1 << 10)
+#define RXFSHR_RXBF (1 << 7)
+#define RXFSHR_RXMF (1 << 6)
+#define RXFSHR_RXUF (1 << 5)
+#define RXFSHR_RXMR (1 << 4)
+#define RXFSHR_RXFT (1 << 3)
+#define RXFSHR_RXFTL (1 << 2)
+#define RXFSHR_RXRF (1 << 1)
+#define RXFSHR_RXCE (1 << 0)
+
+#define KS_RXFHBCR 0x7E
+#define KS_TXQCR 0x80
+#define TXQCR_AETFE (1 << 2)
+#define TXQCR_TXQMAM (1 << 1)
+#define TXQCR_METFE (1 << 0)
+
+#define KS_RXQCR 0x82
+#define RXQCR_RXDTTS (1 << 12)
+#define RXQCR_RXDBCTS (1 << 11)
+#define RXQCR_RXFCTS (1 << 10)
+#define RXQCR_RXIPHTOE (1 << 9)
+#define RXQCR_RXDTTE (1 << 7)
+#define RXQCR_RXDBCTE (1 << 6)
+#define RXQCR_RXFCTE (1 << 5)
+#define RXQCR_ADRFE (1 << 4)
+#define RXQCR_SDA (1 << 3)
+#define RXQCR_RRXEF (1 << 0)
+
+#define KS_TXFDPR 0x84
+#define TXFDPR_TXFPAI (1 << 14)
+#define TXFDPR_TXFP_MASK (0x7ff << 0)
+#define TXFDPR_TXFP_SHIFT (0)
+
+#define KS_RXFDPR 0x86
+#define RXFDPR_RXFPAI (1 << 14)
+
+#define KS_RXDTTR 0x8C
+#define KS_RXDBCTR 0x8E
+
+#define KS_IER 0x90
+#define KS_ISR 0x92
+#define IRQ_LCI (1 << 15)
+#define IRQ_TXI (1 << 14)
+#define IRQ_RXI (1 << 13)
+#define IRQ_RXOI (1 << 11)
+#define IRQ_TXPSI (1 << 9)
+#define IRQ_RXPSI (1 << 8)
+#define IRQ_TXSAI (1 << 6)
+#define IRQ_RXWFDI (1 << 5)
+#define IRQ_RXMPDI (1 << 4)
+#define IRQ_LDI (1 << 3)
+#define IRQ_EDI (1 << 2)
+#define IRQ_SPIBEI (1 << 1)
+#define IRQ_DEDI (1 << 0)
+
+#define KS_RXFCTR 0x9C
+#define KS_RXFC 0x9D
+#define RXFCTR_RXFC_MASK (0xff << 8)
+#define RXFCTR_RXFC_SHIFT (8)
+#define RXFCTR_RXFC_GET(_v) (((_v) >> 8) & 0xff)
+#define RXFCTR_RXFCT_MASK (0xff << 0)
+#define RXFCTR_RXFCT_SHIFT (0)
+
+#define KS_TXNTFSR 0x9E
+
+#define KS_MAHTR0 0xA0
+#define KS_MAHTR1 0xA2
+#define KS_MAHTR2 0xA4
+#define KS_MAHTR3 0xA6
+
+#define KS_FCLWR 0xB0
+#define KS_FCHWR 0xB2
+#define KS_FCOWR 0xB4
+
+#define KS_CIDER 0xC0
+#define CIDER_ID 0x8870
+#define CIDER_REV_MASK (0x7 << 1)
+#define CIDER_REV_SHIFT (1)
+#define CIDER_REV_GET(_v) (((_v) >> 1) & 0x7)
+
+#define KS_CGCR 0xC6
+
+#define KS_IACR 0xC8
+#define IACR_RDEN (1 << 12)
+#define IACR_TSEL_MASK (0x3 << 10)
+#define IACR_TSEL_SHIFT (10)
+#define IACR_TSEL_MIB (0x3 << 10)
+#define IACR_ADDR_MASK (0x1f << 0)
+#define IACR_ADDR_SHIFT (0)
+
+#define KS_IADLR 0xD0
+#define KS_IAHDR 0xD2
+
+#define KS_PMECR 0xD4
+#define PMECR_PME_DELAY (1 << 14)
+#define PMECR_PME_POL (1 << 12)
+#define PMECR_WOL_WAKEUP (1 << 11)
+#define PMECR_WOL_MAGICPKT (1 << 10)
+#define PMECR_WOL_LINKUP (1 << 9)
+#define PMECR_WOL_ENERGY (1 << 8)
+#define PMECR_AUTO_WAKE_EN (1 << 7)
+#define PMECR_WAKEUP_NORMAL (1 << 6)
+#define PMECR_WKEVT_MASK (0xf << 2)
+#define PMECR_WKEVT_SHIFT (2)
+#define PMECR_WKEVT_GET(_v) (((_v) >> 2) & 0xf)
+#define PMECR_WKEVT_ENERGY (0x1 << 2)
+#define PMECR_WKEVT_LINK (0x2 << 2)
+#define PMECR_WKEVT_MAGICPKT (0x4 << 2)
+#define PMECR_WKEVT_FRAME (0x8 << 2)
+#define PMECR_PM_MASK (0x3 << 0)
+#define PMECR_PM_SHIFT (0)
+#define PMECR_PM_NORMAL (0x0 << 0)
+#define PMECR_PM_ENERGY (0x1 << 0)
+#define PMECR_PM_SOFTDOWN (0x2 << 0)
+#define PMECR_PM_POWERSAVE (0x3 << 0)
+
+/* Standard MII PHY data */
+#define KS_P1MBCR 0xE4
+#define KS_P1MBSR 0xE6
+#define KS_PHY1ILR 0xE8
+#define KS_PHY1IHR 0xEA
+#define KS_P1ANAR 0xEC
+#define KS_P1ANLPR 0xEE
+
+#define KS_P1SCLMD 0xF4
+#define P1SCLMD_LEDOFF (1 << 15)
+#define P1SCLMD_TXIDS (1 << 14)
+#define P1SCLMD_RESTARTAN (1 << 13)
+#define P1SCLMD_DISAUTOMDIX (1 << 10)
+#define P1SCLMD_FORCEMDIX (1 << 9)
+#define P1SCLMD_AUTONEGEN (1 << 7)
+#define P1SCLMD_FORCE100 (1 << 6)
+#define P1SCLMD_FORCEFDX (1 << 5)
+#define P1SCLMD_ADV_FLOW (1 << 4)
+#define P1SCLMD_ADV_100BT_FDX (1 << 3)
+#define P1SCLMD_ADV_100BT_HDX (1 << 2)
+#define P1SCLMD_ADV_10BT_FDX (1 << 1)
+#define P1SCLMD_ADV_10BT_HDX (1 << 0)
+
+#define KS_P1CR 0xF6
+#define P1CR_HP_MDIX (1 << 15)
+#define P1CR_REV_POL (1 << 13)
+#define P1CR_OP_100M (1 << 10)
+#define P1CR_OP_FDX (1 << 9)
+#define P1CR_OP_MDI (1 << 7)
+#define P1CR_AN_DONE (1 << 6)
+#define P1CR_LINK_GOOD (1 << 5)
+#define P1CR_PNTR_FLOW (1 << 4)
+#define P1CR_PNTR_100BT_FDX (1 << 3)
+#define P1CR_PNTR_100BT_HDX (1 << 2)
+#define P1CR_PNTR_10BT_FDX (1 << 1)
+#define P1CR_PNTR_10BT_HDX (1 << 0)
+
+/* TX Frame control */
+
+#define TXFR_TXIC (1 << 15)
+#define TXFR_TXFID_MASK (0x3f << 0)
+#define TXFR_TXFID_SHIFT (0)
+
+/* SPI frame opcodes */
+#define KS_SPIOP_RD (0x00)
+#define KS_SPIOP_WR (0x40)
+#define KS_SPIOP_RXFIFO (0x80)
+#define KS_SPIOP_TXFIFO (0xC0)
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply
* [PATCHv2] tcp: Fix MD5 signature checking on IPv4 mapped sockets
From: John Dykstra @ 2009-07-16 15:04 UTC (permalink / raw)
To: netdev, Stephen Hemminger
This revision to the patch removes a misplaced
blank line.
---
Fix MD5 signature checking so that an IPv4 active open
to an IPv6 socket can succeed. In particular, use the
correct address family's signature generation function
for the SYN/ACK.
Reported-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: John Dykstra <john.dykstra1@gmail.com>
---
include/net/tcp.h | 5 +++++
net/ipv4/tcp_ipv4.c | 1 +
net/ipv4/tcp_output.c | 2 +-
net/ipv6/tcp_ipv6.c | 1 +
4 files changed, 8 insertions(+), 1 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 19f4150..88af843 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1425,6 +1425,11 @@ struct tcp_request_sock_ops {
#ifdef CONFIG_TCP_MD5SIG
struct tcp_md5sig_key *(*md5_lookup) (struct sock *sk,
struct request_sock *req);
+ int (*calc_md5_hash) (char *location,
+ struct tcp_md5sig_key *md5,
+ struct sock *sk,
+ struct request_sock *req,
+ struct sk_buff *skb);
#endif
};
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 5a1ca26..7c107eb 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1160,6 +1160,7 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = {
#ifdef CONFIG_TCP_MD5SIG
static struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
.md5_lookup = tcp_v4_reqsk_md5_lookup,
+ .calc_md5_hash = tcp_v4_md5_hash_skb,
};
#endif
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 5bdf08d..bd62712 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2261,7 +2261,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
#ifdef CONFIG_TCP_MD5SIG
/* Okay, we have all we need - do the md5 hash if needed */
if (md5) {
- tp->af_specific->calc_md5_hash(md5_hash_location,
+ tcp_rsk(req)->af_specific->calc_md5_hash(md5_hash_location,
md5, NULL, req, skb);
}
#endif
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 58810c6..ae3d657 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -896,6 +896,7 @@ struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
#ifdef CONFIG_TCP_MD5SIG
static struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
.md5_lookup = tcp_v6_reqsk_md5_lookup,
+ .calc_md5_hash = tcp_v6_md5_hash_skb,
};
#endif
--
1.5.4.3
^ permalink raw reply related
* Re: [PATCH] imwc3200: move iwmc3200 SDIO ids to sdio_ids.h
From: Tomas Winkler @ 2009-07-16 15:04 UTC (permalink / raw)
To: Zhu, Yi
Cc: drzeus-list@drzeus.cx, netdev@vger.kernel.org,
linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <AFAB8585072C7E48BC5C6F9C87F9B79703BA839300@pdsmsx502.ccr.corp.intel.com>
On Thu, Jul 16, 2009 at 3:23 PM, Zhu, Yi <yi.zhu@intel.com> wrote:
>
> > Subject: RE: [PATCH] imwc3200: move iwmc3200 SDIO ids to sdio_ids.h
>
> BTW, subject should be "iwmc3200".
Thanks. I'll address all comments although I'm not sure how to split
the patch since adding SDIO_VENDOR_ID_INTEL to sdio_ids.h it affects
all the drivers anyhow, so I'm suggesting to route it through netdev.
Thanks
Tomas
^ permalink raw reply
* Re: [PATCH 1/8] gianfar: Remove unused DECLARE_MAC_BUF
From: David Miller @ 2009-07-16 13:16 UTC (permalink / raw)
To: klto; +Cc: afleming, galak, netdev
In-Reply-To: <1247734285-20648-1-git-send-email-klto@zhaw.ch>
From: Tobias Klauser <klto@zhaw.ch>
Date: Thu, 16 Jul 2009 10:51:25 +0200
> Is not needed anymore because of the %pM printk conversion specifier.
>
> Signed-off-by: Tobias Klauser <klto@zhaw.ch>
I already applied the same fix from Johannes yesterday.
^ permalink raw reply
* Re: [PATCH] Add mac resource support for w90p910
From: Trilok Soni @ 2009-07-16 13:09 UTC (permalink / raw)
To: Wan ZongShun
Cc: David S. Miller, linux-netdev, linux-arm-kernel, Russell King,
Eric.miao
In-Reply-To: <4A5F21E2.90307@gmail.com>
Hi Wan,
On Thu, Jul 16, 2009 at 6:19 PM, Wan ZongShun<mcuos.com@gmail.com> wrote:
> +
> +struct platform_device w90p910_device_emc = {
static?
--
---Trilok Soni
http://triloksoni.wordpress.com
http://www.linkedin.com/in/triloksoni
^ permalink raw reply
* [PATCH] Add mac driver for w90p910
From: Wan ZongShun @ 2009-07-16 12:55 UTC (permalink / raw)
To: David S. Miller, linux-netdev, linux-arm-kernel, Eric.miao
Dear David,
This is a MAC driver patch for supporting my evaluation board
based on w90p910 SoC, which is a Winbond(nuvoton) ARM SoC.
Thanks!
Add mac driver support for evaluation board based on w90p910.
Signed-off-by: Wan ZongShun <mcuos.com@gmail.com>
---
drivers/net/arm/Kconfig | 8 +
drivers/net/arm/Makefile | 1 +
drivers/net/arm/w90p910_ether.c | 1105 +++++++++++++++++++++++++++++++++++++++
3 files changed, 1114 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/arm/w90p910_ether.c
diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig
index 2895db1..c37ee9e 100644
--- a/drivers/net/arm/Kconfig
+++ b/drivers/net/arm/Kconfig
@@ -63,3 +63,11 @@ config IXP4XX_ETH
help
Say Y here if you want to use built-in Ethernet ports
on IXP4xx processor.
+
+config W90P910_ETH
+ tristate "Nuvoton w90p910 Ethernet support"
+ depends on ARM && ARCH_W90X900
+ select PHYLIB
+ help
+ Say Y here if you want to use built-in Ethernet ports
+ on w90p910 processor.
diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile
index 811a3cc..303171f 100644
--- a/drivers/net/arm/Makefile
+++ b/drivers/net/arm/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
+obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o
diff --git a/drivers/net/arm/w90p910_ether.c b/drivers/net/arm/w90p910_ether.c
new file mode 100644
index 0000000..616fb79
--- /dev/null
+++ b/drivers/net/arm/w90p910_ether.c
@@ -0,0 +1,1105 @@
+/*
+ * Copyright (c) 2008-2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.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;version 2 of the License.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mii.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#define DRV_MODULE_NAME "w90p910-emc"
+#define DRV_MODULE_VERSION "0.1"
+
+/* Ethernet MAC Registers */
+#define REG_CAMCMR 0x00
+#define REG_CAMEN 0x04
+#define REG_CAMM_BASE 0x08
+#define REG_CAML_BASE 0x0c
+#define REG_TXDLSA 0x88
+#define REG_RXDLSA 0x8C
+#define REG_MCMDR 0x90
+#define REG_MIID 0x94
+#define REG_MIIDA 0x98
+#define REG_FFTCR 0x9C
+#define REG_TSDR 0xa0
+#define REG_RSDR 0xa4
+#define REG_DMARFC 0xa8
+#define REG_MIEN 0xac
+#define REG_MISTA 0xb0
+#define REG_CTXDSA 0xcc
+#define REG_CTXBSA 0xd0
+#define REG_CRXDSA 0xd4
+#define REG_CRXBSA 0xd8
+
+/* mac controller bit */
+#define MCMDR_RXON 0x01
+#define MCMDR_ACP (0x01 << 3)
+#define MCMDR_SPCRC (0x01 << 5)
+#define MCMDR_TXON (0x01 << 8)
+#define MCMDR_FDUP (0x01 << 18)
+#define MCMDR_ENMDC (0x01 << 19)
+#define MCMDR_OPMOD (0x01 << 20)
+#define SWR (0x01 << 24)
+
+/* cam command regiser */
+#define CAMCMR_AUP 0x01
+#define CAMCMR_AMP (0x01 << 1)
+#define CAMCMR_ABP (0x01 << 2)
+#define CAMCMR_CCAM (0x01 << 3)
+#define CAMCMR_ECMP (0x01 << 4)
+#define CAM0EN 0x01
+
+/* mac mii controller bit */
+#define MDCCR (0x0a << 20)
+#define PHYAD (0x01 << 8)
+#define PHYWR (0x01 << 16)
+#define PHYBUSY (0x01 << 17)
+#define PHYPRESP (0x01 << 18)
+#define CAM_ENTRY_SIZE 0x08
+
+/* rx and tx status */
+#define TXDS_TXCP (0x01 << 19)
+#define RXDS_CRCE (0x01 << 17)
+#define RXDS_PTLE (0x01 << 19)
+#define RXDS_RXGD (0x01 << 20)
+#define RXDS_ALIE (0x01 << 21)
+#define RXDS_RP (0x01 << 22)
+
+/* mac interrupt status*/
+#define MISTA_EXDEF (0x01 << 19)
+#define MISTA_TXBERR (0x01 << 24)
+#define MISTA_TDU (0x01 << 23)
+#define MISTA_RDU (0x01 << 10)
+#define MISTA_RXBERR (0x01 << 11)
+
+#define ENSTART 0x01
+#define ENRXINTR 0x01
+#define ENRXGD (0x01 << 4)
+#define ENRXBERR (0x01 << 11)
+#define ENTXINTR (0x01 << 16)
+#define ENTXCP (0x01 << 18)
+#define ENTXABT (0x01 << 21)
+#define ENTXBERR (0x01 << 24)
+#define ENMDC (0x01 << 19)
+#define PHYBUSY (0x01 << 17)
+#define MDCCR_VAL 0xa00000
+
+/* rx and tx owner bit */
+#define RX_OWEN_DMA (0x01 << 31)
+#define RX_OWEN_CPU (~(0x03 << 30))
+#define TX_OWEN_DMA (0x01 << 31)
+#define TX_OWEN_CPU (~(0x01 << 31))
+
+/* tx frame desc controller bit */
+#define MACTXINTEN 0x04
+#define CRCMODE 0x02
+#define PADDINGMODE 0x01
+
+/* fftcr controller bit */
+#define TXTHD (0x03 << 8)
+#define BLENGTH (0x01 << 20)
+
+/* global setting for driver */
+#define RX_DESC_SIZE 50
+#define TX_DESC_SIZE 10
+#define MAX_RBUFF_SZ 0x600
+#define MAX_TBUFF_SZ 0x600
+#define TX_TIMEOUT 50
+#define DELAY 1000
+#define CAM0 0x0
+
+static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg);
+
+struct w90p910_rxbd {
+ unsigned int sl;
+ unsigned int buffer;
+ unsigned int reserved;
+ unsigned int next;
+};
+
+struct w90p910_txbd {
+ unsigned int mode;
+ unsigned int buffer;
+ unsigned int sl;
+ unsigned int next;
+};
+
+struct recv_pdesc {
+ struct w90p910_rxbd desclist[RX_DESC_SIZE];
+ char recv_buf[RX_DESC_SIZE][MAX_RBUFF_SZ];
+};
+
+struct tran_pdesc {
+ struct w90p910_txbd desclist[TX_DESC_SIZE];
+ char tran_buf[RX_DESC_SIZE][MAX_TBUFF_SZ];
+};
+
+struct w90p910_ether {
+ struct recv_pdesc *rdesc;
+ struct recv_pdesc *rdesc_phys;
+ struct tran_pdesc *tdesc;
+ struct tran_pdesc *tdesc_phys;
+ struct net_device_stats stats;
+ struct platform_device *pdev;
+ struct sk_buff *skb;
+ struct clk *clk;
+ struct clk *rmiiclk;
+ struct mii_if_info mii;
+ struct timer_list check_timer;
+ void __iomem *reg;
+ unsigned int rxirq;
+ unsigned int txirq;
+ unsigned int cur_tx;
+ unsigned int cur_rx;
+ unsigned int finish_tx;
+ unsigned int rx_packets;
+ unsigned int rx_bytes;
+ unsigned int start_tx_ptr;
+ unsigned int start_rx_ptr;
+ unsigned int linkflag;
+ spinlock_t lock;
+};
+
+static void update_linkspeed_register(struct net_device *dev,
+ unsigned int speed, unsigned int duplex)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = __raw_readl(ether->reg + REG_MCMDR);
+
+ if (speed == SPEED_100) {
+ /* 100 full/half duplex */
+ if (duplex == DUPLEX_FULL) {
+ val |= (MCMDR_OPMOD | MCMDR_FDUP);
+ } else {
+ val |= MCMDR_OPMOD;
+ val &= ~MCMDR_FDUP;
+ }
+ } else {
+ /* 10 full/half duplex */
+ if (duplex == DUPLEX_FULL) {
+ val |= MCMDR_FDUP;
+ val &= ~MCMDR_OPMOD;
+ } else {
+ val &= ~(MCMDR_FDUP | MCMDR_OPMOD);
+ }
+ }
+
+ __raw_writel(val, ether->reg + REG_MCMDR);
+}
+
+static void update_linkspeed(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev;
+ unsigned int bmsr, bmcr, lpa, speed, duplex;
+
+ pdev = ether->pdev;
+
+ if (!mii_link_ok(ðer->mii)) {
+ ether->linkflag = 0x0;
+ netif_carrier_off(dev);
+ dev_warn(&pdev->dev, "%s: Link down.\n", dev->name);
+ return;
+ }
+
+ if (ether->linkflag == 1)
+ return;
+
+ bmsr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMSR);
+ bmcr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMCR);
+
+ if (bmcr & BMCR_ANENABLE) {
+ if (!(bmsr & BMSR_ANEGCOMPLETE))
+ return;
+
+ lpa = w90p910_mdio_read(dev, ether->mii.phy_id, MII_LPA);
+
+ if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF))
+ speed = SPEED_100;
+ else
+ speed = SPEED_10;
+
+ if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL))
+ duplex = DUPLEX_FULL;
+ else
+ duplex = DUPLEX_HALF;
+
+ } else {
+ speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10;
+ duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
+ }
+
+ update_linkspeed_register(dev, speed, duplex);
+
+ dev_info(&pdev->dev, "%s: Link now %i-%s\n", dev->name, speed,
+ (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex");
+ ether->linkflag = 0x01;
+
+ netif_carrier_on(dev);
+}
+
+static void w90p910_check_link(unsigned long dev_id)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ update_linkspeed(dev);
+ mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000));
+}
+
+static void w90p910_write_cam(struct net_device *dev,
+ unsigned int x, unsigned char *pval)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int msw, lsw;
+
+ msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3];
+
+ lsw = (pval[4] << 24) | (pval[5] << 16);
+
+ __raw_writel(lsw, ether->reg + REG_CAML_BASE + x * CAM_ENTRY_SIZE);
+ __raw_writel(msw, ether->reg + REG_CAMM_BASE + x * CAM_ENTRY_SIZE);
+}
+
+static void w90p910_init_desc(struct net_device *dev)
+{
+ struct w90p910_ether *ether;
+ struct w90p910_txbd *tdesc, *tdesc_phys;
+ struct w90p910_rxbd *rdesc, *rdesc_phys;
+ unsigned int i, j;
+
+ ether = netdev_priv(dev);
+
+ ether->tdesc = (struct tran_pdesc *)
+ dma_alloc_coherent(NULL, sizeof(struct tran_pdesc),
+ (dma_addr_t *) ðer->tdesc_phys, GFP_KERNEL);
+
+ ether->rdesc = (struct recv_pdesc *)
+ dma_alloc_coherent(NULL, sizeof(struct recv_pdesc),
+ (dma_addr_t *) ðer->rdesc_phys, GFP_KERNEL);
+
+ for (i = 0; i < TX_DESC_SIZE; i++) {
+ tdesc = &(ether->tdesc->desclist[i]);
+
+ j = ((i + 1) / TX_DESC_SIZE);
+
+ if (j != 0) {
+ tdesc_phys = &(ether->tdesc_phys->desclist[0]);
+ ether->start_tx_ptr = (unsigned int)tdesc_phys;
+ tdesc->next = (unsigned int)ether->start_tx_ptr;
+ } else {
+ tdesc_phys = &(ether->tdesc_phys->desclist[i+1]);
+ tdesc->next = (unsigned int)tdesc_phys;
+ }
+
+ tdesc->buffer = (unsigned int)ether->tdesc_phys->tran_buf[i];
+ tdesc->sl = 0;
+ tdesc->mode = 0;
+ }
+
+ for (i = 0; i < RX_DESC_SIZE; i++) {
+ rdesc = &(ether->rdesc->desclist[i]);
+
+ j = ((i + 1) / RX_DESC_SIZE);
+
+ if (j != 0) {
+ rdesc_phys = &(ether->rdesc_phys->desclist[0]);
+ ether->start_rx_ptr = (unsigned int)rdesc_phys;
+ rdesc->next = (unsigned int)ether->start_rx_ptr;
+ } else {
+ rdesc_phys = &(ether->rdesc_phys->desclist[i+1]);
+ rdesc->next = (unsigned int)rdesc_phys;
+ }
+
+ rdesc->sl = RX_OWEN_DMA;
+ rdesc->buffer = (unsigned int)ether->rdesc_phys->recv_buf[i];
+ }
+}
+
+static void w90p910_set_fifo_threshold(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = TXTHD | BLENGTH;
+ __raw_writel(val, ether->reg + REG_FFTCR);
+}
+
+static void w90p910_return_default_idle(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = __raw_readl(ether->reg + REG_MCMDR);
+ val |= SWR;
+ __raw_writel(val, ether->reg + REG_MCMDR);
+}
+
+static void w90p910_trigger_rx(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ __raw_writel(ENSTART, ether->reg + REG_RSDR);
+}
+
+static void w90p910_trigger_tx(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ __raw_writel(ENSTART, ether->reg + REG_TSDR);
+}
+
+static void w90p910_enable_mac_interrupt(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = ENTXINTR | ENRXINTR | ENRXGD | ENTXCP;
+ val |= ENTXBERR | ENRXBERR | ENTXABT;
+
+ __raw_writel(val, ether->reg + REG_MIEN);
+}
+
+static void w90p910_get_and_clear_int(struct net_device *dev,
+ unsigned int *val)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ *val = __raw_readl(ether->reg + REG_MISTA);
+ __raw_writel(*val, ether->reg + REG_MISTA);
+}
+
+static void w90p910_set_global_maccmd(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = __raw_readl(ether->reg + REG_MCMDR);
+ val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | ENMDC;
+ __raw_writel(val, ether->reg + REG_MCMDR);
+}
+
+static void w90p910_enable_cam(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ w90p910_write_cam(dev, CAM0, dev->dev_addr);
+
+ val = __raw_readl(ether->reg + REG_CAMEN);
+ val |= CAM0EN;
+ __raw_writel(val, ether->reg + REG_CAMEN);
+}
+
+static void w90p910_enable_cam_command(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = CAMCMR_ECMP | CAMCMR_ABP | CAMCMR_AMP;
+ __raw_writel(val, ether->reg + REG_CAMCMR);
+}
+
+static void w90p910_enable_tx(struct net_device *dev, unsigned int enable)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = __raw_readl(ether->reg + REG_MCMDR);
+
+ if (enable)
+ val |= MCMDR_TXON;
+ else
+ val &= ~MCMDR_TXON;
+
+ __raw_writel(val, ether->reg + REG_MCMDR);
+}
+
+static void w90p910_enable_rx(struct net_device *dev, unsigned int enable)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = __raw_readl(ether->reg + REG_MCMDR);
+
+ if (enable)
+ val |= MCMDR_RXON;
+ else
+ val &= ~MCMDR_RXON;
+
+ __raw_writel(val, ether->reg + REG_MCMDR);
+}
+
+static void w90p910_set_curdest(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ __raw_writel(ether->start_rx_ptr, ether->reg + REG_RXDLSA);
+ __raw_writel(ether->start_tx_ptr, ether->reg + REG_TXDLSA);
+}
+
+static void w90p910_reset_mac(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ spin_lock(ðer->lock);
+
+ w90p910_enable_tx(dev, 0);
+ w90p910_enable_rx(dev, 0);
+ w90p910_set_fifo_threshold(dev);
+ w90p910_return_default_idle(dev);
+
+ if (!netif_queue_stopped(dev))
+ netif_stop_queue(dev);
+
+ w90p910_init_desc(dev);
+
+ dev->trans_start = jiffies;
+ ether->cur_tx = 0x0;
+ ether->finish_tx = 0x0;
+ ether->cur_rx = 0x0;
+
+ w90p910_set_curdest(dev);
+ w90p910_enable_cam(dev);
+ w90p910_enable_cam_command(dev);
+ w90p910_enable_mac_interrupt(dev);
+ w90p910_enable_tx(dev, 1);
+ w90p910_enable_rx(dev, 1);
+ w90p910_trigger_tx(dev);
+ w90p910_trigger_rx(dev);
+
+ dev->trans_start = jiffies;
+
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+
+ spin_unlock(ðer->lock);
+}
+
+static void w90p910_mdio_write(struct net_device *dev,
+ int phy_id, int reg, int data)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev;
+ unsigned int val, i;
+
+ pdev = ether->pdev;
+
+ __raw_writel(data, ether->reg + REG_MIID);
+
+ val = (phy_id << 0x08) | reg;
+ val |= PHYBUSY | PHYWR | MDCCR_VAL;
+ __raw_writel(val, ether->reg + REG_MIIDA);
+
+ for (i = 0; i < DELAY; i++) {
+ if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0)
+ break;
+ }
+
+ if (i == DELAY)
+ dev_warn(&pdev->dev, "mdio write timed out\n");
+}
+
+static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev;
+ unsigned int val, i, data;
+
+ pdev = ether->pdev;
+
+ val = (phy_id << 0x08) | reg;
+ val |= PHYBUSY | MDCCR_VAL;
+ __raw_writel(val, ether->reg + REG_MIIDA);
+
+ for (i = 0; i < DELAY; i++) {
+ if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0)
+ break;
+ }
+
+ if (i == DELAY) {
+ dev_warn(&pdev->dev, "mdio read timed out\n");
+ data = 0xffff;
+ } else {
+ data = __raw_readl(ether->reg + REG_MIID);
+ }
+
+ return data;
+}
+
+static int set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+ w90p910_write_cam(dev, CAM0, dev->dev_addr);
+
+ return 0;
+}
+
+static int w90p910_ether_close(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ dma_free_writecombine(NULL, sizeof(struct w90p910_rxbd),
+ ether->rdesc, (dma_addr_t)ether->rdesc_phys);
+ dma_free_writecombine(NULL, sizeof(struct w90p910_txbd),
+ ether->tdesc, (dma_addr_t)ether->tdesc_phys);
+
+ netif_stop_queue(dev);
+
+ del_timer_sync(ðer->check_timer);
+ clk_disable(ether->rmiiclk);
+ clk_disable(ether->clk);
+
+ free_irq(ether->txirq, dev);
+ free_irq(ether->rxirq, dev);
+
+ return 0;
+}
+
+static struct net_device_stats *w90p910_ether_stats(struct net_device *dev)
+{
+ struct w90p910_ether *ether;
+
+ ether = netdev_priv(dev);
+
+ return ðer->stats;
+}
+
+static int w90p910_send_frame(struct net_device *dev,
+ unsigned char *data, int length)
+{
+ struct w90p910_ether *ether;
+ struct w90p910_txbd *txbd;
+ struct platform_device *pdev;
+ unsigned char *buffer;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ txbd = ðer->tdesc->desclist[ether->cur_tx];
+ buffer = ether->tdesc->tran_buf[ether->cur_tx];
+ if (length > 1514) {
+ dev_err(&pdev->dev, "send data %d bytes, check it\n", length);
+ length = 1514;
+ }
+
+ txbd->sl = length & 0xFFFF;
+
+ memcpy(buffer, data, length);
+
+ txbd->mode = TX_OWEN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN;
+
+ w90p910_enable_tx(dev, 1);
+
+ w90p910_trigger_tx(dev);
+
+ ether->cur_tx = (ether->cur_tx+1) % TX_DESC_SIZE;
+ txbd = ðer->tdesc->desclist[ether->cur_tx];
+
+ dev->trans_start = jiffies;
+
+ if (txbd->mode & TX_OWEN_DMA)
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+static int w90p910_ether_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ if (!(w90p910_send_frame(dev, skb->data, skb->len))) {
+ ether->skb = skb;
+ dev_kfree_skb_irq(skb);
+ return 0;
+ }
+ return -1;
+}
+
+static irqreturn_t w90p910_tx_interrupt(int irq, void *dev_id)
+{
+ struct w90p910_ether *ether;
+ struct w90p910_txbd *txbd;
+ struct platform_device *pdev;
+ struct tran_pdesc *tran_pdesc;
+ struct net_device *dev;
+ unsigned int cur_entry, entry, status;
+
+ dev = (struct net_device *)dev_id;
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ spin_lock(ðer->lock);
+
+ w90p910_get_and_clear_int(dev, &status);
+
+ cur_entry = __raw_readl(ether->reg + REG_CTXDSA);
+
+ tran_pdesc = ether->tdesc_phys;
+ entry = (unsigned int)(&tran_pdesc->desclist[ether->finish_tx]);
+
+ while (entry != cur_entry) {
+ txbd = ðer->tdesc->desclist[ether->finish_tx];
+
+ ether->finish_tx = (ether->finish_tx + 1) % TX_DESC_SIZE;
+
+ if (txbd->sl & TXDS_TXCP) {
+ ether->stats.tx_packets++;
+ ether->stats.tx_bytes += txbd->sl & 0xFFFF;
+ } else {
+ ether->stats.tx_errors++;
+ }
+
+ txbd->sl = 0x0;
+ txbd->mode = 0x0;
+
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+
+ entry = (unsigned int)(&tran_pdesc->desclist[ether->finish_tx]);
+ }
+
+ if (status & MISTA_EXDEF) {
+ dev_err(&pdev->dev, "emc defer exceed interrupt\n");
+ } else if (status & MISTA_TXBERR) {
+ dev_err(&pdev->dev, "emc bus error interrupt\n");
+ w90p910_reset_mac(dev);
+ } else if (status & MISTA_TDU) {
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ }
+
+ spin_unlock(ðer->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void netdev_rx(struct net_device *dev)
+{
+ struct w90p910_ether *ether;
+ struct w90p910_rxbd *rxbd;
+ struct platform_device *pdev;
+ struct recv_pdesc *rdesc_phys;
+ struct sk_buff *skb;
+ unsigned char *data;
+ unsigned int length, status, val, entry;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+ rdesc_phys = ether->rdesc_phys;
+
+ rxbd = ðer->rdesc->desclist[ether->cur_rx];
+
+ do {
+ val = __raw_readl(ether->reg + REG_CRXDSA);
+ entry = (unsigned int)&rdesc_phys->desclist[ether->cur_rx];
+
+ if (val == entry)
+ break;
+
+ status = rxbd->sl;
+ length = status & 0xFFFF;
+
+ if (status & RXDS_RXGD) {
+ data = ether->rdesc->recv_buf[ether->cur_rx];
+ skb = dev_alloc_skb(length+2);
+ if (!skb) {
+ dev_err(&pdev->dev, "get skb buffer error\n");
+ ether->stats.rx_dropped++;
+ return;
+ }
+
+ skb->dev = dev;
+ skb_reserve(skb, 2);
+ skb_put(skb, length);
+ skb_copy_to_linear_data(skb, data, length);
+ skb->protocol = eth_type_trans(skb, dev);
+ ether->stats.rx_packets++;
+ ether->stats.rx_bytes += length;
+ netif_rx(skb);
+ } else {
+ ether->stats.rx_errors++;
+
+ if (status & RXDS_RP) {
+ dev_err(&pdev->dev, "rx runt err\n");
+ ether->stats.rx_length_errors++;
+ } else if (status & RXDS_CRCE) {
+ dev_err(&pdev->dev, "rx crc err\n");
+ ether->stats.rx_crc_errors++;
+ }
+
+ if (status & RXDS_ALIE) {
+ dev_err(&pdev->dev, "rx aligment err\n");
+ ether->stats.rx_frame_errors++;
+ } else if (status & RXDS_PTLE) {
+ dev_err(&pdev->dev, "rx longer err\n");
+ ether->stats.rx_over_errors++;
+ }
+ }
+
+ rxbd->sl = RX_OWEN_DMA;
+ rxbd->reserved = 0x0;
+ ether->cur_rx = (ether->cur_rx+1) % RX_DESC_SIZE;
+ rxbd = ðer->rdesc->desclist[ether->cur_rx];
+
+ dev->last_rx = jiffies;
+ } while (1);
+}
+
+static irqreturn_t w90p910_rx_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev;
+ struct w90p910_ether *ether;
+ struct platform_device *pdev;
+ unsigned int status;
+
+ dev = (struct net_device *)dev_id;
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ spin_lock(ðer->lock);
+
+ w90p910_get_and_clear_int(dev, &status);
+
+ if (status & MISTA_RDU) {
+ netdev_rx(dev);
+
+ w90p910_trigger_rx(dev);
+
+ spin_unlock(ðer->lock);
+ return IRQ_HANDLED;
+ } else if (status & MISTA_RXBERR) {
+ dev_err(&pdev->dev, "emc rx bus error\n");
+ w90p910_reset_mac(dev);
+ }
+
+ netdev_rx(dev);
+ spin_unlock(ðer->lock);
+ return IRQ_HANDLED;
+}
+
+static int w90p910_ether_open(struct net_device *dev)
+{
+ struct w90p910_ether *ether;
+ struct platform_device *pdev;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ w90p910_reset_mac(dev);
+ w90p910_set_fifo_threshold(dev);
+ w90p910_set_curdest(dev);
+ w90p910_enable_cam(dev);
+ w90p910_enable_cam_command(dev);
+ w90p910_enable_mac_interrupt(dev);
+ w90p910_set_global_maccmd(dev);
+ w90p910_enable_rx(dev, 1);
+
+ ether->rx_packets = 0x0;
+ ether->rx_bytes = 0x0;
+
+ if (request_irq(ether->txirq, w90p910_tx_interrupt,
+ 0x0, pdev->name, dev)) {
+ dev_err(&pdev->dev, "register irq tx failed\n");
+ return -EAGAIN;
+ }
+
+ if (request_irq(ether->rxirq, w90p910_rx_interrupt,
+ 0x0, pdev->name, dev)) {
+ dev_err(&pdev->dev, "register irq rx failed\n");
+ return -EAGAIN;
+ }
+
+ mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000));
+ netif_start_queue(dev);
+ w90p910_trigger_rx(dev);
+
+ dev_info(&pdev->dev, "%s is OPENED\n", dev->name);
+
+ return 0;
+}
+
+static void w90p910_ether_set_multicast_list(struct net_device *dev)
+{
+ struct w90p910_ether *ether;
+ unsigned int rx_mode;
+
+ ether = netdev_priv(dev);
+
+ if (dev->flags & IFF_PROMISC)
+ rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP;
+ else if ((dev->flags & IFF_ALLMULTI) || dev->mc_list)
+ rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP;
+ else
+ rx_mode = CAMCMR_ECMP | CAMCMR_ABP;
+ __raw_writel(rx_mode, ether->reg + REG_CAMCMR);
+}
+
+static int w90p910_ether_ioctl(struct net_device *dev,
+ struct ifreq *ifr, int cmd)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ struct mii_ioctl_data *data = if_mii(ifr);
+
+ return generic_mii_ioctl(ðer->mii, data, cmd, NULL);
+}
+
+static void w90p910_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_MODULE_NAME);
+ strcpy(info->version, DRV_MODULE_VERSION);
+}
+
+static int w90p910_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ return mii_ethtool_gset(ðer->mii, cmd);
+}
+
+static int w90p910_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ return mii_ethtool_sset(ðer->mii, cmd);
+}
+
+static int w90p910_nway_reset(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ return mii_nway_restart(ðer->mii);
+}
+
+static u32 w90p910_get_link(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ return mii_link_ok(ðer->mii);
+}
+
+static const struct ethtool_ops w90p910_ether_ethtool_ops = {
+ .get_settings = w90p910_get_settings,
+ .set_settings = w90p910_set_settings,
+ .get_drvinfo = w90p910_get_drvinfo,
+ .nway_reset = w90p910_nway_reset,
+ .get_link = w90p910_get_link,
+};
+
+static const struct net_device_ops w90p910_ether_netdev_ops = {
+ .ndo_open = w90p910_ether_open,
+ .ndo_stop = w90p910_ether_close,
+ .ndo_start_xmit = w90p910_ether_start_xmit,
+ .ndo_get_stats = w90p910_ether_stats,
+ .ndo_set_multicast_list = w90p910_ether_set_multicast_list,
+ .ndo_set_mac_address = set_mac_address,
+ .ndo_do_ioctl = w90p910_ether_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static void __init get_mac_address(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev;
+ char addr[6];
+
+ pdev = ether->pdev;
+
+ addr[0] = 0x00;
+ addr[1] = 0x02;
+ addr[2] = 0xac;
+ addr[3] = 0x55;
+ addr[4] = 0x88;
+ addr[5] = 0xa8;
+
+ if (is_valid_ether_addr(addr))
+ memcpy(dev->dev_addr, &addr, 0x06);
+ else
+ dev_err(&pdev->dev, "invalid mac address\n");
+}
+
+static int w90p910_ether_setup(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ ether_setup(dev);
+ dev->netdev_ops = &w90p910_ether_netdev_ops;
+ dev->ethtool_ops = &w90p910_ether_ethtool_ops;
+
+ dev->tx_queue_len = 16;
+ dev->dma = 0x0;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ get_mac_address(dev);
+
+ spin_lock_init(ðer->lock);
+
+ ether->cur_tx = 0x0;
+ ether->cur_rx = 0x0;
+ ether->finish_tx = 0x0;
+ ether->linkflag = 0x0;
+ ether->mii.phy_id = 0x01;
+ ether->mii.phy_id_mask = 0x1f;
+ ether->mii.reg_num_mask = 0x1f;
+ ether->mii.dev = dev;
+ ether->mii.mdio_read = w90p910_mdio_read;
+ ether->mii.mdio_write = w90p910_mdio_write;
+
+ setup_timer(ðer->check_timer, w90p910_check_link,
+ (unsigned long)dev);
+
+ return 0;
+}
+
+static int __devinit w90p910_ether_probe(struct platform_device *pdev)
+{
+ struct w90p910_ether *ether;
+ struct net_device *dev;
+ struct resource *res;
+ int error;
+
+ dev = alloc_etherdev(sizeof(struct w90p910_ether));
+ if (!dev)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
+ error = -ENXIO;
+ goto failed_free;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto failed_free;
+ }
+
+ ether = netdev_priv(dev);
+
+ ether->reg = ioremap(res->start, resource_size(res));
+ if (ether->reg == NULL) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto failed_free_mem;
+ }
+
+ ether->txirq = platform_get_irq(pdev, 0);
+ if (ether->txirq < 0) {
+ dev_err(&pdev->dev, "failed to get ether tx irq\n");
+ error = -ENXIO;
+ goto failed_free_io;
+ }
+
+ ether->rxirq = platform_get_irq(pdev, 1);
+ if (ether->rxirq < 0) {
+ dev_err(&pdev->dev, "failed to get ether rx irq\n");
+ error = -ENXIO;
+ goto failed_free_txirq;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ ether->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(ether->clk)) {
+ dev_err(&pdev->dev, "failed to get ether clock\n");
+ error = PTR_ERR(ether->clk);
+ goto failed_free_rxirq;
+ }
+
+ ether->rmiiclk = clk_get(&pdev->dev, "RMII");
+ if (IS_ERR(ether->rmiiclk)) {
+ dev_err(&pdev->dev, "failed to get ether clock\n");
+ error = PTR_ERR(ether->rmiiclk);
+ goto failed_put_clk;
+ }
+
+ ether->pdev = pdev;
+
+ w90p910_ether_setup(dev);
+
+ error = register_netdev(dev);
+ if (error != 0) {
+ dev_err(&pdev->dev, "Regiter EMC w90p910 FAILED\n");
+ error = -ENODEV;
+ goto failed_put_rmiiclk;
+ }
+
+ return 0;
+failed_put_rmiiclk:
+ clk_put(ether->rmiiclk);
+failed_put_clk:
+ clk_put(ether->clk);
+failed_free_rxirq:
+ free_irq(ether->rxirq, pdev);
+ platform_set_drvdata(pdev, NULL);
+failed_free_txirq:
+ free_irq(ether->txirq, pdev);
+failed_free_io:
+ iounmap(ether->reg);
+failed_free_mem:
+ release_mem_region(res->start, resource_size(res));
+failed_free:
+ free_netdev(dev);
+ return error;
+}
+
+static int __devexit w90p910_ether_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ unregister_netdev(dev);
+ clk_put(ether->rmiiclk);
+ clk_put(ether->clk);
+ del_timer_sync(ðer->check_timer);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+ return 0;
+}
+
+static struct platform_driver w90p910_ether_driver = {
+ .probe = w90p910_ether_probe,
+ .remove = __devexit_p(w90p910_ether_remove),
+ .driver = {
+ .name = "w90p910-emc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init w90p910_ether_init(void)
+{
+ return platform_driver_register(&w90p910_ether_driver);
+}
+
+static void __exit w90p910_ether_exit(void)
+{
+ platform_driver_unregister(&w90p910_ether_driver);
+}
+
+module_init(w90p910_ether_init);
+module_exit(w90p910_ether_exit);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("w90p910 MAC driver!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:w90p910-emc");
+
--
1.5.6.3
^ permalink raw reply related
* [PATCH] Add mac resource support for w90p910
From: Wan ZongShun @ 2009-07-16 12:49 UTC (permalink / raw)
To: David S. Miller, linux-netdev, linux-arm-kernel, Russell King,
"Er
Dear Russell,
This is a mac resource define for my MAC driver for w90p910 platform.
thanks!
Add mac resource support for w90p910 mac driver
Signed-off-by: Wan ZongShun <mcuos.com@gmail.com>
---
arch/arm/mach-w90x900/mach-w90p910evb.c | 28 ++++++++++++++++++++++++++++
1 files changed, 28 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-w90x900/mach-w90p910evb.c b/arch/arm/mach-w90x900/mach-w90p910evb.c
index 7a62bd3..06e1155 100644
--- a/arch/arm/mach-w90x900/mach-w90p910evb.c
+++ b/arch/arm/mach-w90x900/mach-w90p910evb.c
@@ -228,6 +228,33 @@ struct platform_device w90x900_device_usbgadget = {
};
EXPORT_SYMBOL(w90x900_device_usbgadget);
+/* EMC Device */
+
+static struct resource w90x900_emc_resource[] = {
+ [0] = {
+ .start = W90X900_PA_EMC,
+ .end = W90X900_PA_EMC + W90X900_SZ_EMC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_EMCTX,
+ .end = IRQ_EMCTX,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_EMCRX,
+ .end = IRQ_EMCRX,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device w90p910_device_emc = {
+ .name = "w90p910-emc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(w90x900_emc_resource),
+ .resource = w90x900_emc_resource,
+};
+
static struct map_desc w90p910_iodesc[] __initdata = {
};
@@ -242,6 +269,7 @@ static struct platform_device *w90p910evb_dev[] __initdata = {
&w90x900_device_rtc,
&w90x900_device_kpi,
&w90x900_device_usbgadget,
+ &w90p910_device_emc,
};
static void __init w90p910evb_map_io(void)
--
1.5.6.3
^ permalink raw reply related
* netfilter 03/03: nf_conntrack: nf_conntrack_alloc() fixes
From: Patrick McHardy @ 2009-07-16 12:26 UTC (permalink / raw)
To: davem; +Cc: netdev, Patrick McHardy, netfilter-devel
In-Reply-To: <20090716122638.23343.77832.sendpatchset@x2.localnet>
commit 941297f443f871b8c3372feccf27a8733f6ce9e9
Author: Eric Dumazet <eric.dumazet@gmail.com>
Date: Thu Jul 16 14:03:40 2009 +0200
netfilter: nf_conntrack: nf_conntrack_alloc() fixes
When a slab cache uses SLAB_DESTROY_BY_RCU, we must be careful when allocating
objects, since slab allocator could give a freed object still used by lockless
readers.
In particular, nf_conntrack RCU lookups rely on ct->tuplehash[xxx].hnnode.next
being always valid (ie containing a valid 'nulls' value, or a valid pointer to next
object in hash chain.)
kmem_cache_zalloc() setups object with NULL values, but a NULL value is not valid
for ct->tuplehash[xxx].hnnode.next.
Fix is to call kmem_cache_alloc() and do the zeroing ourself.
As spotted by Patrick, we also need to make sure lookup keys are committed to
memory before setting refcount to 1, or a lockless reader could get a reference
on the old version of the object. Its key re-check could then pass the barrier.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>
diff --git a/Documentation/RCU/rculist_nulls.txt b/Documentation/RCU/rculist_nulls.txt
index 93cb28d..18f9651 100644
--- a/Documentation/RCU/rculist_nulls.txt
+++ b/Documentation/RCU/rculist_nulls.txt
@@ -83,11 +83,12 @@ not detect it missed following items in original chain.
obj = kmem_cache_alloc(...);
lock_chain(); // typically a spin_lock()
obj->key = key;
-atomic_inc(&obj->refcnt);
/*
* we need to make sure obj->key is updated before obj->next
+ * or obj->refcnt
*/
smp_wmb();
+atomic_set(&obj->refcnt, 1);
hlist_add_head_rcu(&obj->obj_node, list);
unlock_chain(); // typically a spin_unlock()
@@ -159,6 +160,10 @@ out:
obj = kmem_cache_alloc(cachep);
lock_chain(); // typically a spin_lock()
obj->key = key;
+/*
+ * changes to obj->key must be visible before refcnt one
+ */
+smp_wmb();
atomic_set(&obj->refcnt, 1);
/*
* insert obj in RCU way (readers might be traversing chain)
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 7508f11..b5869b9 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -561,23 +561,38 @@ struct nf_conn *nf_conntrack_alloc(struct net *net,
}
}
- ct = kmem_cache_zalloc(nf_conntrack_cachep, gfp);
+ /*
+ * Do not use kmem_cache_zalloc(), as this cache uses
+ * SLAB_DESTROY_BY_RCU.
+ */
+ ct = kmem_cache_alloc(nf_conntrack_cachep, gfp);
if (ct == NULL) {
pr_debug("nf_conntrack_alloc: Can't alloc conntrack.\n");
atomic_dec(&net->ct.count);
return ERR_PTR(-ENOMEM);
}
-
+ /*
+ * Let ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.next
+ * and ct->tuplehash[IP_CT_DIR_REPLY].hnnode.next unchanged.
+ */
+ memset(&ct->tuplehash[IP_CT_DIR_MAX], 0,
+ sizeof(*ct) - offsetof(struct nf_conn, tuplehash[IP_CT_DIR_MAX]));
spin_lock_init(&ct->lock);
- atomic_set(&ct->ct_general.use, 1);
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig;
+ ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.pprev = NULL;
ct->tuplehash[IP_CT_DIR_REPLY].tuple = *repl;
+ ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev = NULL;
/* Don't set timer yet: wait for confirmation */
setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct);
#ifdef CONFIG_NET_NS
ct->ct_net = net;
#endif
+ /*
+ * changes to lookup keys must be done before setting refcnt to 1
+ */
+ smp_wmb();
+ atomic_set(&ct->ct_general.use, 1);
return ct;
}
EXPORT_SYMBOL_GPL(nf_conntrack_alloc);
^ permalink raw reply related
* netfilter 02/03: xt_osf: fix nf_log_packet() arguments
From: Patrick McHardy @ 2009-07-16 12:26 UTC (permalink / raw)
To: davem; +Cc: netdev, Patrick McHardy, netfilter-devel
In-Reply-To: <20090716122638.23343.77832.sendpatchset@x2.localnet>
commit aa6a03eb0ae859c1371555ef381de4c96ca1e4e6
Author: Patrick McHardy <kaber@trash.net>
Date: Thu Jul 16 14:01:54 2009 +0200
netfilter: xt_osf: fix nf_log_packet() arguments
The first argument is the address family, the second one the hook
number.
Signed-off-by: Patrick McHardy <kaber@trash.net>
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index 863e409..0f482e2 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -330,7 +330,8 @@ static bool xt_osf_match_packet(const struct sk_buff *skb,
fcount++;
if (info->flags & XT_OSF_LOG)
- nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
+ nf_log_packet(p->family, p->hooknum, skb,
+ p->in, p->out, NULL,
"%s [%s:%s] : %pi4:%d -> %pi4:%d hops=%d\n",
f->genre, f->version, f->subtype,
&ip->saddr, ntohs(tcp->source),
@@ -345,7 +346,7 @@ static bool xt_osf_match_packet(const struct sk_buff *skb,
rcu_read_unlock();
if (!fcount && (info->flags & XT_OSF_LOG))
- nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
+ nf_log_packet(p->family, p->hooknum, skb, p->in, p->out, NULL,
"Remote OS is not known: %pi4:%u -> %pi4:%u\n",
&ip->saddr, ntohs(tcp->source),
&ip->daddr, ntohs(tcp->dest));
^ permalink raw reply related
* netfilter 01/03: add netfilter git to MAINTAINERS
From: Patrick McHardy @ 2009-07-16 12:26 UTC (permalink / raw)
To: davem; +Cc: netdev, Patrick McHardy, netfilter-devel
In-Reply-To: <20090716122638.23343.77832.sendpatchset@x2.localnet>
commit 3f1f7cf08ddf10ef7fbf06d881582c95b9747f4e
Author: Joe Perches <joe@perches.com>
Date: Wed Jul 1 11:32:10 2009 +0200
netfilter: add netfilter git to MAINTAINERS
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>
diff --git a/MAINTAINERS b/MAINTAINERS
index fa2a16d..a5042de 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4044,6 +4044,7 @@ L: netfilter@vger.kernel.org
L: coreteam@netfilter.org
W: http://www.netfilter.org/
W: http://www.iptables.org/
+T: git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-2.6.git
S: Supported
F: include/linux/netfilter*
F: include/linux/netfilter/
^ permalink raw reply related
* netfilter 00/03: netfilter fixes
From: Patrick McHardy @ 2009-07-16 12:26 UTC (permalink / raw)
To: davem; +Cc: netdev, Patrick McHardy, netfilter-devel
Hi Dave,
following are two netfilter fixes for 2.6.31 and a MAINTAINERS update:
- a fix for the nf_conntrack_alloc() race from Eric
- a fix for incorrect invocation of nf_log_packet() in the new osf match
- a patch to add my netfilter git tree to MAINTAINERS
Please apply or pull from:
git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-2.6.git master
Thanks!
Documentation/RCU/rculist_nulls.txt | 7 ++++++-
MAINTAINERS | 1 +
net/netfilter/nf_conntrack_core.c | 21 ++++++++++++++++++---
net/netfilter/xt_osf.c | 5 +++--
4 files changed, 28 insertions(+), 6 deletions(-)
Eric Dumazet (1):
netfilter: nf_conntrack: nf_conntrack_alloc() fixes
Joe Perches (1):
netfilter: add netfilter git to MAINTAINERS
Patrick McHardy (1):
netfilter: xt_osf: fix nf_log_packet() arguments
^ permalink raw reply
* RE: [PATCH] imwc3200: move iwmc3200 SDIO ids to sdio_ids.h
From: Zhu, Yi @ 2009-07-16 12:23 UTC (permalink / raw)
To: Zhu, Yi, Winkler, Tomas, drzeus-list@drzeus.cx,
netdev@vger.kernel.org, "linux-w
Cc: Winkler, Tomas
In-Reply-To: <AFAB8585072C7E48BC5C6F9C87F9B79703BA8388BB@pdsmsx502.ccr.corp.intel.com>
> Subject: RE: [PATCH] imwc3200: move iwmc3200 SDIO ids to sdio_ids.h
BTW, subject should be "iwmc3200".
Thanks,
-yi
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox