Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v4 08/10] ARM: mxs: add ocotp read function
From: Uwe Kleine-König @ 2011-01-13 15:19 UTC (permalink / raw)
  To: Shawn Guo
  Cc: davem, gerg, baruch, eric, bryan.wu, r64343, B32542, lw, w.sang,
	s.hauer, jamie, jamie, netdev, linux-arm-kernel
In-Reply-To: <1294297998-26930-9-git-send-email-shawn.guo@freescale.com>

On Thu, Jan 06, 2011 at 03:13:16PM +0800, Shawn Guo wrote:
> Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
> ---
> Changes for v4:
>  - Call cpu_relax() during polling
> 
> Changes for v2:
>  - Add mutex locking for mxs_read_ocotp()
>  - Use type size_t for count and i
>  - Add comment for clk_enable/disable skipping
>  - Add ERROR bit clearing and polling step
> 
>  arch/arm/mach-mxs/Makefile              |    2 +-
>  arch/arm/mach-mxs/include/mach/common.h |    1 +
>  arch/arm/mach-mxs/ocotp.c               |   79 +++++++++++++++++++++++++++++++
>  3 files changed, 81 insertions(+), 1 deletions(-)
>  create mode 100644 arch/arm/mach-mxs/ocotp.c
> 
> diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile
> index 39d3f9c..f23ebbd 100644
> --- a/arch/arm/mach-mxs/Makefile
> +++ b/arch/arm/mach-mxs/Makefile
> @@ -1,5 +1,5 @@
>  # Common support
> -obj-y := clock.o devices.o gpio.o icoll.o iomux.o system.o timer.o
> +obj-y := clock.o devices.o gpio.o icoll.o iomux.o ocotp.o system.o timer.o
is it worth to make ocotp optional?  (and let evk select
CONFIG_MXS_OCOTP)

Best regards
Uwe
>  
>  obj-$(CONFIG_SOC_IMX23) += clock-mx23.o mm-mx23.o
>  obj-$(CONFIG_SOC_IMX28) += clock-mx28.o mm-mx28.o
> diff --git a/arch/arm/mach-mxs/include/mach/common.h b/arch/arm/mach-mxs/include/mach/common.h
> index 59133eb..cf02552 100644
> --- a/arch/arm/mach-mxs/include/mach/common.h
> +++ b/arch/arm/mach-mxs/include/mach/common.h
> @@ -13,6 +13,7 @@
>  
>  struct clk;
>  
> +extern int mxs_read_ocotp(int offset, int count, u32 *values);
>  extern int mxs_reset_block(void __iomem *);
>  extern void mxs_timer_init(struct clk *, int);
>  
> diff --git a/arch/arm/mach-mxs/ocotp.c b/arch/arm/mach-mxs/ocotp.c
> new file mode 100644
> index 0000000..e2d39aa
> --- /dev/null
> +++ b/arch/arm/mach-mxs/ocotp.c
> @@ -0,0 +1,79 @@
> +/*
> + * Copyright 2010 Freescale Semiconductor, Inc. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/mutex.h>
> +
> +#include <mach/mxs.h>
> +
> +#define BM_OCOTP_CTRL_BUSY		(1 << 8)
> +#define BM_OCOTP_CTRL_ERROR		(1 << 9)
> +#define BM_OCOTP_CTRL_RD_BANK_OPEN	(1 << 12)
> +
> +static DEFINE_MUTEX(ocotp_mutex);
> +
> +int mxs_read_ocotp(unsigned offset, size_t count, u32 *values)
> +{
> +	void __iomem *ocotp_base = MXS_IO_ADDRESS(MXS_OCOTP_BASE_ADDR);
> +	int timeout = 0x400;
> +	size_t i;
> +
> +	mutex_lock(&ocotp_mutex);
> +
> +	/*
> +	 * clk_enable(hbus_clk) for ocotp can be skipped
> +	 * as it must be on when system is running.
> +	 */
> +
> +	/* try to clear ERROR bit */
> +	__mxs_clrl(BM_OCOTP_CTRL_ERROR, ocotp_base);
> +
> +	/* check both BUSY and ERROR cleared */
> +	while ((__raw_readl(ocotp_base) &
> +		(BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR)) && --timeout)
> +		cpu_relax();
> +
> +	if (unlikely(!timeout))
> +		goto error_unlock;
> +
> +	/* open OCOTP banks for read */
> +	__mxs_setl(BM_OCOTP_CTRL_RD_BANK_OPEN, ocotp_base);
> +
> +	/* approximately wait 32 hclk cycles */
> +	udelay(1);
> +
> +	/* poll BUSY bit becoming cleared */
> +	timeout = 0x400;
> +	while ((__raw_readl(ocotp_base) & BM_OCOTP_CTRL_BUSY) && --timeout)
> +		cpu_relax();
> +
> +	if (unlikely(!timeout))
> +		goto error_unlock;
> +
> +	for (i = 0; i < count; i++, offset += 4)
> +		*values++ = __raw_readl(ocotp_base + offset);
> +
> +	/* close banks for power saving */
> +	__mxs_clrl(BM_OCOTP_CTRL_RD_BANK_OPEN, ocotp_base);
> +
> +	mutex_unlock(&ocotp_mutex);
> +
> +	return 0;
> +
> +error_unlock:
> +	mutex_unlock(&ocotp_mutex);
> +	pr_err("%s: timeout in reading OCOTP\n", __func__);
> +	return -ETIMEDOUT;
> +}
> -- 
> 1.7.1
> 
> 
> 

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* Re: Realtek r8168C / r8169 driver VLAN TAG stripping
From: Anand Raj Manickam @ 2011-01-13 15:09 UTC (permalink / raw)
  To: netdev
In-Reply-To: <AANLkTi=qVSEh=RyzJ6y4kQvJW+hxqWMh2gAMmhk7GGob@mail.gmail.com>

Sorry,
This happens on a Linux 2.6.30.8 kernel .The driver is  in-kernel.

On Thu, Jan 13, 2011 at 8:10 PM, Anand Raj Manickam <anandrm@gmail.com> wrote:
> Hi ,
> I have a problem on Realtek 8168C chipset - r8169 Driver .
> The VLAN tag on Tx  & Rx gets stripped .
>
> The 8021q module is loaded .
> vconfig add eth0 50
> ifconfig eth0.50 172.16.1.10 up
>
> ping -I eth0.50 172.16.1.1 .
>
> All packets on Tx & Rx have the VLAN tag stripped on Tx & Rx.
>
> On debugging the Driver , we found that on Tx the VLAN tag is present.
>
> static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev)
> {
> .
> .
> tp->tx_skb[entry].len = len;
> txd->addr = cpu_to_le64(mapping);
> txd->opts2 = cpu_to_le32(rtl8169_tx_vlan_tag(tp, skb));
> printk("The Vlan tag %x \n",txd->opts2);
> .
> .
> }
>
> The Vlan tag 23200
>
> In the above value 23200
> 2 - TxVlanTag
> 32 - Vlan tag (50).
>
> From the above we are clear that the Vlan tag reaches the driver .
>
> Can someone shed some light on this issue ?
> Any pointers are welcome.
> Thanks,
> Anand
>

^ permalink raw reply

* Re: STMMAC driver: NFS Problem on 2.6.37
From: Chuck Lever @ 2011-01-13 15:07 UTC (permalink / raw)
  To: deepaksi
  Cc: Trond.Myklebust-HgOvQuBEEgTQT0dZR+AlfA,
	netdev-u79uwXL29TY76Z2rM5mHXA, linux-nfs-u79uwXL29TY76Z2rM5mHXA,
	Armando VISCONTI, Shiraz HASHIM, Viresh KUMAR
In-Reply-To: <4D2EC133.7010607-qxv4g6HH51o@public.gmane.org>


On Jan 13, 2011, at 4:09 AM, deepaksi wrote:

> Hi
> 
> I am facing a problem related to nfs boot, while using the stmmac driver
> ported on 2.6.37 kernel. When we use a JFFS2 file system and mount the kernel,
> the network driver works fine.
> 
> I have been following the mailing list and could find some issues with NFS 
> on 2.6.37 but I am not too sure whether the kernel crash I am getting is 
> related to that.
> 
> The driver worked fine on 2.6.32 kernel, but while booting the 2.6.37
> kernel I get the following log messages:
> 
> stmmac: Rx Checksum Offload Engine supported
>        TX Checksum insertion supported
> IP-Config: Complete:
>     device=eth0, addr=192.168.1.10, mask=255.255.255.0, gw=255.255.255.255,
>     host=192.168.1.10, domain=, nis-domain=(none),
>     bootserver=192.168.1.1, rootserver=192.168.1.1, rootpath=

Why is rootpath left undefined?

> VFS: Unable to mount root fs via NFS, trying floppy.
> VFS: Cannot open root device "nfs" or unknown-block(2,0)
> Please append a correct "root=" boot option; here are the available
> partitions:
> 1f00              64 mtdblock0  (driver?)
> 1f01             256 mtdblock1  (driver?)
> 1f02            2816 mtdblock2  (driver?)
> 1f03            5056 mtdblock3  (driver?)
> Kernel panic - not syncing: VFS: Unable to mount root fs on
> unknown-block(2,0)
> Backtrace:
> [<c00370f0>] (dump_backtrace+0x0/0x110) from [<c0037234>]
> (dump_stack+0x18/0x1c)
> r7:c7b5b000 r6:00000000 r5:c7b5b015 r4:c04296b8
> [<c003721c>] (dump_stack+0x0/0x1c) from [<c004ebf8>] (panic+0x60/0x180)
> [<c004eb98>] (panic+0x0/0x180) from [<c0009114>]
> (mount_block_root+0x1d4/0x214)
> r3:00000000 r2:00000001 r1:c782bf50 r0:c0394851
> [<c0008f40>] (mount_block_root+0x0/0x214) from [<c00091fc>]
> (mount_root+0xa8/0xc8)
> [<c0009154>] (mount_root+0x0/0xc8) from [<c0009388>]
> (prepare_namespace+0x16c/0x1d0)
> r4:c04288c0
> [<c000921c>] (prepare_namespace+0x0/0x1d0) from [<c0008904>]
> (kernel_init+0x1cc/0x220)
> r5:c0402048 r4:c0428860
> [<c0008738>] (kernel_init+0x0/0x220) from [<c00522a8>] (do_exit+0x0/0x5e0)
> r7:00000013 r6:c00522a8 r5:c0008738 r4:00000000
> CPU0: stopping
> Backtrace:
> [<c00370f0>] (dump_backtrace+0x0/0x110) from [<c0037234>]
> (dump_stack+0x18/0x1c)
> r7:c0405484 r6:00000406 r5:00000000 r4:00000000
> [<c003721c>] (dump_stack+0x0/0x1c) from [<c002d334>] (do_IPI+0xb4/0x124)
> [<c002d280>] (do_IPI+0x0/0x124) from [<c0032bb4>] (__irq_svc+0x34/0xc0)
> Exception stack(0xc03f3f50 to 0xc03f3f98)
> 3f40:                                     c0402048 00000000 c03f3f98
> 00000000
> 3f60: c03f2000 c04288dc c0027290 c0405484 000258e8 411fc091 00000000
> c03f3fa4
> 3f80: c03f3fa8 c03f3f98 c0034a24 c0034a28 60000013 ffffffff
> r5:fc800100 r4:ffffffff
> [<c00349fc>] (default_idle+0x0/0x30) from [<c0034874>] (cpu_idle+0x80/0xc0)
> [<c00347f4>] (cpu_idle+0x0/0xc0) from [<c030602c>] (rest_init+0x64/0x7c)
> r5:c04288dc r4:c04020b0
> [<c0305fc8>] (rest_init+0x0/0x7c) from [<c0008bd4>]
> (start_kernel+0x27c/0x2d8)
> [<c0008958>] (start_kernel+0x0/0x2d8) from [<00008038>] (0x8038)
> r5:c0401fac r4:10c5387d
> 
> I have tried the same over latest source picked from linus tree,
> 4162cf64973df51fc885825bc9ca4d055891c49f
> Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
> 
> We are using version 3 of the NFs protocol in kernel's NFS client.
> 
> 
> Regards
> Deepak
> ST Microelectronics
> 
> .
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Chuck Lever
chuck[dot]lever[at]oracle[dot]com




--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v4 06/10] ARM: mx28: update clock and device name for dual fec support
From: Uwe Kleine-König @ 2011-01-13 15:06 UTC (permalink / raw)
  To: Shawn Guo
  Cc: davem, gerg, baruch, eric, bryan.wu, r64343, B32542, lw, w.sang,
	s.hauer, jamie, jamie, netdev, linux-arm-kernel
In-Reply-To: <1294297998-26930-7-git-send-email-shawn.guo@freescale.com>

Hi Shawn,

On Thu, Jan 06, 2011 at 03:13:14PM +0800, Shawn Guo wrote:
> Change device name from "fec" to "imx28-fec", so that fec driver
> can distinguish mx28.
> 
> Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
> ---
> Changes for v4:
>  - Use "imx28-fec" as fec device name
> 
> Changes for v3:
>  - Change device name to "enet-mac"
> 
>  arch/arm/mach-mxs/clock-mx28.c           |    3 ++-
>  arch/arm/mach-mxs/devices/platform-fec.c |    2 +-
>  2 files changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm/mach-mxs/clock-mx28.c b/arch/arm/mach-mxs/clock-mx28.c
> index f20b254..e2a8b0f 100644
> --- a/arch/arm/mach-mxs/clock-mx28.c
> +++ b/arch/arm/mach-mxs/clock-mx28.c
> @@ -606,7 +606,8 @@ static struct clk_lookup lookups[] = {
>  	_REGISTER_CLOCK("duart", "apb_pclk", xbus_clk)
>  	/* for amba-pl011 driver */
>  	_REGISTER_CLOCK("duart", NULL, uart_clk)
> -	_REGISTER_CLOCK("fec.0", NULL, fec_clk)
> +	_REGISTER_CLOCK("imx28-fec.0", NULL, fec_clk)
> +	_REGISTER_CLOCK("imx28-fec.1", NULL, fec_clk)
>  	_REGISTER_CLOCK("rtc", NULL, rtc_clk)
>  	_REGISTER_CLOCK("pll2", NULL, pll2_clk)
>  	_REGISTER_CLOCK(NULL, "hclk", hbus_clk)
> diff --git a/arch/arm/mach-mxs/devices/platform-fec.c b/arch/arm/mach-mxs/devices/platform-fec.c
> index c08168c..c42dff7 100644
> --- a/arch/arm/mach-mxs/devices/platform-fec.c
> +++ b/arch/arm/mach-mxs/devices/platform-fec.c
> @@ -45,6 +45,6 @@ struct platform_device *__init mxs_add_fec(
>  		},
>  	};
>  
> -	return mxs_add_platform_device("fec", data->id,
> +	return mxs_add_platform_device("imx28-fec", data->id,
IMHO "imx28-fec" shouldn't be hard coded here but come from data.  See
imx-spi device registration for an example.

Uwe
>  			res, ARRAY_SIZE(res), pdata, sizeof(*pdata));
>  }
> -- 
> 1.7.1
> 
> 
> 

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* Re: [PATCH 2.6.36] vlan: Avoid hwaccel vlan packets when vid not used
From: Jesse Gross @ 2011-01-13 15:06 UTC (permalink / raw)
  To: Matt Carlson
  Cc: Michael Leun, Michael Chan, Eric Dumazet, David Miller,
	Ben Greear, linux-kernel@vger.kernel.org, netdev@vger.kernel.org
In-Reply-To: <20110113012157.GA28147@mcarlson.broadcom.com>

On Wed, Jan 12, 2011 at 8:21 PM, Matt Carlson <mcarlson@broadcom.com> wrote:
> On Thu, Jan 06, 2011 at 08:36:27PM -0800, Jesse Gross wrote:
>> On Thu, Jan 6, 2011 at 10:24 PM, Matt Carlson <mcarlson@broadcom.com> wrote:
>> > On Sat, Dec 18, 2010 at 07:38:00PM -0800, Jesse Gross wrote:
>> >> On Tue, Dec 14, 2010 at 11:16 PM, Michael Leun
>> >> <lkml20101129@newton.leun.net> wrote:
>> >> > OK - all tests done on that DL320G5:
>> >> >
>> >> > For completeness, 2.6.37-rc5 unpatched:
>> >> >
>> >> > eth0, no vlan configured: totally broken - see double tagged vlans
>> >> > without tag, single or untagged packets missing at all
>> >>
>> >> Random behavior? ?This one is somewhat hard to explain - maybe there
>> >> are some other factors. ?eth0 has ASF on, so it always strips tags. ?I
>> >> would expect it to behave like the vlan configured case.
>> >>
>> >> >
>> >> > eth0, vlan configured: see packets without vlan tag (see double tagged
>> >> > packets with one vlan tag)
>> >>
>> >> Both ASF and vlan group configured cause tag stripping to be enabled.
>> >> Missing tag.
>> >>
>> >> >
>> >> > eth1 same as originally reported:
>> >> > without vlan configured see vlan tags (single and double tagged as
>> >> > expected)
>> >>
>> >> No ASF and no vlan group means tag stripping is disabled. ?Have tag.
>> >>
>> >> > with vlan configured: see packets without vlan tag (see double tagged
>> >> > packets with one vlan tag)
>> >>
>> >> Configuring vlan group causes stripping to be enabled. ?Missing tag.
>> >>
>> >> >
>> >> >
>> >> > 2.6.37-rc5, your tg3 use new vlan-code patch:
>> >> >
>> >> > eth0, no vlan configured: ?see packets without vlan tag (see double
>> >> > tagged packets with one vlan tag)
>> >>
>> >> ASF enables tag stripping. ?Missing tag.
>> >>
>> >> > eth1, no vlan configured: see vlan tags (single and double tagged as
>> >> > expected)
>> >>
>> >> No ASF, no vlan group means no stripping. ?Have tag.
>> >>
>> >> >
>> >> >
>> >> > eth0, vlan configured: as without vlan
>> >>
>> >> ASF enables stripping. ?Missing tag.
>> >>
>> >> > eth1, vlan configured: as without vlan
>> >>
>> >> With this patch vlan stripping is only enabled when ASF is on, so no
>> >> stripping. ?Have tag.
>> >>
>> >> >
>> >> > 2.6.37-rc5, your tg3 use new vlan-code patch with test patch ontop
>> >> >
>> >> > eth1 no vlan configured: see packets without vlan tag (see double tagged
>> >> > packets with one vlan tag)
>> >>
>> >> With the second patch, vlan stripping is always enabled. ?Missing tag.
>> >>
>> >> > eth1 with vlan: the same
>> >>
>> >> Stripping still always enabled. ?Missing tag.
>> >>
>> >> The bottom line is whenever vlan stripping is enabled we're missing
>> >> the outer tag. ?It might be worth adding some debugging in the area
>> >> before napi_gro_receive/vlan_gro_receive (depending on version). ?My
>> >> guess is that (desc->type_flags & RXD_FLAG_VLAN) is false even for
>> >> vlan packets on this NIC.
>> >>
>> >> You said that everything works on the 5752? ?Matt, is it possible that
>> >> the 5714 either has a problem with vlan stripping or a different way
>> >> of reporting it?
>> >
>> > I don't think this is a 5714 specific issue. ?I think the problem is
>> > rooted in the fact that the VLAN tag stripping is enabled.
>>
>> It's definitely related to vlan stripping being enabled.  Other cards
>> using tg3 seem to work fine with stripping though, which is why I
>> thought it might be specific to the 5714.
>
> I just tested this on a 5714S, using a net-next-2.6 snapshot obtained
> today.  It does the right thing in both cases (2nd tg3 patch ommited /
> applied).  The tag is always visible in the packet stream as seen from
> tcpdump.
>
>> > Your RXD_FLAG_VLAN idea sounds unlikely to me, but it's worth a check.
>> >
>> > The patch here is using __vlan_hwaccel_put_tag(), which informs the
>> > stack a VLAN tag is present. ?If this is indeed a reporting problem, I'm
>> > not sure what else the driver should be doing.
>>
>> The code to hand off the tag to the stack looks OK to me.  Michael was
>> seeing this on older versions of the kernel as well with this NIC,
>> which predates both this patch and the larger vlan changes so it
>> doesn't seem like a problem with passing the tag to the network stack.
>>  It's hard to know exactly what is going on though without seeing what
>> the hardware is reporting.
>
> When RX_MODE_KEEP_VLAN_TAG is set, the RXD_FLAG_VLAN flag will not be set
> when receiving a packet.  The driver skips the __vlan_hwaccel_put_tag()
> call.
>
> When RX_MODE_KEEP_VLAN_TAG is unset, the RXD_FLAG_VLAN flag is set, and
> __vlan_hwaccel_put_tag() is called to reinject the packet.

OK, thanks for testing it out.  I'm not sure that there's anything
more we can do without hearing from Michael.

^ permalink raw reply

* Re: [PATCH v4 09/10] ARM: mx28: read fec mac address from ocotp
From: Uwe Kleine-König @ 2011-01-13 14:50 UTC (permalink / raw)
  To: Shawn Guo
  Cc: davem, gerg, baruch, eric, bryan.wu, r64343, B32542, lw, w.sang,
	s.hauer, jamie, jamie, netdev, linux-arm-kernel
In-Reply-To: <1294297998-26930-10-git-send-email-shawn.guo@freescale.com>

Hello Shawn,

$SUBJECT ~= s,mx28,mxs/mx28evk, please

On Thu, Jan 06, 2011 at 03:13:17PM +0800, Shawn Guo wrote:
> Read fec mac address from ocotp and save it into fec_platform_data
> mac field for fec driver to use.
> 
> Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
> ---
> Changes for v2:
>  - It's not necessary to remove "const" for fec_platform_data from
>    platform-fec.c and devices-common.h, so add it back.
>  - Hard-coding Freescale OUI (00:04:9f) instead of just the first
>    two two octets.
>  - Correct the return of mx28evk_fec_get_mac() and check it
>    with caller
> 
>  arch/arm/mach-mxs/mach-mx28evk.c |   32 ++++++++++++++++++++++++++++++++
>  1 files changed, 32 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-mxs/mach-mx28evk.c b/arch/arm/mach-mxs/mach-mx28evk.c
> index def6519..54fa512 100644
> --- a/arch/arm/mach-mxs/mach-mx28evk.c
> +++ b/arch/arm/mach-mxs/mach-mx28evk.c
> @@ -129,12 +129,44 @@ static struct fec_platform_data mx28_fec_pdata[] = {
>  	},
>  };
>  
> +static int __init mx28evk_fec_get_mac(void)
> +{
> +	int i, ret;
> +	u32 val;
> +
> +	/*
> +	 * OCOTP only stores the last 4 octets for each mac address,
> +	 * so hard-code Freescale OUI (00:04:9f) here.
> +	 */
> +	for (i = 0; i < 2; i++) {
> +		ret = mxs_read_ocotp(0x20 + i * 0x10, 1, &val);
> +		if (ret)
> +			goto error;
> +
> +		mx28_fec_pdata[i].mac[0] = 0x00;
> +		mx28_fec_pdata[i].mac[1] = 0x04;
> +		mx28_fec_pdata[i].mac[2] = 0x9f;
> +		mx28_fec_pdata[i].mac[3] = (val >> 16) & 0xff;
> +		mx28_fec_pdata[i].mac[4] = (val >> 8) & 0xff;
> +		mx28_fec_pdata[i].mac[5] = (val >> 0) & 0xff;
> +	}
> +
> +	return 0;
> +
> +error:
> +	pr_err("%s: timeout when reading fec mac from OCOTP\n", __func__);
> +	return ret;
> +}
> +
>  static void __init mx28evk_init(void)
>  {
>  	mxs_iomux_setup_multiple_pads(mx28evk_pads, ARRAY_SIZE(mx28evk_pads));
>  
>  	mx28_add_duart();
>  
> +	if (mx28evk_fec_get_mac())
> +		pr_warn("%s: failed on fec mac setup\n", __func__);
> +
>  	mx28evk_fec_reset();
>  	mx28_add_fec(0, &mx28_fec_pdata[0]);
>  #ifdef CONFIG_FEC2
> -- 
> 1.7.1
> 
> 
> 

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* Re: [PATCH v5] ARM: mx28: add the second fec device registration
From: Uwe Kleine-König @ 2011-01-13 14:49 UTC (permalink / raw)
  To: Shawn Guo
  Cc: s.hauer, davem, gerg, baruch, eric, bryan.wu, r64343, B32542, lw,
	w.sang, jamie, jamie, netdev, linux-arm-kernel
In-Reply-To: <1294747764-4499-1-git-send-email-shawn.guo@freescale.com>

On Tue, Jan 11, 2011 at 08:09:24PM +0800, Shawn Guo wrote:
> Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
> ---
> Changes for v5:
>  - Do not use CONFIG_FEC2 which is a fec driver configration
> 
>  arch/arm/mach-mxs/mach-mx28evk.c |   26 +++++++++++++++++++++++---
>  1 files changed, 23 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm/mach-mxs/mach-mx28evk.c b/arch/arm/mach-mxs/mach-mx28evk.c
> index d162e95..8e2c597 100644
> --- a/arch/arm/mach-mxs/mach-mx28evk.c
> +++ b/arch/arm/mach-mxs/mach-mx28evk.c
> @@ -57,6 +57,19 @@ static const iomux_cfg_t mx28evk_pads[] __initconst = {
>  		(MXS_PAD_8MA | MXS_PAD_3V3 | MXS_PAD_PULLUP),
>  	MX28_PAD_ENET_CLK__CLKCTRL_ENET |
>  		(MXS_PAD_8MA | MXS_PAD_3V3 | MXS_PAD_PULLUP),
> +	/* fec1 */
> +	MX28_PAD_ENET0_CRS__ENET1_RX_EN |
> +		(MXS_PAD_8MA | MXS_PAD_3V3 | MXS_PAD_PULLUP),
> +	MX28_PAD_ENET0_RXD2__ENET1_RXD0 |
> +		(MXS_PAD_8MA | MXS_PAD_3V3 | MXS_PAD_PULLUP),
> +	MX28_PAD_ENET0_RXD3__ENET1_RXD1 |
> +		(MXS_PAD_8MA | MXS_PAD_3V3 | MXS_PAD_PULLUP),
> +	MX28_PAD_ENET0_COL__ENET1_TX_EN |
> +		(MXS_PAD_8MA | MXS_PAD_3V3 | MXS_PAD_PULLUP),
> +	MX28_PAD_ENET0_TXD2__ENET1_TXD0 |
> +		(MXS_PAD_8MA | MXS_PAD_3V3 | MXS_PAD_PULLUP),
> +	MX28_PAD_ENET0_TXD3__ENET1_TXD1 |
> +		(MXS_PAD_8MA | MXS_PAD_3V3 | MXS_PAD_PULLUP),
>  	/* phy power line */
>  	MX28_PAD_SSP1_DATA3__GPIO_2_15 |
>  		(MXS_PAD_4MA | MXS_PAD_3V3 | MXS_PAD_NOPULL),
> @@ -106,8 +119,14 @@ static void __init mx28evk_fec_reset(void)
>  	gpio_set_value(MX28EVK_FEC_PHY_RESET, 1);
>  }
>  
> -static const struct fec_platform_data mx28_fec_pdata __initconst = {
> -	.phy = PHY_INTERFACE_MODE_RMII,
> +static struct fec_platform_data mx28_fec_pdata[] = {
this can still be initdata, doesn't it?

> +	{
> +		/* fec0 */
> +		.phy = PHY_INTERFACE_MODE_RMII,
> +	}, {
> +		/* fec1 */
> +		.phy = PHY_INTERFACE_MODE_RMII,
> +	},
>  };
>  
>  static void __init mx28evk_init(void)
> @@ -117,7 +136,8 @@ static void __init mx28evk_init(void)
>  	mx28_add_duart();
>  
>  	mx28evk_fec_reset();
> -	mx28_add_fec(0, &mx28_fec_pdata);
> +	mx28_add_fec(0, &mx28_fec_pdata[0]);
> +	mx28_add_fec(1, &mx28_fec_pdata[1]);
>  }
>  
>  static void __init mx28evk_timer_init(void)
> -- 
> 1.7.1
> 
> 
> 

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* Re: [PATCH v4 05/10] net/fec: add dual fec support for mx28
From: Uwe Kleine-König @ 2011-01-13 14:48 UTC (permalink / raw)
  To: Shawn Guo
  Cc: davem, gerg, baruch, eric, bryan.wu, r64343, B32542, lw, w.sang,
	s.hauer, jamie, jamie, netdev, linux-arm-kernel
In-Reply-To: <1294297998-26930-6-git-send-email-shawn.guo@freescale.com>

Hello,

hmm, this review comes to late, probably I will post a follow-up patch
for the low-hanging fruits at least.

On Thu, Jan 06, 2011 at 03:13:13PM +0800, Shawn Guo wrote:
> This patch is to add mx28 dual fec support. Here are some key notes
> for mx28 fec controller.
> 
>  - The mx28 fec controller naming ENET-MAC is a different IP from FEC
>    used on other i.mx variants.  But they are basically compatible
>    on software interface, so it's possible to share the same driver.
>  - ENET-MAC design on mx28 made an improper assumption that it runs
>    on a big-endian system. As the result, driver has to swap every
>    frame going to and coming from the controller.
>  - The external phys can only be configured by fec0, which means fec1
>    can not work independently and both phys need to be configured by
>    mii_bus attached on fec0.
>  - ENET-MAC reset will get mac address registers reset too.
>  - ENET-MAC MII/RMII mode and 10M/100M speed are configured
>    differently FEC.
>  - ETHER_EN bit must be set to get ENET-MAC interrupt work.
> 
> Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
> ---
> Changes for v4:
>  - Use #ifndef CONFIG_ARM to include ColdFire header files
I intended you to not use CONFIG_ARCH_MXS at all, at least up to now
CONFIG_ARM works quite well.

>  - Define quirk bits in id_entry.driver_data to handle controller
>    difference, which is more scalable than using device name
>  - Define fec0_mii_bus as a static function in fec_enet_mii_init
>    to fold the mii_bus instance attached on fec0
IMHO not very good.  At least the current code doesn't allow to have two
dual-fecs, because the 2nd dual-fec's slave would be attached to the 1st
dual-fec's mii_bus.  Don't know a nice solution though.  Probably you
either need a slave pointer in platform_data or have to treat a dual-fec
as only a single device.

>  - Use cpu_to_be32 over __swab32 in function swap_buffer
> 
> Changes for v3:
>  - Move v2 changes into patch #3
>  - Use device name to check if it's running on ENET-MAC
> 
>  drivers/net/Kconfig |    7 ++-
>  drivers/net/fec.c   |  148 +++++++++++++++++++++++++++++++++++++++++++++------
>  drivers/net/fec.h   |    5 +-
>  3 files changed, 139 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index 4f1755b..f34629b 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -1944,18 +1944,19 @@ config 68360_ENET
>  config FEC
>  	bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)"
>  	depends on M523x || M527x || M5272 || M528x || M520x || M532x || \
> -		MACH_MX27 || ARCH_MX35 || ARCH_MX25 || ARCH_MX5
> +		MACH_MX27 || ARCH_MX35 || ARCH_MX25 || ARCH_MX5 || SOC_IMX28
IMX_HAVE_PLATFORM_FEC || MXS_HAVE_PLATFORM_FEC ?  Again this calls for a
more global approach for these registration facilities.

>  	select PHYLIB
>  	help
>  	  Say Y here if you want to use the built-in 10/100 Fast ethernet
>  	  controller on some Motorola ColdFire and Freescale i.MX processors.
>  
>  config FEC2
> -	bool "Second FEC ethernet controller (on some ColdFire CPUs)"
> +	bool "Second FEC ethernet controller"
>  	depends on FEC
>  	help
>  	  Say Y here if you want to use the second built-in 10/100 Fast
> -	  ethernet controller on some Motorola ColdFire processors.
> +	  ethernet controller on some Motorola ColdFire and Freescale
> +	  i.MX processors.
>  
>  config FEC_MPC52xx
>  	tristate "MPC52xx FEC driver"
> diff --git a/drivers/net/fec.c b/drivers/net/fec.c
> index 8a1c51f..2a71373 100644
> --- a/drivers/net/fec.c
> +++ b/drivers/net/fec.c
> @@ -17,6 +17,8 @@
>   *
>   * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
>   * Copyright (c) 2004-2006 Macq Electronique SA.
> + *
> + * Copyright (C) 2010 Freescale Semiconductor, Inc.
>   */
>  
>  #include <linux/module.h>
> @@ -45,20 +47,36 @@
>  
>  #include <asm/cacheflush.h>
>  
> -#ifndef CONFIG_ARCH_MXC
> +#ifndef CONFIG_ARM
>  #include <asm/coldfire.h>
>  #include <asm/mcfsim.h>
>  #endif
>  
>  #include "fec.h"
>  
> -#ifdef CONFIG_ARCH_MXC
> -#include <mach/hardware.h>
> +#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28)
>  #define FEC_ALIGNMENT	0xf
>  #else
>  #define FEC_ALIGNMENT	0x3
>  #endif
>  
> +#define DRIVER_NAME	"fec"
> +
> +/* Controller is ENET-MAC */
> +#define FEC_QUIRK_ENET_MAC		(1 << 0)
does this really qualify to be a quirk?

> +/* Controller needs driver to swap frame */
> +#define FEC_QUIRK_SWAP_FRAME		(1 << 1)
IMHO this is a bit misnamed.  FEC_QUIRK_NEEDS_BE_DATA or similar would
be more accurate.

> +static struct platform_device_id fec_devtype[] = {
> +	{
> +		.name = DRIVER_NAME,
> +		.driver_data = 0,
> +	}, {
> +		.name = "imx28-fec",
> +		.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
> +	}
> +};
> +
>  static unsigned char macaddr[ETH_ALEN];
>  module_param_array(macaddr, byte, NULL, 0);
>  MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
> @@ -129,7 +147,8 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
>   * account when setting it.
>   */
>  #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
> -    defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC)
> +    defined(CONFIG_M520x) || defined(CONFIG_M532x) || \
> +    defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28)
I wonder what is excluded here.  FEC depends on

	M523x || M527x || M5272 || M528x || M520x || M532x || \
	MACH_MX27 || ARCH_MX35 || ARCH_MX25 || ARCH_MX5 || SOC_IMX28

so the only difference is that the latter lists M5272 which seems a bit
redundant in the presence of M527x.

>  #define	OPT_FRAME_SIZE	(PKT_MAXBUF_SIZE << 16)
>  #else
>  #define	OPT_FRAME_SIZE	0
> @@ -208,10 +227,23 @@ static void fec_stop(struct net_device *dev);
>  /* Transmitter timeout */
>  #define TX_TIMEOUT (2 * HZ)
>  
> +static void *swap_buffer(void *bufaddr, int len)
> +{
> +	int i;
> +	unsigned int *buf = bufaddr;
> +
> +	for (i = 0; i < (len + 3) / 4; i++, buf++)
> +		*buf = cpu_to_be32(*buf);
if len isn't a multiple of 4 this accesses bytes behind len.  Is this
generally OK here?  (E.g. because skbs always have a length that is a
multiple of 4?)
> +
> +	return bufaddr;
> +}
> +
>  static netdev_tx_t
>  fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
>  {
>  	struct fec_enet_private *fep = netdev_priv(dev);
> +	const struct platform_device_id *id_entry =
> +				platform_get_device_id(fep->pdev);
>  	struct bufdesc *bdp;
>  	void *bufaddr;
>  	unsigned short	status;
> @@ -256,6 +288,14 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
>  		bufaddr = fep->tx_bounce[index];
>  	}
>  
> +	/*
> +	 * Some design made an incorrect assumption on endian mode of
> +	 * the system that it's running on. As the result, driver has to
> +	 * swap every frame going to and coming from the controller.
> +	 */
> +	if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
> +		swap_buffer(bufaddr, skb->len);
> +
>  	/* Save skb pointer */
>  	fep->tx_skbuff[fep->skb_cur] = skb;
>  
> @@ -424,6 +464,8 @@ static void
>  fec_enet_rx(struct net_device *dev)
>  {
>  	struct	fec_enet_private *fep = netdev_priv(dev);
> +	const struct platform_device_id *id_entry =
> +				platform_get_device_id(fep->pdev);
>  	struct bufdesc *bdp;
>  	unsigned short status;
>  	struct	sk_buff	*skb;
> @@ -487,6 +529,9 @@ fec_enet_rx(struct net_device *dev)
>  	        dma_unmap_single(NULL, bdp->cbd_bufaddr, bdp->cbd_datlen,
>          			DMA_FROM_DEVICE);
>  
> +		if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
> +			swap_buffer(data, pkt_len);
> +
>  		/* This does 16 byte alignment, exactly what we need.
>  		 * The packet length includes FCS, but we don't want to
>  		 * include that when passing upstream as it messes up
> @@ -689,6 +734,7 @@ static int fec_enet_mii_probe(struct net_device *dev)
>  	char mdio_bus_id[MII_BUS_ID_SIZE];
>  	char phy_name[MII_BUS_ID_SIZE + 3];
>  	int phy_id;
> +	int dev_id = fep->pdev->id;
>  
>  	fep->phy_dev = NULL;
>  
> @@ -700,6 +746,8 @@ static int fec_enet_mii_probe(struct net_device *dev)
>  			continue;
>  		if (fep->mii_bus->phy_map[phy_id]->phy_id == 0)
>  			continue;
> +		if (dev_id--)
> +			continue;
>  		strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
>  		break;
>  	}
> @@ -737,10 +785,35 @@ static int fec_enet_mii_probe(struct net_device *dev)
>  
>  static int fec_enet_mii_init(struct platform_device *pdev)
>  {
> +	static struct mii_bus *fec0_mii_bus;
>  	struct net_device *dev = platform_get_drvdata(pdev);
>  	struct fec_enet_private *fep = netdev_priv(dev);
> +	const struct platform_device_id *id_entry =
> +				platform_get_device_id(fep->pdev);
>  	int err = -ENXIO, i;
>  
> +	/*
> +	 * The dual fec interfaces are not equivalent with enet-mac.
> +	 * Here are the differences:
> +	 *
> +	 *  - fec0 supports MII & RMII modes while fec1 only supports RMII
> +	 *  - fec0 acts as the 1588 time master while fec1 is slave
> +	 *  - external phys can only be configured by fec0
> +	 *
> +	 * That is to say fec1 can not work independently. It only works
> +	 * when fec0 is working. The reason behind this design is that the
> +	 * second interface is added primarily for Switch mode.
> +	 *
> +	 * Because of the last point above, both phys are attached on fec0
> +	 * mdio interface in board design, and need to be configured by
> +	 * fec0 mii_bus.
> +	 */
> +	if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id) {
> +		/* fec1 uses fec0 mii_bus */
> +		fep->mii_bus = fec0_mii_bus;
> +		return 0;
What happens if imx28-fec.1 is probed before imx28-fec.0?
> +	}
> +
>  	fep->mii_timeout = 0;
>  
>  	/*
> @@ -777,6 +850,10 @@ static int fec_enet_mii_init(struct platform_device *pdev)
>  	if (mdiobus_register(fep->mii_bus))
>  		goto err_out_free_mdio_irq;
>  
> +	/* save fec0 mii_bus */
> +	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
> +		fec0_mii_bus = fep->mii_bus;
> +
>  	return 0;
>  
>  err_out_free_mdio_irq:
> @@ -1148,12 +1225,25 @@ static void
>  fec_restart(struct net_device *dev, int duplex)
>  {
>  	struct fec_enet_private *fep = netdev_priv(dev);
> +	const struct platform_device_id *id_entry =
> +				platform_get_device_id(fep->pdev);
>  	int i;
> +	u32 val, temp_mac[2];
>  
>  	/* Whack a reset.  We should wait for this. */
>  	writel(1, fep->hwp + FEC_ECNTRL);
>  	udelay(10);
>  
> +	/*
> +	 * enet-mac reset will reset mac address registers too,
> +	 * so need to reconfigure it.
> +	 */
> +	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
> +		memcpy(&temp_mac, dev->dev_addr, ETH_ALEN);
> +		writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
> +		writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
> +	}
> +
>  	/* Clear any outstanding interrupt. */
>  	writel(0xffc00000, fep->hwp + FEC_IEVENT);
>  
> @@ -1200,20 +1290,45 @@ fec_restart(struct net_device *dev, int duplex)
>  	/* Set MII speed */
>  	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
>  
> -#ifdef FEC_MIIGSK_ENR
> -	if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) {
> -		/* disable the gasket and wait */
> -		writel(0, fep->hwp + FEC_MIIGSK_ENR);
> -		while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
> -			udelay(1);
> +	/*
> +	 * The phy interface and speed need to get configured
> +	 * differently on enet-mac.
> +	 */
> +	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
> +		val = readl(fep->hwp + FEC_R_CNTRL);
>  
> -		/* configure the gasket: RMII, 50 MHz, no loopback, no echo */
> -		writel(1, fep->hwp + FEC_MIIGSK_CFGR);
> +		/* MII or RMII */
> +		if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
> +			val |= (1 << 8);
Can we have a #define for 1 << 8 please?

> +		else
> +			val &= ~(1 << 8);

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* Realtek r8168C / r8169 driver VLAN TAG stripping
From: Anand Raj Manickam @ 2011-01-13 14:40 UTC (permalink / raw)
  To: netdev

Hi ,
I have a problem on Realtek 8168C chipset - r8169 Driver .
The VLAN tag on Tx  & Rx gets stripped .

The 8021q module is loaded .
vconfig add eth0 50
ifconfig eth0.50 172.16.1.10 up

ping -I eth0.50 172.16.1.1 .

All packets on Tx & Rx have the VLAN tag stripped on Tx & Rx.

On debugging the Driver , we found that on Tx the VLAN tag is present.

static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
.
.
tp->tx_skb[entry].len = len;
txd->addr = cpu_to_le64(mapping);
txd->opts2 = cpu_to_le32(rtl8169_tx_vlan_tag(tp, skb));
printk("The Vlan tag %x \n",txd->opts2);
.
.
}

The Vlan tag 23200

In the above value 23200
2 - TxVlanTag
32 - Vlan tag (50).

>From the above we are clear that the Vlan tag reaches the driver .

Can someone shed some light on this issue ?
Any pointer are welcome.
Thanks,
Anand

^ permalink raw reply

* Re: [PATCH v4] netfilter: ipt_CLUSTERIP: remove "no conntrack!"
From: Eric Dumazet @ 2011-01-13 14:39 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: Pablo Neira Ayuso, Netfilter Development Mailinglist, netdev,
	Patrick McHardy
In-Reply-To: <alpine.LNX.2.01.1101131502240.25211@obet.zrqbmnf.qr>

Le jeudi 13 janvier 2011 à 15:02 +0100, Jan Engelhardt a écrit :
> On Thursday 2011-01-13 14:38, Eric Dumazet wrote:
> 
> >Le jeudi 13 janvier 2011 à 12:54 +0100, Pablo Neira Ayuso a écrit :
> >
> >> But printing this does not provide any useful information. The first
> >> packet that does not belong to the cluster node that has received the
> >> packet, or the first invalid packet, will trigger this.
> >> 
> >> Moreover, this confuses users since they can do nothing if they receive
> >> this message.
> >> 
> >> Moreover, this target should be supersedes by the cluster match, which
> >> has been there for quite some time (it's also more flexible).
> >
> >Now you mentioned it, cluster match is not as flexible right now,
> >its hashing is on source_ip only.
> 
> I think in that case, xt_cluster should be improved rather
> than an old module.

Amen

We should not improve IPv4 support then, I see.

My customers use this old module, and upgrading to xt_cluster is not an
option.

Should we discuss this forever or fix it ?

In the end, people are forced to add useless iptables rule to DROP
INVALID packets before entering ipt_CLUSTERIP, after googling or
eventually asking to experts.

Last time this was discussed, this went nowhere :

http://www.spinics.net/lists/netfilter/msg48676.html

Come on guys, we can do it, dont be afraid.

A non rate limited printk() in kernel is forbidden, especially in
network stack.

Then, cluster match can be improved, I am sure you already have a patch
for it.



--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Eric Dumazet @ 2011-01-13 14:22 UTC (permalink / raw)
  To: Po-Yu Chuang; +Cc: netdev, linux-kernel, Po-Yu Chuang
In-Reply-To: <1294919372-1904-1-git-send-email-ratbert.chuang@gmail.com>

Le jeudi 13 janvier 2011 à 19:49 +0800, Po-Yu Chuang a écrit :
> From: Po-Yu Chuang <ratbert@faraday-tech.com>
> 
> FTMAC100 Ethernet Media Access Controller supports 10/100 Mbps and
> MII.  This driver has been working on some ARM/NDS32 SoC including
> Faraday A320 and Andes AG101.
> 
> Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
> ---
>  drivers/net/Kconfig    |    9 +
>  drivers/net/Makefile   |    1 +
>  drivers/net/ftmac100.c | 1341 ++++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/net/ftmac100.h |  180 +++++++
>  4 files changed, 1531 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/net/ftmac100.c
>  create mode 100644 drivers/net/ftmac100.h

Hi

1) please use netdev_alloc_skb_ip_align() instead of dev_alloc_skb() +
skb_reserve(skb, NET_IP_ALIGN);

2) Dont include a "struct net_device_stats stats" in struct ftmac100,
you can use the one included in struct net_device
  (You can then remove ftmac100_get_stats())

3) Dont "netdev->last_rx = jiffies;" : This is not driver job.
 Ditto for trans_start

4) You must comment why wmb() is needed in ftmac100_xmit()

5) Why isnt NAPI used too for TX completions ?
   BTW, I am not sure we want a define. All new drivers must use NAPI.

6) Use is_valid_ether_addr() instead of is_zero_ether_addr() in
ftmac100_probe()

7) "static struct ethtool_ops ftmac100_ethtool_ops" should be const :
	"static const struct ethtool_ops ftmac100_ethtool_ops"
  Ditto for :
	"static struct net_device_ops ftmac100_netdev_ops"


8) Why an interrupt handler (ftmac100_interrupt()) needs to block
interrupts with spin_lock_irqsave(&priv->hw_lock, flags); ?

9) Instead of dev_info(&netdev->dev ...) , please consider netdev_info()

^ permalink raw reply

* Re: [PATCH] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Ben Hutchings @ 2011-01-13 14:03 UTC (permalink / raw)
  To: Po-Yu Chuang; +Cc: netdev, linux-kernel, Po-Yu Chuang
In-Reply-To: <1294919372-1904-1-git-send-email-ratbert.chuang@gmail.com>

On Thu, 2011-01-13 at 19:49 +0800, Po-Yu Chuang wrote:
> From: Po-Yu Chuang <ratbert@faraday-tech.com>
> 
> FTMAC100 Ethernet Media Access Controller supports 10/100 Mbps and
> MII.  This driver has been working on some ARM/NDS32 SoC including
> Faraday A320 and Andes AG101.
[...]
> diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c
> new file mode 100644
> index 0000000..97d1f8d
> --- /dev/null
> +++ b/drivers/net/ftmac100.c
[...]
> +#include "ftmac100.h"
> +
> +#define USE_NAPI

All new network drivers should use NAPI only, so please remove the
definition of USE_NAPI and change the conditional code to use NAPI
always.

[...]
> +	struct napi_struct	napi;
> +#endif
> +
> +	struct net_device_stats	stats;

There is a net_device_stats structure in the net_device structure; you
should normally use that instead of adding another one.

[...]
> +static int ftmac100_reset(struct ftmac100 *priv)
> +{
> +	struct device *dev = &priv->netdev->dev;
> +	unsigned long flags;
> +	int i;
> +
> +	/* NOTE: reset clears all registers */
> +
> +	spin_lock_irqsave(&priv->hw_lock, flags);
> +	iowrite32(FTMAC100_MACCR_SW_RST, priv->base + FTMAC100_OFFSET_MACCR);
> +	spin_unlock_irqrestore(&priv->hw_lock, flags);
> +
> +	for (i = 0; i < 5; i++) {
> +		int maccr;
> +
> +		spin_lock_irqsave(&priv->hw_lock, flags);
> +		maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR);
> +		spin_unlock_irqrestore(&priv->hw_lock, flags);
> +		if (!(maccr & FTMAC100_MACCR_SW_RST)) {
> +			/*
> +			 * FTMAC100_MACCR_SW_RST cleared does not indicate
> +			 * that hardware reset completed (what the f*ck).
> +			 * We still need to wait for a while.
> +			 */
> +			usleep_range(500, 1000);

Sleeping here means this must be running in process context.  But you
used spin_lock_irqsave() above which implies you're not sure what the
context is.  One of these must be wrong.

I wonder whether hw_lock is even needed; you seem to acquire and release
it around each PIO (read or write).  This should be unnecessary since
each PIO is atomic.

[...]
> +static int ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
> +{
> +	struct net_device *netdev = priv->netdev;
> +	struct device *dev = &netdev->dev;
> +	unsigned long flags;
> +	struct ftmac100_rxdes *rxdes;
> +	struct sk_buff *skb;
> +	int length;
> +	int copied = 0;
> +	int done = 0;
> +
> +	spin_lock_irqsave(&priv->rx_lock, flags);
> +	rxdes = ftmac100_rx_locate_first_segment(priv);
> +	spin_unlock_irqrestore(&priv->rx_lock, flags);
> +	if (!rxdes)
> +		return 0;
> +
> +	if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) {
> +		spin_lock_irqsave(&priv->rx_lock, flags);
> +		ftmac100_rx_drop_packet(priv);
> +		spin_unlock_irqrestore(&priv->rx_lock, flags);

I think you can also get rid of rx_lock; it's only used in the RX data
path which is already serialised by NAPI.

[...]
> +	netif_rx(skb);
> +#endif
> +
> +	netdev->last_rx = jiffies;

Don't set last_rx; the networking core takes care of it now.

> +	priv->stats.rx_packets++;
> +	priv->stats.rx_bytes += skb->len;

This should be done earlier, so that these stats include packets that
are dropped for any reason.

[...]
> +static int ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
> +		dma_addr_t map)
> +{
[...]
> +	ftmac100_txdma_start_polling(priv);
> +	spin_unlock_irqrestore(&priv->hw_lock, flags);
> +	netdev->trans_start = jiffies;

Don't set trans_start; the networking core takes care of it now.

[...]
> +static int ftmac100_alloc_buffers(struct ftmac100 *priv)
> +{
> +	int i;
> +
> +	priv->descs = dma_alloc_coherent(priv->dev,
> +		sizeof(struct ftmac100_descs), &priv->descs_dma_addr,
> +		GFP_KERNEL | GFP_DMA);

On x86, GFP_DMA means the memory must be within the ISA DMA range
(< 16 MB).  I don't know quite what it means on ARM but it may not be
what you want.

[...]
> +/******************************************************************************
> + * interrupt handler
> + *****************************************************************************/
> +static irqreturn_t ftmac100_interrupt(int irq, void *dev_id)
> +{
> +	struct net_device *netdev = dev_id;
> +	struct device *dev = &netdev->dev;
> +	struct ftmac100 *priv = netdev_priv(netdev);
> +	unsigned long flags;
> +	unsigned int status;
> +	unsigned int imr;
> +
> +	spin_lock_irqsave(&priv->hw_lock, flags);
> +	status = ioread32(priv->base + FTMAC100_OFFSET_ISR);
> +	imr = ioread32(priv->base + FTMAC100_OFFSET_IMR);
> +	spin_unlock_irqrestore(&priv->hw_lock, flags);
> +
> +	status &= imr;
> +	if (status & (FTMAC100_INT_RPKT_FINISH | FTMAC100_INT_NORXBUF)) {
> +		/*
> +		 * FTMAC100_INT_RPKT_FINISH:
> +		 *	RX DMA has received packets into RX buffer successfully
> +		 *
> +		 * FTMAC100_INT_NORXBUF:
> +		 *	RX buffer unavailable
> +		 */
> +#ifdef USE_NAPI
> +		/* Disable interrupts for polling */
> +		ftmac100_disable_rxint(priv);
> +
> +		napi_schedule(&priv->napi);
> +#else
> +		int rx = 0;
> +
> +		while (ftmac100_rx_packet(priv, &rx))
> +			;
> +#endif
> +	}
> +
> +	if (status & FTMAC100_INT_NORXBUF) {
> +		/* RX buffer unavailable */
> +		if (printk_ratelimit())
> +			dev_info(dev, "INT_NORXBUF\n");
> +
> +		priv->stats.rx_over_errors++;
> +	}
> +
> +	if (status & (FTMAC100_INT_XPKT_OK | FTMAC100_INT_XPKT_LOST)) {
> +		/*
> +		 * FTMAC100_INT_XPKT_OK:
> +		 *	 packet transmitted to ethernet successfully
> +		 *
> +		 * FTMAC100_INT_XPKT_LOST:
> +		 *	packet transmitted to ethernet lost due to late
> +		 *	collision or excessive collision
> +		 */
> +		ftmac100_tx_complete(priv);
> +	}

TX completions should also be handled through NAPI if possible.

[...]
> +static int ftmac100_open(struct net_device *netdev)
> +{
> +	struct device *dev = &netdev->dev;
> +	struct ftmac100 *priv = netdev_priv(netdev);
> +	int err;
> +
> +	err = ftmac100_alloc_buffers(priv);
> +	if (err) {
> +		dev_err(dev, "failed to allocate buffers\n");
> +		goto err_alloc;
> +	}
> +
> +	err = request_irq(priv->irq, ftmac100_interrupt, 0, netdev->name,
> +		netdev);
> +	if (err) {
> +		dev_err(dev, "failed to request irq %d\n", priv->irq);
> +		goto err_irq;
> +	}
> +
> +	priv->rx_pointer = 0;
> +	priv->tx_clean_pointer = 0;
> +	priv->tx_pointer = 0;
> +	spin_lock_init(&priv->hw_lock);
> +	spin_lock_init(&priv->rx_lock);
> +	spin_lock_init(&priv->tx_lock);

These locks should be initialised in your probe function.

[...]
> +/******************************************************************************
> + * struct platform_driver functions
> + *****************************************************************************/
> +static int ftmac100_remove(struct platform_device *pdev)
> +{
> +	struct net_device *netdev;
> +	struct ftmac100 *priv;
> +
> +	netdev = platform_get_drvdata(pdev);
> +	if (netdev == NULL)
> +		return 0;
> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	priv = netdev_priv(netdev);
> +
> +	unregister_netdev(netdev);
[...]

There should be a netif_napi_del() before this.

A general comment: please use netdev_err(), netdev_info() etc. for
logging.  This ensures that both the platform device address and the
network device name appears in the log messages.

Ben.

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply

* Re: [PATCH v4] netfilter: ipt_CLUSTERIP: remove "no conntrack!"
From: Jan Engelhardt @ 2011-01-13 14:02 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Pablo Neira Ayuso, Netfilter Development Mailinglist, netdev,
	Patrick McHardy
In-Reply-To: <1294925915.3570.87.camel@edumazet-laptop>

On Thursday 2011-01-13 14:38, Eric Dumazet wrote:

>Le jeudi 13 janvier 2011 à 12:54 +0100, Pablo Neira Ayuso a écrit :
>
>> But printing this does not provide any useful information. The first
>> packet that does not belong to the cluster node that has received the
>> packet, or the first invalid packet, will trigger this.
>> 
>> Moreover, this confuses users since they can do nothing if they receive
>> this message.
>> 
>> Moreover, this target should be supersedes by the cluster match, which
>> has been there for quite some time (it's also more flexible).
>
>Now you mentioned it, cluster match is not as flexible right now,
>its hashing is on source_ip only.

I think in that case, xt_cluster should be improved rather
than an old module.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v4] netfilter: ipt_CLUSTERIP: remove "no conntrack!"
From: Eric Dumazet @ 2011-01-13 13:38 UTC (permalink / raw)
  To: Pablo Neira Ayuso
  Cc: Netfilter Development Mailinglist, netdev, Patrick McHardy
In-Reply-To: <4D2EE80B.6010707@netfilter.org>

Le jeudi 13 janvier 2011 à 12:54 +0100, Pablo Neira Ayuso a écrit :

> But printing this does not provide any useful information. The first
> packet that does not belong to the cluster node that has received the
> packet, or the first invalid packet, will trigger this.
> 
> Moreover, this confuses users since they can do nothing if they receive
> this message.
> 
> Moreover, this target should be supersedes by the cluster match, which
> has been there for quite some time (it's also more flexible).

Now you mentioned it, cluster match is not as flexible right now,
its hashing is on source_ip only.

Many people still use ipt_CLUSTERIP, and probably for couple of years.

This issue was raised several times, we should fix CLUSTERIP for good.

Thanks

[PATCH v4] netfilter: ipt_CLUSTERIP: remove "no conntrack!"

When a packet is meant to be handled by another node of the cluster,
silently drop it instead of flooding kernel log.

Note : INVALID packets are also dropped without notice.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Patrick McHardy <kaber@trash.net>
CC: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/ipv4/netfilter/ipt_CLUSTERIP.c |    7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
index 1e26a48..403ca57 100644
--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
+++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
@@ -300,13 +300,8 @@ clusterip_tg(struct sk_buff *skb, const struct xt_action_param *par)
 	 * that the ->target() function isn't called after ->destroy() */
 
 	ct = nf_ct_get(skb, &ctinfo);
-	if (ct == NULL) {
-		pr_info("no conntrack!\n");
-			/* FIXME: need to drop invalid ones, since replies
-			 * to outgoing connections of other nodes will be
-			 * marked as INVALID */
+	if (ct == NULL)
 		return NF_DROP;
-	}
 
 	/* special case: ICMP error handling. conntrack distinguishes between
 	 * error messages (RELATED) and information requests (see below) */


--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [PATCH 00/22] ipvs namespaces v3.3
From: Simon Horman @ 2011-01-13 13:18 UTC (permalink / raw)
  To: Pablo Neira Ayuso
  Cc: netfilter-devel, lvs-devel, netdev, Patrick McHardy,
	Julian Anastasov, Hans Schillstrom
In-Reply-To: <4D2EDDD0.8060903@netfilter.org>

On Thu, Jan 13, 2011 at 12:11:12PM +0100, Pablo Neira Ayuso wrote:
> On 13/01/11 02:52, Simon Horman wrote:
> > Hi Pablo,
> > 
> > this changest includes the following changes since the v3.2 series
> > which was most recently posted as "[GIT PULL nf-next-2.6] ipvs namespaces".
> > 
> > * Remove several hunks that only make whitespace changes
> 
> Thanks a lot for doing this.
> 
> > * Add Acked-by: Julian Anastasov <ja@ssi.bg>
> >   (It was an omission from v3.2)
> > * Fix merge conflicts
> > 
> > There are two changes that produce conflicts
> > * In the current net-next-2.6 tree but absent from the current nf-next-2.6 tree
> >   there is "workqueue: convert
> >   cancel_rearming_delayed_work[queue]() users to cancel_delayed_work_sync()"
> > * And in the current nf-next-2.6 tree  but absent from the current
> >   net-next-2.6 tree there is "net: use the macros defined for the members
> >   of flowi"
> 
> nf-*-2.6 are Patrick's trees. My trees are here:
> 
> http://1984.lsi.us.es/git/

Thanks, noted.

> > In order to create this series I merged net-next-2.6 into nf-next-2.6.
> > The result is at
> > git://git.kernel.org/pub/scm/linux/kernel/git/horms/lvs-test-2.6 ipvs-netns3.3
> > 
> > However, I guess that you have already done your own merge and simply
> > pulling the branch above will create a bit of a mess. Please let me know
> > if you have a tree/branch that I should use as a base for a pull request.
> 
> I have pulled it, everything was fine. Thanks Simon!

Great!

^ permalink raw reply

* Re: panic in tg3 driver
From: Stephen Clark @ 2011-01-13 13:12 UTC (permalink / raw)
  To: Matt Carlson; +Cc: Linux Kernel Network Developers, Michael Chan
In-Reply-To: <20110112030652.GA27164@mcarlson.broadcom.com>

On 01/11/2011 10:06 PM, Matt Carlson wrote:
> lspci -vvv -xxx -s 81:00.0



Further information - I found these messages in /var/log/messages. It looks
like after it switched to INTx mode interrupts for other devices were hosed.

Jan 12 08:37:49 localhost kernel: tg3 0000:81:00.0: eth2: No interrupt was gener
ated using MSI. Switching to INTx mode. Please report this failure to the PCI ma
intainer and include system chipset information
Jan 12 08:37:49 localhost kernel: ADDRCONF(NETDEV_UP): eth2: link is not ready
Jan 12 08:38:50 localhost kernel: ata2: lost interrupt (Status 0x50)
Jan 12 08:38:50 localhost kernel: ata2.01: exception Emask 0x0 SAct 0x0 SErr 0x0
  action 0x6 frozen
Jan 12 08:38:50 localhost kernel: ata2.01: failed command: WRITE DMA
Jan 12 08:38:50 localhost kernel: ata2.01: cmd 
ca/00:08:e0:bc:51/00:00:00:00:00/f0 tag 0 dma 4096 out
Jan 12 08:38:50 localhost kernel:          res 
40/00:01:00:4f:c2/00:00:00:00:00/b0 Emask 0x4 (timeout)
Jan 12 08:38:50 localhost kernel: ata2.01: status: { DRDY }
Jan 12 08:38:50 localhost kernel: ata2: soft resetting link
Jan 12 08:38:50 localhost kernel: do_IRQ: 0.64 No irq handler for vector (irq -1)
Jan 12 08:38:50 localhost kernel: ata2.01: configured for UDMA/33
Jan 12 08:38:54 localhost pppd[1983]: No response to 3 echo-requests
Jan 12 08:39:55 localhost pppoe[1988]: Inactivity timeout... something wicked 
happened on session 3363




^ permalink raw reply

* Re: [PATCH] ipv4: devconf: start IPV4_DEVCONF_* from 0
From: Alexey Kuznetsov @ 2011-01-13 12:51 UTC (permalink / raw)
  To: tgraf, netdev
In-Reply-To: <20110113100224.GB30432@canuck.infradead.org>

Hello!

On Thu, Jan 13, 2011 at 05:02:24AM -0500, Thomas Graf wrote:
> The reason I didn't change anything was the same as Dave's reply, I
> thought it must have been done on purpose.

The days, when this was written, 0 was interpreted as end of array by sysctl core.
BTW, -1=CTL_ANY also used to be special, that's why NET_PROTO_CONF_ALL=-2 rather than -1.

So, yes, it was done on purpose. And, yes, this purpose is not a legal purpose now.

Alexey

^ permalink raw reply

* Re: [patch v2] phonet: some signedness bugs
From: Rémi Denis-Courmont @ 2011-01-13 12:32 UTC (permalink / raw)
  To: error27, dan.j.rosenberg; +Cc: netdev, kernel-janitors
In-Reply-To: <20110110.160620.133889003.davem@davemloft.net>

On Tuesday 11 January 2011 02:06:20 ext David Miller, you wrote:
> From: Dan Carpenter <error27@gmail.com>
> Date: Mon, 10 Jan 2011 17:06:58 +0300
> 
> > Dan Rosenberg pointed out that there were some signed comparison bugs
> > in the phonet protocol.
> > 
> > http://marc.info/?l=full-disclosure&m=129424528425330&w=2
> > 
> > The problem is that we check for array overflows but "protocol" is
> > signed and we don't check for array underflows.  If you have already
> > have CAP_SYS_ADMIN then you could use the bugs to get root, or someone
> > could cause an oops by mistake.
> > 
> > Signed-off-by: Dan Carpenter <error27@gmail.com>
> 
> Applied.

Shouldn't this be sent to stable trees?

-- 
Rémi Denis-Courmont
Nokia Devices R&D, Maemo Software, Helsinki

^ permalink raw reply

* Re: [PATCH] ipv4: devconf: start IPV4_DEVCONF_* from 0
From: Lucian Adrian Grijincu @ 2011-01-13 12:23 UTC (permalink / raw)
  To: Lucian Adrian Grijincu, David Miller, netdev, kuznet, pekkas,
	jmorris, yoshfuji
In-Reply-To: <20110113100224.GB30432@canuck.infradead.org>

On Thu, Jan 13, 2011 at 12:02 PM, Thomas Graf <tgraf@infradead.org> wrote:
> On Thu, Jan 13, 2011 at 09:50:14AM +0200, Lucian Adrian Grijincu wrote:
>> Yes it works, but there does not seem to be a good reason why to
>> complicate things like this (again the sentinel nature of zero is not
>> used in any place here).
>
> I have no objects to changing this at all but we don't gain much either.


Should I post a new patch in which IFLA_INET_CONF cfgid values start
from 0 also or must the ABI for libnl be kept as-is (counting from 1)?


-- 
 .
..: Lucian

^ permalink raw reply

* Re: [PATCH 00/22] ipvs namespaces v3.3
From: Pablo Neira Ayuso @ 2011-01-13 12:16 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Simon Horman, netfilter-devel, lvs-devel, netdev,
	Julian Anastasov, Hans Schillstrom
In-Reply-To: <4D2EE286.1040207@trash.net>

On 13/01/11 12:31, Patrick McHardy wrote:
> On 13.01.2011 12:11, Pablo Neira Ayuso wrote:
>> On 13/01/11 02:52, Simon Horman wrote:
>>> In order to create this series I merged net-next-2.6 into nf-next-2.6.
>>> The result is at
>>> git://git.kernel.org/pub/scm/linux/kernel/git/horms/lvs-test-2.6 ipvs-netns3.3
>>>
>>> However, I guess that you have already done your own merge and simply
>>> pulling the branch above will create a bit of a mess. Please let me know
>>> if you have a tree/branch that I should use as a base for a pull request.
>>
>> I have pulled it, everything was fine. Thanks Simon!
>>
> 
> Thanks Pablo. I'm back up to speed, if you want, I can pull your tree
> into mine.

Go ahead Patrick.

^ permalink raw reply

* Re: [PATCH] netfilter: ipt_CLUSTERIP: dont flood with "no conntrack!"
From: Pablo Neira Ayuso @ 2011-01-13 11:54 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: Netfilter Development Mailinglist, netdev, Patrick McHardy
In-Reply-To: <1294918365.3570.56.camel@edumazet-laptop>

On 13/01/11 12:32, Eric Dumazet wrote:
> Le jeudi 13 janvier 2011 à 12:23 +0100, Pablo Neira Ayuso a écrit :
>> Hi Eric,
>>
>> On 13/01/11 12:13, Eric Dumazet wrote:
>>> ipt_CLUSTERIP users might hit this annoying printk, if they forgot an
>>> "iptables -I INPUT -m state --state INVALID -j DROP" before CLUSTERIP
>>> rule. We could use net_ratelimit() here, or not log the message at all.
>>> I chose to log it once per config.
>>
>> I think that this should be converted to pr_debug() instead, there's
>> also another reference to "unknown protocol" that should be converted as
>> well.
> 
> Problem is pr_debug() is a noop most of the time,
> and printk(KERN_DEBUG is a bit ugly ...
> 
> If we print the message once, better to really print it ;)

But printing this does not provide any useful information. The first
packet that does not belong to the cluster node that has received the
packet, or the first invalid packet, will trigger this.

Moreover, this confuses users since they can do nothing if they receive
this message.

Moreover, this target should be supersedes by the cluster match, which
has been there for quite some time (it's also more flexible).

^ permalink raw reply

* Re: [RFC] net_sched: mark packet staying on queue too long
From: Patrick McHardy @ 2011-01-13 11:50 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: Stephen Hemminger, Eric Dumazet, hadi, Jarek Poplawski,
	David Miller, netdev
In-Reply-To: <Pine.LNX.4.64.1101041506300.2576@ask.diku.dk>

On 04.01.2011 15:19, Jesper Dangaard Brouer wrote:
>> ...
>> You might want to look into CHOKe and ECSFQ which are other AQM models
>> that have shown up in research.
> 
> Have you looked at the SFB (Stochastic Fair Blue) implementation by Juliusz Chroboczek?
> 
> http://www.pps.jussieu.fr/~jch/software/sfb/

I had a closer look at this some time ago and noticed a couple of bugs
(f.i. double buffering might be enabled or disabled or the buffers
switched while a packet is queued, so on dequeue the wrong buffer will
have its queue length decremented) and also found the hashing quite
inefficient, so I've implemented my own version. There's still a minor
bug somewhere, but if people are interested I could finish it some
time soon and post the patches.

^ permalink raw reply

* [PATCH] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Po-Yu Chuang @ 2011-01-13 11:49 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel, Po-Yu Chuang

From: Po-Yu Chuang <ratbert@faraday-tech.com>

FTMAC100 Ethernet Media Access Controller supports 10/100 Mbps and
MII.  This driver has been working on some ARM/NDS32 SoC including
Faraday A320 and Andes AG101.

Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
---
 drivers/net/Kconfig    |    9 +
 drivers/net/Makefile   |    1 +
 drivers/net/ftmac100.c | 1341 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/ftmac100.h |  180 +++++++
 4 files changed, 1531 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/ftmac100.c
 create mode 100644 drivers/net/ftmac100.h

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 3fda24a..0720acc 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2014,6 +2014,15 @@ config BCM63XX_ENET
 	  This driver supports the ethernet MACs in the Broadcom 63xx
 	  MIPS chipset family (BCM63XX).
 
+config FTMAC100
+	tristate "Faraday FTMAC100 10/100 Ethernet support"
+	depends on ARM
+	select MII
+	help
+	  This driver supports the FTMAC100 Ethernet controller from
+	  Faraday. It is used on Faraday A320, Andes AG101, AG101P
+	  and some other ARM/NDS32 SoC's.
+
 source "drivers/net/fs_enet/Kconfig"
 
 source "drivers/net/octeon/Kconfig"
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b90738d..7c21711 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -147,6 +147,7 @@ obj-$(CONFIG_FORCEDETH) += forcedeth.o
 obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
 obj-$(CONFIG_AX88796) += ax88796.o
 obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o
+obj-$(CONFIG_FTMAC100) += ftmac100.o
 
 obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
 obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c
new file mode 100644
index 0000000..97d1f8d
--- /dev/null
+++ b/drivers/net/ftmac100.c
@@ -0,0 +1,1341 @@
+/*
+ * Faraday FTMAC100 10/100 Ethernet
+ *
+ * (C) Copyright 2009-2011 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+
+#include "ftmac100.h"
+
+#define USE_NAPI
+
+#define DRV_NAME	"ftmac100"
+#define DRV_VERSION	"0.1"
+
+#define RX_QUEUE_ENTRIES	128	/* must be power of 2 */
+#define TX_QUEUE_ENTRIES	16	/* must be power of 2 */
+
+#define MAX_PKT_SIZE		1518
+#define RX_BUF_SIZE		2044	/* must be smaller than 0x7ff */
+
+/******************************************************************************
+ * priveate data
+ *****************************************************************************/
+struct ftmac100_descs {
+	struct ftmac100_rxdes	rxdes[RX_QUEUE_ENTRIES];
+	struct ftmac100_txdes	txdes[TX_QUEUE_ENTRIES];
+};
+
+struct ftmac100 {
+	struct resource		*res;
+	void			*base;
+	int			irq;
+
+	struct ftmac100_descs	*descs;
+	dma_addr_t		descs_dma_addr;
+
+	unsigned int		rx_pointer;
+	unsigned int		tx_clean_pointer;
+	unsigned int		tx_pointer;
+	unsigned int		tx_pending;
+
+	spinlock_t		hw_lock;
+	spinlock_t		rx_lock;
+	spinlock_t		tx_lock;
+
+	struct net_device	*netdev;
+	struct device		*dev;
+#ifdef USE_NAPI
+	struct napi_struct	napi;
+#endif
+
+	struct net_device_stats	stats;
+
+	struct mii_if_info	mii;
+};
+
+/******************************************************************************
+ * internal functions (hardware register access)
+ *****************************************************************************/
+#define INT_MASK_RX_DISABLED	(FTMAC100_INT_XPKT_OK		|	\
+				 FTMAC100_INT_XPKT_LOST		|	\
+				 FTMAC100_INT_RPKT_LOST		|	\
+				 FTMAC100_INT_AHB_ERR		|	\
+				 FTMAC100_INT_PHYSTS_CHG)
+
+#define INT_MASK_ALL_ENABLED	(INT_MASK_RX_DISABLED		|	\
+				 FTMAC100_INT_RPKT_FINISH	|	\
+				 FTMAC100_INT_NORXBUF)
+
+#define INT_MASK_ALL_DISABLED	0
+
+#ifdef USE_NAPI
+static inline void ftmac100_disable_rxint(struct ftmac100 *priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->hw_lock, flags);
+	iowrite32(INT_MASK_RX_DISABLED, priv->base + FTMAC100_OFFSET_IMR);
+	spin_unlock_irqrestore(&priv->hw_lock, flags);
+}
+#endif
+
+static inline void ftmac100_enable_all_int(struct ftmac100 *priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->hw_lock, flags);
+	iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTMAC100_OFFSET_IMR);
+	spin_unlock_irqrestore(&priv->hw_lock, flags);
+}
+
+static inline void ftmac100_disable_all_int(struct ftmac100 *priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->hw_lock, flags);
+	iowrite32(INT_MASK_ALL_DISABLED, priv->base + FTMAC100_OFFSET_IMR);
+	spin_unlock_irqrestore(&priv->hw_lock, flags);
+}
+
+static inline void ftmac100_set_receive_ring_base(struct ftmac100 *priv,
+		dma_addr_t addr)
+{
+	iowrite32(addr, priv->base + FTMAC100_OFFSET_RXR_BADR);
+}
+
+static inline void ftmac100_set_transmit_ring_base(struct ftmac100 *priv,
+		dma_addr_t addr)
+{
+	iowrite32(addr, priv->base + FTMAC100_OFFSET_TXR_BADR);
+}
+
+static inline void ftmac100_txdma_start_polling(struct ftmac100 *priv)
+{
+	iowrite32(1, priv->base + FTMAC100_OFFSET_TXPD);
+}
+
+static int ftmac100_reset(struct ftmac100 *priv)
+{
+	struct device *dev = &priv->netdev->dev;
+	unsigned long flags;
+	int i;
+
+	/* NOTE: reset clears all registers */
+
+	spin_lock_irqsave(&priv->hw_lock, flags);
+	iowrite32(FTMAC100_MACCR_SW_RST, priv->base + FTMAC100_OFFSET_MACCR);
+	spin_unlock_irqrestore(&priv->hw_lock, flags);
+
+	for (i = 0; i < 5; i++) {
+		int maccr;
+
+		spin_lock_irqsave(&priv->hw_lock, flags);
+		maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR);
+		spin_unlock_irqrestore(&priv->hw_lock, flags);
+		if (!(maccr & FTMAC100_MACCR_SW_RST)) {
+			/*
+			 * FTMAC100_MACCR_SW_RST cleared does not indicate
+			 * that hardware reset completed (what the f*ck).
+			 * We still need to wait for a while.
+			 */
+			usleep_range(500, 1000);
+			return 0;
+		}
+
+		usleep_range(1000, 10000);
+	}
+
+	dev_err(dev, "software reset failed\n");
+	return -EIO;
+}
+
+static void ftmac100_set_mac(struct ftmac100 *priv, const unsigned char *mac)
+{
+	unsigned int maddr = mac[0] << 8 | mac[1];
+	unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
+
+	iowrite32(maddr, priv->base + FTMAC100_OFFSET_MAC_MADR);
+	iowrite32(laddr, priv->base + FTMAC100_OFFSET_MAC_LADR);
+}
+
+static int ftmac100_start_hw(struct ftmac100 *priv)
+{
+	struct net_device *netdev = priv->netdev;
+	unsigned long flags;
+	int maccr;
+
+	if (ftmac100_reset(priv))
+		return -EIO;
+
+	/* setup ring buffer base registers */
+
+	spin_lock_irqsave(&priv->hw_lock, flags);
+	ftmac100_set_receive_ring_base(priv,
+		priv->descs_dma_addr + offsetof(struct ftmac100_descs, rxdes));
+	ftmac100_set_transmit_ring_base(priv,
+		priv->descs_dma_addr + offsetof(struct ftmac100_descs, txdes));
+
+	iowrite32(FTMAC100_APTC_RXPOLL_CNT(1),
+		priv->base + FTMAC100_OFFSET_APTC);
+
+	ftmac100_set_mac(priv, netdev->dev_addr);
+
+	maccr = FTMAC100_MACCR_XMT_EN |
+		FTMAC100_MACCR_RCV_EN |
+		FTMAC100_MACCR_XDMA_EN |
+		FTMAC100_MACCR_RDMA_EN |
+		FTMAC100_MACCR_CRC_APD |
+		FTMAC100_MACCR_FULLDUP |
+		FTMAC100_MACCR_RX_RUNT |
+		FTMAC100_MACCR_RX_BROADPKT;
+
+	iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR);
+	spin_unlock_irqrestore(&priv->hw_lock, flags);
+
+	return 0;
+}
+
+static void ftmac100_stop_hw(struct ftmac100 *priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->hw_lock, flags);
+	iowrite32(0, priv->base + FTMAC100_OFFSET_MACCR);
+	spin_unlock_irqrestore(&priv->hw_lock, flags);
+}
+
+/******************************************************************************
+ * internal functions (receive descriptor)
+ *****************************************************************************/
+static inline int ftmac100_rxdes_first_segment(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & FTMAC100_RXDES0_FRS;
+}
+
+static inline int ftmac100_rxdes_last_segment(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & FTMAC100_RXDES0_LRS;
+}
+
+static inline int ftmac100_rxdes_owned_by_dma(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & FTMAC100_RXDES0_RXDMA_OWN;
+}
+
+static inline void ftmac100_rxdes_set_dma_own(struct ftmac100_rxdes *rxdes)
+{
+	/* clear status bits */
+	rxdes->rxdes0 = FTMAC100_RXDES0_RXDMA_OWN;
+}
+
+static inline int ftmac100_rxdes_rx_error(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & FTMAC100_RXDES0_RX_ERR;
+}
+
+static inline int ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & FTMAC100_RXDES0_CRC_ERR;
+}
+
+static inline int ftmac100_rxdes_frame_too_long(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & FTMAC100_RXDES0_FTL;
+}
+
+static inline int ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & FTMAC100_RXDES0_RUNT;
+}
+
+static inline int ftmac100_rxdes_odd_nibble(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & FTMAC100_RXDES0_RX_ODD_NB;
+}
+
+static inline unsigned int ftmac100_rxdes_frame_length(
+		struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & FTMAC100_RXDES0_RFL;
+}
+
+static inline int ftmac100_rxdes_multicast(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & FTMAC100_RXDES0_MULTICAST;
+}
+
+static inline void ftmac100_rxdes_set_buffer_size(struct ftmac100_rxdes *rxdes,
+		unsigned int size)
+{
+	rxdes->rxdes1 = (rxdes->rxdes1 & FTMAC100_RXDES1_EDORR) |
+			 FTMAC100_RXDES1_RXBUF_SIZE(size);
+}
+
+static inline void ftmac100_rxdes_set_end_of_ring(struct ftmac100_rxdes *rxdes)
+{
+	rxdes->rxdes1 |= FTMAC100_RXDES1_EDORR;
+}
+
+static inline void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes,
+		dma_addr_t addr)
+{
+	rxdes->rxdes2 = addr;
+}
+
+static inline dma_addr_t ftmac100_rxdes_get_dma_addr(
+		struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes2;
+}
+
+/* rxdes3 is not used by hardware, we use it to keep track of buffer */
+static inline void ftmac100_rxdes_set_va(struct ftmac100_rxdes *rxdes,
+		void *addr)
+{
+	rxdes->rxdes3 = (unsigned int)addr;
+}
+
+static inline void *ftmac100_rxdes_get_va(struct ftmac100_rxdes *rxdes)
+{
+	return (void *)rxdes->rxdes3;
+}
+
+/******************************************************************************
+ * internal functions (receive)
+ *****************************************************************************/
+static inline int ftmac100_next_rx_pointer(int pointer)
+{
+	return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
+}
+
+static inline void ftmac100_rx_pointer_advance(struct ftmac100 *priv)
+{
+	priv->rx_pointer = ftmac100_next_rx_pointer(priv->rx_pointer);
+}
+
+static inline struct ftmac100_rxdes *ftmac100_current_rxdes(
+		struct ftmac100 *priv)
+{
+	return &priv->descs->rxdes[priv->rx_pointer];
+}
+
+static struct ftmac100_rxdes *ftmac100_rx_locate_first_segment(
+		struct ftmac100 *priv)
+{
+	struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
+
+	while (!ftmac100_rxdes_owned_by_dma(rxdes)) {
+		if (ftmac100_rxdes_first_segment(rxdes))
+			return rxdes;
+
+		ftmac100_rxdes_set_dma_own(rxdes);
+		ftmac100_rx_pointer_advance(priv);
+		rxdes = ftmac100_current_rxdes(priv);
+	}
+
+	return NULL;
+}
+
+static int ftmac100_rx_packet_error(struct ftmac100 *priv,
+		struct ftmac100_rxdes *rxdes)
+{
+	struct device *dev = &priv->netdev->dev;
+	int error = 0;
+
+	if (unlikely(ftmac100_rxdes_rx_error(rxdes))) {
+		if (printk_ratelimit())
+			dev_info(dev, "rx err\n");
+
+		priv->stats.rx_errors++;
+		error = 1;
+	}
+
+	if (unlikely(ftmac100_rxdes_crc_error(rxdes))) {
+		if (printk_ratelimit())
+			dev_info(dev, "rx crc err\n");
+
+		priv->stats.rx_crc_errors++;
+		error = 1;
+	}
+
+	if (unlikely(ftmac100_rxdes_frame_too_long(rxdes))) {
+		if (printk_ratelimit())
+			dev_info(dev, "rx frame too long\n");
+
+		priv->stats.rx_length_errors++;
+		error = 1;
+	}
+
+	if (unlikely(ftmac100_rxdes_runt(rxdes))) {
+		if (printk_ratelimit())
+			dev_info(dev, "rx runt\n");
+
+		priv->stats.rx_length_errors++;
+		error = 1;
+	}
+
+	if (unlikely(ftmac100_rxdes_odd_nibble(rxdes))) {
+		if (printk_ratelimit())
+			dev_info(dev, "rx odd nibble\n");
+
+		priv->stats.rx_length_errors++;
+		error = 1;
+	}
+
+	return error;
+}
+
+static void ftmac100_rx_drop_packet(struct ftmac100 *priv)
+{
+	struct device *dev = &priv->netdev->dev;
+	struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
+	int done = 0;
+
+	if (printk_ratelimit())
+		dev_dbg(dev, "drop packet %p\n", rxdes);
+
+	do {
+		if (ftmac100_rxdes_last_segment(rxdes))
+			done = 1;
+
+		ftmac100_rxdes_set_dma_own(rxdes);
+		ftmac100_rx_pointer_advance(priv);
+		rxdes = ftmac100_current_rxdes(priv);
+	} while (!done && !ftmac100_rxdes_owned_by_dma(rxdes));
+
+	priv->stats.rx_dropped++;
+}
+
+static int ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
+{
+	struct net_device *netdev = priv->netdev;
+	struct device *dev = &netdev->dev;
+	unsigned long flags;
+	struct ftmac100_rxdes *rxdes;
+	struct sk_buff *skb;
+	int length;
+	int copied = 0;
+	int done = 0;
+
+	spin_lock_irqsave(&priv->rx_lock, flags);
+	rxdes = ftmac100_rx_locate_first_segment(priv);
+	spin_unlock_irqrestore(&priv->rx_lock, flags);
+	if (!rxdes)
+		return 0;
+
+	if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) {
+		spin_lock_irqsave(&priv->rx_lock, flags);
+		ftmac100_rx_drop_packet(priv);
+		spin_unlock_irqrestore(&priv->rx_lock, flags);
+		return 1;
+	}
+
+	/* start processing */
+
+	length = ftmac100_rxdes_frame_length(rxdes);
+
+	skb = dev_alloc_skb(length + NET_IP_ALIGN);
+	if (unlikely(!skb)) {
+		if (printk_ratelimit())
+			dev_err(dev, "rx skb alloc failed\n");
+
+		spin_lock_irqsave(&priv->rx_lock, flags);
+		ftmac100_rx_drop_packet(priv);
+		spin_unlock_irqrestore(&priv->rx_lock, flags);
+		return 1;
+	}
+
+	if (unlikely(ftmac100_rxdes_multicast(rxdes)))
+		priv->stats.multicast++;
+
+	skb_reserve(skb, NET_IP_ALIGN);
+
+	do {
+		dma_addr_t d = ftmac100_rxdes_get_dma_addr(rxdes);
+		void *buf = ftmac100_rxdes_get_va(rxdes);
+		int size;
+
+		size = min(length - copied, RX_BUF_SIZE);
+
+		dma_sync_single_for_cpu(priv->dev, d, RX_BUF_SIZE,
+			DMA_FROM_DEVICE);
+		memcpy(skb_put(skb, size), buf, size);
+
+		copied += size;
+
+		if (ftmac100_rxdes_last_segment(rxdes))
+			done = 1;
+
+		dma_sync_single_for_device(priv->dev, d, RX_BUF_SIZE,
+			DMA_FROM_DEVICE);
+
+		spin_lock_irqsave(&priv->rx_lock, flags);
+
+		ftmac100_rxdes_set_dma_own(rxdes);
+
+		ftmac100_rx_pointer_advance(priv);
+		rxdes = ftmac100_current_rxdes(priv);
+
+		spin_unlock_irqrestore(&priv->rx_lock, flags);
+	} while (!done && copied < length);
+
+	skb->protocol = eth_type_trans(skb, netdev);
+
+	/* push packet to protocol stack */
+
+#ifdef USE_NAPI
+	netif_receive_skb(skb);
+#else
+	netif_rx(skb);
+#endif
+
+	netdev->last_rx = jiffies;
+
+	priv->stats.rx_packets++;
+	priv->stats.rx_bytes += skb->len;
+
+	(*processed)++;
+
+	return 1;
+}
+
+/******************************************************************************
+ * internal functions (transmit descriptor)
+ *****************************************************************************/
+static inline void ftmac100_txdes_reset(struct ftmac100_txdes *txdes)
+{
+	/* clear all except end of ring bit */
+	txdes->txdes0 = 0;
+	txdes->txdes1 &= FTMAC100_TXDES1_EDOTR;
+	txdes->txdes2 = 0;
+	txdes->txdes3 = 0;
+}
+
+static inline int ftmac100_txdes_owned_by_dma(struct ftmac100_txdes *txdes)
+{
+	return txdes->txdes0 & FTMAC100_TXDES0_TXDMA_OWN;
+}
+
+static inline void ftmac100_txdes_set_dma_own(struct ftmac100_txdes *txdes)
+{
+	txdes->txdes0 |= FTMAC100_TXDES0_TXDMA_OWN;
+}
+
+static inline int ftmac100_txdes_excessive_collision(
+		struct ftmac100_txdes *txdes)
+{
+	return txdes->txdes0 & FTMAC100_TXDES0_TXPKT_EXSCOL;
+}
+
+static inline int ftmac100_txdes_late_collision(struct ftmac100_txdes *txdes)
+{
+	return txdes->txdes0 & FTMAC100_TXDES0_TXPKT_LATECOL;
+}
+
+static inline void ftmac100_txdes_set_end_of_ring(struct ftmac100_txdes *txdes)
+{
+	txdes->txdes1 |= FTMAC100_TXDES1_EDOTR;
+}
+
+static inline void ftmac100_txdes_set_first_segment(
+		struct ftmac100_txdes *txdes)
+{
+	txdes->txdes1 |= FTMAC100_TXDES1_FTS;
+}
+
+static inline void ftmac100_txdes_set_last_segment(struct ftmac100_txdes *txdes)
+{
+	txdes->txdes1 |= FTMAC100_TXDES1_LTS;
+}
+
+static inline void ftmac100_txdes_set_txint(struct ftmac100_txdes *txdes)
+{
+	txdes->txdes1 |= FTMAC100_TXDES1_TXIC;
+}
+
+static inline void ftmac100_txdes_set_buffer_size(struct ftmac100_txdes *txdes,
+		unsigned int len)
+{
+	txdes->txdes1 |= FTMAC100_TXDES1_TXBUF_SIZE(len);
+}
+
+static inline void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes,
+		dma_addr_t addr)
+{
+	txdes->txdes2 = addr;
+}
+
+static inline dma_addr_t ftmac100_txdes_get_dma_addr(
+		struct ftmac100_txdes *txdes)
+{
+	return txdes->txdes2;
+}
+
+/* txdes3 is not used by hardware, we use it to keep track of socket buffer */
+static inline void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes,
+		struct sk_buff *skb)
+{
+	txdes->txdes3 = (unsigned int)skb;
+}
+
+static inline struct sk_buff *ftmac100_txdes_get_skb(
+		struct ftmac100_txdes *txdes)
+{
+	return (struct sk_buff *)txdes->txdes3;
+}
+
+/******************************************************************************
+ * internal functions (transmit)
+ *****************************************************************************/
+static inline int ftmac100_next_tx_pointer(int pointer)
+{
+	return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
+}
+
+static inline void ftmac100_tx_pointer_advance(struct ftmac100 *priv)
+{
+	priv->tx_pointer = ftmac100_next_tx_pointer(priv->tx_pointer);
+}
+
+static inline void ftmac100_tx_clean_pointer_advance(struct ftmac100 *priv)
+{
+	priv->tx_clean_pointer =
+		ftmac100_next_tx_pointer(priv->tx_clean_pointer);
+}
+
+static inline struct ftmac100_txdes *ftmac100_current_txdes(
+		struct ftmac100 *priv)
+{
+	return &priv->descs->txdes[priv->tx_pointer];
+}
+
+static inline struct ftmac100_txdes *ftmac100_current_clean_txdes(
+		struct ftmac100 *priv)
+{
+	return &priv->descs->txdes[priv->tx_clean_pointer];
+}
+
+static int ftmac100_tx_complete_packet(struct ftmac100 *priv)
+{
+	struct net_device *netdev = priv->netdev;
+	struct ftmac100_txdes *txdes;
+	struct sk_buff *skb;
+	dma_addr_t map;
+
+	if (priv->tx_pending == 0)
+		return 0;
+
+	txdes = ftmac100_current_clean_txdes(priv);
+
+	if (ftmac100_txdes_owned_by_dma(txdes))
+		return 0;
+
+	skb = ftmac100_txdes_get_skb(txdes);
+	map = ftmac100_txdes_get_dma_addr(txdes);
+
+	if (unlikely(ftmac100_txdes_excessive_collision(txdes) ||
+			ftmac100_txdes_late_collision(txdes))) {
+		/*
+		 * packet transmitted to ethernet lost due to late collision
+		 * or excessive collision
+		 */
+		priv->stats.tx_aborted_errors++;
+	} else {
+		priv->stats.tx_packets++;
+		priv->stats.tx_bytes += skb->len;
+	}
+
+	dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
+
+	dev_kfree_skb_irq(skb);
+
+	ftmac100_txdes_reset(txdes);
+
+	ftmac100_tx_clean_pointer_advance(priv);
+
+	priv->tx_pending--;
+	netif_wake_queue(netdev);
+
+	return 1;
+}
+
+static void ftmac100_tx_complete(struct ftmac100 *priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->tx_lock, flags);
+	while (ftmac100_tx_complete_packet(priv))
+		;
+	spin_unlock_irqrestore(&priv->tx_lock, flags);
+}
+
+static int ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
+		dma_addr_t map)
+{
+	struct net_device *netdev = priv->netdev;
+	struct device *dev = &netdev->dev;
+	struct ftmac100_txdes *txdes;
+	unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+	unsigned long flags;
+
+	txdes = ftmac100_current_txdes(priv);
+	ftmac100_tx_pointer_advance(priv);
+
+	/* setup TX descriptor */
+
+	spin_lock_irqsave(&priv->tx_lock, flags);
+	ftmac100_txdes_set_skb(txdes, skb);
+	ftmac100_txdes_set_dma_addr(txdes, map);
+
+	ftmac100_txdes_set_first_segment(txdes);
+	ftmac100_txdes_set_last_segment(txdes);
+	ftmac100_txdes_set_txint(txdes);
+	ftmac100_txdes_set_buffer_size(txdes, len);
+
+	priv->tx_pending++;
+	if (priv->tx_pending == TX_QUEUE_ENTRIES) {
+		if (printk_ratelimit())
+			dev_info(dev, "tx queue full\n");
+
+		netif_stop_queue(netdev);
+	}
+
+	/* start transmit */
+
+	wmb();
+	ftmac100_txdes_set_dma_own(txdes);
+	spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+	spin_lock_irqsave(&priv->hw_lock, flags);
+	ftmac100_txdma_start_polling(priv);
+	spin_unlock_irqrestore(&priv->hw_lock, flags);
+	netdev->trans_start = jiffies;
+
+	return NETDEV_TX_OK;
+}
+
+/******************************************************************************
+ * internal functions (buffer)
+ *****************************************************************************/
+static void ftmac100_free_buffers(struct ftmac100 *priv)
+{
+	int i;
+
+	for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) {
+		struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+		dma_addr_t d = ftmac100_rxdes_get_dma_addr(rxdes);
+		void *page = ftmac100_rxdes_get_va(rxdes);
+
+		if (d)
+			dma_unmap_single(priv->dev, d, PAGE_SIZE,
+				DMA_FROM_DEVICE);
+
+		if (page != NULL)
+			free_page((unsigned long)page);
+	}
+
+	for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
+		struct ftmac100_txdes *txdes = &priv->descs->txdes[i];
+		struct sk_buff *skb = ftmac100_txdes_get_skb(txdes);
+
+		if (skb) {
+			dma_addr_t map;
+
+			map = ftmac100_txdes_get_dma_addr(txdes);
+			dma_unmap_single(priv->dev, map, skb_headlen(skb),
+				DMA_TO_DEVICE);
+			dev_kfree_skb(skb);
+		}
+	}
+
+	dma_free_coherent(priv->dev, sizeof(struct ftmac100_descs),
+		priv->descs, priv->descs_dma_addr);
+}
+
+static int ftmac100_alloc_buffers(struct ftmac100 *priv)
+{
+	int i;
+
+	priv->descs = dma_alloc_coherent(priv->dev,
+		sizeof(struct ftmac100_descs), &priv->descs_dma_addr,
+		GFP_KERNEL | GFP_DMA);
+	if (priv->descs == NULL)
+		return -ENOMEM;
+
+	memset(priv->descs, 0, sizeof(struct ftmac100_descs));
+
+	/* initialize RX ring */
+
+	ftmac100_rxdes_set_end_of_ring(
+		&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
+
+	for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) {
+		struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+		void *page;
+		dma_addr_t d;
+
+		page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+		if (page == NULL)
+			goto err;
+
+		d = dma_map_single(priv->dev, page, PAGE_SIZE,
+			DMA_FROM_DEVICE);
+		if (unlikely(dma_mapping_error(priv->dev, d))) {
+			free_page((unsigned long)page);
+			goto err;
+		}
+
+		/*
+		 * The hardware enforces a sub-2K maximum packet size, so we
+		 * put two buffers on every hardware page.
+		 */
+		ftmac100_rxdes_set_va(rxdes, page);
+		ftmac100_rxdes_set_va(rxdes + 1, page + PAGE_SIZE / 2);
+
+		ftmac100_rxdes_set_dma_addr(rxdes, d);
+		ftmac100_rxdes_set_dma_addr(rxdes + 1, d + PAGE_SIZE / 2);
+
+		ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE);
+		ftmac100_rxdes_set_buffer_size(rxdes + 1, RX_BUF_SIZE);
+
+		ftmac100_rxdes_set_dma_own(rxdes);
+		ftmac100_rxdes_set_dma_own(rxdes + 1);
+	}
+
+	/* initialize TX ring */
+
+	ftmac100_txdes_set_end_of_ring(
+		&priv->descs->txdes[TX_QUEUE_ENTRIES - 1]);
+	return 0;
+
+err:
+	ftmac100_free_buffers(priv);
+	return -ENOMEM;
+}
+
+/******************************************************************************
+ * struct mii_if_info functions
+ *****************************************************************************/
+static int ftmac100_mdio_read(struct net_device *netdev, int phy_id, int reg)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	struct device *dev = &netdev->dev;
+	unsigned long flags;
+	int phycr;
+	int i;
+
+	phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
+		FTMAC100_PHYCR_REGAD(reg) |
+		FTMAC100_PHYCR_MIIRD;
+
+	spin_lock_irqsave(&priv->hw_lock, flags);
+	iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
+	spin_unlock_irqrestore(&priv->hw_lock, flags);
+
+	for (i = 0; i < 10; i++) {
+		phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
+
+		if ((phycr & FTMAC100_PHYCR_MIIRD) == 0)
+			return phycr & FTMAC100_PHYCR_MIIRDATA;
+
+		usleep_range(100, 1000);
+	}
+
+	dev_err(dev, "mdio read timed out\n");
+	return 0xffff;
+}
+
+static void ftmac100_mdio_write(struct net_device *netdev, int phy_id, int reg,
+		int data)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	struct device *dev = &netdev->dev;
+	unsigned long flags;
+	int phycr;
+	int i;
+
+	phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
+		FTMAC100_PHYCR_REGAD(reg) |
+		FTMAC100_PHYCR_MIIWR;
+
+	data = FTMAC100_PHYWDATA_MIIWDATA(data);
+
+	spin_lock_irqsave(&priv->hw_lock, flags);
+	iowrite32(data, priv->base + FTMAC100_OFFSET_PHYWDATA);
+	iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
+	spin_unlock_irqrestore(&priv->hw_lock, flags);
+
+	for (i = 0; i < 10; i++) {
+		phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
+
+		if ((phycr & FTMAC100_PHYCR_MIIWR) == 0)
+			return;
+
+		usleep_range(100, 1000);
+	}
+
+	dev_err(dev, "mdio write timed out\n");
+}
+
+/******************************************************************************
+ * struct ethtool_ops functions
+ *****************************************************************************/
+static void ftmac100_get_drvinfo(struct net_device *netdev,
+		struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	strcpy(info->bus_info, dev_name(&netdev->dev));
+}
+
+static int ftmac100_get_settings(struct net_device *netdev,
+		struct ethtool_cmd *cmd)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	return mii_ethtool_gset(&priv->mii, cmd);
+}
+
+static int ftmac100_set_settings(struct net_device *netdev,
+		struct ethtool_cmd *cmd)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	return mii_ethtool_sset(&priv->mii, cmd);
+}
+
+static int ftmac100_nway_reset(struct net_device *netdev)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	return mii_nway_restart(&priv->mii);
+}
+
+static u32 ftmac100_get_link(struct net_device *netdev)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	return mii_link_ok(&priv->mii);
+}
+
+static struct ethtool_ops ftmac100_ethtool_ops = {
+	.set_settings		= ftmac100_set_settings,
+	.get_settings		= ftmac100_get_settings,
+	.get_drvinfo		= ftmac100_get_drvinfo,
+	.nway_reset		= ftmac100_nway_reset,
+	.get_link		= ftmac100_get_link,
+};
+
+/******************************************************************************
+ * interrupt handler
+ *****************************************************************************/
+static irqreturn_t ftmac100_interrupt(int irq, void *dev_id)
+{
+	struct net_device *netdev = dev_id;
+	struct device *dev = &netdev->dev;
+	struct ftmac100 *priv = netdev_priv(netdev);
+	unsigned long flags;
+	unsigned int status;
+	unsigned int imr;
+
+	spin_lock_irqsave(&priv->hw_lock, flags);
+	status = ioread32(priv->base + FTMAC100_OFFSET_ISR);
+	imr = ioread32(priv->base + FTMAC100_OFFSET_IMR);
+	spin_unlock_irqrestore(&priv->hw_lock, flags);
+
+	status &= imr;
+	if (status & (FTMAC100_INT_RPKT_FINISH | FTMAC100_INT_NORXBUF)) {
+		/*
+		 * FTMAC100_INT_RPKT_FINISH:
+		 *	RX DMA has received packets into RX buffer successfully
+		 *
+		 * FTMAC100_INT_NORXBUF:
+		 *	RX buffer unavailable
+		 */
+#ifdef USE_NAPI
+		/* Disable interrupts for polling */
+		ftmac100_disable_rxint(priv);
+
+		napi_schedule(&priv->napi);
+#else
+		int rx = 0;
+
+		while (ftmac100_rx_packet(priv, &rx))
+			;
+#endif
+	}
+
+	if (status & FTMAC100_INT_NORXBUF) {
+		/* RX buffer unavailable */
+		if (printk_ratelimit())
+			dev_info(dev, "INT_NORXBUF\n");
+
+		priv->stats.rx_over_errors++;
+	}
+
+	if (status & (FTMAC100_INT_XPKT_OK | FTMAC100_INT_XPKT_LOST)) {
+		/*
+		 * FTMAC100_INT_XPKT_OK:
+		 *	 packet transmitted to ethernet successfully
+		 *
+		 * FTMAC100_INT_XPKT_LOST:
+		 *	packet transmitted to ethernet lost due to late
+		 *	collision or excessive collision
+		 */
+		ftmac100_tx_complete(priv);
+	}
+
+	if (status & FTMAC100_INT_RPKT_LOST) {
+		/* received packet lost due to RX FIFO full */
+		if (printk_ratelimit())
+			dev_info(dev, "INT_RPKT_LOST\n");
+
+		priv->stats.rx_fifo_errors++;
+	}
+
+	if (status & FTMAC100_INT_AHB_ERR) {
+		/* AHB error */
+		if (printk_ratelimit())
+			dev_info(dev, "INT_AHB_ERR\n");
+
+		/* do nothing */
+	}
+
+	if (status & FTMAC100_INT_PHYSTS_CHG) {
+		/* PHY link status change */
+		if (printk_ratelimit())
+			dev_info(dev, "INT_PHYSTS_CHG\n");
+
+		mii_check_link(&priv->mii);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/******************************************************************************
+ * struct napi_struct functions
+ *****************************************************************************/
+#ifdef USE_NAPI
+static int ftmac100_poll(struct napi_struct *napi, int budget)
+{
+	struct ftmac100 *priv = container_of(napi, struct ftmac100, napi);
+	int retry;
+	int rx = 0;
+
+	do {
+		retry = ftmac100_rx_packet(priv, &rx);
+	} while (retry && rx < budget);
+
+	if (!retry || rx < budget) {
+		/* stop polling */
+		napi_complete(napi);
+		ftmac100_enable_all_int(priv);
+	}
+
+	return rx;
+}
+#endif
+
+/******************************************************************************
+ * struct net_device_ops functions
+ *****************************************************************************/
+static int ftmac100_open(struct net_device *netdev)
+{
+	struct device *dev = &netdev->dev;
+	struct ftmac100 *priv = netdev_priv(netdev);
+	int err;
+
+	err = ftmac100_alloc_buffers(priv);
+	if (err) {
+		dev_err(dev, "failed to allocate buffers\n");
+		goto err_alloc;
+	}
+
+	err = request_irq(priv->irq, ftmac100_interrupt, 0, netdev->name,
+		netdev);
+	if (err) {
+		dev_err(dev, "failed to request irq %d\n", priv->irq);
+		goto err_irq;
+	}
+
+	priv->rx_pointer = 0;
+	priv->tx_clean_pointer = 0;
+	priv->tx_pointer = 0;
+	spin_lock_init(&priv->hw_lock);
+	spin_lock_init(&priv->rx_lock);
+	spin_lock_init(&priv->tx_lock);
+	priv->tx_pending = 0;
+
+	err = ftmac100_start_hw(priv);
+	if (err)
+		goto err_hw;
+
+#ifdef USE_NAPI
+	napi_enable(&priv->napi);
+#endif
+	netif_start_queue(netdev);
+
+	ftmac100_enable_all_int(priv);
+
+	return 0;
+
+err_hw:
+	free_irq(priv->irq, netdev);
+err_irq:
+	ftmac100_free_buffers(priv);
+err_alloc:
+	return err;
+}
+
+static int ftmac100_stop(struct net_device *netdev)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+
+	ftmac100_disable_all_int(priv);
+	netif_stop_queue(netdev);
+#ifdef USE_NAPI
+	napi_disable(&priv->napi);
+#endif
+	ftmac100_stop_hw(priv);
+	free_irq(priv->irq, netdev);
+	ftmac100_free_buffers(priv);
+
+	return 0;
+}
+
+static int ftmac100_hard_start_xmit(struct sk_buff *skb,
+		struct net_device *netdev)
+{
+	struct device *dev = &netdev->dev;
+	struct ftmac100 *priv = netdev_priv(netdev);
+	dma_addr_t map;
+
+	if (unlikely(skb->len > MAX_PKT_SIZE)) {
+		if (printk_ratelimit())
+			dev_dbg(dev, "tx packet too big\n");
+
+		priv->stats.tx_dropped++;
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	map = dma_map_single(priv->dev, skb->data, skb_headlen(skb),
+		DMA_TO_DEVICE);
+	if (unlikely(dma_mapping_error(priv->dev, map))) {
+		/* drop packet */
+		if (printk_ratelimit())
+			dev_err(dev, "map socket buffer failed\n");
+
+		priv->stats.tx_dropped++;
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	return ftmac100_xmit(priv, skb, map);
+}
+
+static struct net_device_stats *ftmac100_get_stats(struct net_device *netdev)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	return &priv->stats;
+}
+
+/* optional */
+static int ftmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr,
+		int cmd)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	struct mii_ioctl_data *data = if_mii(ifr);
+
+	return generic_mii_ioctl(&priv->mii, data, cmd, NULL);
+}
+
+static struct net_device_ops ftmac100_netdev_ops = {
+	.ndo_open		= ftmac100_open,
+	.ndo_stop		= ftmac100_stop,
+	.ndo_start_xmit		= ftmac100_hard_start_xmit,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_get_stats		= ftmac100_get_stats,
+	.ndo_do_ioctl		= ftmac100_do_ioctl,
+};
+
+/******************************************************************************
+ * struct platform_driver functions
+ *****************************************************************************/
+static int ftmac100_remove(struct platform_device *pdev)
+{
+	struct net_device *netdev;
+	struct ftmac100 *priv;
+
+	netdev = platform_get_drvdata(pdev);
+	if (netdev == NULL)
+		return 0;
+
+	platform_set_drvdata(pdev, NULL);
+
+	priv = netdev_priv(netdev);
+
+	unregister_netdev(netdev);
+
+	if (priv->base != NULL)
+		iounmap(priv->base);
+
+	if (priv->res != NULL)
+		release_resource(priv->res);
+
+	free_netdev(netdev);
+
+	return 0;
+}
+
+static int ftmac100_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int irq;
+	struct net_device *netdev;
+	struct ftmac100 *priv;
+	int err;
+
+	if (!pdev)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	/* setup net_device */
+
+	netdev = alloc_etherdev(sizeof(struct ftmac100));
+	if (netdev == NULL) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	SET_NETDEV_DEV(netdev, &pdev->dev);
+	SET_ETHTOOL_OPS(netdev, &ftmac100_ethtool_ops);
+	netdev->netdev_ops = &ftmac100_netdev_ops;
+
+	platform_set_drvdata(pdev, netdev);
+
+	/* setup private data */
+
+	priv = netdev_priv(netdev);
+	priv->netdev = netdev;
+	priv->dev = &pdev->dev;
+
+#ifdef USE_NAPI
+	/* initialize NAPI */
+	netif_napi_add(netdev, &priv->napi, ftmac100_poll, 64);
+#endif
+
+	/* map io memory */
+
+	priv->res = request_mem_region(res->start, res->end - res->start,
+			dev_name(&pdev->dev));
+	if (priv->res == NULL) {
+		dev_err(&pdev->dev, "Could not reserve memory region\n");
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	priv->base = ioremap(res->start, res->end - res->start);
+	if (priv->base == NULL) {
+		dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
+		err = -EIO;
+		goto err_out;
+	}
+
+	priv->irq = irq;
+
+	/* initialize struct mii_if_info */
+
+	priv->mii.phy_id	= 0;
+	priv->mii.phy_id_mask	= 0x1f;
+	priv->mii.reg_num_mask	= 0x1f;
+	priv->mii.dev		= netdev;
+	priv->mii.mdio_read	= ftmac100_mdio_read;
+	priv->mii.mdio_write	= ftmac100_mdio_write;
+
+	/* register network device */
+
+	err = register_netdev(netdev);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to register netdev\n");
+		goto err_out;
+	}
+
+	dev_info(&netdev->dev, "irq %d, mapped at %p\n", priv->irq, priv->base);
+
+	if (is_zero_ether_addr(netdev->dev_addr)) {
+		random_ether_addr(netdev->dev_addr);
+		dev_info(&netdev->dev, "generated random MAC address "
+			"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n",
+			netdev->dev_addr[0], netdev->dev_addr[1],
+			netdev->dev_addr[2], netdev->dev_addr[3],
+			netdev->dev_addr[4], netdev->dev_addr[5]);
+	}
+
+	return 0;
+
+err_out:
+	ftmac100_remove(pdev);
+	return err;
+}
+
+static struct platform_driver ftmac100_driver = {
+	.probe		= ftmac100_probe,
+	.remove		= ftmac100_remove,
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+/******************************************************************************
+ * initialization / finalization
+ *****************************************************************************/
+static int __init ftmac100_init(void)
+{
+	printk(KERN_INFO "Loading " DRV_NAME ": version " DRV_VERSION " ...\n");
+	return platform_driver_register(&ftmac100_driver);
+}
+
+static void __exit ftmac100_exit(void)
+{
+	platform_driver_unregister(&ftmac100_driver);
+}
+
+module_init(ftmac100_init);
+module_exit(ftmac100_exit);
+
+MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
+MODULE_DESCRIPTION("FTMAC100 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ftmac100.h b/drivers/net/ftmac100.h
new file mode 100644
index 0000000..46a0c47
--- /dev/null
+++ b/drivers/net/ftmac100.h
@@ -0,0 +1,180 @@
+/*
+ * Faraday FTMAC100 10/100 Ethernet
+ *
+ * (C) Copyright 2009-2011 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __FTMAC100_H
+#define __FTMAC100_H
+
+#define	FTMAC100_OFFSET_ISR		0x00
+#define	FTMAC100_OFFSET_IMR		0x04
+#define	FTMAC100_OFFSET_MAC_MADR	0x08
+#define	FTMAC100_OFFSET_MAC_LADR	0x0c
+#define	FTMAC100_OFFSET_MAHT0		0x10
+#define	FTMAC100_OFFSET_MAHT1		0x14
+#define	FTMAC100_OFFSET_TXPD		0x18
+#define	FTMAC100_OFFSET_RXPD		0x1c
+#define	FTMAC100_OFFSET_TXR_BADR	0x20
+#define	FTMAC100_OFFSET_RXR_BADR	0x24
+#define	FTMAC100_OFFSET_ITC		0x28
+#define	FTMAC100_OFFSET_APTC		0x2c
+#define	FTMAC100_OFFSET_DBLAC		0x30
+#define	FTMAC100_OFFSET_MACCR		0x88
+#define	FTMAC100_OFFSET_MACSR		0x8c
+#define	FTMAC100_OFFSET_PHYCR		0x90
+#define	FTMAC100_OFFSET_PHYWDATA	0x94
+#define	FTMAC100_OFFSET_FCR		0x98
+#define	FTMAC100_OFFSET_BPR		0x9c
+#define	FTMAC100_OFFSET_TS		0xc4
+#define	FTMAC100_OFFSET_DMAFIFOS	0xc8
+#define	FTMAC100_OFFSET_TM		0xcc
+#define	FTMAC100_OFFSET_TX_MCOL_SCOL	0xd4
+#define	FTMAC100_OFFSET_RPF_AEP		0xd8
+#define	FTMAC100_OFFSET_XM_PG		0xdc
+#define	FTMAC100_OFFSET_RUNT_TLCC	0xe0
+#define	FTMAC100_OFFSET_CRCER_FTL	0xe4
+#define	FTMAC100_OFFSET_RLC_RCC		0xe8
+#define	FTMAC100_OFFSET_BROC		0xec
+#define	FTMAC100_OFFSET_MULCA		0xf0
+#define	FTMAC100_OFFSET_RP		0xf4
+#define	FTMAC100_OFFSET_XP		0xf8
+
+/*
+ * Interrupt status register & interrupt mask register
+ */
+#define	FTMAC100_INT_RPKT_FINISH	(1 << 0)
+#define	FTMAC100_INT_NORXBUF		(1 << 1)
+#define	FTMAC100_INT_XPKT_FINISH	(1 << 2)
+#define	FTMAC100_INT_NOTXBUF		(1 << 3)
+#define	FTMAC100_INT_XPKT_OK		(1 << 4)
+#define	FTMAC100_INT_XPKT_LOST		(1 << 5)
+#define	FTMAC100_INT_RPKT_SAV		(1 << 6)
+#define	FTMAC100_INT_RPKT_LOST		(1 << 7)
+#define	FTMAC100_INT_AHB_ERR		(1 << 8)
+#define	FTMAC100_INT_PHYSTS_CHG		(1 << 9)
+
+/*
+ * Interrupt timer control register
+ */
+#define FTMAC100_ITC_RXINT_CNT(x)	(((x) & 0xf) << 0)
+#define FTMAC100_ITC_RXINT_THR(x)	(((x) & 0x7) << 4)
+#define FTMAC100_ITC_RXINT_TIME_SEL	(1 << 7)
+#define FTMAC100_ITC_TXINT_CNT(x)	(((x) & 0xf) << 8)
+#define FTMAC100_ITC_TXINT_THR(x)	(((x) & 0x7) << 12)
+#define FTMAC100_ITC_TXINT_TIME_SEL	(1 << 15)
+
+/*
+ * Automatic polling timer control register
+ */
+#define	FTMAC100_APTC_RXPOLL_CNT(x)	(((x) & 0xf) << 0)
+#define	FTMAC100_APTC_RXPOLL_TIME_SEL	(1 << 4)
+#define	FTMAC100_APTC_TXPOLL_CNT(x)	(((x) & 0xf) << 8)
+#define	FTMAC100_APTC_TXPOLL_TIME_SEL	(1 << 12)
+
+/*
+ * DMA burst length and arbitration control register
+ */
+#define FTMAC100_DBLAC_INCR4_EN		(1 << 0)
+#define FTMAC100_DBLAC_INCR8_EN		(1 << 1)
+#define FTMAC100_DBLAC_INCR16_EN	(1 << 2)
+#define FTMAC100_DBLAC_RXFIFO_LTHR(x)	(((x) & 0x7) << 3)
+#define FTMAC100_DBLAC_RXFIFO_HTHR(x)	(((x) & 0x7) << 6)
+#define FTMAC100_DBLAC_RX_THR_EN	(1 << 9)
+
+/*
+ * MAC control register
+ */
+#define	FTMAC100_MACCR_XDMA_EN		(1 << 0)
+#define	FTMAC100_MACCR_RDMA_EN		(1 << 1)
+#define	FTMAC100_MACCR_SW_RST		(1 << 2)
+#define	FTMAC100_MACCR_LOOP_EN		(1 << 3)
+#define	FTMAC100_MACCR_CRC_DIS		(1 << 4)
+#define	FTMAC100_MACCR_XMT_EN		(1 << 5)
+#define	FTMAC100_MACCR_ENRX_IN_HALFTX	(1 << 6)
+#define	FTMAC100_MACCR_RCV_EN		(1 << 8)
+#define	FTMAC100_MACCR_HT_MULTI_EN	(1 << 9)
+#define	FTMAC100_MACCR_RX_RUNT		(1 << 10)
+#define	FTMAC100_MACCR_RX_FTL		(1 << 11)
+#define	FTMAC100_MACCR_RCV_ALL		(1 << 12)
+#define	FTMAC100_MACCR_CRC_APD		(1 << 14)
+#define	FTMAC100_MACCR_FULLDUP		(1 << 15)
+#define	FTMAC100_MACCR_RX_MULTIPKT	(1 << 16)
+#define	FTMAC100_MACCR_RX_BROADPKT	(1 << 17)
+
+/*
+ * PHY control register
+ */
+#define FTMAC100_PHYCR_MIIRDATA		0xffff
+#define FTMAC100_PHYCR_PHYAD(x)		(((x) & 0x1f) << 16)
+#define FTMAC100_PHYCR_REGAD(x)		(((x) & 0x1f) << 21)
+#define FTMAC100_PHYCR_MIIRD		(1 << 26)
+#define FTMAC100_PHYCR_MIIWR		(1 << 27)
+
+/*
+ * PHY write data register
+ */
+#define FTMAC100_PHYWDATA_MIIWDATA(x)	((x) & 0xffff)
+
+/*
+ * Transmit descriptor, aligned to 16 bytes
+ */
+struct ftmac100_txdes {
+	unsigned int	txdes0;
+	unsigned int	txdes1;
+	unsigned int	txdes2;	/* TXBUF_BADR */
+	unsigned int	txdes3;	/* not used by HW */
+} __attribute__ ((aligned(16)));
+
+#define	FTMAC100_TXDES0_TXPKT_LATECOL	(1 << 0)
+#define	FTMAC100_TXDES0_TXPKT_EXSCOL	(1 << 1)
+#define	FTMAC100_TXDES0_TXDMA_OWN	(1 << 31)
+
+#define	FTMAC100_TXDES1_TXBUF_SIZE(x)	((x) & 0x7ff)
+#define	FTMAC100_TXDES1_LTS		(1 << 27)
+#define	FTMAC100_TXDES1_FTS		(1 << 28)
+#define	FTMAC100_TXDES1_TX2FIC		(1 << 29)
+#define	FTMAC100_TXDES1_TXIC		(1 << 30)
+#define	FTMAC100_TXDES1_EDOTR		(1 << 31)
+
+/*
+ * Receive descriptor, aligned to 16 bytes
+ */
+struct ftmac100_rxdes {
+	unsigned int	rxdes0;
+	unsigned int	rxdes1;
+	unsigned int	rxdes2;	/* RXBUF_BADR */
+	unsigned int	rxdes3;	/* not used by HW */
+} __attribute__ ((aligned(16)));
+
+#define	FTMAC100_RXDES0_RFL		0x7ff
+#define	FTMAC100_RXDES0_MULTICAST	(1 << 16)
+#define	FTMAC100_RXDES0_BROADCAST	(1 << 17)
+#define	FTMAC100_RXDES0_RX_ERR		(1 << 18)
+#define	FTMAC100_RXDES0_CRC_ERR		(1 << 19)
+#define	FTMAC100_RXDES0_FTL		(1 << 20)
+#define	FTMAC100_RXDES0_RUNT		(1 << 21)
+#define	FTMAC100_RXDES0_RX_ODD_NB	(1 << 22)
+#define	FTMAC100_RXDES0_LRS		(1 << 28)
+#define	FTMAC100_RXDES0_FRS		(1 << 29)
+#define	FTMAC100_RXDES0_RXDMA_OWN	(1 << 31)
+
+#define	FTMAC100_RXDES1_RXBUF_SIZE(x)	((x) & 0x7ff)
+#define	FTMAC100_RXDES1_EDORR		(1 << 31)
+
+#endif /* __FTMAC100_H */
-- 
1.6.3.3


^ permalink raw reply related

* Re: STMMAC driver: NFS Problem on 2.6.37
From: Peppe CAVALLARO @ 2011-01-13 11:48 UTC (permalink / raw)
  To: Deepak SIKRI
  Cc: Trond.Myklebust-HgOvQuBEEgTQT0dZR+AlfA@public.gmane.org,
	netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-nfs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Armando VISCONTI, Shiraz HASHIM, Viresh KUMAR
In-Reply-To: <4D2EC133.7010607-qxv4g6HH51o@public.gmane.org>

> Hi
>
> I am facing a problem related to nfs boot, while using the stmmac driver
> ported on 2.6.37 kernel. When we use a JFFS2 file system and mount the
> kernel,
> the network driver works fine.
>
> I have been following the mailing list and could find some issues with NFS
> on 2.6.37 but I am not too sure whether the kernel crash I am getting is
> related to that.
>
> The driver worked fine on 2.6.32 kernel, but while booting the 2.6.37
> kernel I get the following log messages:
>
Also no problem on my side with the Kernel 2.6.32 plus
the latest device driver from net-2.6.

Peppe

> stmmac: Rx Checksum Offload Engine supported
>         TX Checksum insertion supported
> IP-Config: Complete:
>      device=eth0, addr=192.168.1.10, mask=255.255.255.0,
> gw=255.255.255.255,
>      host=192.168.1.10, domain=, nis-domain=(none),
>      bootserver=192.168.1.1, rootserver=192.168.1.1, rootpath=
> VFS: Unable to mount root fs via NFS, trying floppy.
> VFS: Cannot open root device "nfs" or unknown-block(2,0)
> Please append a correct "root=" boot option; here are the available
> partitions:
> 1f00              64 mtdblock0  (driver?)
> 1f01             256 mtdblock1  (driver?)
> 1f02            2816 mtdblock2  (driver?)
> 1f03            5056 mtdblock3  (driver?)
> Kernel panic - not syncing: VFS: Unable to mount root fs on
> unknown-block(2,0)
> Backtrace:
> [<c00370f0>] (dump_backtrace+0x0/0x110) from [<c0037234>]
> (dump_stack+0x18/0x1c)
>  r7:c7b5b000 r6:00000000 r5:c7b5b015 r4:c04296b8
> [<c003721c>] (dump_stack+0x0/0x1c) from [<c004ebf8>] (panic+0x60/0x180)
> [<c004eb98>] (panic+0x0/0x180) from [<c0009114>]
> (mount_block_root+0x1d4/0x214)
>  r3:00000000 r2:00000001 r1:c782bf50 r0:c0394851
> [<c0008f40>] (mount_block_root+0x0/0x214) from [<c00091fc>]
> (mount_root+0xa8/0xc8)
> [<c0009154>] (mount_root+0x0/0xc8) from [<c0009388>]
> (prepare_namespace+0x16c/0x1d0)
>  r4:c04288c0
> [<c000921c>] (prepare_namespace+0x0/0x1d0) from [<c0008904>]
> (kernel_init+0x1cc/0x220)
>  r5:c0402048 r4:c0428860
> [<c0008738>] (kernel_init+0x0/0x220) from [<c00522a8>] (do_exit+0x0/0x5e0)
>  r7:00000013 r6:c00522a8 r5:c0008738 r4:00000000
> CPU0: stopping
> Backtrace:
> [<c00370f0>] (dump_backtrace+0x0/0x110) from [<c0037234>]
> (dump_stack+0x18/0x1c)
>  r7:c0405484 r6:00000406 r5:00000000 r4:00000000
> [<c003721c>] (dump_stack+0x0/0x1c) from [<c002d334>] (do_IPI+0xb4/0x124)
> [<c002d280>] (do_IPI+0x0/0x124) from [<c0032bb4>] (__irq_svc+0x34/0xc0)
> Exception stack(0xc03f3f50 to 0xc03f3f98)
> 3f40:                                     c0402048 00000000 c03f3f98
> 00000000
> 3f60: c03f2000 c04288dc c0027290 c0405484 000258e8 411fc091 00000000
> c03f3fa4
> 3f80: c03f3fa8 c03f3f98 c0034a24 c0034a28 60000013 ffffffff
>  r5:fc800100 r4:ffffffff
> [<c00349fc>] (default_idle+0x0/0x30) from [<c0034874>]
> (cpu_idle+0x80/0xc0)
> [<c00347f4>] (cpu_idle+0x0/0xc0) from [<c030602c>] (rest_init+0x64/0x7c)
>  r5:c04288dc r4:c04020b0
> [<c0305fc8>] (rest_init+0x0/0x7c) from [<c0008bd4>]
> (start_kernel+0x27c/0x2d8)
> [<c0008958>] (start_kernel+0x0/0x2d8) from [<00008038>] (0x8038)
>  r5:c0401fac r4:10c5387d
>
> I have tried the same over latest source picked from linus tree,
> 4162cf64973df51fc885825bc9ca4d055891c49f
> Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
>
> We are using version 3 of the NFs protocol in kernel's NFS client.
>
>
> Regards
> Deepak
> ST Microelectronics
>
> .
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH V9 13/13] ptp: Added a clock driver for the National Semiconductor PHYTER.
From: Richard Cochran @ 2011-01-13 11:36 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-api, netdev, Alan Cox, Arnd Bergmann, Christoph Lameter,
	David Miller, John Stultz, Krzysztof Halasa, Peter Zijlstra,
	Rodolfo Giometti, Thomas Gleixner
In-Reply-To: <cover.1294917347.git.richard.cochran@omicron.at>

This patch adds support for the PTP clock found on the DP83640.
The basic clock operations and one external time stamp have
been implemented.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 drivers/net/phy/Kconfig       |   29 ++
 drivers/net/phy/Makefile      |    1 +
 drivers/net/phy/dp83640.c     |  896 +++++++++++++++++++++++++++++++++++++++++
 drivers/net/phy/dp83640_reg.h |  261 ++++++++++++
 4 files changed, 1187 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/phy/dp83640.c
 create mode 100644 drivers/net/phy/dp83640_reg.h

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 35fda5a..1f1399a 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -76,6 +76,35 @@ config NATIONAL_PHY
 	---help---
 	  Currently supports the DP83865 PHY.
 
+config DP83640_PHY
+	tristate "Driver for the National Semiconductor DP83640 PHYTER"
+	depends on PTP_1588_CLOCK
+	depends on NETWORK_PHY_TIMESTAMPING
+	---help---
+	  Supports the DP83640 PHYTER with IEEE 1588 features.
+
+	  This driver adds support for using the DP83640 as a PTP
+	  clock. This clock is only useful if your PTP programs are
+	  getting hardware time stamps on the PTP Ethernet packets
+	  using the SO_TIMESTAMPING API.
+
+	  In order for this to work, your MAC driver must also
+	  implement the skb_tx_timetamp() function.
+
+config DP83640_PHY_STATUS_FRAMES
+	bool "DP83640 Status Frames"
+	default y
+	depends on DP83640_PHY
+	---help---
+	  This option allows the DP83640 PHYTER driver to obtain time
+	  stamps from the PHY via special status frames, rather than
+	  reading over the MDIO bus. Using status frames is therefore
+	  more efficient. However, if enabled, this option will cause
+	  the driver to add a mutlicast address to the MAC.
+
+	  Say Y here, unless your MAC does not support multicast
+	  destination addresses.
+
 config STE10XP
 	depends on PHYLIB
 	tristate "Driver for STMicroelectronics STe10Xp PHYs"
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 13bebab..2333215 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_FIXED_PHY)		+= fixed.o
 obj-$(CONFIG_MDIO_BITBANG)	+= mdio-bitbang.o
 obj-$(CONFIG_MDIO_GPIO)		+= mdio-gpio.o
 obj-$(CONFIG_NATIONAL_PHY)	+= national.o
+obj-$(CONFIG_DP83640_PHY)	+= dp83640.o
 obj-$(CONFIG_STE10XP)		+= ste10Xp.o
 obj-$(CONFIG_MICREL_PHY)	+= micrel.o
 obj-$(CONFIG_MDIO_OCTEON)	+= mdio-octeon.o
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
new file mode 100644
index 0000000..5860d66
--- /dev/null
+++ b/drivers/net/phy/dp83640.c
@@ -0,0 +1,896 @@
+/*
+ * Driver for the National Semiconductor DP83640 PHYTER
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/ethtool.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/net_tstamp.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "dp83640_reg.h"
+
+#ifdef CONFIG_DP83640_PHY_STATUS_FRAMES
+#define USE_STATUS_FRAMES
+#endif
+
+#define DP83640_PHY_ID	0x20005ce1
+#define PAGESEL		0x13
+#define LAYER4		0x02
+#define LAYER2		0x01
+#define MAX_RXTS	4
+#define MAX_TXTS	4
+#define N_EXT_TS	1
+#define PSF_PTPVER	2
+#define PSF_EVNT	0x4000
+#define PSF_RX		0x2000
+#define PSF_TX		0x1000
+#define EXT_EVENT	1
+#define EXT_GPIO	1
+
+#if defined(__BIG_ENDIAN)
+#define ENDIAN_FLAG	0
+#elif defined(__LITTLE_ENDIAN)
+#define ENDIAN_FLAG	PSF_ENDIAN
+#endif
+
+#define SKB_PTP_TYPE(__skb) (*(unsigned int *)((__skb)->cb))
+
+struct phy_rxts {
+	u16 ns_lo;   /* ns[15:0] */
+	u16 ns_hi;   /* overflow[1:0], ns[29:16] */
+	u16 sec_lo;  /* sec[15:0] */
+	u16 sec_hi;  /* sec[31:16] */
+	u16 seqid;   /* sequenceId[15:0] */
+	u16 msgtype; /* messageType[3:0], hash[11:0] */
+};
+
+struct phy_txts {
+	u16 ns_lo;   /* ns[15:0] */
+	u16 ns_hi;   /* overflow[1:0], ns[29:16] */
+	u16 sec_lo;  /* sec[15:0] */
+	u16 sec_hi;  /* sec[31:16] */
+};
+
+struct rxts {
+	struct list_head list;
+	unsigned long tmo;
+	u64 ns;
+	u16 seqid;
+	u8  msgtype;
+	u16 hash;
+};
+
+struct dp83640_private {
+	struct ptp_clock_info caps;
+	struct phy_device *phydev;
+	struct work_struct ts_work;
+	int hwts_tx_en;
+	int hwts_rx_en;
+	int layer;
+	int version;
+	/* protects extended registers from concurrent access */
+	struct mutex extreg_mux;
+	int page;
+	/* remember the last event time stamp */
+	struct phy_txts edata;
+	/* list of rx timestamps */
+	struct list_head rxts;
+	struct list_head rxpool;
+	struct rxts rx_pool_data[MAX_RXTS];
+	/* protects above three fields from concurrent access */
+	spinlock_t rx_lock;
+
+	struct sk_buff_head rx_queue;
+	struct sk_buff_head tx_queue;
+};
+
+/* globals */
+
+static struct ptp_clock *dp83640_clock;
+DEFINE_MUTEX(clock_lock); /* protects the one and only dp83640_clock */
+
+static void do_timestamp_work(struct work_struct *work);
+
+/* extended register access functions */
+
+static int ext_read(struct phy_device *phydev, int page, u32 regnum)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+	int val;
+
+	if (dp83640->page != page) {
+		phy_write(phydev, PAGESEL, page);
+		dp83640->page = page;
+	}
+	val = phy_read(phydev, regnum);
+
+	return val;
+}
+
+static void ext_write(struct phy_device *phydev, int page, u32 regnum, u16 val)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	if (dp83640->page != page) {
+		phy_write(phydev, PAGESEL, page);
+		dp83640->page = page;
+	}
+	phy_write(phydev, regnum, val);
+}
+
+static int tdr_write(struct phy_device *phydev, const struct timespec *ts, u16 cmd)
+{
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_nsec & 0xffff);/* ns[15:0] */
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_nsec >> 16);   /* ns[31:16] */
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_sec & 0xffff); /* sec[15:0] */
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_sec >> 16);    /* sec[31:16] */
+
+	ext_write(phydev, PAGE4, PTP_CTL, cmd);
+
+	return 0;
+}
+
+/* convert phy timestamps into driver timestamps */
+
+static void phy2rxts(struct phy_rxts *p, struct rxts *rxts)
+{
+	u32 sec;
+
+	sec = p->sec_lo;
+	sec |= p->sec_hi << 16;
+
+	rxts->ns = p->ns_lo;
+	rxts->ns |= (p->ns_hi & 0x3fff) << 16;
+	rxts->ns += ((u64)sec) * 1000000000ULL;
+	rxts->seqid = p->seqid;
+	rxts->msgtype = (p->msgtype >> 12) & 0xf;
+	rxts->hash = p->msgtype & 0x0fff;
+}
+
+static u64 phy2txts(struct phy_txts *p)
+{
+	u64 ns;
+	u32 sec;
+
+	sec = p->sec_lo;
+	sec |= p->sec_hi << 16;
+
+	ns = p->ns_lo;
+	ns |= (p->ns_hi & 0x3fff) << 16;
+	ns += ((u64)sec) * 1000000000ULL;
+
+	return ns;
+}
+
+/* ptp clock methods */
+
+static int ptp_dp83640_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+	struct dp83640_private *dp83640 =
+		container_of(ptp, struct dp83640_private, caps);
+	struct phy_device *phydev = dp83640->phydev;
+	u64 rate;
+	int neg_adj = 0;
+	u16 hi, lo;
+
+	if (ppb < 0) {
+		neg_adj = 1;
+		ppb = -ppb;
+	}
+	rate = ppb;
+	rate <<= 26;
+	rate = div_u64(rate, 1953125);
+
+	hi = (rate >> 16) & PTP_RATE_HI_MASK;
+	if (neg_adj)
+		hi |= PTP_RATE_DIR;
+
+	lo = rate & 0xffff;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	ext_write(phydev, PAGE4, PTP_RATEH, hi);
+	ext_write(phydev, PAGE4, PTP_RATEL, lo);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return 0;
+}
+
+static int ptp_dp83640_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+	struct dp83640_private *dp83640 =
+		container_of(ptp, struct dp83640_private, caps);
+	struct timespec ts;
+	int err;
+
+	ts = ns_to_timespec(delta);
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	err = tdr_write(dp83640->phydev, &ts, PTP_STEP_CLK);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return err;
+}
+
+static int ptp_dp83640_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+	struct dp83640_private *dp83640 =
+		container_of(ptp, struct dp83640_private, caps);
+	struct phy_device *phydev = dp83640->phydev;
+	unsigned int val[4];
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	ext_write(phydev, PAGE4, PTP_CTL, PTP_RD_CLK);
+
+	val[0] = ext_read(phydev, PAGE4, PTP_TDR); /* ns[15:0] */
+	val[1] = ext_read(phydev, PAGE4, PTP_TDR); /* ns[31:16] */
+	val[2] = ext_read(phydev, PAGE4, PTP_TDR); /* sec[15:0] */
+	val[3] = ext_read(phydev, PAGE4, PTP_TDR); /* sec[31:16] */
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	ts->tv_nsec = val[0] | (val[1] << 16);
+	ts->tv_sec  = val[2] | (val[3] << 16);
+
+	return 0;
+}
+
+static int ptp_dp83640_settime(struct ptp_clock_info *ptp,
+			       const struct timespec *ts)
+{
+	struct dp83640_private *dp83640 =
+		container_of(ptp, struct dp83640_private, caps);
+	int err;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	err = tdr_write(dp83640->phydev, ts, PTP_LOAD_CLK);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return err;
+}
+
+static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
+			      struct ptp_clock_request *rq, int on)
+{
+	struct dp83640_private *dp83640 =
+		container_of(ptp, struct dp83640_private, caps);
+	struct phy_device *phydev = dp83640->phydev;
+	u16 evnt;
+
+	switch (rq->type) {
+	case PTP_CLK_REQ_EXTTS:
+		if (rq->extts.index != 0)
+			return -EINVAL;
+		evnt = EVNT_WR | (EXT_EVENT & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
+		if (on) {
+			evnt |= (EXT_GPIO & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+			evnt |= EVNT_RISE;
+		}
+		ext_write(phydev, PAGE5, PTP_EVNT, evnt);
+		return 0;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info dp83640_caps = {
+	.owner		= THIS_MODULE,
+	.name		= "dp83640 timer",
+	.max_adj	= 1953124,
+	.n_alarm	= 0,
+	.n_ext_ts	= N_EXT_TS,
+	.n_per_out	= 0,
+	.pps		= 0,
+	.adjfreq	= ptp_dp83640_adjfreq,
+	.adjtime	= ptp_dp83640_adjtime,
+	.gettime	= ptp_dp83640_gettime,
+	.settime	= ptp_dp83640_settime,
+	.enable		= ptp_dp83640_enable,
+};
+
+/* status frame conditional code */
+
+#ifdef USE_STATUS_FRAMES
+
+static u8 status_frame_dst[6] = { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 };
+static u8 status_frame_src[6] = { 0x08, 0x00, 0x17, 0x0B, 0x6B, 0x0F };
+
+static void enable_status_frames(struct phy_device *phydev, bool on)
+{
+	u16 cfg0 = 0, ver;
+
+	if (on)
+		cfg0 = PSF_EVNT_EN | PSF_RXTS_EN | PSF_TXTS_EN | ENDIAN_FLAG;
+
+	ver = (PSF_PTPVER & VERSIONPTP_MASK) << VERSIONPTP_SHIFT;
+
+	ext_write(phydev, PAGE5, PSF_CFG0, cfg0);
+	ext_write(phydev, PAGE6, PSF_CFG1, ver);
+
+	if (!phydev->attached_dev) {
+		pr_err("expected to find an attached netdevice\n");
+		BUG();
+	}
+
+	if (on) {
+		if (dev_mc_add(phydev->attached_dev, status_frame_dst))
+			pr_warning("dp83640: failed to add mc address\n");
+	} else {
+		if (dev_mc_del(phydev->attached_dev, status_frame_dst))
+			pr_warning("dp83640: failed to delete mc address\n");
+	}
+}
+
+static bool is_status_frame(struct sk_buff *skb, int type)
+{
+	struct ethhdr *h = eth_hdr(skb);
+
+	if (PTP_CLASS_V2_L2 == type &&
+	    !memcmp(h->h_source, status_frame_src, sizeof(status_frame_src)))
+		return true;
+	else
+		return false;
+}
+
+static void rx_reading_work(struct dp83640_private *dp83640)
+{
+}
+
+static void tx_timestamp_work(struct dp83640_private *dp83640)
+{
+}
+
+#else /* USE_STATUS_FRAMES */
+
+static void enable_status_frames(struct phy_device *phydev, bool on)
+{
+}
+
+static bool is_status_frame(struct sk_buff *skb, int type)
+{
+	return false;
+}
+
+static void read_rxts(struct phy_device *phydev, struct rxts *rxts)
+{
+	struct phy_rxts p;
+
+	p.ns_lo   = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.ns_hi   = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.sec_lo  = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.sec_hi  = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.seqid   = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.msgtype = ext_read(phydev, PAGE4, PTP_RXTS);
+
+	rxts->tmo = jiffies + HZ;
+	phy2rxts(&p, rxts);
+}
+
+static u64 read_txts(struct phy_device *phydev)
+{
+	struct phy_txts p;
+
+	p.ns_lo  = ext_read(phydev, PAGE4, PTP_TXTS);
+	p.ns_hi  = ext_read(phydev, PAGE4, PTP_TXTS);
+	p.sec_lo = ext_read(phydev, PAGE4, PTP_TXTS);
+	p.sec_hi = ext_read(phydev, PAGE4, PTP_TXTS);
+
+	return phy2txts(&p);
+}
+
+static void rx_reading_work(struct dp83640_private *dp83640)
+{
+	struct rxts *rxts;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	for (;;) {
+		int val = ext_read(dp83640->phydev, PAGE4, PTP_STS);
+		if (!(val & RXTS_RDY))
+			break;
+		if (list_empty(&dp83640->rxpool)) {
+			pr_warning("dp83640: rx timestamp pool is empty\n");
+			break;
+		}
+		rxts = list_first_entry(&dp83640->rxpool, struct rxts, list);
+		list_del_init(&rxts->list);
+		read_rxts(dp83640->phydev, rxts);
+		list_add_tail(&rxts->list, &dp83640->rxts);
+	}
+
+	mutex_unlock(&dp83640->extreg_mux);
+}
+
+static void tx_timestamp_work(struct dp83640_private *dp83640)
+{
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct sk_buff *skb;
+	u64 ns;
+	int val;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	while ((skb = skb_dequeue(&dp83640->tx_queue)) != NULL) {
+
+		val = ext_read(dp83640->phydev, PAGE4, PTP_STS);
+		if (!(val & TXTS_RDY)) {
+			skb_queue_head(&dp83640->tx_queue, skb);
+			break;
+		}
+		ns = read_txts(dp83640->phydev);
+		memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+		shhwtstamps.hwtstamp = ns_to_ktime(ns);
+		skb_complete_tx_timestamp(skb, &shhwtstamps);
+	}
+
+	mutex_unlock(&dp83640->extreg_mux);
+}
+
+#endif /* !USE_STATUS_FRAMES */
+
+/* time stamping methods */
+
+static void decode_evnt(struct dp83640_private *dp83640,
+			struct phy_txts *phy_txts, u16 ests)
+{
+	struct ptp_clock_event event;
+	int words = (ests >> EVNT_TS_LEN_SHIFT) & EVNT_TS_LEN_MASK;
+
+	switch (words) { /* fall through in every case */
+	case 3:
+		dp83640->edata.sec_hi = phy_txts->sec_hi;
+	case 2:
+		dp83640->edata.sec_lo = phy_txts->sec_lo;
+	case 1:
+		dp83640->edata.ns_hi = phy_txts->ns_hi;
+	case 0:
+		dp83640->edata.ns_lo = phy_txts->ns_lo;
+	}
+
+	event.type = PTP_CLOCK_EXTTS;
+	event.index = 0;
+	event.timestamp = phy2txts(&dp83640->edata);
+
+	ptp_clock_event(dp83640_clock, &event);
+}
+
+static void decode_rxts(struct dp83640_private *dp83640,
+			struct phy_rxts *phy_rxts)
+{
+	struct rxts *rxts;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dp83640->rx_lock, flags);
+
+	if (list_empty(&dp83640->rxpool)) {
+		pr_warning("dp83640: rx timestamp pool is empty\n");
+		goto out;
+	}
+	rxts = list_first_entry(&dp83640->rxpool, struct rxts, list);
+	list_del_init(&rxts->list);
+	phy2rxts(phy_rxts, rxts);
+	list_add_tail(&rxts->list, &dp83640->rxts);
+out:
+	spin_unlock_irqrestore(&dp83640->rx_lock, flags);
+}
+
+static void decode_txts(struct dp83640_private *dp83640,
+			struct phy_txts *phy_txts)
+{
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct sk_buff *skb;
+	u64 ns;
+
+	/* We must already have the skb that triggered this. */
+
+	skb = skb_dequeue(&dp83640->tx_queue);
+
+	if (!skb) {
+		pr_warning("dp83640: have timestamp but tx_queue empty\n");
+		return;
+	}
+	ns = phy2txts(phy_txts);
+	memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+	shhwtstamps.hwtstamp = ns_to_ktime(ns);
+	skb_complete_tx_timestamp(skb, &shhwtstamps);
+}
+
+static void decode_status_frame(struct dp83640_private *dp83640,
+				struct sk_buff *skb)
+{
+	struct phy_rxts *phy_rxts;
+	struct phy_txts *phy_txts;
+	u8 *ptr;
+	int len, size;
+	u16 ests, type;
+
+	ptr = skb->data + 2;
+
+	for (len = skb_headlen(skb) - 2; len > sizeof(type); len -= size) {
+
+		type = *(u16 *)ptr;
+		ests = type & 0x0fff;
+		type = type & 0xf000;
+		len -= sizeof(type);
+		ptr += sizeof(type);
+
+		if (PSF_RX == type && len >= sizeof(*phy_rxts)) {
+
+			phy_rxts = (struct phy_rxts *) ptr;
+			decode_rxts(dp83640, phy_rxts);
+			size = sizeof(*phy_rxts);
+
+		} else if (PSF_TX == type && len >= sizeof(*phy_txts)) {
+
+			phy_txts = (struct phy_txts *) ptr;
+			decode_txts(dp83640, phy_txts);
+			size = sizeof(*phy_txts);
+
+		} else if (PSF_EVNT == type && len >= sizeof(*phy_txts)) {
+
+			phy_txts = (struct phy_txts *) ptr;
+			decode_evnt(dp83640, phy_txts, ests);
+			size = sizeof(*phy_txts);
+
+		} else {
+			size = 0;
+			break;
+		}
+		ptr += size;
+	}
+}
+
+static int expired(struct rxts *rxts)
+{
+	return time_after(jiffies, rxts->tmo);
+}
+
+static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts)
+{
+	u16 *seqid;
+	u8 *msgtype, *data = skb_mac_header(skb);
+
+	/* check sequenceID, messageType, 12 bit hash of offset 20-29 */
+	/* We assume that the IPv4 header has no options. */
+
+	switch (type) {
+	case PTP_CLASS_V1_IPV4:
+		msgtype = data + 42 + 32;
+		seqid = (u16 *)(data + 42 + 30);
+		break;
+	case PTP_CLASS_V1_IPV6:
+		msgtype = data + 62 + 32;
+		seqid = (u16 *)(data + 62 + 30);
+		break;
+	case PTP_CLASS_V2_IPV4:
+		msgtype = data + 42 + 0;
+		seqid = (u16 *)(data + 42 + 30);
+		break;
+	case PTP_CLASS_V2_IPV6:
+		msgtype = data + 62 + 0;
+		seqid = (u16 *)(data + 62 + 30);
+		break;
+	case PTP_CLASS_V2_L2:
+		msgtype = data + 14 + 0;
+		seqid = (u16 *)(data + 14 + 30);
+		break;
+	case PTP_CLASS_V2_VLAN:
+		msgtype = data + 18 + 0;
+		seqid = (u16 *)(data + 18 + 30);
+		break;
+	default:
+		return 0;
+	}
+
+	return ((*msgtype & 0xf) == rxts->msgtype && *seqid == rxts->seqid);
+}
+
+static int dp83640_probe(struct phy_device *phydev)
+{
+	struct dp83640_private *dp83640;
+	int i;
+
+	dp83640 = kzalloc(sizeof(struct dp83640_private), GFP_KERNEL);
+	if (!dp83640)
+		return -ENOMEM;
+
+	dp83640->phydev = phydev;
+	INIT_WORK(&dp83640->ts_work, do_timestamp_work);
+	mutex_init(&dp83640->extreg_mux);
+
+	INIT_LIST_HEAD(&dp83640->rxts);
+	INIT_LIST_HEAD(&dp83640->rxpool);
+	for (i = 0; i < MAX_RXTS; i++)
+		list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool);
+
+	phydev->priv = dp83640;
+
+	spin_lock_init(&dp83640->rx_lock);
+	skb_queue_head_init(&dp83640->rx_queue);
+	skb_queue_head_init(&dp83640->tx_queue);
+
+	mutex_lock(&clock_lock);
+
+	if (!dp83640_clock) {
+		dp83640->caps = dp83640_caps;
+		dp83640_clock = ptp_clock_register(&dp83640->caps);
+		if (IS_ERR(dp83640_clock)) {
+			mutex_unlock(&clock_lock);
+			kfree(dp83640);
+			return PTR_ERR(dp83640_clock);
+		}
+	}
+	mutex_unlock(&clock_lock);
+
+	return 0;
+}
+
+static void dp83640_remove(struct phy_device *phydev)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	enable_status_frames(phydev, false);
+
+	cancel_work_sync(&dp83640->ts_work);
+
+	mutex_lock(&clock_lock);
+
+	if (dp83640->caps.owner) {
+		ptp_clock_unregister(dp83640_clock);
+		dp83640_clock = NULL;
+	}
+	mutex_unlock(&clock_lock);
+
+	mutex_destroy(&dp83640->extreg_lock);
+
+	kfree(dp83640);
+}
+
+static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+	struct hwtstamp_config cfg;
+	u16 txcfg0, rxcfg0;
+
+	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+		return -EFAULT;
+
+	if (cfg.flags) /* reserved for future extensions */
+		return -EINVAL;
+
+	switch (cfg.tx_type) {
+	case HWTSTAMP_TX_OFF:
+		dp83640->hwts_tx_en = 0;
+		break;
+	case HWTSTAMP_TX_ON:
+		dp83640->hwts_tx_en = 1;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	switch (cfg.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		dp83640->hwts_rx_en = 0;
+		dp83640->layer = 0;
+		dp83640->version = 0;
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER4;
+		dp83640->version = 1;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER4;
+		dp83640->version = 2;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER2;
+		dp83640->version = 2;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER4|LAYER2;
+		dp83640->version = 2;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	txcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT;
+	rxcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT;
+
+	if (dp83640->layer & LAYER2) {
+		txcfg0 |= TX_L2_EN;
+		rxcfg0 |= RX_L2_EN;
+	}
+	if (dp83640->layer & LAYER4) {
+		txcfg0 |= TX_IPV6_EN | TX_IPV4_EN;
+		rxcfg0 |= RX_IPV6_EN | RX_IPV4_EN;
+	}
+
+	if (dp83640->hwts_tx_en)
+		txcfg0 |= TX_TS_EN;
+
+	if (dp83640->hwts_rx_en)
+		rxcfg0 |= RX_TS_EN;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	if (dp83640->hwts_tx_en || dp83640->hwts_rx_en) {
+		enable_status_frames(phydev, true);
+		ext_write(phydev, PAGE4, PTP_CTL, PTP_ENABLE);
+	}
+
+	ext_write(phydev, PAGE5, PTP_TXCFG0, txcfg0);
+	ext_write(phydev, PAGE5, PTP_RXCFG0, rxcfg0);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
+
+static void rx_timestamp_work(struct dp83640_private *dp83640)
+{
+	struct list_head *this, *next;
+	struct rxts *rxts;
+	struct skb_shared_hwtstamps *shhwtstamps;
+	struct sk_buff *skb;
+	unsigned int type;
+	unsigned long flags;
+
+	/* Deliver each deferred packet, with or without a time stamp. */
+
+	while ((skb = skb_dequeue(&dp83640->rx_queue)) != NULL) {
+		type = SKB_PTP_TYPE(skb);
+		spin_lock_irqsave(&dp83640->rx_lock, flags);
+		list_for_each_safe(this, next, &dp83640->rxts) {
+			rxts = list_entry(this, struct rxts, list);
+			if (match(skb, type, rxts)) {
+				shhwtstamps = skb_hwtstamps(skb);
+				memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+				shhwtstamps->hwtstamp = ns_to_ktime(rxts->ns);
+				list_del_init(&rxts->list);
+				list_add(&rxts->list, &dp83640->rxpool);
+				break;
+			}
+		}
+		spin_unlock_irqrestore(&dp83640->rx_lock, flags);
+		netif_rx(skb);
+	}
+
+	/* Clear out expired time stamps. */
+
+	spin_lock_irqsave(&dp83640->rx_lock, flags);
+	list_for_each_safe(this, next, &dp83640->rxts) {
+		rxts = list_entry(this, struct rxts, list);
+		if (expired(rxts)) {
+			list_del_init(&rxts->list);
+			list_add(&rxts->list, &dp83640->rxpool);
+		}
+	}
+	spin_unlock_irqrestore(&dp83640->rx_lock, flags);
+}
+
+static void do_timestamp_work(struct work_struct *work)
+{
+	struct dp83640_private *dp83640 =
+		container_of(work, struct dp83640_private, ts_work);
+
+	rx_reading_work(dp83640);
+	rx_timestamp_work(dp83640);
+	tx_timestamp_work(dp83640);
+}
+
+static bool dp83640_rxtstamp(struct phy_device *phydev,
+			     struct sk_buff *skb, int type)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	if (!dp83640->hwts_rx_en)
+		return false;
+
+	if (is_status_frame(skb, type)) {
+		decode_status_frame(dp83640, skb);
+		/* Let the stack drop this frame. */
+		return false;
+	}
+
+	SKB_PTP_TYPE(skb) = type;
+	skb_queue_tail(&dp83640->rx_queue, skb);
+	schedule_work(&dp83640->ts_work);
+
+	return true;
+}
+
+static void dp83640_txtstamp(struct phy_device *phydev,
+			     struct sk_buff *skb, int type)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	if (!dp83640->hwts_tx_en) {
+		kfree_skb(skb);
+		return;
+	}
+	skb_queue_tail(&dp83640->tx_queue, skb);
+	schedule_work(&dp83640->ts_work);
+}
+
+static struct phy_driver dp83640_driver = {
+	.phy_id		= DP83640_PHY_ID,
+	.phy_id_mask	= 0xfffffff0,
+	.name		= "NatSemi DP83640",
+	.features	= PHY_BASIC_FEATURES,
+	.flags		= 0,
+	.probe		= dp83640_probe,
+	.remove		= dp83640_remove,
+	.config_aneg	= genphy_config_aneg,
+	.read_status	= genphy_read_status,
+	.hwtstamp	= dp83640_hwtstamp,
+	.rxtstamp	= dp83640_rxtstamp,
+	.txtstamp	= dp83640_txtstamp,
+	.driver		= {.owner = THIS_MODULE,}
+};
+
+static int __init dp83640_init(void)
+{
+	return phy_driver_register(&dp83640_driver);
+}
+
+static void __exit dp83640_exit(void)
+{
+	phy_driver_unregister(&dp83640_driver);
+}
+
+MODULE_DESCRIPTION("National Semiconductor DP83640 PHY driver");
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_LICENSE("GPL");
+
+module_init(dp83640_init);
+module_exit(dp83640_exit);
+
+static struct mdio_device_id dp83640_tbl[] = {
+	{ DP83640_PHY_ID, 0xfffffff0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(mdio, dp83640_tbl);
diff --git a/drivers/net/phy/dp83640_reg.h b/drivers/net/phy/dp83640_reg.h
new file mode 100644
index 0000000..9d34bb6
--- /dev/null
+++ b/drivers/net/phy/dp83640_reg.h
@@ -0,0 +1,261 @@
+/* dp83640_reg.h
+ * Generated by regen.tcl on Fri Jul 23 09:45:18 AM CEST 2010
+ */
+#ifndef HAVE_DP83640_REGISTERS
+#define HAVE_DP83640_REGISTERS
+
+#define PAGE4                     0x0004
+#define PTP_CTL                   0x0014 /* PTP Control Register */
+#define PTP_TDR                   0x0015 /* PTP Time Data Register */
+#define PTP_STS                   0x0016 /* PTP Status Register */
+#define PTP_TSTS                  0x0017 /* PTP Trigger Status Register */
+#define PTP_RATEL                 0x0018 /* PTP Rate Low Register */
+#define PTP_RATEH                 0x0019 /* PTP Rate High Register */
+#define PTP_RDCKSUM               0x001a /* PTP Read Checksum */
+#define PTP_WRCKSUM               0x001b /* PTP Write Checksum */
+#define PTP_TXTS                  0x001c /* PTP Transmit Timestamp Register, in four 16-bit reads */
+#define PTP_RXTS                  0x001d /* PTP Receive Timestamp Register, in six? 16-bit reads */
+#define PTP_ESTS                  0x001e /* PTP Event Status Register */
+#define PTP_EDATA                 0x001f /* PTP Event Data Register */
+
+#define PAGE5                     0x0005
+#define PTP_TRIG                  0x0014 /* PTP Trigger Configuration Register */
+#define PTP_EVNT                  0x0015 /* PTP Event Configuration Register */
+#define PTP_TXCFG0                0x0016 /* PTP Transmit Configuration Register 0 */
+#define PTP_TXCFG1                0x0017 /* PTP Transmit Configuration Register 1 */
+#define PSF_CFG0                  0x0018 /* PHY Status Frame Configuration Register 0 */
+#define PTP_RXCFG0                0x0019 /* PTP Receive Configuration Register 0 */
+#define PTP_RXCFG1                0x001a /* PTP Receive Configuration Register 1 */
+#define PTP_RXCFG2                0x001b /* PTP Receive Configuration Register 2 */
+#define PTP_RXCFG3                0x001c /* PTP Receive Configuration Register 3 */
+#define PTP_RXCFG4                0x001d /* PTP Receive Configuration Register 4 */
+#define PTP_TRDL                  0x001e /* PTP Temporary Rate Duration Low Register */
+#define PTP_TRDH                  0x001f /* PTP Temporary Rate Duration High Register */
+
+#define PAGE6                     0x0006
+#define PTP_COC                   0x0014 /* PTP Clock Output Control Register */
+#define PSF_CFG1                  0x0015 /* PHY Status Frame Configuration Register 1 */
+#define PSF_CFG2                  0x0016 /* PHY Status Frame Configuration Register 2 */
+#define PSF_CFG3                  0x0017 /* PHY Status Frame Configuration Register 3 */
+#define PSF_CFG4                  0x0018 /* PHY Status Frame Configuration Register 4 */
+#define PTP_SFDCFG                0x0019 /* PTP SFD Configuration Register */
+#define PTP_INTCTL                0x001a /* PTP Interrupt Control Register */
+#define PTP_CLKSRC                0x001b /* PTP Clock Source Register */
+#define PTP_ETR                   0x001c /* PTP Ethernet Type Register */
+#define PTP_OFF                   0x001d /* PTP Offset Register */
+#define PTP_GPIOMON               0x001e /* PTP GPIO Monitor Register */
+#define PTP_RXHASH                0x001f /* PTP Receive Hash Register */
+
+/* Bit definitions for the PTP_CTL register */
+#define TRIG_SEL_SHIFT            (10) /* PTP Trigger Select */
+#define TRIG_SEL_MASK             (0x7)
+#define TRIG_DIS                  (1<<9) /* Disable PTP Trigger */
+#define TRIG_EN                   (1<<8) /* Enable PTP Trigger */
+#define TRIG_READ                 (1<<7) /* Read PTP Trigger */
+#define TRIG_LOAD                 (1<<6) /* Load PTP Trigger */
+#define PTP_RD_CLK                (1<<5) /* Read PTP Clock */
+#define PTP_LOAD_CLK              (1<<4) /* Load PTP Clock */
+#define PTP_STEP_CLK              (1<<3) /* Step PTP Clock */
+#define PTP_ENABLE                (1<<2) /* Enable PTP Clock */
+#define PTP_DISABLE               (1<<1) /* Disable PTP Clock */
+#define PTP_RESET                 (1<<0) /* Reset PTP Clock */
+
+/* Bit definitions for the PTP_STS register */
+#define TXTS_RDY                  (1<<11) /* Transmit Timestamp Ready */
+#define RXTS_RDY                  (1<<10) /* Receive Timestamp Ready */
+#define TRIG_DONE                 (1<<9) /* PTP Trigger Done */
+#define EVENT_RDY                 (1<<8) /* PTP Event Timestamp Ready */
+#define TXTS_IE                   (1<<3) /* Transmit Timestamp Interrupt Enable */
+#define RXTS_IE                   (1<<2) /* Receive Timestamp Interrupt Enable */
+#define TRIG_IE                   (1<<1) /* Trigger Interrupt Enable */
+#define EVENT_IE                  (1<<0) /* Event Interrupt Enable */
+
+/* Bit definitions for the PTP_TSTS register */
+#define TRIG7_ERROR               (1<<15) /* Trigger 7 Error */
+#define TRIG7_ACTIVE              (1<<14) /* Trigger 7 Active */
+#define TRIG6_ERROR               (1<<13) /* Trigger 6 Error */
+#define TRIG6_ACTIVE              (1<<12) /* Trigger 6 Active */
+#define TRIG5_ERROR               (1<<11) /* Trigger 5 Error */
+#define TRIG5_ACTIVE              (1<<10) /* Trigger 5 Active */
+#define TRIG4_ERROR               (1<<9) /* Trigger 4 Error */
+#define TRIG4_ACTIVE              (1<<8) /* Trigger 4 Active */
+#define TRIG3_ERROR               (1<<7) /* Trigger 3 Error */
+#define TRIG3_ACTIVE              (1<<6) /* Trigger 3 Active */
+#define TRIG2_ERROR               (1<<5) /* Trigger 2 Error */
+#define TRIG2_ACTIVE              (1<<4) /* Trigger 2 Active */
+#define TRIG1_ERROR               (1<<3) /* Trigger 1 Error */
+#define TRIG1_ACTIVE              (1<<2) /* Trigger 1 Active */
+#define TRIG0_ERROR               (1<<1) /* Trigger 0 Error */
+#define TRIG0_ACTIVE              (1<<0) /* Trigger 0 Active */
+
+/* Bit definitions for the PTP_RATEH register */
+#define PTP_RATE_DIR              (1<<15) /* PTP Rate Direction */
+#define PTP_TMP_RATE              (1<<14) /* PTP Temporary Rate */
+#define PTP_RATE_HI_SHIFT         (0) /* PTP Rate High 10-bits */
+#define PTP_RATE_HI_MASK          (0x3ff)
+
+/* Bit definitions for the PTP_ESTS register */
+#define EVNTS_MISSED_SHIFT        (8) /* Indicates number of events missed */
+#define EVNTS_MISSED_MASK         (0x7)
+#define EVNT_TS_LEN_SHIFT         (6) /* Indicates length of the Timestamp field in 16-bit words minus 1 */
+#define EVNT_TS_LEN_MASK          (0x3)
+#define EVNT_RF                   (1<<5) /* Indicates whether the event is a rise or falling event */
+#define EVNT_NUM_SHIFT            (2) /* Indicates Event Timestamp Unit which detected an event */
+#define EVNT_NUM_MASK             (0x7)
+#define MULT_EVNT                 (1<<1) /* Indicates multiple events were detected at the same time */
+#define EVENT_DET                 (1<<0) /* PTP Event Detected */
+
+/* Bit definitions for the PTP_EDATA register */
+#define E7_RISE                   (1<<15) /* Indicates direction of Event 7 */
+#define E7_DET                    (1<<14) /* Indicates Event 7 detected */
+#define E6_RISE                   (1<<13) /* Indicates direction of Event 6 */
+#define E6_DET                    (1<<12) /* Indicates Event 6 detected */
+#define E5_RISE                   (1<<11) /* Indicates direction of Event 5 */
+#define E5_DET                    (1<<10) /* Indicates Event 5 detected */
+#define E4_RISE                   (1<<9) /* Indicates direction of Event 4 */
+#define E4_DET                    (1<<8) /* Indicates Event 4 detected */
+#define E3_RISE                   (1<<7) /* Indicates direction of Event 3 */
+#define E3_DET                    (1<<6) /* Indicates Event 3 detected */
+#define E2_RISE                   (1<<5) /* Indicates direction of Event 2 */
+#define E2_DET                    (1<<4) /* Indicates Event 2 detected */
+#define E1_RISE                   (1<<3) /* Indicates direction of Event 1 */
+#define E1_DET                    (1<<2) /* Indicates Event 1 detected */
+#define E0_RISE                   (1<<1) /* Indicates direction of Event 0 */
+#define E0_DET                    (1<<0) /* Indicates Event 0 detected */
+
+/* Bit definitions for the PTP_TRIG register */
+#define TRIG_PULSE                (1<<15) /* generate a Pulse rather than a single edge */
+#define TRIG_PER                  (1<<14) /* generate a periodic signal */
+#define TRIG_IF_LATE              (1<<13) /* trigger immediately if already past */
+#define TRIG_NOTIFY               (1<<12) /* Trigger Notification Enable */
+#define TRIG_GPIO_SHIFT           (8) /* Trigger GPIO Connection, value 1-12 */
+#define TRIG_GPIO_MASK            (0xf)
+#define TRIG_TOGGLE               (1<<7) /* Trigger Toggle Mode Enable */
+#define TRIG_CSEL_SHIFT           (1) /* Trigger Configuration Select */
+#define TRIG_CSEL_MASK            (0x7)
+#define TRIG_WR                   (1<<0) /* Trigger Configuration Write */
+
+/* Bit definitions for the PTP_EVNT register */
+#define EVNT_RISE                 (1<<14) /* Event Rise Detect Enable */
+#define EVNT_FALL                 (1<<13) /* Event Fall Detect Enable */
+#define EVNT_SINGLE               (1<<12) /* enable single event capture operation */
+#define EVNT_GPIO_SHIFT           (8) /* Event GPIO Connection, value 1-12 */
+#define EVNT_GPIO_MASK            (0xf)
+#define EVNT_SEL_SHIFT            (1) /* Event Select */
+#define EVNT_SEL_MASK             (0x7)
+#define EVNT_WR                   (1<<0) /* Event Configuration Write */
+
+/* Bit definitions for the PTP_TXCFG0 register */
+#define SYNC_1STEP                (1<<15) /* insert timestamp into transmit Sync Messages */
+#define DR_INSERT                 (1<<13) /* Insert Delay_Req Timestamp in Delay_Resp (dangerous) */
+#define NTP_TS_EN                 (1<<12) /* Enable Timestamping of NTP Packets */
+#define IGNORE_2STEP              (1<<11) /* Ignore Two_Step flag for One-Step operation */
+#define CRC_1STEP                 (1<<10) /* Disable checking of CRC for One-Step operation */
+#define CHK_1STEP                 (1<<9) /* Enable UDP Checksum correction for One-Step Operation */
+#define IP1588_EN                 (1<<8) /* Enable IEEE 1588 defined IP address filter */
+#define TX_L2_EN                  (1<<7) /* Layer2 Timestamp Enable */
+#define TX_IPV6_EN                (1<<6) /* IPv6 Timestamp Enable */
+#define TX_IPV4_EN                (1<<5) /* IPv4 Timestamp Enable */
+#define TX_PTP_VER_SHIFT          (1) /* Enable Timestamp capture for IEEE 1588 version X */
+#define TX_PTP_VER_MASK           (0xf)
+#define TX_TS_EN                  (1<<0) /* Transmit Timestamp Enable */
+
+/* Bit definitions for the PTP_TXCFG1 register */
+#define BYTE0_MASK_SHIFT          (8) /* Bit mask to be used for matching Byte0 of the PTP Message */
+#define BYTE0_MASK_MASK           (0xff)
+#define BYTE0_DATA_SHIFT          (0) /* Data to be used for matching Byte0 of the PTP Message */
+#define BYTE0_DATA_MASK           (0xff)
+
+/* Bit definitions for the PSF_CFG0 register */
+#define MAC_SRC_ADD_SHIFT         (11) /* Status Frame Mac Source Address */
+#define MAC_SRC_ADD_MASK          (0x3)
+#define MIN_PRE_SHIFT             (8) /* Status Frame Minimum Preamble */
+#define MIN_PRE_MASK              (0x7)
+#define PSF_ENDIAN                (1<<7) /* Status Frame Endian Control */
+#define PSF_IPV4                  (1<<6) /* Status Frame IPv4 Enable */
+#define PSF_PCF_RD                (1<<5) /* Control Frame Read PHY Status Frame Enable */
+#define PSF_ERR_EN                (1<<4) /* Error PHY Status Frame Enable */
+#define PSF_TXTS_EN               (1<<3) /* Transmit Timestamp PHY Status Frame Enable */
+#define PSF_RXTS_EN               (1<<2) /* Receive Timestamp PHY Status Frame Enable */
+#define PSF_TRIG_EN               (1<<1) /* Trigger PHY Status Frame Enable */
+#define PSF_EVNT_EN               (1<<0) /* Event PHY Status Frame Enable */
+
+/* Bit definitions for the PTP_RXCFG0 register */
+#define DOMAIN_EN                 (1<<15) /* Domain Match Enable */
+#define ALT_MAST_DIS              (1<<14) /* Alternate Master Timestamp Disable */
+#define USER_IP_SEL               (1<<13) /* Selects portion of IP address accessible thru PTP_RXCFG2 */
+#define USER_IP_EN                (1<<12) /* Enable User-programmed IP address filter */
+#define RX_SLAVE                  (1<<11) /* Receive Slave Only */
+#define IP1588_EN_SHIFT           (8) /* Enable IEEE 1588 defined IP address filters */
+#define IP1588_EN_MASK            (0xf)
+#define RX_L2_EN                  (1<<7) /* Layer2 Timestamp Enable */
+#define RX_IPV6_EN                (1<<6) /* IPv6 Timestamp Enable */
+#define RX_IPV4_EN                (1<<5) /* IPv4 Timestamp Enable */
+#define RX_PTP_VER_SHIFT          (1) /* Enable Timestamp capture for IEEE 1588 version X */
+#define RX_PTP_VER_MASK           (0xf)
+#define RX_TS_EN                  (1<<0) /* Receive Timestamp Enable */
+
+/* Bit definitions for the PTP_RXCFG1 register */
+#define BYTE0_MASK_SHIFT          (8) /* Bit mask to be used for matching Byte0 of the PTP Message */
+#define BYTE0_MASK_MASK           (0xff)
+#define BYTE0_DATA_SHIFT          (0) /* Data to be used for matching Byte0 of the PTP Message */
+#define BYTE0_DATA_MASK           (0xff)
+
+/* Bit definitions for the PTP_RXCFG3 register */
+#define TS_MIN_IFG_SHIFT          (12) /* Minimum Inter-frame Gap */
+#define TS_MIN_IFG_MASK           (0xf)
+#define ACC_UDP                   (1<<11) /* Record Timestamp if UDP Checksum Error */
+#define ACC_CRC                   (1<<10) /* Record Timestamp if CRC Error */
+#define TS_APPEND                 (1<<9) /* Append Timestamp for L2 */
+#define TS_INSERT                 (1<<8) /* Enable Timestamp Insertion */
+#define PTP_DOMAIN_SHIFT          (0) /* PTP Message domainNumber field */
+#define PTP_DOMAIN_MASK           (0xff)
+
+/* Bit definitions for the PTP_RXCFG4 register */
+#define IPV4_UDP_MOD              (1<<15) /* Enable IPV4 UDP Modification */
+#define TS_SEC_EN                 (1<<14) /* Enable Timestamp Seconds */
+#define TS_SEC_LEN_SHIFT          (12) /* Inserted Timestamp Seconds Length */
+#define TS_SEC_LEN_MASK           (0x3)
+#define RXTS_NS_OFF_SHIFT         (6) /* Receive Timestamp Nanoseconds offset */
+#define RXTS_NS_OFF_MASK          (0x3f)
+#define RXTS_SEC_OFF_SHIFT        (0) /* Receive Timestamp Seconds offset */
+#define RXTS_SEC_OFF_MASK         (0x3f)
+
+/* Bit definitions for the PTP_COC register */
+#define PTP_CLKOUT_EN             (1<<15) /* PTP Clock Output Enable */
+#define PTP_CLKOUT_SEL            (1<<14) /* PTP Clock Output Source Select */
+#define PTP_CLKOUT_SPEEDSEL       (1<<13) /* PTP Clock Output I/O Speed Select */
+#define PTP_CLKDIV_SHIFT          (0) /* PTP Clock Divide-by Value */
+#define PTP_CLKDIV_MASK           (0xff)
+
+/* Bit definitions for the PSF_CFG1 register */
+#define PTPRESERVED_SHIFT         (12) /* PTP v2 reserved field */
+#define PTPRESERVED_MASK          (0xf)
+#define VERSIONPTP_SHIFT          (8) /* PTP v2 versionPTP field */
+#define VERSIONPTP_MASK           (0xf)
+#define TRANSPORT_SPECIFIC_SHIFT  (4) /* PTP v2 Header transportSpecific field */
+#define TRANSPORT_SPECIFIC_MASK   (0xf)
+#define MESSAGETYPE_SHIFT         (0) /* PTP v2 messageType field */
+#define MESSAGETYPE_MASK          (0xf)
+
+/* Bit definitions for the PTP_SFDCFG register */
+#define TX_SFD_GPIO_SHIFT         (4) /* TX SFD GPIO Select, value 1-12 */
+#define TX_SFD_GPIO_MASK          (0xf)
+#define RX_SFD_GPIO_SHIFT         (0) /* RX SFD GPIO Select, value 1-12 */
+#define RX_SFD_GPIO_MASK          (0xf)
+
+/* Bit definitions for the PTP_INTCTL register */
+#define PTP_INT_GPIO_SHIFT        (0) /* PTP Interrupt GPIO Select */
+#define PTP_INT_GPIO_MASK         (0xf)
+
+/* Bit definitions for the PTP_CLKSRC register */
+#define CLK_SRC_SHIFT             (14) /* PTP Clock Source Select */
+#define CLK_SRC_MASK              (0x3)
+#define CLK_SRC_PER_SHIFT         (0) /* PTP Clock Source Period */
+#define CLK_SRC_PER_MASK          (0x7f)
+
+/* Bit definitions for the PTP_OFF register */
+#define PTP_OFFSET_SHIFT          (0) /* PTP Message offset from preceding header */
+#define PTP_OFFSET_MASK           (0xff)
+
+#endif
-- 
1.7.0.4


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox