Netdev List
 help / color / mirror / Atom feed
* Re: [Bugme-new] [Bug 15239] New: Problem with network performance after a while.
From: Brandeburg, Jesse @ 2010-02-08 23:06 UTC (permalink / raw)
  To: Andrew Morton
  Cc: netdev@vger.kernel.org, e1000-devel@lists.sourceforge.net,
	bugzilla-daemon@bugzilla.kernel.org,
	bugme-daemon@bugzilla.kernel.org, Kirsher, Jeffrey T,
	Allan, Bruce W, Duyck, Alexander H, Waskiewicz Jr, Peter P,
	Ronciak, John, pstaszewski@artcom.pl, stable@kernel.org
In-Reply-To: <20100208141513.b5d0be56.akpm@linux-foundation.org>


Andrew, thanks.

On Mon, 8 Feb 2010, Andrew Morton wrote:
> > Hello the problem is that after some time of working my router have some
> > performance problems.
> > 
> > The problem exist on kernel 2.6.32.7 and 2.6.33-rc6-git5
> > The problem dont exist on 2.6.32.2 kernel.
> 
> That would be bad.  That's a regression in 2.6.33-rc, and we've
> backported that regression into 2.6.32.x -stable.

there were no changes to e1000e from v2.6.32..v2.6.32.7
# git whatchanged v2.6.32..v2.6.32.7 drivers/net/e1000e

 
> > on 2.6.33-rc6-git5 i have this info:

> > BUG: soft lockup - CPU#1 stuck for 61s!
> > [events/1:28]

so why is this events/1 thing here?  I've see this kernel thread related 
to ipv6 stack hanging when under heavy incoming packet load as the 
interface comes up.  Was the interface just brought up or something 
changed in rules or something?

> > Modules linked in:
> > CPU 1
> > Pid: 28, comm: events/1 Not tainted 2.6.33-rc6-git5 #1 X7DCT/X7DCT
> > RIP: 0010:[<ffffffff810a3d89>]  [<ffffffff810a3d89>]
> > kmem_cache_free+0x11b/0x11c
> > RSP: 0018:ffff880028243e50  EFLAGS: 00000292
> > RAX: 0000000000000032 RBX: 000000000000007d RCX: ffff8803190683c0
> > RDX: 0000000000000031 RSI: ffff8803190683c0 RDI: ffff88031f83e680
> > RBP: ffffffff81002893 R08: 0000000000000000 R09: 000000000000007c
> > R10: ffff88030d776800 R11: ffff88030d7768a0 R12: ffff880028243dd0
> > R13: ffffc900008b2f80 R14: ffff88031fa7c800 R15: ffffffff81012da7
> > FS:  0000000000000000(0000) GS:ffff880028240000(0000) knlGS:0000000000000000
> > CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
> > CR2: 00007fd61d5bd000 CR3: 000000031e55c000 CR4: 00000000000006a0
> > DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> > DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> > Process events/1 (pid: 28, threadinfo ffff88031f9c8000, task ffff88031f9a4f80)
> > Stack:
> >  ffffffff8126826f ffff88031faa4600 ffffffff8126834a 000096ba00000023
> > <0> 01ffc90000000024 ffff88031fbb4000 ffff88031faa4600 0000000000000040
> > <0> 0000000000000040 ffff88031faa4788 ffff88031faa4600 0000000000000740
> > Call Trace:
> >  <IRQ>
> >  [<ffffffff8126826f>] ? e1000_put_txbuf+0x62/0x74
> >  [<ffffffff8126834a>] ? e1000_clean_tx_irq+0xc9/0x235

clean_tx_irq in e1000e has a break-out condition to keep the transmit 
cleanup for running forever (if another cpu is transmitting) so it seems 
that something else must have happened.  Can kmem_cache_free sleep or 
stall on a lock?


> >  [<ffffffff8126b71b>] ? e1000_clean+0x5c/0x21c
> >  [<ffffffff812f29a3>] ? net_rx_action+0x71/0x15d
> >  [<ffffffff81035311>] ? __do_softirq+0xd7/0x196
> >  [<ffffffff81002dac>] ? call_softirq+0x1c/0x28
> >  [<ffffffff812f768f>] ? dst_gc_task+0x0/0x1a7
> >  [<ffffffff81002dac>] ? call_softirq+0x1c/0x28
> >  <EOI>
> >  [<ffffffff81004599>] ? do_softirq+0x31/0x63
> >  [<ffffffff81034ec1>] ? local_bh_enable_ip+0x75/0x86
> >  [<ffffffff812f768f>] ? dst_gc_task+0x0/0x1a7
> >  [<ffffffff812f775d>] ? dst_gc_task+0xce/0x1a7
> >  [<ffffffff8136b08c>] ? schedule+0x82c/0x906
> >  [<ffffffff8103c44f>] ? lock_timer_base+0x26/0x4b
> >  [<ffffffff810a41d6>] ? cache_reap+0x0/0x11d
> >  [<ffffffff81044c38>] ? worker_thread+0x14c/0x1dc
> >  [<ffffffff81047dcd>] ? autoremove_wake_function+0x0/0x2e
> >  [<ffffffff81044aec>] ? worker_thread+0x0/0x1dc
> >  [<ffffffff810479bd>] ? kthread+0x79/0x81
> >  [<ffffffff81002cb4>] ? kernel_thread_helper+0x4/0x10
> >  [<ffffffff81047944>] ? kthread+0x0/0x81
> >  [<ffffffff81002cb0>] ? kernel_thread_helper+0x0/0x10
> > Code: fe 79 4c 00 48 85 db 74 14 48 8b 74 24 10 48 89 ef ff 13 48 83 c3 08 48
> > 83 3b 00 eb ea 48 83 c4 18 5b 5d 41 5c 41 5d 41 5e 41 5f <c3> 55 48 89 f5 53 48
> > 89 fb 48 83 ec 08 48 8b 76 18 48 2b 75 10
> > Call Trace:
> >  <IRQ>  [<ffffffff8126826f>] ? e1000_put_txbuf+0x62/0x74
> >  [<ffffffff8126834a>] ? e1000_clean_tx_irq+0xc9/0x235
> >  [<ffffffff8126b71b>] ? e1000_clean+0x5c/0x21c
> >  [<ffffffff812f29a3>] ? net_rx_action+0x71/0x15d
> >  [<ffffffff81035311>] ? __do_softirq+0xd7/0x196
> >  [<ffffffff81002dac>] ? call_softirq+0x1c/0x28
> >  [<ffffffff812f768f>] ? dst_gc_task+0x0/0x1a7
> >  [<ffffffff81002dac>] ? call_softirq+0x1c/0x28
> >  <EOI>  [<ffffffff81004599>] ? do_softirq+0x31/0x63
> >  [<ffffffff81034ec1>] ? local_bh_enable_ip+0x75/0x86
> >  [<ffffffff812f768f>] ? dst_gc_task+0x0/0x1a7
> >  [<ffffffff812f775d>] ? dst_gc_task+0xce/0x1a7
> >  [<ffffffff8136b08c>] ? schedule+0x82c/0x906
> >  [<ffffffff8103c44f>] ? lock_timer_base+0x26/0x4b
> >  [<ffffffff810a41d6>] ? cache_reap+0x0/0x11d
> >  [<ffffffff81044c38>] ? worker_thread+0x14c/0x1dc
> >  [<ffffffff81047dcd>] ? autoremove_wake_function+0x0/0x2e
> >  [<ffffffff81044aec>] ? worker_thread+0x0/0x1dc
> >  [<ffffffff810479bd>] ? kthread+0x79/0x81
> >  [<ffffffff81002cb4>] ? kernel_thread_helper+0x4/0x10
> >  [<ffffffff81047944>] ? kthread+0x0/0x81
> >  [<ffffffff81002cb0>] ? kernel_thread_helper+0x0/0x10
> > 
> > And on 2.6.32.7
> > Feb  4 13:12:40 TM_01_C1 ------------[ cut here ]------------
> > Feb  4 13:12:40 TM_01_C1 WARNING: at net/sched/sch_generic.c:261
> > dev_watchdog+0x130/0x1d6()
> > Feb  4 13:12:40 TM_01_C1 Hardware name: X7DCT
> > Feb  4 13:12:40 TM_01_C1 NETDEV WATCHDOG: eth0 (e1000e): transmit queue 0 timed
> > out
> > Feb  4 13:12:40 TM_01_C1 Modules linked in: oprofile
> > Feb  4 13:12:40 TM_01_C1 Pid: 0, comm: swapper Not tainted 2.6.32 #1

the above doesn't match your statement that it is 2.6.32.7

> > Feb  4 13:12:40 TM_01_C1 Call Trace:
> > Feb  4 13:12:40 TM_01_C1 <IRQ>  [<ffffffff812fcaf7>] ? dev_watchdog+0x130/0x1d6
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff812fcaf7>] ? dev_watchdog+0x130/0x1d6
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff81038811>] ? warn_slowpath_common+0x77/0xa3
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff81038899>] ? warn_slowpath_fmt+0x51/0x59
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff8102897e>] ? activate_task+0x3f/0x4e
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff81034fe5>] ? try_to_wake_up+0x1eb/0x1f8
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff812eb768>] ? netdev_drivername+0x3b/0x40
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff812fcaf7>] ? dev_watchdog+0x130/0x1d6
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff8102d1e3>] ? __wake_up+0x30/0x44
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff812fc9c7>] ? dev_watchdog+0x0/0x1d6
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff810448c4>] ? run_timer_softirq+0x1ff/0x29d
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff810556ab>] ? ktime_get+0x5f/0xb7
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff8103e0fd>] ? __do_softirq+0xd7/0x196
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff8100be7c>] ? call_softirq+0x1c/0x28
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff8100d645>] ? do_softirq+0x31/0x66
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff8101b148>] ?
> > smp_apic_timer_interrupt+0x87/0x95
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff8100b873>] ? apic_timer_interrupt+0x13/0x20
> > Feb  4 13:12:40 TM_01_C1 <EOI>  [<ffffffff810111f5>] ? mwait_idle+0x9b/0xa0
> > Feb  4 13:12:40 TM_01_C1 [<ffffffff8100a236>] ? cpu_idle+0x49/0x7c
> > Feb  4 13:12:40 TM_01_C1 ---[ end trace c670a6a17be040e5 ]---
> > 
> > 
> > Machine is a BGP router with some iproute traffic management.

please send a full dmesg from boot.  also full lspci -vvv.

We sporadically see some issue with some people's use of traffic 
management with some e1000/e devices, we have no repro or common 
reproduction case, so haven't solved the issue yet.  I suspect there is a 
strange interaction with HTB or one of the other queueing disciplines that 
causes some odd skb or set of skb's to arrive, but that is just a theory.

I see two different bugs above, one is the tx hang, could it be that you 
didn't have SOFTLOCKUP detection enabled on 2.6.32?  A softlockup could 
cause a tx hang.

The other bug is the softlockup and I don't know how the driver could be 
at fault in that listed stack path.  We simply can't loop more than 
tx_ring->count times, which could never take > 60 seconds unless one of 
the calls we were making was getting stalled for some reason.

So your traffic is "heavy" forwarding at the time of the failure, do you 
have any input on average packet size?  I'm trying to figure out how we 
might reproduce this.

Jesse

^ permalink raw reply

* Re: [PATCH] dst: call cond_resched() in dst_gc_task()
From: David Miller @ 2010-02-08 23:01 UTC (permalink / raw)
  To: eric.dumazet; +Cc: pstaszewski, netdev
In-Reply-To: <1265657560.4236.80.camel@edumazet-laptop>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Mon, 08 Feb 2010 20:32:40 +0100

> [PATCH] dst: call cond_resched() in dst_gc_task()
> 
> On some workloads, it is quite possible to get a huge dst list to
> process in dst_gc_task(), and trigger soft lockup detection.
> 
> Fix is to call cond_resched(), as we run in process context.
> 
> Reported-by: Pawel Staszewski <pstaszewski@itcare.pl>
> Tested-by: Pawel Staszewski <pstaszewski@itcare.pl>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied and queued up to -stable.

When fixing bugs with kernel bugzilla entries, please
mention them in the commit message.  I fixed this up for
you but please take care of it next time.

Thanks!

^ permalink raw reply

* Re: [Bugme-new] [Bug 15239] New: Problem with network performance after a while.
From: David Miller @ 2010-02-08 22:58 UTC (permalink / raw)
  To: rjw; +Cc: akpm, netdev, maciej.rutecki
In-Reply-To: <201002082351.30629.rjw@sisk.pl>

From: "Rafael J. Wysocki" <rjw@sisk.pl>
Date: Mon, 8 Feb 2010 23:51:30 +0100

> On Monday 08 February 2010, Andrew Morton wrote:
>> On Sat, 6 Feb 2010 14:39:46 GMT
>> bugzilla-daemon@bugzilla.kernel.org wrote:
>> 
>> > http://bugzilla.kernel.org/show_bug.cgi?id=15239
>> 
>> post-2.6.32 regression, backported into -stable :(
> 
> Added, CCing netdev.

No need, we already worked on this and have a fix,
see:

http://marc.info/?l=linux-netdev&m=126565759512718&w=2

^ permalink raw reply

* Re: [Bugme-new] [Bug 15239] New: Problem with network performance after a while.
From: David Miller @ 2010-02-08 22:55 UTC (permalink / raw)
  To: akpm
  Cc: netdev, e1000-devel, bugzilla-daemon, bugme-daemon,
	jeffrey.t.kirsher, jesse.brandeburg, bruce.w.allan,
	alexander.h.duyck, peter.p.waskiewicz.jr, john.ronciak,
	pstaszewski, stable
In-Reply-To: <20100208141513.b5d0be56.akpm@linux-foundation.org>

From: Andrew Morton <akpm@linux-foundation.org>
Date: Mon, 8 Feb 2010 14:15:13 -0800

> That would be bad.  That's a regression in 2.6.33-rc, and we've
> backported that regression into 2.6.32.x -stable.

Luckily we have a fix already. :-)

^ permalink raw reply

* Re: [Bugme-new] [Bug 15239] New: Problem with network performance after a while.
From: Rafael J. Wysocki @ 2010-02-08 22:51 UTC (permalink / raw)
  To: Andrew Morton; +Cc: NetDev, Maciej Rutecki
In-Reply-To: <20100208141537.babeed5f.akpm@linux-foundation.org>

On Monday 08 February 2010, Andrew Morton wrote:
> On Sat, 6 Feb 2010 14:39:46 GMT
> bugzilla-daemon@bugzilla.kernel.org wrote:
> 
> > http://bugzilla.kernel.org/show_bug.cgi?id=15239
> 
> post-2.6.32 regression, backported into -stable :(

Added, CCing netdev.

^ permalink raw reply

* Re: MAX_SKB_FRAGS and GRO
From: Herbert Xu @ 2010-02-08 22:21 UTC (permalink / raw)
  To: Brandeburg, Jesse
  Cc: Anton Blanchard, Kirsher, Jeffrey T, Allan, Bruce W,
	Duyck, Alexander H, Waskiewicz Jr, Peter P, Ronciak, John,
	divy@chelsio.com, netdev@vger.kernel.org
In-Reply-To: <alpine.WNT.2.00.1002080936180.6892@jbrandeb-desk1.amr.corp.intel.com>

On Mon, Feb 08, 2010 at 09:41:58AM -0800, Brandeburg, Jesse wrote:
>  
> > In fact I think the ixgbe code may be broken as it is since it's
> > not marking RSC packets in any way to prevent them from being
> > forwarded through another interface.
> 
> Herbert, what are we missing? Is there some interface for us to do what 
> you describe?  do we need to set DODGY?

The problem is that AFAIK RSC does not impose enough restrictions
on what is merged.  This means that information is lost during the
merge process.  IOW if the merged packet is forwarded we cannot
regenerate the original fragments exactly.

Therefore whenever bridging/routing is used on that interface RSC
must be turned off.

The easiest way to do this is to do what LRO does, which is to
set gso_type to zero and gso_size non-zero.  Of course you'll
also need to use the LRO toggle to control whether RSC is enabled
or not.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: PROBLEM: oops w/ bridge in 2.6.32.7
From: Andrew Morton @ 2010-02-08 22:20 UTC (permalink / raw)
  To: lkml; +Cc: Linux Kernel-Liste, netdev, Stephen Hemminger, bridge
In-Reply-To: <20100206165351.GA9411@localhost>

On Sat, 6 Feb 2010 17:53:52 +0100
lkml@Think-Future.de wrote:

>   Hi, 
> 
> 
> One line summary of the problem:    
> With vboxdrv module loaded, tap0 device (no IP assigned), adding a bridge if w/ tap0 as single port: kernel ooops @brctl
> command. See oops msg below.
> 

Thanks.  let's cc the appropriate lists and individuals.

> A small shell script or example program which triggers the problem:
> ifup br0 did it already. 
> Goal was to use tap0 as bridge port and enable host to guest networking with virtualbox bridge networking.
> virtualbox-ose              3.1.2-dfsg-1+b1
> virtualbox-ose-source       3.1.2-dfsg-1
> 
> 
> Keywords (i.e., modules, networking, kernel):
> bridge, tap, uml, virtualbox, kernel is bigmem, 4gb, lxc enabled
> 
> 
> /proc/version:
> --------------
> Linux version 2.6.32.7 (me@mypole) (gcc version 4.3.4 (Debian 4.3.4-6) ) #8 SMP PREEMPT Thu Feb 4 22:01:23 CET 2010
> 
> 
> Output of Oops.. message:
> 
> Feb  6 16:40:27 localhost kernel: [463555.689331] BUG: unable to handle kernel NULL pointer dereference at 00000022
> Feb  6 16:40:27 localhost kernel: [463555.689343] IP: [<c1134a3a>] sysfs_addrm_start+0x2a/0xb0
> Feb  6 16:40:27 localhost kernel: [463555.689361] *pdpt = 0000000027de2001 *pde = 0000000000000000
> Feb  6 16:40:27 localhost kernel: [463555.689371] Oops: 0000 [#1] PREEMPT SMP
> Feb  6 16:40:27 localhost kernel: [463555.689379] last sysfs file: /sys/devices/virtual/net/br0/address
> Feb  6 16:40:27 localhost kernel: [463555.689386] Modules linked in: bridge vboxdrv iwlagn iwlcore btusb stp tp_smapi thinkpad_ec uvcvideo nf_conntrack_netlink iptable_nat nf_nat iptable_mangle ipt_LOG xt_tcpudp xt_state xt_multiport iptable_filter ip_tables x_tables e1000e rtc_cmos [last unloaded: vboxdrv]
> Feb  6 16:40:27 localhost kernel: [463555.689433]
> Feb  6 16:40:27 localhost kernel: [463555.689440] Pid: 30082, comm: brctl Not tainted (2.6.32.7 #7) 7666B4G
> Feb  6 16:40:27 localhost kernel: [463555.689447] EIP: 0060:[<c1134a3a>] EFLAGS: 00210282 CPU: 1
> Feb  6 16:40:27 localhost kernel: [463555.689455] EIP is at sysfs_addrm_start+0x2a/0xb0
> Feb  6 16:40:27 localhost kernel: [463555.689461] EAX: c19bf5c8 EBX: 00000002 ECX: 00000000 EDX: ee27a000
> Feb  6 16:40:27 localhost kernel: [463555.689467] ESI: ee27be34 EDI: ee27be44 EBP: 00000002 ESP: ee27be20
> Feb  6 16:40:27 localhost kernel: [463555.689473]  DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
> Feb  6 16:40:27 localhost kernel: [463555.689480] Process brctl (pid: 30082, ti=ee27a000 task=f016aa70 task.ti=ee27a000)
> Feb  6 16:40:27 localhost kernel: [463555.689486] Stack:
> Feb  6 16:40:27 localhost kernel: [463555.689489]  f02372bc f02372bc e8e81400 fffffff4 c1134f9f 00000002 00000000 00000000
> Feb  6 16:40:27 localhost kernel: [463555.689504] <0> 00000000 fa2f6cda 00000002 f0237000 c1b5be80 c1135053 ee27be74 00000000
> Feb  6 16:40:27 localhost kernel: [463555.689520] <0> c1136522 00000000 fa2f7380 00000000 f02372bc 00000010 00000000 f02372bc
> Feb  6 16:40:27 localhost kernel: [463555.689537] Call Trace:
> Feb  6 16:40:27 localhost kernel: [463555.689548]  [<c1134f9f>] ? create_dir+0x3f/0x90
> Feb  6 16:40:27 localhost kernel: [463555.689558]  [<c1135053>] ? sysfs_create_subdir+0x13/0x20
> Feb  6 16:40:27 localhost kernel: [463555.689568]  [<c1136522>] ? internal_create_group+0x52/0x180
> Feb  6 16:40:27 localhost kernel: [463555.689582]  [<fa2f4973>] ? br_sysfs_addbr+0x23/0xf0 [bridge]
> Feb  6 16:40:27 localhost kernel: [463555.689594]  [<fa2f0b2f>] ? br_add_bridge+0x18f/0x1a0 [bridge]
> Feb  6 16:40:27 localhost kernel: [463555.689605]  [<fa2f1799>] ? br_ioctl_deviceless_stub+0x209/0x230 [bridge]
> Feb  6 16:40:27 localhost kernel: [463555.689617]  [<fa2f1590>] ? br_ioctl_deviceless_stub+0x0/0x230 [bridge]
> Feb  6 16:40:27 localhost kernel: [463555.689629]  [<c15b234d>] ? sock_ioctl+0xcd/0x260
> Feb  6 16:40:27 localhost kernel: [463555.689638]  [<c15b2280>] ? sock_ioctl+0x0/0x260
> Feb  6 16:40:27 localhost kernel: [463555.689647]  [<c10ec05b>] ? vfs_ioctl+0x2b/0x90
> Feb  6 16:40:27 localhost kernel: [463555.689656]  [<c10ec553>] ? do_vfs_ioctl+0x3b3/0x5d0
> Feb  6 16:40:27 localhost kernel: [463555.689664]  [<c15b3be0>] ? sys_socket+0x60/0x80
> Feb  6 16:40:27 localhost kernel: [463555.689675]  [<c1024f1e>] ? do_page_fault+0x12e/0x300
> Feb  6 16:40:27 localhost kernel: [463555.689684]  [<c10ec7d6>] ? sys_ioctl+0x66/0x70
> Feb  6 16:40:27 localhost kernel: [463555.689692]  [<c1002ef0>] ? sysenter_do_call+0x12/0x22
> Feb  6 16:40:27 localhost kernel: [463555.689698] Code: 00 83 ec 10 b9 04 00 00 00 89 5c 24 04 89 d3 89 74 24 08 89 c6 89 7c 24 0c 89 c7 31 c0 f3 ab b8 c8 f5 9b c1 89 16 e8 56 bc 5f 00 <8b> 53 20 b9 20 44 13 c1 a1 24 38 b3 c1 89 1c 24 e8 41 d5 fb ff
> Feb  6 16:40:27 localhost kernel: [463555.689793] EIP: [<c1134a3a>] sysfs_addrm_start+0x2a/0xb0 SS:ESP 0068:ee27be20
> Feb  6 16:40:27 localhost kernel: [463555.689807] CR2: 0000000000000022
> Feb  6 16:40:27 localhost kernel: [463555.689814] ---[ end trace 9bf3a7ef2bd853ba ]---
> 
> 
> /usr/src/linux/scripts/ver_linux:
> ---------------------------------
> Linux mypole 2.6.32.7 #8 SMP PREEMPT Thu Feb 4 22:01:23 CET 2010 i686 GNU/Linux
>  
> Gnu C                  4.3.4
> Gnu make               3.81
> binutils               2.20
> util-linux             2.16.2
> mount                  support
> module-init-tools      3.11
> e2fsprogs              1.41.9
> PPP                    2.4.4
> Linux C Library        2.10.2
> Dynamic linker (ldd)   2.10.2
> Procps                 3.2.8
> Net-tools              1.60
> Kbd                    1.15.1
> Sh-utils               7.4
> wireless-tools         30
> Modules Loaded         vboxdrv ipt_LOG xt_tcpudp xt_state iptable_filter iptable_mangle iptable_nat nf_nat ip_tables x_tables tp_smapi thinkpad_ec iwlagn rtc_cmos e1000e iwlcore
> 
> 
> /proc/cpuinfo:
> --------------
> 
> processor	: 0
> vendor_id	: GenuineIntel
> cpu family	: 6
> model		: 15
> model name	: Intel(R) Core(TM)2 Duo CPU     L7700  @ 1.80GHz
> stepping	: 11
> cpu MHz		: 1800.000
> cache size	: 4096 KB
> physical id	: 0
> siblings	: 2
> core id		: 0
> cpu cores	: 2
> apicid		: 0
> initial apicid	: 0
> fdiv_bug	: no
> hlt_bug		: no
> f00f_bug	: no
> coma_bug	: no
> fpu		: yes
> fpu_exception	: yes
> cpuid level	: 10
> wp		: yes
> flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm lahf_lm ida tpr_shadow vnmi flexpriority
> bogomips	: 3591.04
> clflush size	: 64
> cache_alignment	: 64
> address sizes	: 36 bits physical, 48 bits virtual
> power management:
> 
> processor	: 1
> vendor_id	: GenuineIntel
> cpu family	: 6
> model		: 15
> model name	: Intel(R) Core(TM)2 Duo CPU     L7700  @ 1.80GHz
> stepping	: 11
> cpu MHz		: 1801.000
> cache size	: 4096 KB
> physical id	: 0
> siblings	: 2
> core id		: 1
> cpu cores	: 2
> apicid		: 1
> initial apicid	: 1
> fdiv_bug	: no
> hlt_bug		: no
> f00f_bug	: no
> coma_bug	: no
> fpu		: yes
> fpu_exception	: yes
> cpuid level	: 10
> wp		: yes
> flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm lahf_lm ida tpr_shadow vnmi flexpriority
> bogomips	: 3590.82
> clflush size	: 64
> cache_alignment	: 64
> address sizes	: 36 bits physical, 48 bits virtual
> power management:
> 
> 
> 
> /proc/modules:
> --------------
> 
> vboxdrv 140109 1 - Live 0xfd2be000
> ipt_LOG 4487 5 - Live 0xf8ec8000
> xt_tcpudp 1887 15 - Live 0xf8ebd000
> xt_state 975 12 - Live 0xf8eb3000
> iptable_filter 1898 1 - Live 0xf8e9f000
> iptable_mangle 2425 0 - Live 0xf8e8c000
> iptable_nat 3979 1 - Live 0xf8e79000
> nf_nat 12235 1 iptable_nat, Live 0xf8e6c000
> ip_tables 9034 3 iptable_filter,iptable_mangle,iptable_nat, Live 0xf8e5f000
> x_tables 10572 5 ipt_LOG,xt_tcpudp,xt_state,iptable_nat,ip_tables, Live 0xf8e50000
> tp_smapi 15816 0 - Live 0xf89e4000
> thinkpad_ec 3782 1 tp_smapi, Live 0xf89d5000
> iwlagn 71544 0 - Live 0xf88bd000
> rtc_cmos 7728 0 - Live 0xf883c000
> e1000e 115466 0 - Live 0xf880f000
> iwlcore 83530 1 iwlagn, Live 0xf87c9000
> 
> 
> /proc/ioports:
> --------------
> 
> 0000-001f : dma1
> 0020-0021 : pic1
> 0040-0043 : timer0
> 004f-004f : smapi
> 0050-0053 : timer1
> 0060-0060 : keyboard
> 0064-0064 : keyboard
> 0070-0071 : rtc0
> 0080-008f : dma page reg
> 00a0-00a1 : pic2
> 00b2-00b2 : smapi
> 00c0-00df : dma2
> 00f0-00ff : fpu
> 0170-0177 : 0000:00:1f.1
>   0170-0177 : ata_piix
> 01f0-01f7 : 0000:00:1f.1
>   01f0-01f7 : ata_piix
> 02f8-02ff : serial
> 0376-0376 : 0000:00:1f.1
>   0376-0376 : ata_piix
> 03c0-03df : vga+
> 03f6-03f6 : 0000:00:1f.1
>   03f6-03f6 : ata_piix
> 03f8-03ff : serial
> 0800-080f : pnp 00:02
> 0cf8-0cff : PCI conf1
> 1000-107f : 0000:00:1f.0
>   1000-107f : pnp 00:02
>     1000-1003 : ACPI PM1a_EVT_BLK
>     1004-1005 : ACPI PM1a_CNT_BLK
>     1008-100b : ACPI PM_TMR
>     1010-1015 : ACPI CPU throttle
>     1020-1020 : ACPI PM2_CNT_BLK
>     1028-102f : ACPI GPE0_BLK
> 1180-11bf : 0000:00:1f.0
>   1180-11bf : pnp 00:02
> 15e0-15ef : pnp 00:02
> 1600-161f : thinkpad_ec
> 164e-164f : pnp 00:02
> 1800-1807 : 0000:00:02.0
> 1840-185f : 0000:00:19.0
> 1860-187f : 0000:00:1a.0
>   1860-187f : uhci_hcd
> 1880-189f : 0000:00:1a.1
>   1880-189f : uhci_hcd
> 18a0-18bf : 0000:00:1d.0
>   18a0-18bf : uhci_hcd
> 18c0-18df : 0000:00:1d.1
>   18c0-18df : uhci_hcd
> 18e0-18ef : 0000:00:1f.1
>   18e0-18ef : ata_piix
> 1c00-1c1f : 0000:00:1f.2
>   1c00-1c1f : ahci
> 1c20-1c23 : 0000:00:1f.2
>   1c20-1c23 : ahci
> 1c24-1c27 : 0000:00:1f.2
>   1c24-1c27 : ahci
> 1c28-1c2f : 0000:00:1f.2
>   1c28-1c2f : ahci
> 1c30-1c37 : 0000:00:1f.2
>   1c30-1c37 : ahci
> 1c40-1c5f : 0000:00:1f.3
>   1c40-1c5f : i801_smbus
> 2000-2fff : PCI Bus 0000:02
> 3000-3fff : PCI Bus 0000:03
> 4000-7fff : PCI Bus 0000:05
>   4000-40ff : PCI CardBus 0000:06
>   4400-44ff : PCI CardBus 0000:06
> 
> 
> /proc/iomem:
> ------------
> 
> 00000000-00001fff : System RAM
> 00002000-00005fff : reserved
> 00006000-0009d7ff : System RAM
> 0009d800-0009ffff : reserved
> 000a0000-000bffff : Video RAM area
> 000c0000-000c7fff : Video ROM
> 000c8000-000cbfff : pnp 00:00
> 000cc000-000cffff : pnp 00:00
> 000e0000-000fffff : reserved
>   000f0000-000fffff : System ROM
> 00100000-bf6affff : System RAM
>   01000000-01747a89 : Kernel code
>   01747a8a-01a5b6b7 : Kernel data
>   01ada000-01b7d5a7 : Kernel bss
> bf6b0000-bf6cbfff : ACPI Tables
> bf6cc000-bf6fffff : ACPI Non-volatile Storage
> bf700000-bfffffff : reserved
> c0000000-c3ffffff : PCI CardBus 0000:06
> c4000000-c4000fff : Intel Flush Page
> d4000000-d7efffff : PCI Bus 0000:05
>   d7efe400-d7efe4ff : 0000:05:00.2
>     d7efe400-d7efe4ff : mmc0
>   d7efe800-d7efefff : 0000:05:00.1
>   d7eff000-d7efffff : 0000:05:00.0
>     d7eff000-d7efffff : yenta_socket
> d8000000-dbffffff : PCI Bus 0000:05
>   d8000000-dbffffff : PCI CardBus 0000:06
> e0000000-efffffff : 0000:00:02.0
> f0000000-f3ffffff : PCI MMCONFIG 0 [00-3f]
>   f0000000-f3ffffff : reserved
>     f0000000-f3ffffff : pnp 00:02
> f4000000-f5ffffff : PCI Bus 0000:02
> f6000000-f7ffffff : PCI Bus 0000:03
>   f7f00000-f7f01fff : 0000:03:00.0
>     f7f00000-f7f01fff : iwlagn
> f8000000-f80fffff : 0000:00:02.0
> f8100000-f81fffff : 0000:00:02.1
> f8200000-f821ffff : 0000:00:19.0
>   f8200000-f821ffff : e1000e
> f8220000-f8223fff : 0000:00:1b.0
>   f8220000-f8223fff : ICH HD audio
> f8225000-f8225fff : 0000:00:19.0
>   f8225000-f8225fff : e1000e
> f8426000-f84267ff : 0000:00:1f.2
>   f8426000-f84267ff : ahci
> f8426c00-f8426fff : 0000:00:1a.7
>   f8426c00-f8426fff : ehci_hcd
> f8427000-f84273ff : 0000:00:1d.7
>   f8427000-f84273ff : ehci_hcd
> f8427400-f84274ff : 0000:00:1f.3
> f8500000-f85fffff : PCI Bus 0000:02
> f8600000-f86fffff : PCI Bus 0000:03
> fec00000-fec0ffff : reserved
>   fec00000-fec00fff : IOAPIC 0
> fed00000-fed003ff : HPET 0
>   fed00000-fed003ff : reserved
> fed14000-fed19fff : reserved
>   fed14000-fed17fff : pnp 00:02
>   fed18000-fed18fff : pnp 00:02
>   fed19000-fed19fff : pnp 00:02
> fed1c000-fed8ffff : reserved
>   fed1c000-fed1ffff : pnp 00:02
>   fed45000-fed4bfff : pnp 00:02
> fee00000-fee00fff : Local APIC
>   fee00000-fee00fff : reserved
> ff000000-ffffffff : reserved
> 100000000-13bffffff : System RAM
> 
> 
> lspci -vvv:
> -----------
> 
> 00:00.0 Host bridge: Intel Corporation Mobile PM965/GM965/GL960 Memory Controller Hub (rev 0c)
> 	Subsystem: Lenovo T61
> 	Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort+ >SERR- <PERR- INTx-
> 	Latency: 0
> 	Capabilities: <access denied>
> 	Kernel driver in use: agpgart-intel
> 
> 00:02.0 VGA compatible controller: Intel Corporation Mobile GM965/GL960 Integrated Graphics Controller (rev 0c) (prog-if 00 [VGA controller])
> 	Subsystem: Lenovo T61
> 	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
> 	Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Interrupt: pin A routed to IRQ 26
> 	Region 0: Memory at f8000000 (64-bit, non-prefetchable) [size=1M]
> 	Region 2: Memory at e0000000 (64-bit, prefetchable) [size=256M]
> 	Region 4: I/O ports at 1800 [size=8]
> 	Expansion ROM at <unassigned> [disabled]
> 	Capabilities: <access denied>
> 	Kernel driver in use: i915
> 
> 00:02.1 Display controller: Intel Corporation Mobile GM965/GL960 Integrated Graphics Controller (rev 0c)
> 	Subsystem: Lenovo T61
> 	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Region 0: Memory at f8100000 (64-bit, non-prefetchable) [size=1M]
> 	Capabilities: <access denied>
> 
> 00:19.0 Ethernet controller: Intel Corporation 82566MM Gigabit Network Connection (rev 03)
> 	Subsystem: Lenovo Device 20de
> 	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx+
> 	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Interrupt: pin A routed to IRQ 28
> 	Region 0: Memory at f8200000 (32-bit, non-prefetchable) [size=128K]
> 	Region 1: Memory at f8225000 (32-bit, non-prefetchable) [size=4K]
> 	Region 2: I/O ports at 1840 [size=32]
> 	Capabilities: <access denied>
> 	Kernel driver in use: e1000e
> 
> 00:1a.0 USB Controller: Intel Corporation 82801H (ICH8 Family) USB UHCI Controller #4 (rev 03) (prog-if 00 [UHCI])
> 	Subsystem: Lenovo ThinkPad T61
> 	Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Interrupt: pin A routed to IRQ 20
> 	Region 4: I/O ports at 1860 [size=32]
> 	Kernel driver in use: uhci_hcd
> 
> 00:1a.1 USB Controller: Intel Corporation 82801H (ICH8 Family) USB UHCI Controller #5 (rev 03) (prog-if 00 [UHCI])
> 	Subsystem: Lenovo ThinkPad T60
> 	Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Interrupt: pin B routed to IRQ 21
> 	Region 4: I/O ports at 1880 [size=32]
> 	Kernel driver in use: uhci_hcd
> 
> 00:1a.7 USB Controller: Intel Corporation 82801H (ICH8 Family) USB2 EHCI Controller #2 (rev 03) (prog-if 20 [EHCI])
> 	Subsystem: Lenovo ThinkPad T61
> 	Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Interrupt: pin C routed to IRQ 22
> 	Region 0: Memory at f8426c00 (32-bit, non-prefetchable) [size=1K]
> 	Capabilities: <access denied>
> 	Kernel driver in use: ehci_hcd
> 
> 00:1b.0 Audio device: Intel Corporation 82801H (ICH8 Family) HD Audio Controller (rev 03)
> 	Subsystem: Lenovo ThinkPad T61
> 	Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0, Cache Line Size: 64 bytes
> 	Interrupt: pin B routed to IRQ 17
> 	Region 0: Memory at f8220000 (64-bit, non-prefetchable) [size=16K]
> 	Capabilities: <access denied>
> 	Kernel driver in use: HDA Intel
> 
> 00:1c.0 PCI bridge: Intel Corporation 82801H (ICH8 Family) PCI Express Port 1 (rev 03) (prog-if 00 [Normal decode])
> 	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx+
> 	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0, Cache Line Size: 64 bytes
> 	Bus: primary=00, secondary=02, subordinate=02, sec-latency=0
> 	I/O behind bridge: 00002000-00002fff
> 	Memory behind bridge: f4000000-f5ffffff
> 	Prefetchable memory behind bridge: 00000000f8500000-00000000f85fffff
> 	Secondary status: 66MHz- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- <SERR- <PERR-
> 	BridgeCtl: Parity- SERR- NoISA+ VGA- MAbort- >Reset- FastB2B-
> 		PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-
> 	Capabilities: <access denied>
> 	Kernel driver in use: pcieport
> 
> 00:1c.1 PCI bridge: Intel Corporation 82801H (ICH8 Family) PCI Express Port 2 (rev 03) (prog-if 00 [Normal decode])
> 	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx+
> 	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0, Cache Line Size: 64 bytes
> 	Bus: primary=00, secondary=03, subordinate=03, sec-latency=0
> 	I/O behind bridge: 00003000-00003fff
> 	Memory behind bridge: f6000000-f7ffffff
> 	Prefetchable memory behind bridge: 00000000f8600000-00000000f86fffff
> 	Secondary status: 66MHz- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- <SERR- <PERR-
> 	BridgeCtl: Parity- SERR- NoISA+ VGA- MAbort- >Reset- FastB2B-
> 		PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-
> 	Capabilities: <access denied>
> 	Kernel driver in use: pcieport
> 
> 00:1d.0 USB Controller: Intel Corporation 82801H (ICH8 Family) USB UHCI Controller #1 (rev 03) (prog-if 00 [UHCI])
> 	Subsystem: Lenovo ThinkPad T61
> 	Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Interrupt: pin A routed to IRQ 16
> 	Region 4: I/O ports at 18a0 [size=32]
> 	Kernel driver in use: uhci_hcd
> 
> 00:1d.1 USB Controller: Intel Corporation 82801H (ICH8 Family) USB UHCI Controller #2 (rev 03) (prog-if 00 [UHCI])
> 	Subsystem: Lenovo ThinkPad T61
> 	Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Interrupt: pin B routed to IRQ 17
> 	Region 4: I/O ports at 18c0 [size=32]
> 	Kernel driver in use: uhci_hcd
> 
> 00:1d.7 USB Controller: Intel Corporation 82801H (ICH8 Family) USB2 EHCI Controller #1 (rev 03) (prog-if 20 [EHCI])
> 	Subsystem: Lenovo ThinkPad T61
> 	Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Interrupt: pin D routed to IRQ 19
> 	Region 0: Memory at f8427000 (32-bit, non-prefetchable) [size=1K]
> 	Capabilities: <access denied>
> 	Kernel driver in use: ehci_hcd
> 
> 00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev f3) (prog-if 01 [Subtractive decode])
> 	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Bus: primary=00, secondary=05, subordinate=08, sec-latency=32
> 	I/O behind bridge: 00004000-00007fff
> 	Memory behind bridge: d4000000-d7efffff
> 	Prefetchable memory behind bridge: 00000000d8000000-00000000dbffffff
> 	Secondary status: 66MHz- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort+ <SERR- <PERR-
> 	BridgeCtl: Parity- SERR- NoISA- VGA- MAbort- >Reset- FastB2B-
> 		PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-
> 	Capabilities: <access denied>
> 
> 00:1f.0 ISA bridge: Intel Corporation 82801HBM (ICH8M-E) LPC Interface Controller (rev 03)
> 	Subsystem: Lenovo T61
> 	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Capabilities: <access denied>
> 
> 00:1f.1 IDE interface: Intel Corporation 82801HBM/HEM (ICH8M/ICH8M-E) IDE Controller (rev 03) (prog-if 8a [Master SecP PriP])
> 	Subsystem: Lenovo ThinkPad T61
> 	Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx+
> 	Latency: 0
> 	Interrupt: pin C routed to IRQ 16
> 	Region 0: I/O ports at 01f0 [size=8]
> 	Region 1: I/O ports at 03f4 [size=1]
> 	Region 2: I/O ports at 0170 [size=8]
> 	Region 3: I/O ports at 0374 [size=1]
> 	Region 4: I/O ports at 18e0 [size=16]
> 	Kernel driver in use: ata_piix
> 
> 00:1f.2 SATA controller: Intel Corporation 82801HBM/HEM (ICH8M/ICH8M-E) SATA AHCI Controller (rev 03) (prog-if 01 [AHCI 1.0])
> 	Subsystem: Lenovo ThinkPad T61
> 	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
> 	Status: Cap+ 66MHz+ UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0
> 	Interrupt: pin B routed to IRQ 27
> 	Region 0: I/O ports at 1c30 [size=8]
> 	Region 1: I/O ports at 1c24 [size=4]
> 	Region 2: I/O ports at 1c28 [size=8]
> 	Region 3: I/O ports at 1c20 [size=4]
> 	Region 4: I/O ports at 1c00 [size=32]
> 	Region 5: Memory at f8426000 (32-bit, non-prefetchable) [size=2K]
> 	Capabilities: <access denied>
> 	Kernel driver in use: ahci
> 
> 00:1f.3 SMBus: Intel Corporation 82801H (ICH8 Family) SMBus Controller (rev 03)
> 	Subsystem: Lenovo ThinkPad T61
> 	Control: I/O+ Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx-
> 	Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Interrupt: pin A routed to IRQ 23
> 	Region 0: Memory at f8427400 (32-bit, non-prefetchable) [size=256]
> 	Region 4: I/O ports at 1c40 [size=32]
> 	Kernel driver in use: i801_smbus
> 
> 03:00.0 Network controller: Intel Corporation PRO/Wireless 4965 AG or AGN [Kedron] Network Connection (rev 61)
> 	Subsystem: Intel Corporation Lenovo ThinkPad T61
> 	Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 0, Cache Line Size: 64 bytes
> 	Interrupt: pin A routed to IRQ 29
> 	Region 0: Memory at f7f00000 (64-bit, non-prefetchable) [size=8K]
> 	Capabilities: <access denied>
> 	Kernel driver in use: iwlagn
> 
> 05:00.0 CardBus bridge: Ricoh Co Ltd RL5c476 II (rev ba)
> 	Subsystem: Lenovo Device 20c6
> 	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 168, Cache Line Size: 64 bytes
> 	Interrupt: pin A routed to IRQ 16
> 	Region 0: Memory at d7eff000 (32-bit, non-prefetchable) [size=4K]
> 	Bus: primary=05, secondary=06, subordinate=07, sec-latency=176
> 	Memory window 0: d8000000-dbfff000 (prefetchable)
> 	Memory window 1: c0000000-c3fff000
> 	I/O window 0: 00004000-000040ff
> 	I/O window 1: 00004400-000044ff
> 	BridgeCtl: Parity- SERR- ISA- VGA- MAbort- >Reset- 16bInt+ PostWrite+
> 	16-bit legacy interface ports at 0001
> 	Kernel driver in use: yenta_cardbus
> 
> 05:00.1 FireWire (IEEE 1394): Ricoh Co Ltd R5C832 IEEE 1394 Controller (rev 04) (prog-if 10 [OHCI])
> 	Subsystem: Lenovo Device 20c7
> 	Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 32 (500ns min, 1000ns max)
> 	Interrupt: pin B routed to IRQ 11
> 	Region 0: Memory at d7efe800 (32-bit, non-prefetchable) [size=2K]
> 	Capabilities: <access denied>
> 
> 05:00.2 SD Host controller: Ricoh Co Ltd R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (rev 21)
> 	Subsystem: Lenovo Device 20c8
> 	Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
> 	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> 	Latency: 64
> 	Interrupt: pin C routed to IRQ 18
> 	Region 0: Memory at d7efe400 (32-bit, non-prefetchable) [size=256]
> 	Capabilities: <access denied>
> 	Kernel driver in use: sdhci-pci
> 
> 
> /proc/cmdline:
> --------------
> 
> root=/dev/sda1 ro selinux=0 quiet thinkpad_acpi.experimental=1 thinkpad_acpi.fan_control=1 fastboot i915.modeset=1 video=intelfb:off
> 
> 
> /proc/interrupts:
> -----------------
> 
>            CPU0       CPU1       
>   0:     301541     304446   IO-APIC-edge      timer
>   1:          4          4   IO-APIC-edge      i8042
>   8:          0          1   IO-APIC-edge      rtc0
>   9:       4160       4240   IO-APIC-fasteoi   acpi
>  12:         82         72   IO-APIC-edge      i8042
>  14:          0          0   IO-APIC-edge      ata_piix
>  15:          0          0   IO-APIC-edge      ata_piix
>  16:       7058       7346   IO-APIC-fasteoi   yenta, uhci_hcd:usb5
>  17:       2286       2268   IO-APIC-fasteoi   uhci_hcd:usb6, HDA Intel
>  18:          0          0   IO-APIC-fasteoi   mmc0
>  19:          0          3   IO-APIC-fasteoi   ehci_hcd:usb2
>  20:         30         26   IO-APIC-fasteoi   uhci_hcd:usb3
>  21:          0          0   IO-APIC-fasteoi   uhci_hcd:usb4
>  22:          1          2   IO-APIC-fasteoi   ehci_hcd:usb1
>  26:       7304       7248   PCI-MSI-edge      i915
>  27:      16997      16401   PCI-MSI-edge      ahci
>  28:       2648       2738   PCI-MSI-edge      eth0
>  29:      36782      33811   PCI-MSI-edge      iwlagn
> NMI:          0          0   Non-maskable interrupts
> LOC:     295497     234740   Local timer interrupts
> SPU:          0          0   Spurious interrupts
> PMI:          0          0   Performance monitoring interrupts
> PND:          0          0   Performance pending work
> RES:     169127     182977   Rescheduling interrupts
> CAL:      13546      17347   Function call interrupts
> TLB:        832       1097   TLB shootdowns
> TRM:          0          0   Thermal event interrupts
> THR:          0          0   Threshold APIC interrupts
> MCE:          0          0   Machine check exceptions
> MCP:          4          4   Machine check polls
> ERR:          1
> MIS:          0
> 
> 
> 

<config trimmed>

> uptime:
> -------
> 
>  17:38:41 up 17 min, 14 users,  load average: 0.12, 0.43, 0.45
> 
> 
> free:
> -----
> 
>              total       used       free     shared    buffers     cached
> Mem:       4040720    1589392    2451328          0      38248     758072
> -/+ buffers/cache:     793072    3247648
> Swap:      1951856          0    1951856
> 
> 
> dmesg:
> ------
> 
> [    0.000000] Initializing cgroup subsys cpuset
> [    0.000000] Initializing cgroup subsys cpu
> [    0.000000] Linux version 2.6.32.7 (me@mypole) (gcc version 4.3.4 (Debian 4.3.4-6) ) #8 SMP PREEMPT Thu Feb 4 22:01:23 CET 2010
> [    0.000000] KERNEL supported cpus:
> [    0.000000]   Intel GenuineIntel
> [    0.000000]   AMD AuthenticAMD
> [    0.000000]   NSC Geode by NSC
> [    0.000000]   Cyrix CyrixInstead
> [    0.000000]   Centaur CentaurHauls
> [    0.000000]   Transmeta GenuineTMx86
> [    0.000000]   Transmeta TransmetaCPU
> [    0.000000]   UMC UMC UMC UMC
> [    0.000000] BIOS-provided physical RAM map:
> [    0.000000]  BIOS-e820: 0000000000000000 - 000000000009d800 (usable)
> [    0.000000]  BIOS-e820: 000000000009d800 - 00000000000a0000 (reserved)
> [    0.000000]  BIOS-e820: 00000000000e0000 - 0000000000100000 (reserved)
> [    0.000000]  BIOS-e820: 0000000000100000 - 00000000bf6b0000 (usable)
> [    0.000000]  BIOS-e820: 00000000bf6b0000 - 00000000bf6cc000 (ACPI data)
> [    0.000000]  BIOS-e820: 00000000bf6cc000 - 00000000bf700000 (ACPI NVS)
> [    0.000000]  BIOS-e820: 00000000bf700000 - 00000000c0000000 (reserved)
> [    0.000000]  BIOS-e820: 00000000f0000000 - 00000000f4000000 (reserved)
> [    0.000000]  BIOS-e820: 00000000fec00000 - 00000000fec10000 (reserved)
> [    0.000000]  BIOS-e820: 00000000fed00000 - 00000000fed00400 (reserved)
> [    0.000000]  BIOS-e820: 00000000fed14000 - 00000000fed1a000 (reserved)
> [    0.000000]  BIOS-e820: 00000000fed1c000 - 00000000fed90000 (reserved)
> [    0.000000]  BIOS-e820: 00000000fee00000 - 00000000fee01000 (reserved)
> [    0.000000]  BIOS-e820: 00000000ff000000 - 0000000100000000 (reserved)
> [    0.000000]  BIOS-e820: 0000000100000000 - 000000013c000000 (usable)
> [    0.000000] DMI present.
> [    0.000000] last_pfn = 0x13c000 max_arch_pfn = 0x1000000
> [    0.000000] MTRR default type: uncachable
> [    0.000000] MTRR fixed ranges enabled:
> [    0.000000]   00000-9FFFF write-back
> [    0.000000]   A0000-BFFFF uncachable
> [    0.000000]   C0000-CFFFF write-protect
> [    0.000000]   D0000-DFFFF uncachable
> [    0.000000]   E0000-FFFFF write-protect
> [    0.000000] MTRR variable ranges enabled:
> [    0.000000]   0 base 0C0000000 mask FC0000000 uncachable
> [    0.000000]   1 base 13C000000 mask FFC000000 uncachable
> [    0.000000]   2 base 000000000 mask F00000000 write-back
> [    0.000000]   3 base 100000000 mask FC0000000 write-back
> [    0.000000]   4 base 0BF700000 mask FFFF00000 uncachable
> [    0.000000]   5 base 0BF800000 mask FFF800000 uncachable
> [    0.000000]   6 disabled
> [    0.000000]   7 disabled
> [    0.000000] e820 update range: 00000000bf700000 - 0000000100000000 (usable) ==> (reserved)
> [    0.000000] e820 update range: 0000000000002000 - 0000000000006000 (usable) ==> (reserved)
> [    0.000000] Scanning 1 areas for low memory corruption
> [    0.000000] modified physical RAM map:
> [    0.000000]  modified: 0000000000000000 - 0000000000002000 (usable)
> [    0.000000]  modified: 0000000000002000 - 0000000000006000 (reserved)
> [    0.000000]  modified: 0000000000006000 - 000000000009d800 (usable)
> [    0.000000]  modified: 000000000009d800 - 00000000000a0000 (reserved)
> [    0.000000]  modified: 00000000000e0000 - 0000000000100000 (reserved)
> [    0.000000]  modified: 0000000000100000 - 00000000bf6b0000 (usable)
> [    0.000000]  modified: 00000000bf6b0000 - 00000000bf6cc000 (ACPI data)
> [    0.000000]  modified: 00000000bf6cc000 - 00000000bf700000 (ACPI NVS)
> [    0.000000]  modified: 00000000bf700000 - 00000000c0000000 (reserved)
> [    0.000000]  modified: 00000000f0000000 - 00000000f4000000 (reserved)
> [    0.000000]  modified: 00000000fec00000 - 00000000fec10000 (reserved)
> [    0.000000]  modified: 00000000fed00000 - 00000000fed00400 (reserved)
> [    0.000000]  modified: 00000000fed14000 - 00000000fed1a000 (reserved)
> [    0.000000]  modified: 00000000fed1c000 - 00000000fed90000 (reserved)
> [    0.000000]  modified: 00000000fee00000 - 00000000fee01000 (reserved)
> [    0.000000]  modified: 00000000ff000000 - 0000000100000000 (reserved)
> [    0.000000]  modified: 0000000100000000 - 000000013c000000 (usable)
> [    0.000000] initial memory mapped : 0 - 02000000
> [    0.000000] init_memory_mapping: 0000000000000000-00000000379fe000
> [    0.000000] NX (Execute Disable) protection: active
> [    0.000000]  0000000000 - 0000200000 page 4k
> [    0.000000]  0000200000 - 0037800000 page 2M
> [    0.000000]  0037800000 - 00379fe000 page 4k
> [    0.000000] kernel direct mapping tables up to 379fe000 @ 7000-d000
> [    0.000000] RAMDISK: 7fe7c000 - 7ffef118
> [    0.000000] Allocated new RAMDISK: 00100000 - 00273118
> [    0.000000] Move RAMDISK from 000000007fe7c000 - 000000007ffef117 to 00100000 - 00273117
> [    0.000000] ACPI: RSDP 000f6950 00024 (v02 LENOVO)
> [    0.000000] ACPI: XSDT bf6bd368 00094 (v01 LENOVO TP-7N    00002190  LTP 00000000)
> [    0.000000] ACPI: FACP bf6bd500 000F4 (v03 LENOVO TP-7N    00002190 LNVO 00000001)
> [    0.000000] ACPI Warning: 32/64X length mismatch in Gpe1Block: 0/32 (20090903/tbfadt-526)
> [    0.000000] ACPI Warning: Optional field Gpe1Block has zero address or length: 000000000000102C/0 (20090903/tbfadt-557)
> [    0.000000] ACPI: DSDT bf6bd8db 0E32A (v01 LENOVO TP-7N    00002190 MSFT 03000000)
> [    0.000000] ACPI: FACS bf6e4000 00040
> [    0.000000] ACPI: SSDT bf6bd6b4 00227 (v01 LENOVO TP-7N    00002190 MSFT 03000000)
> [    0.000000] ACPI: ECDT bf6cbc05 00052 (v01 LENOVO TP-7N    00002190 LNVO 00000001)
> [    0.000000] ACPI: TCPA bf6cbc57 00032 (v02 LENOVO TP-7N    00002190 LNVO 00000001)
> [    0.000000] ACPI: APIC bf6cbc89 00068 (v01 LENOVO TP-7N    00002190 LNVO 00000001)
> [    0.000000] ACPI: MCFG bf6cbcf1 0003C (v01 LENOVO TP-7N    00002190 LNVO 00000001)
> [    0.000000] ACPI: HPET bf6cbd2d 00038 (v01 LENOVO TP-7N    00002190 LNVO 00000001)
> [    0.000000] ACPI: SLIC bf6cbdf0 00176 (v01 LENOVO TP-7N    00002190  LTP 00000000)
> [    0.000000] ACPI: BOOT bf6cbf66 00028 (v01 LENOVO TP-7N    00002190  LTP 00000001)
> [    0.000000] ACPI: ASF! bf6cbf8e 00072 (v16 LENOVO TP-7N    00002190 PTL  00000001)
> [    0.000000] ACPI: SSDT bf6e2697 0025F (v01 LENOVO TP-7N    00002190 INTL 20050513)
> [    0.000000] ACPI: SSDT bf6e28f6 000A6 (v01 LENOVO TP-7N    00002190 INTL 20050513)
> [    0.000000] ACPI: SSDT bf6e299c 004F7 (v01 LENOVO TP-7N    00002190 INTL 20050513)
> [    0.000000] ACPI: SSDT bf6e2e93 001D8 (v01 LENOVO TP-7N    00002190 INTL 20050513)
> [    0.000000] ACPI: DMI detected: Lenovo ThinkPad X61
> [    0.000000] ACPI: Added _OSI(Linux)
> [    0.000000] ACPI: Local APIC address 0xfee00000
> [    0.000000] 4166MB HIGHMEM available.
> [    0.000000] 889MB LOWMEM available.
> [    0.000000]   mapped low ram: 0 - 379fe000
> [    0.000000]   low ram: 0 - 379fe000
> [    0.000000]   node 0 low ram: 00000000 - 379fe000
> [    0.000000]   node 0 bootmap 00009000 - 0000ff40
> [    0.000000] (9 early reservations) ==> bootmem [0000000000 - 00379fe000]
> [    0.000000]   #0 [0000000000 - 0000001000]   BIOS data page ==> [0000000000 - 0000001000]
> [    0.000000]   #1 [0000001000 - 0000002000]    EX TRAMPOLINE ==> [0000001000 - 0000002000]
> [    0.000000]   #2 [0000006000 - 0000007000]       TRAMPOLINE ==> [0000006000 - 0000007000]
> [    0.000000]   #3 [0001000000 - 0001b7d5a8]    TEXT DATA BSS ==> [0001000000 - 0001b7d5a8]
> [    0.000000]   #4 [000009d800 - 0000100000]    BIOS reserved ==> [000009d800 - 0000100000]
> [    0.000000]   #5 [0001b7e000 - 0001b8e138]              BRK ==> [0001b7e000 - 0001b8e138]
> [    0.000000]   #6 [0000007000 - 0000009000]          PGTABLE ==> [0000007000 - 0000009000]
> [    0.000000]   #7 [0000100000 - 0000273118]      NEW RAMDISK ==> [0000100000 - 0000273118]
> [    0.000000]   #8 [0000009000 - 0000010000]          BOOTMAP ==> [0000009000 - 0000010000]
> [    0.000000] found SMP MP-table at [c00f6980] f6980
> [    0.000000] Zone PFN ranges:
> [    0.000000]   DMA      0x00000000 -> 0x00001000
> [    0.000000]   Normal   0x00001000 -> 0x000379fe
> [    0.000000]   HighMem  0x000379fe -> 0x0013c000
> [    0.000000] Movable zone start PFN for each node
> [    0.000000] early_node_map[4] active PFN ranges
> [    0.000000]     0: 0x00000000 -> 0x00000002
> [    0.000000]     0: 0x00000006 -> 0x0000009d
> [    0.000000]     0: 0x00000100 -> 0x000bf6b0
> [    0.000000]     0: 0x00100000 -> 0x0013c000
> [    0.000000] On node 0 totalpages: 1029705
> [    0.000000] free_area_init_node: node 0, pgdat c1a4f780, node_mem_map c1b90000
> [    0.000000]   DMA zone: 32 pages used for memmap
> [    0.000000]   DMA zone: 0 pages reserved
> [    0.000000]   DMA zone: 3961 pages, LIFO batch:0
> [    0.000000]   Normal zone: 1748 pages used for memmap
> [    0.000000]   Normal zone: 221994 pages, LIFO batch:31
> [    0.000000]   HighMem zone: 8333 pages used for memmap
> [    0.000000]   HighMem zone: 793637 pages, LIFO batch:31
> [    0.000000] Using APIC driver default
> [    0.000000] ACPI: PM-Timer IO Port: 0x1008
> [    0.000000] ACPI: Local APIC address 0xfee00000
> [    0.000000] ACPI: LAPIC (acpi_id[0x00] lapic_id[0x00] enabled)
> [    0.000000] ACPI: LAPIC (acpi_id[0x01] lapic_id[0x01] enabled)
> [    0.000000] ACPI: LAPIC_NMI (acpi_id[0x00] high edge lint[0x1])
> [    0.000000] ACPI: LAPIC_NMI (acpi_id[0x01] high edge lint[0x1])
> [    0.000000] ACPI: IOAPIC (id[0x01] address[0xfec00000] gsi_base[0])
> [    0.000000] IOAPIC[0]: apic_id 1, version 32, address 0xfec00000, GSI 0-23
> [    0.000000] ACPI: INT_SRC_OVR (bus 0 bus_irq 0 global_irq 2 dfl dfl)
> [    0.000000] ACPI: INT_SRC_OVR (bus 0 bus_irq 9 global_irq 9 high level)
> [    0.000000] ACPI: IRQ0 used by override.
> [    0.000000] ACPI: IRQ2 used by override.
> [    0.000000] ACPI: IRQ9 used by override.
> [    0.000000] Enabling APIC mode:  Flat.  Using 1 I/O APICs
> [    0.000000] Using ACPI (MADT) for SMP configuration information
> [    0.000000] ACPI: HPET id: 0x8086a201 base: 0xfed00000
> [    0.000000] SMP: Allowing 2 CPUs, 0 hotplug CPUs
> [    0.000000] nr_irqs_gsi: 24
> [    0.000000] Allocating PCI resources starting at c0000000 (gap: c0000000:30000000)
> [    0.000000] NR_CPUS:2 nr_cpumask_bits:2 nr_cpu_ids:2 nr_node_ids:1
> [    0.000000] PERCPU: Embedded 14 pages/cpu @c4400000 s35224 r0 d22120 u1048576
> [    0.000000] pcpu-alloc: s35224 r0 d22120 u1048576 alloc=1*2097152
> [    0.000000] pcpu-alloc: [0] 0 1 
> [    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 1019592
> [    0.000000] Kernel command line: root=/dev/sda1 ro selinux=0 quiet thinkpad_acpi.experimental=1 thinkpad_acpi.fan_control=1 fastboot i915.modeset=1 video=intelfb:off
> [    0.000000] PID hash table entries: 4096 (order: 2, 16384 bytes)
> [    0.000000] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
> [    0.000000] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
> [    0.000000] Enabling fast FPU save and restore... done.
> [    0.000000] Enabling unmasked SIMD FPU exception support... done.
> [    0.000000] Initializing CPU#0
> [    0.000000] allocated 25886720 bytes of page_cgroup
> [    0.000000] please try 'cgroup_disable=memory' option if you don't want memory cgroups
> [    0.000000] Initializing HighMem for node 0 (000379fe:0013c000)
> [    0.000000] Memory: 4037836k/5177344k available (7454k kernel code, 80060k reserved, 3151k data, 476k init, 3207880k highmem)
> [    0.000000] virtual kernel memory layout:
> [    0.000000]     fixmap  : 0xfff91000 - 0xfffff000   ( 440 kB)
> [    0.000000]     pkmap   : 0xffa00000 - 0xffc00000   (2048 kB)
> [    0.000000]     vmalloc : 0xf81fe000 - 0xff9fe000   ( 120 MB)
> [    0.000000]     lowmem  : 0xc0000000 - 0xf79fe000   ( 889 MB)
> [    0.000000]       .init : 0xc1a5c000 - 0xc1ad3000   ( 476 kB)
> [    0.000000]       .data : 0xc1747a8a - 0xc1a5b6b8   (3151 kB)
> [    0.000000]       .text : 0xc1000000 - 0xc1747a8a   (7454 kB)
> [    0.000000] Checking if this processor honours the WP bit even in supervisor mode...Ok.
> [    0.000000] Experimental preemptable hierarchical RCU implementation.
> [    0.000000] NR_IRQS:320
> [    0.000000] Extended CMOS year: 2000
> [    0.000000] Console: colour VGA+ 80x25
> [    0.000000] console [tty0] enabled
> [    0.000000] hpet clockevent registered
> [    0.000000] HPET: 3 timers in total, 0 timers will be used for per-cpu timer
> [    0.000000] Fast TSC calibration using PIT
> [    0.000000] Detected 1795.523 MHz processor.
> [    0.001005] Calibrating delay loop (skipped), value calculated using timer frequency.. 3591.04 BogoMIPS (lpj=1795523)
> [    0.001044] Security Framework initialized
> [    0.001051] SELinux:  Disabled at boot.
> [    0.001067] Mount-cache hash table entries: 512
> [    0.001182] Initializing cgroup subsys debug
> [    0.001185] Initializing cgroup subsys ns
> [    0.001188] Initializing cgroup subsys cpuacct
> [    0.001191] Initializing cgroup subsys memory
> [    0.001197] Initializing cgroup subsys devices
> [    0.001199] Initializing cgroup subsys freezer
> [    0.001223] CPU: L1 I cache: 32K, L1 D cache: 32K
> [    0.001225] CPU: L2 cache: 4096K
> [    0.001228] CPU: Physical Processor ID: 0
> [    0.001229] CPU: Processor Core ID: 0
> [    0.001233] mce: CPU supports 6 MCE banks
> [    0.001240] CPU0: Thermal monitoring enabled (TM2)
> [    0.001244] using mwait in idle threads.
> [    0.001250] Performance Events: Core2 events, Intel PMU driver.
> [    0.001255] ... version:                2
> [    0.001257] ... bit width:              40
> [    0.001258] ... generic registers:      2
> [    0.001260] ... value mask:             000000ffffffffff
> [    0.001261] ... max period:             000000007fffffff
> [    0.001263] ... fixed-purpose events:   3
> [    0.001264] ... event mask:             0000000700000003
> [    0.001268] Checking 'hlt' instruction... OK.
> [    0.005985] ACPI: Core revision 20090903
> [    0.026451] ..TIMER: vector=0x30 apic1=0 pin1=2 apic2=-1 pin2=-1
> [    0.036743] CPU0: Intel(R) Core(TM)2 Duo CPU     L7700  @ 1.80GHz stepping 0b
> [    0.037015] Booting processor 1 APIC 0x1 ip 0x6000
> [    0.001999] Initializing CPU#1
> [    0.001999] CPU: L1 I cache: 32K, L1 D cache: 32K
> [    0.001999] CPU: L2 cache: 4096K
> [    0.001999] CPU: Physical Processor ID: 0
> [    0.001999] CPU: Processor Core ID: 1
> [    0.001999] CPU1: Thermal monitoring enabled (TM2)
> [    0.108101] CPU1: Intel(R) Core(TM)2 Duo CPU     L7700  @ 1.80GHz stepping 0b
> [    0.108114] checking TSC synchronization [CPU#0 -> CPU#1]:
> [    0.108983] Measured 924102 cycles TSC warp between CPUs, turning off TSC clock.
> [    0.108983] Marking TSC unstable due to check_tsc_sync_source failed
> [    0.109007] Brought up 2 CPUs
> [    0.109010] Total of 2 processors activated (7181.87 BogoMIPS).
> [    0.109628] CPU0 attaching sched-domain:
> [    0.109632]  domain 0: span 0-1 level MC
> [    0.109634]   groups: 0 1
> [    0.109640] CPU1 attaching sched-domain:
> [    0.109643]  domain 0: span 0-1 level MC
> [    0.109645]   groups: 1 0
> [    0.111209] regulator: core version 0.5
> [    0.111230] NET: Registered protocol family 16
> [    0.112014] ACPI FADT declares the system doesn't support PCIe ASPM, so disable it
> [    0.112014] ACPI: bus type pci registered
> [    0.112028] PCI: MCFG configuration 0: base f0000000 segment 0 buses 0 - 63
> [    0.112028] PCI: MCFG area at f0000000 reserved in E820
> [    0.112028] PCI: Using MMCONFIG for extended config space
> [    0.112028] PCI: Using configuration type 1 for base access
> [    0.122054] bio: create slab <bio-0> at 0
> [    0.125498] ACPI: EC: EC description table is found, configuring boot EC
> [    0.132054] ACPI: BIOS _OSI(Linux) query honored via DMI
> [    0.140805] ACPI: Interpreter enabled
> [    0.140812] ACPI: (supports S0 S3 S5)
> [    0.140833] ACPI: Using IOAPIC for interrupt routing
> [    0.152185] ACPI: EC: GPE = 0x12, I/O: command/status = 0x66, data = 0x62
> [    0.152185] ACPI: Power Resource [PUBS] (on)
> [    0.155176] ACPI: ACPI Dock Station Driver: 3 docks/bays found
> [    0.155569] ACPI: PCI Root Bridge [PCI0] (0000:00)
> [    0.155608] pci 0000:00:02.0: reg 10 64bit mmio: [0xf8000000-0xf80fffff]
> [    0.155608] pci 0000:00:02.0: reg 18 64bit mmio pref: [0xe0000000-0xefffffff]
> [    0.155608] pci 0000:00:02.0: reg 20 io port: [0x1800-0x1807]
> [    0.155608] pci 0000:00:02.1: reg 10 64bit mmio: [0xf8100000-0xf81fffff]
> [    0.155990] pci 0000:00:19.0: reg 10 32bit mmio: [0xf8200000-0xf821ffff]
> [    0.156006] pci 0000:00:19.0: reg 14 32bit mmio: [0xf8225000-0xf8225fff]
> [    0.156023] pci 0000:00:19.0: reg 18 io port: [0x1840-0x185f]
> [    0.156154] pci 0000:00:19.0: PME# supported from D0 D3hot D3cold
> [    0.156163] pci 0000:00:19.0: PME# disabled
> [    0.156295] pci 0000:00:1a.0: reg 20 io port: [0x1860-0x187f]
> [    0.156454] pci 0000:00:1a.1: reg 20 io port: [0x1880-0x189f]
> [    0.156609] pci 0000:00:1a.7: reg 10 32bit mmio: [0xf8426c00-0xf8426fff]
> [    0.156754] pci 0000:00:1a.7: PME# supported from D0 D3hot D3cold
> [    0.156764] pci 0000:00:1a.7: PME# disabled
> [    0.156884] pci 0000:00:1b.0: reg 10 64bit mmio: [0xf8220000-0xf8223fff]
> [    0.157042] pci 0000:00:1b.0: PME# supported from D0 D3hot D3cold
> [    0.157051] pci 0000:00:1b.0: PME# disabled
> [    0.157277] pci 0000:00:1c.0: PME# supported from D0 D3hot D3cold
> [    0.157287] pci 0000:00:1c.0: PME# disabled
> [    0.157520] pci 0000:00:1c.1: PME# supported from D0 D3hot D3cold
> [    0.157527] pci 0000:00:1c.1: PME# disabled
> [    0.157695] pci 0000:00:1d.0: reg 20 io port: [0x18a0-0x18bf]
> [    0.157856] pci 0000:00:1d.1: reg 20 io port: [0x18c0-0x18df]
> [    0.158035] pci 0000:00:1d.7: reg 10 32bit mmio: [0xf8427000-0xf84273ff]
> [    0.158192] pci 0000:00:1d.7: PME# supported from D0 D3hot D3cold
> [    0.158202] pci 0000:00:1d.7: PME# disabled
> [    0.158584] pci 0000:00:1f.0: quirk: region 1000-107f claimed by ICH6 ACPI/GPIO/TCO
> [    0.158591] pci 0000:00:1f.0: quirk: region 1180-11bf claimed by ICH6 GPIO
> [    0.158598] pci 0000:00:1f.0: ICH7 LPC Generic IO decode 1 PIO at 1600 (mask 007f)
> [    0.158605] pci 0000:00:1f.0: ICH7 LPC Generic IO decode 2 PIO at 15e0 (mask 000f)
> [    0.158611] pci 0000:00:1f.0: ICH7 LPC Generic IO decode 3 PIO at 1680 (mask 001f)
> [    0.158723] pci 0000:00:1f.1: reg 10 io port: [0x00-0x07]
> [    0.158740] pci 0000:00:1f.1: reg 14 io port: [0x00-0x03]
> [    0.158758] pci 0000:00:1f.1: reg 18 io port: [0x00-0x07]
> [    0.158775] pci 0000:00:1f.1: reg 1c io port: [0x00-0x03]
> [    0.158792] pci 0000:00:1f.1: reg 20 io port: [0x18e0-0x18ef]
> [    0.158982] pci 0000:00:1f.2: reg 10 io port: [0x1c30-0x1c37]
> [    0.158999] pci 0000:00:1f.2: reg 14 io port: [0x1c24-0x1c27]
> [    0.159017] pci 0000:00:1f.2: reg 18 io port: [0x1c28-0x1c2f]
> [    0.159034] pci 0000:00:1f.2: reg 1c io port: [0x1c20-0x1c23]
> [    0.159051] pci 0000:00:1f.2: reg 20 io port: [0x1c00-0x1c1f]
> [    0.159069] pci 0000:00:1f.2: reg 24 32bit mmio: [0xf8426000-0xf84267ff]
> [    0.159178] pci 0000:00:1f.2: PME# supported from D3hot
> [    0.159188] pci 0000:00:1f.2: PME# disabled
> [    0.159265] pci 0000:00:1f.3: reg 10 32bit mmio: [0xf8427400-0xf84274ff]
> [    0.159325] pci 0000:00:1f.3: reg 20 io port: [0x1c40-0x1c5f]
> [    0.159534] pci 0000:00:1c.0: bridge io port: [0x2000-0x2fff]
> [    0.159543] pci 0000:00:1c.0: bridge 32bit mmio: [0xf4000000-0xf5ffffff]
> [    0.159559] pci 0000:00:1c.0: bridge 64bit mmio pref: [0xf8500000-0xf85fffff]
> [    0.159709] pci 0000:03:00.0: reg 10 64bit mmio: [0xf7f00000-0xf7f01fff]
> [    0.159859] pci 0000:03:00.0: PME# supported from D0 D3hot D3cold
> [    0.159871] pci 0000:03:00.0: PME# disabled
> [    0.160029] pci 0000:00:1c.1: bridge io port: [0x3000-0x3fff]
> [    0.160039] pci 0000:00:1c.1: bridge 32bit mmio: [0xf6000000-0xf7ffffff]
> [    0.160056] pci 0000:00:1c.1: bridge 64bit mmio pref: [0xf8600000-0xf86fffff]
> [    0.160154] pci 0000:05:00.0: reg 10 32bit mmio: [0xd7eff000-0xd7efffff]
> [    0.160210] pci 0000:05:00.0: supports D1 D2
> [    0.160212] pci 0000:05:00.0: PME# supported from D0 D1 D2 D3hot D3cold
> [    0.160222] pci 0000:05:00.0: PME# disabled
> [    0.160324] pci 0000:05:00.1: reg 10 32bit mmio: [0xd7efe800-0xd7efefff]
> [    0.160473] pci 0000:05:00.1: supports D1 D2
> [    0.160476] pci 0000:05:00.1: PME# supported from D0 D1 D2 D3hot D3cold
> [    0.160486] pci 0000:05:00.1: PME# disabled
> [    0.160587] pci 0000:05:00.2: reg 10 32bit mmio: [0xd7efe400-0xd7efe4ff]
> [    0.160736] pci 0000:05:00.2: supports D1 D2
> [    0.160739] pci 0000:05:00.2: PME# supported from D0 D1 D2 D3hot D3cold
> [    0.160749] pci 0000:05:00.2: PME# disabled
> [    0.160903] pci 0000:00:1e.0: transparent bridge
> [    0.160913] pci 0000:00:1e.0: bridge io port: [0x4000-0x7fff]
> [    0.160923] pci 0000:00:1e.0: bridge 32bit mmio: [0xd4000000-0xd7efffff]
> [    0.160941] pci 0000:00:1e.0: bridge 64bit mmio pref: [0xd8000000-0xdbffffff]
> [    0.161039] pci_bus 0000:00: on NUMA node 0
> [    0.161044] ACPI: PCI Interrupt Routing Table [\_SB_.PCI0._PRT]
> [    0.161216] ACPI: PCI Interrupt Routing Table [\_SB_.PCI0.EXP0._PRT]
> [    0.161289] ACPI: PCI Interrupt Routing Table [\_SB_.PCI0.EXP1._PRT]
> [    0.161373] ACPI: PCI Interrupt Routing Table [\_SB_.PCI0.PCI1._PRT]
> [    0.168125] ACPI: PCI Interrupt Link [LNKA] (IRQs 3 4 5 6 7 9 *10 11)
> [    0.168333] ACPI: PCI Interrupt Link [LNKB] (IRQs 3 4 5 6 7 9 10 *11)
> [    0.168537] ACPI: PCI Interrupt Link [LNKC] (IRQs 3 4 5 6 7 9 10 *11)
> [    0.168740] ACPI: PCI Interrupt Link [LNKD] (IRQs 3 4 5 6 7 9 10 *11)
> [    0.169098] ACPI: PCI Interrupt Link [LNKE] (IRQs 3 4 5 6 7 9 10 *11)
> [    0.169301] ACPI: PCI Interrupt Link [LNKF] (IRQs 3 4 5 6 7 9 10 *11)
> [    0.169504] ACPI: PCI Interrupt Link [LNKG] (IRQs 3 4 5 6 7 9 10 *11)
> [    0.169706] ACPI: PCI Interrupt Link [LNKH] (IRQs 3 4 5 6 7 9 10 *11)
> [    0.169805] vgaarb: device added: PCI:0000:00:02.0,decodes=io+mem,owns=io+mem,locks=none
> [    0.169805] vgaarb: loaded
> [    0.170079] SCSI subsystem initialized
> [    0.171991] libata version 3.00 loaded.
> [    0.172012] usbcore: registered new interface driver usbfs
> [    0.172012] usbcore: registered new interface driver hub
> [    0.172013] usbcore: registered new device driver usb
> [    0.172013] PCI: Using ACPI for IRQ routing
> [    0.172238] NET: Registered protocol family 23
> [    0.172238] Bluetooth: Core ver 2.15
> [    0.172986] NET: Registered protocol family 31
> [    0.172986] Bluetooth: HCI device and connection manager initialized
> [    0.172986] Bluetooth: HCI socket layer initialized
> [    0.173017] cfg80211: Using static regulatory domain info
> [    0.173017] cfg80211: Regulatory domain: US
> [    0.173017] 	(start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)
> [    0.173017] 	(2402000 KHz - 2472000 KHz @ 40000 KHz), (600 mBi, 2700 mBm)
> [    0.173017] 	(5170000 KHz - 5190000 KHz @ 40000 KHz), (600 mBi, 2300 mBm)
> [    0.173017] 	(5190000 KHz - 5210000 KHz @ 40000 KHz), (600 mBi, 2300 mBm)
> [    0.173017] 	(5210000 KHz - 5230000 KHz @ 40000 KHz), (600 mBi, 2300 mBm)
> [    0.173017] 	(5230000 KHz - 5330000 KHz @ 40000 KHz), (600 mBi, 2300 mBm)
> [    0.173017] 	(5735000 KHz - 5835000 KHz @ 40000 KHz), (600 mBi, 3000 mBm)
> [    0.173017] cfg80211: Calling CRDA for country: US
> [    0.173024] NetLabel: Initializing
> [    0.173024] NetLabel:  domain hash size = 128
> [    0.173024] NetLabel:  protocols = UNLABELED CIPSOv4
> [    0.173024] NetLabel:  unlabeled traffic allowed by default
> [    0.173990] Switching to clocksource hpet
> [    0.178279] pnp: PnP ACPI init
> [    0.178289] ACPI: bus type pnp registered
> [    0.182336] pnp: PnP ACPI: found 11 devices
> [    0.182338] ACPI: ACPI bus type pnp unregistered
> [    0.182349] system 00:00: iomem range 0x0-0x9ffff could not be reserved
> [    0.182353] system 00:00: iomem range 0xc0000-0xc3fff could not be reserved
> [    0.182357] system 00:00: iomem range 0xc4000-0xc7fff could not be reserved
> [    0.182360] system 00:00: iomem range 0xc8000-0xcbfff has been reserved
> [    0.182363] system 00:00: iomem range 0xcc000-0xcffff has been reserved
> [    0.182366] system 00:00: iomem range 0xe0000-0xe3fff could not be reserved
> [    0.182370] system 00:00: iomem range 0xe4000-0xe7fff could not be reserved
> [    0.182373] system 00:00: iomem range 0xe8000-0xebfff could not be reserved
> [    0.182376] system 00:00: iomem range 0xec000-0xeffff could not be reserved
> [    0.182379] system 00:00: iomem range 0xf0000-0xfffff could not be reserved
> [    0.182383] system 00:00: iomem range 0x100000-0xbfffffff could not be reserved
> [    0.182386] system 00:00: iomem range 0xfec00000-0xfed3ffff could not be reserved
> [    0.182390] system 00:00: iomem range 0xfed4c000-0xffffffff could not be reserved
> [    0.182398] system 00:02: ioport range 0x164e-0x164f has been reserved
> [    0.182402] system 00:02: ioport range 0x1000-0x107f has been reserved
> [    0.182405] system 00:02: ioport range 0x1180-0x11bf has been reserved
> [    0.182408] system 00:02: ioport range 0x800-0x80f has been reserved
> [    0.182411] system 00:02: ioport range 0x15e0-0x15ef has been reserved
> [    0.182414] system 00:02: ioport range 0x1600-0x165f could not be reserved
> [    0.182418] system 00:02: iomem range 0xf0000000-0xf3ffffff has been reserved
> [    0.182421] system 00:02: iomem range 0xfed1c000-0xfed1ffff has been reserved
> [    0.182425] system 00:02: iomem range 0xfed14000-0xfed17fff has been reserved
> [    0.182428] system 00:02: iomem range 0xfed18000-0xfed18fff has been reserved
> [    0.182431] system 00:02: iomem range 0xfed19000-0xfed19fff has been reserved
> [    0.182435] system 00:02: iomem range 0xfed45000-0xfed4bfff has been reserved
> [    0.219681] pci 0000:00:1c.0: PCI bridge, secondary bus 0000:02
> [    0.219687] pci 0000:00:1c.0:   IO window: 0x2000-0x2fff
> [    0.219699] pci 0000:00:1c.0:   MEM window: 0xf4000000-0xf5ffffff
> [    0.219709] pci 0000:00:1c.0:   PREFETCH window: 0x000000f8500000-0x000000f85fffff
> [    0.219727] pci 0000:00:1c.1: PCI bridge, secondary bus 0000:03
> [    0.219733] pci 0000:00:1c.1:   IO window: 0x3000-0x3fff
> [    0.219746] pci 0000:00:1c.1:   MEM window: 0xf6000000-0xf7ffffff
> [    0.219756] pci 0000:00:1c.1:   PREFETCH window: 0x000000f8600000-0x000000f86fffff
> [    0.219777] pci 0000:05:00.0: CardBus bridge, secondary bus 0000:06
> [    0.219780] pci 0000:05:00.0:   IO window: 0x004000-0x0040ff
> [    0.219788] pci 0000:05:00.0:   IO window: 0x004400-0x0044ff
> [    0.219798] pci 0000:05:00.0:   PREFETCH window: 0xd8000000-0xdbffffff
> [    0.219808] pci 0000:05:00.0:   MEM window: 0xc0000000-0xc3ffffff
> [    0.219818] pci 0000:00:1e.0: PCI bridge, secondary bus 0000:05
> [    0.219824] pci 0000:00:1e.0:   IO window: 0x4000-0x7fff
> [    0.219838] pci 0000:00:1e.0:   MEM window: 0xd4000000-0xd7efffff
> [    0.219848] pci 0000:00:1e.0:   PREFETCH window: 0x000000d8000000-0x000000dbffffff
> [    0.219886] pci 0000:00:1c.0: PCI INT A -> GSI 20 (level, low) -> IRQ 20
> [    0.219896] pci 0000:00:1c.0: setting latency timer to 64
> [    0.219919] pci 0000:00:1c.1: PCI INT B -> GSI 21 (level, low) -> IRQ 21
> [    0.219927] pci 0000:00:1c.1: setting latency timer to 64
> [    0.219938] pci 0000:00:1e.0: enabling device (0005 -> 0007)
> [    0.219951] pci 0000:00:1e.0: setting latency timer to 64
> [    0.219973] pci 0000:05:00.0: PCI INT A -> GSI 16 (level, low) -> IRQ 16
> [    0.219985] pci_bus 0000:00: resource 0 io:  [0x00-0xffff]
> [    0.219987] pci_bus 0000:00: resource 1 mem: [0x000000-0xffffffffffffffff]
> [    0.219990] pci_bus 0000:02: resource 0 io:  [0x2000-0x2fff]
> [    0.219993] pci_bus 0000:02: resource 1 mem: [0xf4000000-0xf5ffffff]
> [    0.219996] pci_bus 0000:02: resource 2 pref mem [0xf8500000-0xf85fffff]
> [    0.219999] pci_bus 0000:03: resource 0 io:  [0x3000-0x3fff]
> [    0.220002] pci_bus 0000:03: resource 1 mem: [0xf6000000-0xf7ffffff]
> [    0.220017] pci_bus 0000:03: resource 2 pref mem [0xf8600000-0xf86fffff]
> [    0.220020] pci_bus 0000:05: resource 0 io:  [0x4000-0x7fff]
> [    0.220023] pci_bus 0000:05: resource 1 mem: [0xd4000000-0xd7efffff]
> [    0.220025] pci_bus 0000:05: resource 2 pref mem [0xd8000000-0xdbffffff]
> [    0.220028] pci_bus 0000:05: resource 3 io:  [0x00-0xffff]
> [    0.220031] pci_bus 0000:05: resource 4 mem: [0x000000-0xffffffffffffffff]
> [    0.220034] pci_bus 0000:06: resource 0 io:  [0x4000-0x40ff]
> [    0.220036] pci_bus 0000:06: resource 1 io:  [0x4400-0x44ff]
> [    0.220039] pci_bus 0000:06: resource 2 pref mem [0xd8000000-0xdbffffff]
> [    0.220041] pci_bus 0000:06: resource 3 mem: [0xc0000000-0xc3ffffff]
> [    0.220115] NET: Registered protocol family 2
> [    0.220227] IP route cache hash table entries: 32768 (order: 5, 131072 bytes)
> [    0.220581] TCP established hash table entries: 131072 (order: 8, 1048576 bytes)
> [    0.220926] TCP bind hash table entries: 65536 (order: 7, 524288 bytes)
> [    0.221120] TCP: Hash tables configured (established 131072 bind 65536)
> [    0.221123] TCP reno registered
> [    0.221217] NET: Registered protocol family 1
> [    0.223081] RPC: Registered udp transport module.
> [    0.223083] RPC: Registered tcp transport module.
> [    0.223085] RPC: Registered tcp NFSv4.1 backchannel transport module.
> [    0.223095] pci 0000:00:02.0: Boot video device
> [    0.223308] Trying to unpack rootfs image as initramfs...
> [    0.264622] Freeing initrd memory: 1484k freed
> [    0.265286] Simple Boot Flag at 0x35 set to 0x1
> [    0.266392] cpu0(2) debug files 152
> [    0.269405] cpu1(2) debug files 152
> [    0.269972] microcode: CPU0 sig=0x6fb, pf=0x80, revision=0xb6
> [    0.269984] microcode: CPU1 sig=0x6fb, pf=0x80, revision=0xb6
> [    0.270054] Microcode Update Driver: v2.00 <tigran@aivazian.fsnet.co.uk>, Peter Oruba
> [    0.270057] Scanning for low memory corruption every 60 seconds
> [    0.270503] audit: initializing netlink socket (disabled)
> [    0.270515] type=2000 audit(1265476886.270:1): initialized
> [    0.270755] highmem bounce pool size: 64 pages
> [    0.273019] VFS: Disk quotas dquot_6.5.2
> [    0.273061] Dquot-cache hash table entries: 1024 (order 0, 4096 bytes)
> [    0.273299] DLM (built Feb  4 2010 21:32:56) installed
> [    0.273520] squashfs: version 4.0 (2009/01/31) Phillip Lougher
> [    0.273711] Installing knfsd (copyright (C) 1996 okir@monad.swb.de).
> [    0.273978] Slow work thread pool: Starting up
> [    0.274033] Slow work thread pool: Ready
> [    0.274035] NTFS driver 2.1.29 [Flags: R/W].
> [    0.274096] ROMFS MTD (C) 2007 Red Hat, Inc.
> [    0.274115] fuse init (API version 7.13)
> [    0.274312] msgmni has been set to 1625
> [    0.277050] alg: No test for cipher_null (cipher_null-generic)
> [    0.277083] alg: No test for ecb(cipher_null) (ecb-cipher_null)
> [    0.277112] alg: No test for digest_null (digest_null-generic)
> [    0.277141] alg: No test for compress_null (compress_null-generic)
> [    0.280361] alg: No test for stdrng (krng)
> [    0.287375] alg: No test for ghash (ghash-generic)
> [    0.287388] io scheduler noop registered
> [    0.287390] io scheduler anticipatory registered
> [    0.287392] io scheduler deadline registered
> [    0.287419] io scheduler cfq registered (default)
> [    0.287720] pcieport 0000:00:1c.0: irq 24 for MSI/MSI-X
> [    0.287747] pcieport 0000:00:1c.0: setting latency timer to 64
> [    0.288224] pcieport 0000:00:1c.1: irq 25 for MSI/MSI-X
> [    0.288249] pcieport 0000:00:1c.1: setting latency timer to 64
> [    0.289119] ACPI: AC Adapter [AC] (on-line)
> [    0.289284] input: Lid Switch as /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00/input/input0
> [    0.289481] ACPI: Lid Switch [LID]
> [    0.289565] input: Sleep Button as /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/input/input1
> [    0.289574] ACPI: Sleep Button [SLPB]
> [    0.289665] input: Power Button as /devices/LNXSYSTM:00/LNXPWRBN:00/input/input2
> [    0.289668] ACPI: Power Button [PWRF]
> [    0.290248] ACPI: SSDT bf6e1b32 00282 (v01  PmRef  Cpu0Ist 00000100 INTL 20050513)
> [    0.290969] ACPI: SSDT bf6e1e39 0085E (v01  PmRef  Cpu0Cst 00000100 INTL 20050513)
> [    0.294023] Monitor-Mwait will be used to enter C-1 state
> [    0.295022] Monitor-Mwait will be used to enter C-2 state
> [    0.296022] Monitor-Mwait will be used to enter C-3 state
> [    0.296142] processor LNXCPU:00: registered as cooling_device0
> [    0.296502] ACPI: SSDT bf6e1a6a 000C8 (v01  PmRef  Cpu1Ist 00000100 INTL 20050513)
> [    0.296975] ACPI: SSDT bf6e1db4 00085 (v01  PmRef  Cpu1Cst 00000100 INTL 20050513)
> [    0.298694] processor LNXCPU:01: registered as cooling_device1
> [    0.303866] thermal LNXTHERM:01: registered as thermal_zone0
> [    0.303874] ACPI: Thermal Zone [THM0] (67 C)
> [    0.305513] thermal LNXTHERM:02: registered as thermal_zone1
> [    0.305522] ACPI: Thermal Zone [THM1] (67 C)
> [    0.316432] ACPI: Battery Slot [BAT0] (battery present)
> [    0.349266] Non-volatile memory driver v1.3
> [    0.349355] Linux agpgart interface v0.103
> [    0.349371] agpgart-intel 0000:00:00.0: Intel 965GM Chipset
> [    0.350650] agpgart-intel 0000:00:00.0: detected 7676K stolen memory
> [    0.355710] agpgart-intel 0000:00:00.0: AGP aperture is 256M @ 0xe0000000
> [    0.355776] Hangcheck: starting hangcheck timer 0.9.0 (tick is 180 seconds, margin is 60 seconds).
> [    0.355779] Hangcheck: Using get_cycles().
> [    0.355845] [drm] Initialized drm 1.1.0 20060810
> [    0.355922] i915 0000:00:02.0: power state changed by ACPI to D0
> [    0.355929] i915 0000:00:02.0: PCI INT A -> GSI 16 (level, low) -> IRQ 16
> [    0.355934] i915 0000:00:02.0: setting latency timer to 64
> [    0.357195] mtrr: type mismatch for e0000000,10000000 old: write-back new: write-combining
> [    0.357198] [drm] MTRR allocation failed.  Graphics performance may suffer.
> [    0.357298] i915 0000:00:02.0: irq 26 for MSI/MSI-X
> [    0.357305] [drm] set up 7M of stolen space
> [    0.992597] [drm] LVDS-8: set mode 1024x768 c
> [    1.081420] Console: switching to colour frame buffer device 128x48
> [    1.103621] fb0: inteldrmfb frame buffer device
> [    1.103623] registered panic notifier
> [    1.106087] acpi device:01: registered as cooling_device2
> [    1.106356] input: Video Bus as /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/LNXVIDEO:00/input/input3
> [    1.106364] ACPI: Video Device [VID] (multi-head: yes  rom: no  post: no)
> [    1.106448] [drm] Initialized i915 1.6.0 20080730 for 0000:00:02.0 on minor 0
> [    1.106518] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
> [    4.118058] floppy0: no floppy controllers found
> [    4.120435] brd: module loaded
> [    4.121572] loop: module loaded
> [    4.121846] nbd: registered device at major 43
> [    4.124535] ahci 0000:00:1f.2: version 3.0
> [    4.124560] ahci 0000:00:1f.2: PCI INT B -> GSI 16 (level, low) -> IRQ 16
> [    4.124644] ahci 0000:00:1f.2: irq 27 for MSI/MSI-X
> [    4.124722] ahci 0000:00:1f.2: AHCI 0001.0100 32 slots 3 ports 1.5 Gbps 0x5 impl SATA mode
> [    4.124726] ahci 0000:00:1f.2: flags: 64bit ncq sntf pm led clo pio slum part ccc 
> [    4.124737] ahci 0000:00:1f.2: setting latency timer to 64
> [    4.124904] scsi0 : ahci
> [    4.125097] scsi1 : ahci
> [    4.125239] scsi2 : ahci
> [    4.125418] ata1: SATA max UDMA/133 abar m2048@0xf8426000 port 0xf8426100 irq 27
> [    4.125421] ata2: DUMMY
> [    4.125426] ata3: SATA max UDMA/133 abar m2048@0xf8426000 port 0xf8426200 irq 27
> [    4.125494] ata_piix 0000:00:1f.1: version 2.13
> [    4.125505] ata_piix 0000:00:1f.1: PCI INT C -> GSI 16 (level, low) -> IRQ 16
> [    4.125551] ata_piix 0000:00:1f.1: setting latency timer to 64
> [    4.125663] scsi3 : ata_piix
> [    4.125797] scsi4 : ata_piix
> [    4.126645] ata4: PATA max UDMA/100 cmd 0x1f0 ctl 0x3f6 bmdma 0x18e0 irq 14
> [    4.126649] ata5: PATA max UDMA/100 cmd 0x170 ctl 0x376 bmdma 0x18e8 irq 15
> [    4.126706] PPP generic driver version 2.4.2
> [    4.126830] PPP Deflate Compression module registered
> [    4.126835] PPP BSD Compression module registered
> [    4.127065] ata5: port disabled. ignoring.
> [    4.127175] PPP MPPE Compression module registered
> [    4.127178] NET: Registered protocol family 24
> [    4.127192] PPPoL2TP kernel driver, V1.0
> [    4.127195] SLIP: version 0.8.4-NET3.019-NEWTTY (dynamic channels, max=256) (6 bit encapsulation enabled).
> [    4.127197] CSLIP: code copyright 1989 Regents of the University of California.
> [    4.127199] SLIP linefill/keepalive option.
> [    4.127202] tun: Universal TUN/TAP device driver, 1.6
> [    4.127204] tun: (C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>
> [    4.127323] usbcore: registered new interface driver cdc_ether
> [    4.127368] usbcore: registered new interface driver rndis_host
> [    4.127420] usbcore: registered new interface driver irda-usb
> [    4.127424] USB IrDA support registered
> [    4.127473] yenta_cardbus 0000:05:00.0: CardBus bridge found [17aa:20c6]
> [    4.430041] ata3: SATA link down (SStatus 0 SControl 300)
> [    4.430070] ata1: SATA link up 1.5 Gbps (SStatus 113 SControl 300)
> [    4.431143] ata1.00: ACPI cmd ef/02:00:00:00:00:a0 (SET FEATURES) succeeded
> [    4.431147] ata1.00: ACPI cmd f5/00:00:00:00:00:a0 (SECURITY FREEZE LOCK) filtered out
> [    4.431151] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES) filtered out
> [    4.431945] ata1.00: ATA-7: ST9120822AS, 3.ALC, max UDMA/133
> [    4.431948] ata1.00: 234441648 sectors, multi 16: LBA48 NCQ (depth 31/32)
> [    4.433123] ata1.00: ACPI cmd ef/02:00:00:00:00:a0 (SET FEATURES) succeeded
> [    4.433127] ata1.00: ACPI cmd f5/00:00:00:00:00:a0 (SECURITY FREEZE LOCK) filtered out
> [    4.433130] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES) filtered out
> [    4.433998] ata1.00: configured for UDMA/133
> [    4.446930] ata1.00: configured for UDMA/133
> [    4.446933] ata1: EH complete
> [    4.447047] scsi 0:0:0:0: Direct-Access     ATA      ST9120822AS      3.AL PQ: 0 ANSI: 5
> [    4.447387] sd 0:0:0:0: Attached scsi generic sg0 type 0
> [    4.447423] sd 0:0:0:0: [sda] 234441648 512-byte logical blocks: (120 GB/111 GiB)
> [    4.447474] sd 0:0:0:0: [sda] Write Protect is off
> [    4.447477] sd 0:0:0:0: [sda] Mode Sense: 00 3a 00 00
> [    4.447502] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
> [    4.447632]  sda: sda1 sda2 sda3 sda4 < sda5 sda6 >
> [    4.483593] sd 0:0:0:0: [sda] Attached SCSI disk
> [    4.605880] yenta_cardbus 0000:05:00.0: ISA IRQ mask 0x0cb8, PCI irq 16
> [    4.605886] yenta_cardbus 0000:05:00.0: Socket status: 30000006
> [    4.605891] yenta_cardbus 0000:05:00.0: pcmcia: parent PCI bridge I/O window: 0x4000 - 0x7fff
> [    4.605894] yenta_cardbus 0000:05:00.0: pcmcia: parent PCI bridge Memory window: 0xd4000000 - 0xd7efffff
> [    4.605897] yenta_cardbus 0000:05:00.0: pcmcia: parent PCI bridge Memory window: 0xd8000000 - 0xdbffffff
> [    4.857330] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
> [    4.857552] ehci_hcd 0000:00:1a.7: power state changed by ACPI to D0
> [    4.857567] ehci_hcd 0000:00:1a.7: PCI INT C -> GSI 22 (level, low) -> IRQ 22
> [    4.857582] ehci_hcd 0000:00:1a.7: setting latency timer to 64
> [    4.857588] ehci_hcd 0000:00:1a.7: EHCI Host Controller
> [    4.857678] ehci_hcd 0000:00:1a.7: new USB bus registered, assigned bus number 1
> [    4.861618] ehci_hcd 0000:00:1a.7: cache line size of 32 is not supported
> [    4.861634] ehci_hcd 0000:00:1a.7: irq 22, io mem 0xf8426c00
> [    4.871024] ehci_hcd 0000:00:1a.7: USB 2.0 started, EHCI 1.00
> [    4.871046] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002
> [    4.871048] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
> [    4.871051] usb usb1: Product: EHCI Host Controller
> [    4.871053] usb usb1: Manufacturer: Linux 2.6.32.7 ehci_hcd
> [    4.871055] usb usb1: SerialNumber: 0000:00:1a.7
> [    4.871158] usb usb1: configuration #1 chosen from 1 choice
> [    4.871223] hub 1-0:1.0: USB hub found
> [    4.871232] hub 1-0:1.0: 4 ports detected
> [    4.871530] ehci_hcd 0000:00:1d.7: power state changed by ACPI to D0
> [    4.871545] ehci_hcd 0000:00:1d.7: PCI INT D -> GSI 19 (level, low) -> IRQ 19
> [    4.871560] ehci_hcd 0000:00:1d.7: setting latency timer to 64
> [    4.871566] ehci_hcd 0000:00:1d.7: EHCI Host Controller
> [    4.871647] ehci_hcd 0000:00:1d.7: new USB bus registered, assigned bus number 2
> [    4.871690] ehci_hcd 0000:00:1d.7: debug port 1
> [    4.875599] ehci_hcd 0000:00:1d.7: cache line size of 32 is not supported
> [    4.875615] ehci_hcd 0000:00:1d.7: irq 19, io mem 0xf8427000
> [    4.885028] ehci_hcd 0000:00:1d.7: USB 2.0 started, EHCI 1.00
> [    4.885045] usb usb2: New USB device found, idVendor=1d6b, idProduct=0002
> [    4.885048] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
> [    4.885051] usb usb2: Product: EHCI Host Controller
> [    4.885053] usb usb2: Manufacturer: Linux 2.6.32.7 ehci_hcd
> [    4.885055] usb usb2: SerialNumber: 0000:00:1d.7
> [    4.885161] usb usb2: configuration #1 chosen from 1 choice
> [    4.885230] hub 2-0:1.0: USB hub found
> [    4.885237] hub 2-0:1.0: 4 ports detected
> [    4.885388] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
> [    4.885438] uhci_hcd: USB Universal Host Controller Interface driver
> [    4.885472] uhci_hcd 0000:00:1a.0: PCI INT A -> GSI 20 (level, low) -> IRQ 20
> [    4.885483] uhci_hcd 0000:00:1a.0: setting latency timer to 64
> [    4.885489] uhci_hcd 0000:00:1a.0: UHCI Host Controller
> [    4.885565] uhci_hcd 0000:00:1a.0: new USB bus registered, assigned bus number 3
> [    4.885609] uhci_hcd 0000:00:1a.0: irq 20, io base 0x00001860
> [    4.885648] usb usb3: New USB device found, idVendor=1d6b, idProduct=0001
> [    4.885650] usb usb3: New USB device strings: Mfr=3, Product=2, SerialNumber=1
> [    4.885653] usb usb3: Product: UHCI Host Controller
> [    4.885655] usb usb3: Manufacturer: Linux 2.6.32.7 uhci_hcd
> [    4.885657] usb usb3: SerialNumber: 0000:00:1a.0
> [    4.885751] usb usb3: configuration #1 chosen from 1 choice
> [    4.885813] hub 3-0:1.0: USB hub found
> [    4.885820] hub 3-0:1.0: 2 ports detected
> [    4.886084] uhci_hcd 0000:00:1a.1: power state changed by ACPI to D0
> [    4.886093] uhci_hcd 0000:00:1a.1: PCI INT B -> GSI 21 (level, low) -> IRQ 21
> [    4.886104] uhci_hcd 0000:00:1a.1: setting latency timer to 64
> [    4.886110] uhci_hcd 0000:00:1a.1: UHCI Host Controller
> [    4.886202] uhci_hcd 0000:00:1a.1: new USB bus registered, assigned bus number 4
> [    4.886248] uhci_hcd 0000:00:1a.1: irq 21, io base 0x00001880
> [    4.886287] usb usb4: New USB device found, idVendor=1d6b, idProduct=0001
> [    4.886290] usb usb4: New USB device strings: Mfr=3, Product=2, SerialNumber=1
> [    4.886292] usb usb4: Product: UHCI Host Controller
> [    4.886294] usb usb4: Manufacturer: Linux 2.6.32.7 uhci_hcd
> [    4.886297] usb usb4: SerialNumber: 0000:00:1a.1
> [    4.886389] usb usb4: configuration #1 chosen from 1 choice
> [    4.886451] hub 4-0:1.0: USB hub found
> [    4.886458] hub 4-0:1.0: 2 ports detected
> [    4.886752] uhci_hcd 0000:00:1d.0: power state changed by ACPI to D0
> [    4.886761] uhci_hcd 0000:00:1d.0: PCI INT A -> GSI 16 (level, low) -> IRQ 16
> [    4.886772] uhci_hcd 0000:00:1d.0: setting latency timer to 64
> [    4.886777] uhci_hcd 0000:00:1d.0: UHCI Host Controller
> [    4.886858] uhci_hcd 0000:00:1d.0: new USB bus registered, assigned bus number 5
> [    4.886894] uhci_hcd 0000:00:1d.0: irq 16, io base 0x000018a0
> [    4.886930] usb usb5: New USB device found, idVendor=1d6b, idProduct=0001
> [    4.886933] usb usb5: New USB device strings: Mfr=3, Product=2, SerialNumber=1
> [    4.886936] usb usb5: Product: UHCI Host Controller
> [    4.886938] usb usb5: Manufacturer: Linux 2.6.32.7 uhci_hcd
> [    4.886940] usb usb5: SerialNumber: 0000:00:1d.0
> [    4.887067] usb usb5: configuration #1 chosen from 1 choice
> [    4.887132] hub 5-0:1.0: USB hub found
> [    4.887139] hub 5-0:1.0: 2 ports detected
> [    4.887246] uhci_hcd 0000:00:1d.1: PCI INT B -> GSI 17 (level, low) -> IRQ 17
> [    4.887258] uhci_hcd 0000:00:1d.1: setting latency timer to 64
> [    4.887262] uhci_hcd 0000:00:1d.1: UHCI Host Controller
> [    4.887338] uhci_hcd 0000:00:1d.1: new USB bus registered, assigned bus number 6
> [    4.887379] uhci_hcd 0000:00:1d.1: irq 17, io base 0x000018c0
> [    4.887415] usb usb6: New USB device found, idVendor=1d6b, idProduct=0001
> [    4.887417] usb usb6: New USB device strings: Mfr=3, Product=2, SerialNumber=1
> [    4.887420] usb usb6: Product: UHCI Host Controller
> [    4.887422] usb usb6: Manufacturer: Linux 2.6.32.7 uhci_hcd
> [    4.887424] usb usb6: SerialNumber: 0000:00:1d.1
> [    4.887516] usb usb6: configuration #1 chosen from 1 choice
> [    4.887580] hub 6-0:1.0: USB hub found
> [    4.887587] hub 6-0:1.0: 2 ports detected
> [    4.887743] sl811: driver sl811-hcd, 19 May 2005
> [    4.887881] usbcore: registered new interface driver cdc_acm
> [    4.887885] cdc_acm: v0.26:USB Abstract Control Model driver for USB modems and ISDN adapters
> [    4.887935] usbcore: registered new interface driver usblp
> [    4.887984] usbcore: registered new interface driver cdc_wdm
> [    4.887987] Initializing USB Mass Storage driver...
> [    4.888071] usbcore: registered new interface driver usb-storage
> [    4.888074] USB Mass Storage support registered.
> [    4.888140] usbcore: registered new interface driver libusual
> [    4.888188] usbcore: registered new interface driver ums-alauda
> [    4.888238] usbcore: registered new interface driver ums-datafab
> [    4.888298] usbcore: registered new interface driver ums-freecom
> [    4.888346] usbcore: registered new interface driver ums-isd200
> [    4.888392] usbcore: registered new interface driver ums-jumpshot
> [    4.888439] usbcore: registered new interface driver ums-karma
> [    4.888486] usbcore: registered new interface driver ums-onetouch
> [    4.888533] usbcore: registered new interface driver ums-sddr09
> [    4.888580] usbcore: registered new interface driver ums-sddr55
> [    4.888628] usbcore: registered new interface driver ums-usbat
> [    4.888678] usbcore: registered new interface driver berry_charge
> [    4.888797] PNP: PS/2 Controller [PNP0303:KBD,PNP0f13:MOU] at 0x60,0x64 irq 1,12
> [    4.900359] serio: i8042 KBD port at 0x60,0x64 irq 1
> [    4.900365] serio: i8042 AUX port at 0x60,0x64 irq 12
> [    4.900591] mice: PS/2 mouse device common for all mice
> [    4.901067] i2c /dev entries driver
> [    4.901306] i801_smbus 0000:00:1f.3: PCI INT A -> GSI 23 (level, low) -> IRQ 23
> [    4.901493] Linux video capture interface: v2.00
> [    4.901537] gspca: main v2.7.0 registered
> [    4.902236] device-mapper: ioctl: 4.15.0-ioctl (2009-04-01) initialised: dm-devel@redhat.com
> [    4.904334] device-mapper: multipath: version 1.1.0 loaded
> [    4.904337] device-mapper: multipath round-robin: version 1.0.0 loaded
> [    4.904410] Bluetooth: Generic Bluetooth SDIO driver ver 0.1
> [    4.904458] EDAC MC: Ver: 2.1.0 Feb  4 2010
> [    4.907489] input: AT Translated Set 2 keyboard as /devices/platform/i8042/serio0/input/input4
> [    4.908386] cpuidle: using governor ladder
> [    4.909167] cpuidle: using governor menu
> [    4.909365] sdhci: Secure Digital Host Controller Interface driver
> [    4.909367] sdhci: Copyright(c) Pierre Ossman
> [    4.909385] sdhci-pci 0000:05:00.2: SDHCI controller found [1180:0822] (rev 21)
> [    4.909419] sdhci-pci 0000:05:00.2: PCI INT C -> GSI 18 (level, low) -> IRQ 18
> [    4.910439] sdhci-pci 0000:05:00.2: Will use DMA mode even though HW doesn't fully claim to support it.
> [    4.910450] sdhci-pci 0000:05:00.2: setting latency timer to 64
> [    4.911608] Registered led device: mmc0::
> [    4.912766] mmc0: SDHCI controller on PCI [0000:05:00.2] using DMA
> [    4.912870] ricoh-mmc: Ricoh MMC Controller disabling driver
> [    4.912872] ricoh-mmc: Copyright(c) Philip Langdale
> [    4.912972] wbsd: Winbond W83L51xD SD/MMC card interface driver
> [    4.912974] wbsd: Copyright(c) Pierre Ossman
> [    4.918841] wacom driver registered
> [    4.919292] usbcore: registered new interface driver hiddev
> [    4.919415] usbcore: registered new interface driver usbhid
> [    4.919418] usbhid: v2.6:USB HID core driver
> [    4.920050] thinkpad_acpi: ThinkPad ACPI Extras v0.23
> [    4.920053] thinkpad_acpi: http://ibm-acpi.sf.net/
> [    4.920055] thinkpad_acpi: ThinkPad BIOS 7NETB9WW (2.19 ), EC 7MHT25WW-1.03
> [    4.920057] thinkpad_acpi: Lenovo ThinkPad X61s, model 7666B4G
> [    4.920353] thinkpad_acpi: radio switch found; radios are enabled
> [    4.920464] thinkpad_acpi: This ThinkPad has standard ACPI backlight brightness control, supported by the ACPI video driver
> [    4.920466] thinkpad_acpi: Disabling thinkpad-acpi brightness events by default...
> [    4.926615] Registered led device: tpacpi::thinklight
> [    4.926759] Registered led device: tpacpi::power
> [    4.926885] Registered led device: tpacpi::standby
> [    4.927036] Registered led device: tpacpi::thinkvantage
> [    4.930329] thinkpad_acpi: Standard ACPI backlight interface available, not loading native one.
> [    4.930829] input: ThinkPad Extra Buttons as /devices/virtual/input/input5
> [    4.931076] Advanced Linux Sound Architecture Driver Version 1.0.21.
> [    4.932503] HDA Intel 0000:00:1b.0: PCI INT B -> GSI 17 (level, low) -> IRQ 17
> [    4.932507] hda_intel: probe_mask set to 0x1 for device 17aa:20ac
> [    4.932529] HDA Intel 0000:00:1b.0: setting latency timer to 64
> [    5.336295] input: HDA Digital PCBeep as /devices/pci0000:00/0000:00:1b.0/input/input6
> [    5.340030] usbcore: registered new interface driver snd-usb-audio
> [    5.340034] ALSA device list:
> [    5.340036]   #0: HDA Intel at 0xf8220000 irq 17
> [    5.340130] NET: Registered protocol family 26
> [    5.340133] Mirror/redirect action on
> [    5.340493] netem: version 1.2
> [    5.340495] u32 classifier
> [    5.340496]     Performance counters on
> [    5.340498]     input device check on 
> [    5.340499]     Actions configured 
> [    5.340503] Netfilter messages via NETLINK v0.30.
> [    5.340519] nf_conntrack version 0.5.0 (16384 buckets, 65536 max)
> [    5.340644] CONFIG_NF_CT_ACCT is deprecated and will be removed soon. Please use
> [    5.340647] nf_conntrack.acct=1 kernel parameter, acct=1 nf_conntrack module option or
> [    5.340649] sysctl net.netfilter.nf_conntrack_acct=1 to enable it.
> [    5.341164] IPv4 over IPv4 tunneling driver
> [    5.341529] GRE over IPv4 tunneling driver
> [    5.342318] TCP cubic registered
> [    5.342320] Initializing XFRM netlink socket
> [    5.342324] NET: Registered protocol family 17
> [    5.342329] NET: Registered protocol family 15
> [    5.343115] IrCOMM protocol (Dag Brattli)
> [    5.348059] Bluetooth: L2CAP ver 2.14
> [    5.348061] Bluetooth: L2CAP socket layer initialized
> [    5.348064] Bluetooth: SCO (Voice Link) ver 0.6
> [    5.348066] Bluetooth: SCO socket layer initialized
> [    5.348089] Bluetooth: RFCOMM TTY layer initialized
> [    5.348092] Bluetooth: RFCOMM socket layer initialized
> [    5.348094] Bluetooth: RFCOMM ver 1.11
> [    5.348096] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
> [    5.348098] Bluetooth: BNEP filters: protocol multicast
> [    5.348100] Bluetooth: HIDP (Human Interface Emulation) ver 1.2
> [    5.348674] SCTP: Hash tables configured (established 65536 bind 65536)
> [    5.349862] Using IPI No-Shortcut mode
> [    5.350311] registered taskstats version 1
> [    5.351252] drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
> [    5.351355] Freeing unused kernel memory: 476k freed
> [    5.351642] Write protecting the kernel text: 7456k
> [    5.351774] Write protecting the kernel read-only data: 2504k
> [    5.434116] IBM TrackPoint firmware: 0x0e, buttons: 3/3
> [    5.452387] input: TPPS/2 IBM TrackPoint as /devices/platform/i8042/serio1/input/input7
> [    5.583125] usb 3-1: new full speed USB device using uhci_hcd and address 2
> [    5.732704] usb 3-1: New USB device found, idVendor=0a5c, idProduct=2110
> [    5.732711] usb 3-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
> [    5.732717] usb 3-1: Product: BCM2045B
> [    5.732722] usb 3-1: Manufacturer: Broadcom Corp
> [    5.733069] usb 3-1: configuration #1 chosen from 1 choice
> [    5.942124] usb 3-2: new full speed USB device using uhci_hcd and address 3
> [    6.102703] usb 3-2: New USB device found, idVendor=0483, idProduct=2016
> [    6.102710] usb 3-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
> [    6.102716] usb 3-2: Product: Biometric Coprocessor
> [    6.102721] usb 3-2: Manufacturer: STMicroelectronics
> [    6.103073] usb 3-2: configuration #1 chosen from 1 choice
> [   12.753713] EXT3-fs: INFO: recovery required on readonly filesystem.
> [   12.753720] EXT3-fs: write access will be enabled during recovery.
> [   13.059470] EXT3-fs: recovery complete.
> [   13.059668] kjournald starting.  Commit interval 5 seconds
> [   13.066962] EXT3-fs: mounted filesystem with writeback data mode.
> [   18.334910] e1000e: Intel(R) PRO/1000 Network Driver - 1.0.2-k2
> [   18.334917] e1000e: Copyright (c) 1999-2008 Intel Corporation.
> [   18.335051] e1000e 0000:00:19.0: PCI INT A -> GSI 20 (level, low) -> IRQ 20
> [   18.335075] e1000e 0000:00:19.0: setting latency timer to 64
> [   18.336604] e1000e 0000:00:19.0: irq 28 for MSI/MSI-X
> [   18.339011] rtc_cmos 00:07: RTC can wake from S4
> [   18.339365] rtc_cmos 00:07: rtc core: registered rtc_cmos as rtc0
> [   18.339433] rtc0: alarms up to one month, y3k, 114 bytes nvram, hpet irqs
> [   18.345437] Bluetooth: Generic Bluetooth USB driver ver 0.6
> [   18.346241] usbcore: registered new interface driver btusb
> [   18.408803] iwlagn: Intel(R) Wireless WiFi Link AGN driver for Linux, 1.3.27ks
> [   18.408806] iwlagn: Copyright(c) 2003-2009 Intel Corporation
> [   18.408904] iwlagn 0000:03:00.0: PCI INT A -> GSI 17 (level, low) -> IRQ 17
> [   18.408941] iwlagn 0000:03:00.0: setting latency timer to 64
> [   18.408996] iwlagn 0000:03:00.0: Detected Intel Wireless WiFi Link 4965AGN REV=0x4
> [   18.461043] iwlagn 0000:03:00.0: Tunable channels: 13 802.11bg, 19 802.11a channels
> [   18.461141] iwlagn 0000:03:00.0: irq 29 for MSI/MSI-X
> [   18.465767] phy0: Selected rate control algorithm 'iwl-agn-rs'
> [   18.476812] udev: renamed network interface wlan0 to eth1
> [   18.570366] 0000:00:19.0: eth0: (PCI Express:2.5GB/s:Width x1) 00:16:d3:c6:d1:c1
> [   18.570373] 0000:00:19.0: eth0: Intel(R) PRO/1000 Network Connection
> [   18.570412] 0000:00:19.0: eth0: MAC: 6, PHY: 6, PBA No: ffffff-0ff
> [   19.671561] EXT3 FS on dm-0, internal journal
> [   23.387140] thinkpad_ec: thinkpad_ec 0.40 loaded.
> [   23.397492] tp_smapi 0.40 loading...
> [   23.397958] tp_smapi successfully loaded (smapi_port=0xb2).
> [   23.498470] vboxdrv: Trying to deactivate the NMI watchdog permanently...
> [   23.498474] vboxdrv: Successfully done.
> [   23.498475] vboxdrv: Found 2 processor cores.
> [   23.498654] vboxdrv: fAsync=1 offMin=0xe1024 offMax=0xe1024
> [   23.498945] vboxdrv: TSC mode is 'asynchronous', kernel timer mode is 'normal'.
> [   23.498947] vboxdrv: Successfully loaded version 1.6.6_OSE (interface 0x00100001).
> [   23.911188] EXT3-fs warning: maximal mount count reached, running e2fsck is recommended
> [   23.911220] kjournald starting.  Commit interval 5 seconds
> [   23.911643] EXT3 FS on sda1, internal journal
> [   23.911649] EXT3-fs: recovery complete.
> [   23.911656] EXT3-fs: mounted filesystem with writeback data mode.
> [   24.409303] kjournald starting.  Commit interval 5 seconds
> [   24.420536] EXT3 FS on dm-1, internal journal
> [   24.420542] EXT3-fs: recovery complete.
> [   24.420919] EXT3-fs: mounted filesystem with writeback data mode.
> [   24.506354] EXT3-fs warning: maximal mount count reached, running e2fsck is recommended
> [   24.506534] kjournald starting.  Commit interval 5 seconds
> [   24.514296] EXT3 FS on dm-2, internal journal
> [   24.514302] EXT3-fs: recovery complete.
> [   24.514309] EXT3-fs: mounted filesystem with writeback data mode.
> [   24.673459] Adding 1951856k swap on /dev/mapper/cswap.  Priority:-1 extents:1 across:1951856k 
> [   26.253756] ip_tables: (C) 2000-2006 Netfilter Core Team
> [   28.078446] iwlagn 0000:03:00.0: firmware: requesting iwlwifi-4965-2.ucode
> [   28.190080] iwlagn 0000:03:00.0: loaded firmware version 228.61.2.24
> [   28.407977] Registered led device: iwl-phy0::radio
> [   28.407996] Registered led device: iwl-phy0::assoc
> [   28.408012] Registered led device: iwl-phy0::RX
> [   28.408028] Registered led device: iwl-phy0::TX
> [   29.747044] ttyS0: LSR safety check engaged!
> [   29.748210] ttyS0: LSR safety check engaged!
> [   29.749735] ttyS1: LSR safety check engaged!
> [   29.755419] ttyS1: LSR safety check engaged!
> [   30.404058] input: ACPI Virtual Keyboard Device as /devices/virtual/input/input11
> [   34.982146] smapi smapi: set_real_thresh: set start to 79 for bat=0
> [   35.190171] smapi smapi: set_real_thresh: set stop to 93 for bat=0
> [   42.954199] usb 3-1: USB disconnect, address 2
> [   43.002415] usbcore: deregistering interface driver btusb
> [   44.105667] e1000e 0000:00:19.0: irq 28 for MSI/MSI-X
> [   44.156225] e1000e 0000:00:19.0: irq 28 for MSI/MSI-X
> [   45.993769] e1000e: eth0 NIC Link is Up 100 Mbps Full Duplex, Flow Control: None
> [   45.993778] 0000:00:19.0: eth0: 10/100 speed: disabling TSO
> [   47.590769] ttyS0: LSR safety check engaged!
> [   47.591892] ttyS1: LSR safety check engaged!
> [   55.803510] eth1: deauthenticating from 02:24:01:49:a6:a9 by local choice (reason=3)
> [   55.803540] eth1: direct probe to AP 02:24:01:49:a6:a9 (try 1)
> [   56.003058] eth1: direct probe to AP 02:24:01:49:a6:a9 (try 2)
> [   56.005702] eth1: direct probe responded
> [   56.005705] eth1: authenticate with AP 02:24:01:49:a6:a9 (try 1)
> [   56.205036] eth1: authenticate with AP 02:24:01:49:a6:a9 (try 2)
> [   56.207081] eth1: authenticated
> [   56.207100] eth1: associate with AP 02:24:01:49:a6:a9 (try 1)
> [   56.407106] eth1: associate with AP 02:24:01:49:a6:a9 (try 2)
> [   56.409689] eth1: RX AssocResp from 02:24:01:49:a6:a9 (capab=0x401 status=0 aid=2)
> [   56.409697] eth1: associated
> [   56.699288] eth1: deauthenticating from 02:24:01:49:a6:a9 by local choice (reason=3)
> [   56.987398] Registered led device: iwl-phy0::radio
> [   56.987418] Registered led device: iwl-phy0::assoc
> [   56.987436] Registered led device: iwl-phy0::RX
> [   56.987452] Registered led device: iwl-phy0::TX
> [   57.016322] eth1: direct probe to AP 02:24:01:49:a6:a9 (try 1)
> [   57.216067] eth1: direct probe to AP 02:24:01:49:a6:a9 (try 2)
> [   57.417042] eth1: direct probe to AP 02:24:01:49:a6:a9 (try 3)
> [   57.419744] eth1: direct probe responded
> [   57.419750] eth1: authenticate with AP 02:24:01:49:a6:a9 (try 1)
> [   57.619094] eth1: authenticate with AP 02:24:01:49:a6:a9 (try 2)
> [   57.620968] eth1: authenticated
> [   57.621033] eth1: associate with AP 02:24:01:49:a6:a9 (try 1)
> [   57.821088] eth1: associate with AP 02:24:01:49:a6:a9 (try 2)
> [   57.826321] eth1: RX AssocResp from 02:24:01:49:a6:a9 (capab=0x401 status=0 aid=2)
> [   57.826328] eth1: associated
> 
> ### EOF
> ### vim:tw=256:et:sts=2:st=2:sw=2:com+=b\:###:fo+=cqtr:tags=ctags:


^ permalink raw reply

* Re: [Bugme-new] [Bug 15239] New: Problem with network performance after a while.
From: Andrew Morton @ 2010-02-08 22:15 UTC (permalink / raw)
  To: netdev, e1000-devel
  Cc: Alex, pstaszewski, bugzilla-daemon, Bruce Allan, Jesse Brandeburg,
	John Ronciak, Jeff Kirsher, bugme-daemon, stable
In-Reply-To: <bug-15239-10286@http.bugzilla.kernel.org/>


(switched to email.  Please respond via emailed reply-to-all, not via the
bugzilla web interface).

On Sat, 6 Feb 2010 14:39:46 GMT
bugzilla-daemon@bugzilla.kernel.org wrote:

> http://bugzilla.kernel.org/show_bug.cgi?id=15239
> 
>            Summary: Problem with network performance after a while.
>            Product: Networking
>            Version: 2.5
>     Kernel Version: 2.6.33-rc6-git5
>           Platform: All
>         OS/Version: Linux
>               Tree: Mainline
>             Status: NEW
>           Severity: normal
>           Priority: P1
>          Component: IPV4
>         AssignedTo: shemminger@linux-foundation.org
>         ReportedBy: pstaszewski@artcom.pl
>         Regression: No
> 
> 
> Hello the problem is that after some time of working my router have some
> performance problems.
> 
> The problem exist on kernel 2.6.32.7 and 2.6.33-rc6-git5
> The problem dont exist on 2.6.32.2 kernel.

That would be bad.  That's a regression in 2.6.33-rc, and we've
backported that regression into 2.6.32.x -stable.

> on 2.6.33-rc6-git5 i have this info:BUG: soft lockup - CPU#1 stuck for 61s!
> [events/1:28]
> Modules linked in:
> CPU 1
> Pid: 28, comm: events/1 Not tainted 2.6.33-rc6-git5 #1 X7DCT/X7DCT
> RIP: 0010:[<ffffffff810a3d89>]  [<ffffffff810a3d89>]
> kmem_cache_free+0x11b/0x11c
> RSP: 0018:ffff880028243e50  EFLAGS: 00000292
> RAX: 0000000000000032 RBX: 000000000000007d RCX: ffff8803190683c0
> RDX: 0000000000000031 RSI: ffff8803190683c0 RDI: ffff88031f83e680
> RBP: ffffffff81002893 R08: 0000000000000000 R09: 000000000000007c
> R10: ffff88030d776800 R11: ffff88030d7768a0 R12: ffff880028243dd0
> R13: ffffc900008b2f80 R14: ffff88031fa7c800 R15: ffffffff81012da7
> FS:  0000000000000000(0000) GS:ffff880028240000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
> CR2: 00007fd61d5bd000 CR3: 000000031e55c000 CR4: 00000000000006a0
> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> Process events/1 (pid: 28, threadinfo ffff88031f9c8000, task ffff88031f9a4f80)
> Stack:
>  ffffffff8126826f ffff88031faa4600 ffffffff8126834a 000096ba00000023
> <0> 01ffc90000000024 ffff88031fbb4000 ffff88031faa4600 0000000000000040
> <0> 0000000000000040 ffff88031faa4788 ffff88031faa4600 0000000000000740
> Call Trace:
>  <IRQ>
>  [<ffffffff8126826f>] ? e1000_put_txbuf+0x62/0x74
>  [<ffffffff8126834a>] ? e1000_clean_tx_irq+0xc9/0x235
>  [<ffffffff8126b71b>] ? e1000_clean+0x5c/0x21c
>  [<ffffffff812f29a3>] ? net_rx_action+0x71/0x15d
>  [<ffffffff81035311>] ? __do_softirq+0xd7/0x196
>  [<ffffffff81002dac>] ? call_softirq+0x1c/0x28
>  [<ffffffff812f768f>] ? dst_gc_task+0x0/0x1a7
>  [<ffffffff81002dac>] ? call_softirq+0x1c/0x28
>  <EOI>
>  [<ffffffff81004599>] ? do_softirq+0x31/0x63
>  [<ffffffff81034ec1>] ? local_bh_enable_ip+0x75/0x86
>  [<ffffffff812f768f>] ? dst_gc_task+0x0/0x1a7
>  [<ffffffff812f775d>] ? dst_gc_task+0xce/0x1a7
>  [<ffffffff8136b08c>] ? schedule+0x82c/0x906
>  [<ffffffff8103c44f>] ? lock_timer_base+0x26/0x4b
>  [<ffffffff810a41d6>] ? cache_reap+0x0/0x11d
>  [<ffffffff81044c38>] ? worker_thread+0x14c/0x1dc
>  [<ffffffff81047dcd>] ? autoremove_wake_function+0x0/0x2e
>  [<ffffffff81044aec>] ? worker_thread+0x0/0x1dc
>  [<ffffffff810479bd>] ? kthread+0x79/0x81
>  [<ffffffff81002cb4>] ? kernel_thread_helper+0x4/0x10
>  [<ffffffff81047944>] ? kthread+0x0/0x81
>  [<ffffffff81002cb0>] ? kernel_thread_helper+0x0/0x10
> Code: fe 79 4c 00 48 85 db 74 14 48 8b 74 24 10 48 89 ef ff 13 48 83 c3 08 48
> 83 3b 00 eb ea 48 83 c4 18 5b 5d 41 5c 41 5d 41 5e 41 5f <c3> 55 48 89 f5 53 48
> 89 fb 48 83 ec 08 48 8b 76 18 48 2b 75 10
> Call Trace:
>  <IRQ>  [<ffffffff8126826f>] ? e1000_put_txbuf+0x62/0x74
>  [<ffffffff8126834a>] ? e1000_clean_tx_irq+0xc9/0x235
>  [<ffffffff8126b71b>] ? e1000_clean+0x5c/0x21c
>  [<ffffffff812f29a3>] ? net_rx_action+0x71/0x15d
>  [<ffffffff81035311>] ? __do_softirq+0xd7/0x196
>  [<ffffffff81002dac>] ? call_softirq+0x1c/0x28
>  [<ffffffff812f768f>] ? dst_gc_task+0x0/0x1a7
>  [<ffffffff81002dac>] ? call_softirq+0x1c/0x28
>  <EOI>  [<ffffffff81004599>] ? do_softirq+0x31/0x63
>  [<ffffffff81034ec1>] ? local_bh_enable_ip+0x75/0x86
>  [<ffffffff812f768f>] ? dst_gc_task+0x0/0x1a7
>  [<ffffffff812f775d>] ? dst_gc_task+0xce/0x1a7
>  [<ffffffff8136b08c>] ? schedule+0x82c/0x906
>  [<ffffffff8103c44f>] ? lock_timer_base+0x26/0x4b
>  [<ffffffff810a41d6>] ? cache_reap+0x0/0x11d
>  [<ffffffff81044c38>] ? worker_thread+0x14c/0x1dc
>  [<ffffffff81047dcd>] ? autoremove_wake_function+0x0/0x2e
>  [<ffffffff81044aec>] ? worker_thread+0x0/0x1dc
>  [<ffffffff810479bd>] ? kthread+0x79/0x81
>  [<ffffffff81002cb4>] ? kernel_thread_helper+0x4/0x10
>  [<ffffffff81047944>] ? kthread+0x0/0x81
>  [<ffffffff81002cb0>] ? kernel_thread_helper+0x0/0x10
> 
> And on 2.6.32.7
> Feb  4 13:12:40 TM_01_C1 ------------[ cut here ]------------
> Feb  4 13:12:40 TM_01_C1 WARNING: at net/sched/sch_generic.c:261
> dev_watchdog+0x130/0x1d6()
> Feb  4 13:12:40 TM_01_C1 Hardware name: X7DCT
> Feb  4 13:12:40 TM_01_C1 NETDEV WATCHDOG: eth0 (e1000e): transmit queue 0 timed
> out
> Feb  4 13:12:40 TM_01_C1 Modules linked in: oprofile
> Feb  4 13:12:40 TM_01_C1 Pid: 0, comm: swapper Not tainted 2.6.32 #1
> Feb  4 13:12:40 TM_01_C1 Call Trace:
> Feb  4 13:12:40 TM_01_C1 <IRQ>  [<ffffffff812fcaf7>] ? dev_watchdog+0x130/0x1d6
> Feb  4 13:12:40 TM_01_C1 [<ffffffff812fcaf7>] ? dev_watchdog+0x130/0x1d6
> Feb  4 13:12:40 TM_01_C1 [<ffffffff81038811>] ? warn_slowpath_common+0x77/0xa3
> Feb  4 13:12:40 TM_01_C1 [<ffffffff81038899>] ? warn_slowpath_fmt+0x51/0x59
> Feb  4 13:12:40 TM_01_C1 [<ffffffff8102897e>] ? activate_task+0x3f/0x4e
> Feb  4 13:12:40 TM_01_C1 [<ffffffff81034fe5>] ? try_to_wake_up+0x1eb/0x1f8
> Feb  4 13:12:40 TM_01_C1 [<ffffffff812eb768>] ? netdev_drivername+0x3b/0x40
> Feb  4 13:12:40 TM_01_C1 [<ffffffff812fcaf7>] ? dev_watchdog+0x130/0x1d6
> Feb  4 13:12:40 TM_01_C1 [<ffffffff8102d1e3>] ? __wake_up+0x30/0x44
> Feb  4 13:12:40 TM_01_C1 [<ffffffff812fc9c7>] ? dev_watchdog+0x0/0x1d6
> Feb  4 13:12:40 TM_01_C1 [<ffffffff810448c4>] ? run_timer_softirq+0x1ff/0x29d
> Feb  4 13:12:40 TM_01_C1 [<ffffffff810556ab>] ? ktime_get+0x5f/0xb7
> Feb  4 13:12:40 TM_01_C1 [<ffffffff8103e0fd>] ? __do_softirq+0xd7/0x196
> Feb  4 13:12:40 TM_01_C1 [<ffffffff8100be7c>] ? call_softirq+0x1c/0x28
> Feb  4 13:12:40 TM_01_C1 [<ffffffff8100d645>] ? do_softirq+0x31/0x66
> Feb  4 13:12:40 TM_01_C1 [<ffffffff8101b148>] ?
> smp_apic_timer_interrupt+0x87/0x95
> Feb  4 13:12:40 TM_01_C1 [<ffffffff8100b873>] ? apic_timer_interrupt+0x13/0x20
> Feb  4 13:12:40 TM_01_C1 <EOI>  [<ffffffff810111f5>] ? mwait_idle+0x9b/0xa0
> Feb  4 13:12:40 TM_01_C1 [<ffffffff8100a236>] ? cpu_idle+0x49/0x7c
> Feb  4 13:12:40 TM_01_C1 ---[ end trace c670a6a17be040e5 ]---
> 
> 
> Machine is a BGP router with some iproute traffic management.


------------------------------------------------------------------------------
The Planet: dedicated and managed hosting, cloud storage, colocation
Stay online with enterprise data centers and the best network in the business
Choose flexible plans and management services without long-term contracts
Personal 24x7 support from experience hosting pros just a phone call away.
http://p.sf.net/sfu/theplanet-com
_______________________________________________
E1000-devel mailing list
E1000-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/e1000-devel
To learn more about Intel&#174; Ethernet, visit http://communities.intel.com/community/wired

^ permalink raw reply

* Re: [PATCH 0/3][v2] tcp: fix ICMP-RTO war
From: Damian Lukowski @ 2010-02-08 21:58 UTC (permalink / raw)
  To: David Miller; +Cc: netdev
In-Reply-To: <op.u7s6j0qup498uc@nexus>

Damian Lukowski schrieb:
> Am 01.02.2010, 08:33 Uhr, schrieb David Miller <davem@davemloft.net>:
> 
>> From: Damian Lukowski <damian@tvk.rwth-aachen.de>
>> Date: Fri, 29 Jan 2010 23:15:51 +0100
>>
>>> This patches fix the current RTO calculation routine, when
>>> srtt and rttvar are zero, yielding an RTO of zero
>>> Under some circumstances, TCPs srtt and rttvar are zero,
>>> yielding a calculated RTO of zero.
>>> This is particularly unfortunate for ICMP based RTO recalculation
>>> as introduced in f1ecd5d9e736660 (Revert Backoff [v3]: Revert RTO
>>> on ICMP destination unreachable), as it results in RTO retransmission
>>> flooding.
>>>
>>> Thanks to Ilpo Jarvinen for providing debug patches and to
>>> Denys Fedoryshchenko for reporting and testing.
>>>
>>> Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
>>
>> I still haven't seen a detailed enough analysis of why these
>> tiny RTOs can come to exist in the first place.
>>
>> Please show me a list of events, function by function, the value of
>> relevant variables and per-socket TCP state, in the TCP stack, that
>> show how this ends up happening.
>>
>> Thanks for all of your work on this so far.
> 
> I might have figured it out, but could not verify it, so maybe you can
> comment my thought.
> 
> When a listening TCP receives a SYN, it will send a SYN+ACK
> and wait for an ACK to complete the handshake.
> Look at tcp_rcv_state_process::step 5::case SYN_RECV::acceptable
> and the code after the comment "tcp_ack considers this ACK as duplicate
> and does not calculate rtt".
> 
> If the connecting client has disabled timestamps, the rtt statistics
> won't be updated here, while the state is changed above.
> I printk'ed at the very end of TCP_SYN_RECV and got the following:
> state 1 (ESTABLISHED), srtt 0, rttvar 0.
> 
> So my suspicion is: If connectivity breaks right after a listening TCP
> has completed the handshake without timestamps, and the listening TCP
> sends data after establishing the connection, we will get the observed
> behaviour.

Just verified that by dropping pure ACKs coming from the originally
listening TCP using iptables.

Regards
 Damian

^ permalink raw reply

* [PATCH 2.6.33 3/3] net: Makefile change for KSZ884X driver
From: Ha, Tristram @ 2010-02-08 21:40 UTC (permalink / raw)
  To: Dave Miller; +Cc: netdev, linux-kernel

From: Tristram Ha <Tristram.Ha@micrel.com>

Add Micrel KSZ884X network driver for KSZ8841/KSZ8842 PCI Ethernet chips.

Signed-off-by: Tristram Ha <Tristram.Ha@micrel.com>
---
This is a resubmission of the Micrel KSZ8841/2 PCI Ethernet driver.

--- linux-2.6.33-rc3.old/drivers/net/Makefile	2010-01-05 16:02:46.000000000 -0800
+++ linux-2.6.33-rc3.new/drivers/net/Makefile	2010-01-12 18:56:37.000000000 -0800
@@ -95,6 +95,7 @@ obj-$(CONFIG_SKFP) += skfp/
 obj-$(CONFIG_KS8842)	+= ks8842.o
 obj-$(CONFIG_KS8851)	+= ks8851.o
 obj-$(CONFIG_KS8851_MLL)	+= ks8851_mll.o
+obj-$(CONFIG_KSZ884X_PCI)	+= ksz884x.o
 obj-$(CONFIG_VIA_RHINE) += via-rhine.o
 obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o
 obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o

^ permalink raw reply

* [PATCH 2.6.33 2/3] net: Kconfig change for KSZ884X driver
From: Ha, Tristram @ 2010-02-08 21:38 UTC (permalink / raw)
  To: Dave Miller; +Cc: netdev, linux-kernel

From: Tristram Ha <Tristram.Ha@micrel.com>

Add Micrel KSZ8841/KSZ8842 PCI Ethernet chip support.

Signed-off-by: Tristram Ha <Tristram.Ha@micrel.com>
---
This is a resubmission of the Micrel KSZ8841/2 PCI Ethernet driver.

--- linux-2.6.33-rc5.old/drivers/net/Kconfig	2010-01-21 15:31:35.000000000 -0800
+++ linux-2.6.33-rc5.new/drivers/net/Kconfig	2010-02-04 12:12:58.000000000 -0800
@@ -1368,6 +1368,17 @@ config AC3200
 	  To compile this driver as a module, choose M here. The module
 	  will be called ac3200.
 
+config KSZ884X_PCI
+	tristate "Micrel KSZ8841/2 PCI"
+	depends on NET_PCI && PCI
+	select MII
+	select CRC32
+	help
+	  This PCI driver is for Micrel KSZ8841/KSZ8842 PCI Ethernet chip.
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called ksz884x.
+
 config APRICOT
 	tristate "Apricot Xen-II on board Ethernet"
 	depends on NET_PCI && ISA

^ permalink raw reply

* Re: [PATCH v2 00/41] CAPI: Major rework, tons of bug fixes
From: Alan Cox @ 2010-02-08 21:38 UTC (permalink / raw)
  To: Jan Kiszka
  Cc: David Miller, Karsten Keil, linux-kernel, i4ldeveloper,
	isdn4linux, netdev, Marcel Holtmann
In-Reply-To: <cover.1265659933.git.jan.kiszka@web.de>

>  o reworked locking of the NCCI TTY using latest and greatest tty_port
>    features and kref, hopefully in the right way

Looks sane to me

Alan

^ permalink raw reply

* [PATCH 2.6.33 1/3] net: Micrel KSZ8841/2 PCI Ethernet driver
From: Ha, Tristram @ 2010-02-08 21:36 UTC (permalink / raw)
  To: Dave Miller; +Cc: netdev, linux-kernel

From: Tristram Ha <Tristram.Ha@micrel.com>

This is a new network driver for Micrel KSZ8841/KSZ8842 PCI Ethernet chips.  The same driver can run both chips at the same time.  It supports IPv4 TCP hardware checksumming and so can use scatter/gather transmission.

The KSZ8842 switch has 2 ports.  Some users like to take direct control of those ports.  So KSZ8842 has a multiple devices mode in which the driver creates another network device so that users can specify which port to send packets.  This mode is enabled by passing the "multi_dev=1" parameter to the driver during loading.

The KSZ884X can receive huge frames with size up to 1916 bytes.  To use this feature change the network device MTU from 1500 to 1898.

The KSZ884X driver has 3 packet receive processing functions for the normal mode, multiple devices mode, and huge frame mode.  Those functions are identical except for more checking for the new features.  In normal programming point of view the huge frame version is the one to use because it covers all the cases, but this is done for performance consideration because the target clients for the KSZ884X chips are embedded systems, which unlike desktop PC may not have powerful CPU.

Signed-off-by: Tristram Ha <Tristram.Ha@micrel.com>
---
This is a resubmission of the Micrel KSZ8841/2 PCI Ethernet driver.

--- linux-2.6.33-rc6.old/drivers/net/ksz884x.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.33-rc6.new/drivers/net/ksz884x.c	2010-02-08 12:50:51.000000000 -0800
@@ -0,0 +1,7335 @@
+/**
+ * drivers/net/ksx884x.c - Micrel KSZ8841/2 PCI Ethernet driver
+ *
+ * Copyright (c) 2009-2010 Micrel, Inc.
+ * 	Tristram Ha <Tristram.Ha@micrel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/mii.h>
+#include <linux/platform_device.h>
+#include <linux/ethtool.h>
+#include <linux/etherdevice.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/if_vlan.h>
+#include <linux/crc32.h>
+#include <linux/sched.h>
+
+
+/* DMA Registers */
+
+#define KS_DMA_TX_CTRL			0x0000
+#define DMA_TX_ENABLE			0x00000001
+#define DMA_TX_CRC_ENABLE		0x00000002
+#define DMA_TX_PAD_ENABLE		0x00000004
+#define DMA_TX_LOOPBACK			0x00000100
+#define DMA_TX_FLOW_ENABLE		0x00000200
+#define DMA_TX_CSUM_IP			0x00010000
+#define DMA_TX_CSUM_TCP			0x00020000
+#define DMA_TX_CSUM_UDP			0x00040000
+#define DMA_TX_BURST_SIZE		0x3F000000
+
+#define KS_DMA_RX_CTRL			0x0004
+#define DMA_RX_ENABLE			0x00000001
+#define KS884X_DMA_RX_MULTICAST		0x00000002
+#define DMA_RX_PROMISCUOUS		0x00000004
+#define DMA_RX_ERROR			0x00000008
+#define DMA_RX_UNICAST			0x00000010
+#define DMA_RX_ALL_MULTICAST		0x00000020
+#define DMA_RX_BROADCAST		0x00000040
+#define DMA_RX_FLOW_ENABLE		0x00000200
+#define DMA_RX_CSUM_IP			0x00010000
+#define DMA_RX_CSUM_TCP			0x00020000
+#define DMA_RX_CSUM_UDP			0x00040000
+#define DMA_RX_BURST_SIZE		0x3F000000
+
+#define DMA_BURST_SHIFT			24
+#define DMA_BURST_DEFAULT		8
+
+#define KS_DMA_TX_START			0x0008
+#define KS_DMA_RX_START			0x000C
+#define DMA_START			0x00000001
+
+#define KS_DMA_TX_ADDR			0x0010
+#define KS_DMA_RX_ADDR			0x0014
+
+#define DMA_ADDR_LIST_MASK		0xFFFFFFFC
+#define DMA_ADDR_LIST_SHIFT		2
+
+/* MTR0 */
+#define KS884X_MULTICAST_0_OFFSET	0x0020
+#define KS884X_MULTICAST_1_OFFSET	0x0021
+#define KS884X_MULTICAST_2_OFFSET	0x0022
+#define KS884x_MULTICAST_3_OFFSET	0x0023
+/* MTR1 */
+#define KS884X_MULTICAST_4_OFFSET	0x0024
+#define KS884X_MULTICAST_5_OFFSET	0x0025
+#define KS884X_MULTICAST_6_OFFSET	0x0026
+#define KS884X_MULTICAST_7_OFFSET	0x0027
+
+/* Interrupt Registers */
+
+/* INTEN */
+#define KS884X_INTERRUPTS_ENABLE	0x0028
+/* INTST */
+#define KS884X_INTERRUPTS_STATUS	0x002C
+
+#define KS884X_INT_RX_STOPPED		0x02000000
+#define KS884X_INT_TX_STOPPED		0x04000000
+#define KS884X_INT_RX_OVERRUN		0x08000000
+#define KS884X_INT_TX_EMPTY		0x10000000
+#define KS884X_INT_RX			0x20000000
+#define KS884X_INT_TX			0x40000000
+#define KS884X_INT_PHY			0x80000000
+
+#define KS884X_INT_RX_MASK		\
+	(KS884X_INT_RX | KS884X_INT_RX_OVERRUN)
+#define KS884X_INT_TX_MASK		\
+	(KS884X_INT_TX | KS884X_INT_TX_EMPTY)
+#define KS884X_INT_MASK	(KS884X_INT_RX | KS884X_INT_TX | KS884X_INT_PHY)
+
+/* MAC Additional Station Address */
+
+/* MAAL0 */
+#define KS_ADD_ADDR_0_LO		0x0080
+/* MAAH0 */
+#define KS_ADD_ADDR_0_HI		0x0084
+/* MAAL1 */
+#define KS_ADD_ADDR_1_LO		0x0088
+/* MAAH1 */
+#define KS_ADD_ADDR_1_HI		0x008C
+/* MAAL2 */
+#define KS_ADD_ADDR_2_LO		0x0090
+/* MAAH2 */
+#define KS_ADD_ADDR_2_HI		0x0094
+/* MAAL3 */
+#define KS_ADD_ADDR_3_LO		0x0098
+/* MAAH3 */
+#define KS_ADD_ADDR_3_HI		0x009C
+/* MAAL4 */
+#define KS_ADD_ADDR_4_LO		0x00A0
+/* MAAH4 */
+#define KS_ADD_ADDR_4_HI		0x00A4
+/* MAAL5 */
+#define KS_ADD_ADDR_5_LO		0x00A8
+/* MAAH5 */
+#define KS_ADD_ADDR_5_HI		0x00AC
+/* MAAL6 */
+#define KS_ADD_ADDR_6_LO		0x00B0
+/* MAAH6 */
+#define KS_ADD_ADDR_6_HI		0x00B4
+/* MAAL7 */
+#define KS_ADD_ADDR_7_LO		0x00B8
+/* MAAH7 */
+#define KS_ADD_ADDR_7_HI		0x00BC
+/* MAAL8 */
+#define KS_ADD_ADDR_8_LO		0x00C0
+/* MAAH8 */
+#define KS_ADD_ADDR_8_HI		0x00C4
+/* MAAL9 */
+#define KS_ADD_ADDR_9_LO		0x00C8
+/* MAAH9 */
+#define KS_ADD_ADDR_9_HI		0x00CC
+/* MAAL10 */
+#define KS_ADD_ADDR_A_LO		0x00D0
+/* MAAH10 */
+#define KS_ADD_ADDR_A_HI		0x00D4
+/* MAAL11 */
+#define KS_ADD_ADDR_B_LO		0x00D8
+/* MAAH11 */
+#define KS_ADD_ADDR_B_HI		0x00DC
+/* MAAL12 */
+#define KS_ADD_ADDR_C_LO		0x00E0
+/* MAAH12 */
+#define KS_ADD_ADDR_C_HI		0x00E4
+/* MAAL13 */
+#define KS_ADD_ADDR_D_LO		0x00E8
+/* MAAH13 */
+#define KS_ADD_ADDR_D_HI		0x00EC
+/* MAAL14 */
+#define KS_ADD_ADDR_E_LO		0x00F0
+/* MAAH14 */
+#define KS_ADD_ADDR_E_HI		0x00F4
+/* MAAL15 */
+#define KS_ADD_ADDR_F_LO		0x00F8
+/* MAAH15 */
+#define KS_ADD_ADDR_F_HI		0x00FC
+
+#define ADD_ADDR_HI_MASK		0x0000FFFF
+#define ADD_ADDR_ENABLE			0x80000000
+#define ADD_ADDR_INCR			8
+
+/* Miscellaneous Registers */
+
+/* MARL */
+#define KS884X_ADDR_0_OFFSET		0x0200
+#define KS884X_ADDR_1_OFFSET		0x0201
+/* MARM */
+#define KS884X_ADDR_2_OFFSET		0x0202
+#define KS884X_ADDR_3_OFFSET		0x0203
+/* MARH */
+#define KS884X_ADDR_4_OFFSET		0x0204
+#define KS884X_ADDR_5_OFFSET		0x0205
+
+/* OBCR */
+#define KS884X_BUS_CTRL_OFFSET		0x0210
+
+#define BUS_SPEED_125_MHZ		0x0000
+#define BUS_SPEED_62_5_MHZ		0x0001
+#define BUS_SPEED_41_66_MHZ		0x0002
+#define BUS_SPEED_25_MHZ		0x0003
+
+/* EEPCR */
+#define KS884X_EEPROM_CTRL_OFFSET	0x0212
+
+#define EEPROM_CHIP_SELECT		0x0001
+#define EEPROM_SERIAL_CLOCK		0x0002
+#define EEPROM_DATA_OUT			0x0004
+#define EEPROM_DATA_IN			0x0008
+#define EEPROM_ACCESS_ENABLE		0x0010
+
+/* MBIR */
+#define KS884X_MEM_INFO_OFFSET		0x0214
+
+#define RX_MEM_TEST_FAILED		0x0008
+#define RX_MEM_TEST_FINISHED		0x0010
+#define TX_MEM_TEST_FAILED		0x0800
+#define TX_MEM_TEST_FINISHED		0x1000
+
+/* GCR */
+#define KS884X_GLOBAL_CTRL_OFFSET	0x0216
+#define GLOBAL_SOFTWARE_RESET		0x0001
+
+#define KS8841_POWER_MANAGE_OFFSET	0x0218
+
+/* WFCR */
+#define KS8841_WOL_CTRL_OFFSET		0x021A
+#define KS8841_WOL_MAGIC_ENABLE		0x0080
+#define KS8841_WOL_FRAME3_ENABLE	0x0008
+#define KS8841_WOL_FRAME2_ENABLE	0x0004
+#define KS8841_WOL_FRAME1_ENABLE	0x0002
+#define KS8841_WOL_FRAME0_ENABLE	0x0001
+
+/* WF0 */
+#define KS8841_WOL_FRAME_CRC_OFFSET	0x0220
+#define KS8841_WOL_FRAME_BYTE0_OFFSET	0x0224
+#define KS8841_WOL_FRAME_BYTE2_OFFSET	0x0228
+
+/* IACR */
+#define KS884X_IACR_P			0x04A0
+#define KS884X_IACR_OFFSET		KS884X_IACR_P
+
+/* IADR1 */
+#define KS884X_IADR1_P			0x04A2
+#define KS884X_IADR2_P			0x04A4
+#define KS884X_IADR3_P			0x04A6
+#define KS884X_IADR4_P			0x04A8
+#define KS884X_IADR5_P			0x04AA
+
+#define KS884X_ACC_CTRL_SEL_OFFSET	KS884X_IACR_P
+#define KS884X_ACC_CTRL_INDEX_OFFSET	(KS884X_ACC_CTRL_SEL_OFFSET + 1)
+
+#define KS884X_ACC_DATA_0_OFFSET	KS884X_IADR4_P
+#define KS884X_ACC_DATA_1_OFFSET	(KS884X_ACC_DATA_0_OFFSET + 1)
+#define KS884X_ACC_DATA_2_OFFSET	KS884X_IADR5_P
+#define KS884X_ACC_DATA_3_OFFSET	(KS884X_ACC_DATA_2_OFFSET + 1)
+#define KS884X_ACC_DATA_4_OFFSET	KS884X_IADR2_P
+#define KS884X_ACC_DATA_5_OFFSET	(KS884X_ACC_DATA_4_OFFSET + 1)
+#define KS884X_ACC_DATA_6_OFFSET	KS884X_IADR3_P
+#define KS884X_ACC_DATA_7_OFFSET	(KS884X_ACC_DATA_6_OFFSET + 1)
+#define KS884X_ACC_DATA_8_OFFSET	KS884X_IADR1_P
+
+/* P1MBCR */
+#define KS884X_P1MBCR_P			0x04D0
+#define KS884X_P1MBSR_P			0x04D2
+#define KS884X_PHY1ILR_P		0x04D4
+#define KS884X_PHY1IHR_P		0x04D6
+#define KS884X_P1ANAR_P			0x04D8
+#define KS884X_P1ANLPR_P		0x04DA
+
+/* P2MBCR */
+#define KS884X_P2MBCR_P			0x04E0
+#define KS884X_P2MBSR_P			0x04E2
+#define KS884X_PHY2ILR_P		0x04E4
+#define KS884X_PHY2IHR_P		0x04E6
+#define KS884X_P2ANAR_P			0x04E8
+#define KS884X_P2ANLPR_P		0x04EA
+
+#define KS884X_PHY_1_CTRL_OFFSET	KS884X_P1MBCR_P
+#define PHY_CTRL_INTERVAL		(KS884X_P2MBCR_P - KS884X_P1MBCR_P)
+
+#define KS884X_PHY_CTRL_OFFSET		0x00
+
+/* Mode Control Register */
+#define PHY_REG_CTRL			0
+
+#define PHY_RESET			0x8000
+#define PHY_LOOPBACK			0x4000
+#define PHY_SPEED_100MBIT		0x2000
+#define PHY_AUTO_NEG_ENABLE		0x1000
+#define PHY_POWER_DOWN			0x0800
+#define PHY_MII_DISABLE			0x0400
+#define PHY_AUTO_NEG_RESTART		0x0200
+#define PHY_FULL_DUPLEX			0x0100
+#define PHY_COLLISION_TEST		0x0080
+#define PHY_HP_MDIX			0x0020
+#define PHY_FORCE_MDIX			0x0010
+#define PHY_AUTO_MDIX_DISABLE		0x0008
+#define PHY_REMOTE_FAULT_DISABLE	0x0004
+#define PHY_TRANSMIT_DISABLE		0x0002
+#define PHY_LED_DISABLE			0x0001
+
+#define KS884X_PHY_STATUS_OFFSET	0x02
+
+/* Mode Status Register */
+#define PHY_REG_STATUS			1
+
+#define PHY_100BT4_CAPABLE		0x8000
+#define PHY_100BTX_FD_CAPABLE		0x4000
+#define PHY_100BTX_CAPABLE		0x2000
+#define PHY_10BT_FD_CAPABLE		0x1000
+#define PHY_10BT_CAPABLE		0x0800
+#define PHY_MII_SUPPRESS_CAPABLE	0x0040
+#define PHY_AUTO_NEG_ACKNOWLEDGE	0x0020
+#define PHY_REMOTE_FAULT		0x0010
+#define PHY_AUTO_NEG_CAPABLE		0x0008
+#define PHY_LINK_STATUS			0x0004
+#define PHY_JABBER_DETECT		0x0002
+#define PHY_EXTENDED_CAPABILITY		0x0001
+
+#define KS884X_PHY_ID_1_OFFSET		0x04
+#define KS884X_PHY_ID_2_OFFSET		0x06
+
+/* PHY Identifier Registers */
+#define PHY_REG_ID_1			2
+#define PHY_REG_ID_2			3
+
+#define KS884X_PHY_AUTO_NEG_OFFSET	0x08
+
+/* Auto-Negotiation Advertisement Register */
+#define PHY_REG_AUTO_NEGOTIATION	4
+
+#define PHY_AUTO_NEG_NEXT_PAGE		0x8000
+#define PHY_AUTO_NEG_REMOTE_FAULT	0x2000
+/* Not supported. */
+#define PHY_AUTO_NEG_ASYM_PAUSE		0x0800
+#define PHY_AUTO_NEG_SYM_PAUSE		0x0400
+#define PHY_AUTO_NEG_100BT4		0x0200
+#define PHY_AUTO_NEG_100BTX_FD		0x0100
+#define PHY_AUTO_NEG_100BTX		0x0080
+#define PHY_AUTO_NEG_10BT_FD		0x0040
+#define PHY_AUTO_NEG_10BT		0x0020
+#define PHY_AUTO_NEG_SELECTOR		0x001F
+#define PHY_AUTO_NEG_802_3		0x0001
+
+#define PHY_AUTO_NEG_PAUSE  (PHY_AUTO_NEG_SYM_PAUSE | PHY_AUTO_NEG_ASYM_PAUSE)
+
+#define KS884X_PHY_REMOTE_CAP_OFFSET	0x0A
+
+/* Auto-Negotiation Link Partner Ability Register */
+#define PHY_REG_REMOTE_CAPABILITY	5
+
+#define PHY_REMOTE_NEXT_PAGE		0x8000
+#define PHY_REMOTE_ACKNOWLEDGE		0x4000
+#define PHY_REMOTE_REMOTE_FAULT		0x2000
+#define PHY_REMOTE_SYM_PAUSE		0x0400
+#define PHY_REMOTE_100BTX_FD		0x0100
+#define PHY_REMOTE_100BTX		0x0080
+#define PHY_REMOTE_10BT_FD		0x0040
+#define PHY_REMOTE_10BT			0x0020
+
+/* P1VCT */
+#define KS884X_P1VCT_P			0x04F0
+#define KS884X_P1PHYCTRL_P		0x04F2
+
+/* P2VCT */
+#define KS884X_P2VCT_P			0x04F4
+#define KS884X_P2PHYCTRL_P		0x04F6
+
+#define KS884X_PHY_SPECIAL_OFFSET	KS884X_P1VCT_P
+#define PHY_SPECIAL_INTERVAL		(KS884X_P2VCT_P - KS884X_P1VCT_P)
+
+#define KS884X_PHY_LINK_MD_OFFSET	0x00
+
+#define PHY_START_CABLE_DIAG		0x8000
+#define PHY_CABLE_DIAG_RESULT		0x6000
+#define PHY_CABLE_STAT_NORMAL		0x0000
+#define PHY_CABLE_STAT_OPEN		0x2000
+#define PHY_CABLE_STAT_SHORT		0x4000
+#define PHY_CABLE_STAT_FAILED		0x6000
+#define PHY_CABLE_10M_SHORT		0x1000
+#define PHY_CABLE_FAULT_COUNTER		0x01FF
+
+#define KS884X_PHY_PHY_CTRL_OFFSET	0x02
+
+#define PHY_STAT_REVERSED_POLARITY	0x0020
+#define PHY_STAT_MDIX			0x0010
+#define PHY_FORCE_LINK			0x0008
+#define PHY_POWER_SAVING_DISABLE	0x0004
+#define PHY_REMOTE_LOOPBACK		0x0002
+
+/* SIDER */
+#define KS884X_SIDER_P			0x0400
+#define KS884X_CHIP_ID_OFFSET		KS884X_SIDER_P
+#define KS884X_FAMILY_ID_OFFSET		(KS884X_CHIP_ID_OFFSET + 1)
+
+#define REG_FAMILY_ID			0x88
+
+#define REG_CHIP_ID_41			0x8810
+#define REG_CHIP_ID_42			0x8800
+
+#define KS884X_CHIP_ID_MASK_41		0xFF10
+#define KS884X_CHIP_ID_MASK		0xFFF0
+#define KS884X_CHIP_ID_SHIFT		4
+#define KS884X_REVISION_MASK		0x000E
+#define KS884X_REVISION_SHIFT		1
+#define KS8842_START			0x0001
+
+#define CHIP_IP_41_M			0x8810
+#define CHIP_IP_42_M			0x8800
+#define CHIP_IP_61_M			0x8890
+#define CHIP_IP_62_M			0x8880
+
+#define CHIP_IP_41_P			0x8850
+#define CHIP_IP_42_P			0x8840
+#define CHIP_IP_61_P			0x88D0
+#define CHIP_IP_62_P			0x88C0
+
+/* SGCR1 */
+#define KS8842_SGCR1_P			0x0402
+#define KS8842_SWITCH_CTRL_1_OFFSET	KS8842_SGCR1_P
+
+#define SWITCH_PASS_ALL			0x8000
+#define SWITCH_TX_FLOW_CTRL		0x2000
+#define SWITCH_RX_FLOW_CTRL		0x1000
+#define SWITCH_CHECK_LENGTH		0x0800
+#define SWITCH_AGING_ENABLE		0x0400
+#define SWITCH_FAST_AGING		0x0200
+#define SWITCH_AGGR_BACKOFF		0x0100
+#define SWITCH_PASS_PAUSE		0x0008
+#define SWITCH_LINK_AUTO_AGING		0x0001
+
+/* SGCR2 */
+#define KS8842_SGCR2_P			0x0404
+#define KS8842_SWITCH_CTRL_2_OFFSET	KS8842_SGCR2_P
+
+#define SWITCH_VLAN_ENABLE		0x8000
+#define SWITCH_IGMP_SNOOP		0x4000
+#define IPV6_MLD_SNOOP_ENABLE		0x2000
+#define IPV6_MLD_SNOOP_OPTION		0x1000
+#define PRIORITY_SCHEME_SELECT		0x0800
+#define SWITCH_MIRROR_RX_TX		0x0100
+#define UNICAST_VLAN_BOUNDARY		0x0080
+#define MULTICAST_STORM_DISABLE		0x0040
+#define SWITCH_BACK_PRESSURE		0x0020
+#define FAIR_FLOW_CTRL			0x0010
+#define NO_EXC_COLLISION_DROP		0x0008
+#define SWITCH_HUGE_PACKET		0x0004
+#define SWITCH_LEGAL_PACKET		0x0002
+#define SWITCH_BUF_RESERVE		0x0001
+
+/* SGCR3 */
+#define KS8842_SGCR3_P			0x0406
+#define KS8842_SWITCH_CTRL_3_OFFSET	KS8842_SGCR3_P
+
+#define BROADCAST_STORM_RATE_LO		0xFF00
+#define SWITCH_REPEATER			0x0080
+#define SWITCH_HALF_DUPLEX		0x0040
+#define SWITCH_FLOW_CTRL		0x0020
+#define SWITCH_10_MBIT			0x0010
+#define SWITCH_REPLACE_NULL_VID		0x0008
+#define BROADCAST_STORM_RATE_HI		0x0007
+
+#define BROADCAST_STORM_RATE		0x07FF
+
+/* SGCR4 */
+#define KS8842_SGCR4_P			0x0408
+
+/* SGCR5 */
+#define KS8842_SGCR5_P			0x040A
+#define KS8842_SWITCH_CTRL_5_OFFSET	KS8842_SGCR5_P
+
+#define LED_MODE			0x8200
+#define LED_SPEED_DUPLEX_ACT		0x0000
+#define LED_SPEED_DUPLEX_LINK_ACT	0x8000
+#define LED_DUPLEX_10_100		0x0200
+
+/* SGCR6 */
+#define KS8842_SGCR6_P			0x0410
+#define KS8842_SWITCH_CTRL_6_OFFSET	KS8842_SGCR6_P
+
+#define KS8842_PRIORITY_MASK		3
+#define KS8842_PRIORITY_SHIFT		2
+
+/* SGCR7 */
+#define KS8842_SGCR7_P			0x0412
+#define KS8842_SWITCH_CTRL_7_OFFSET	KS8842_SGCR7_P
+
+#define SWITCH_UNK_DEF_PORT_ENABLE	0x0008
+#define SWITCH_UNK_DEF_PORT_3		0x0004
+#define SWITCH_UNK_DEF_PORT_2		0x0002
+#define SWITCH_UNK_DEF_PORT_1		0x0001
+
+/* MACAR1 */
+#define KS8842_MACAR1_P			0x0470
+#define KS8842_MACAR2_P			0x0472
+#define KS8842_MACAR3_P			0x0474
+#define KS8842_MAC_ADDR_1_OFFSET	KS8842_MACAR1_P
+#define KS8842_MAC_ADDR_0_OFFSET	(KS8842_MAC_ADDR_1_OFFSET + 1)
+#define KS8842_MAC_ADDR_3_OFFSET	KS8842_MACAR2_P
+#define KS8842_MAC_ADDR_2_OFFSET	(KS8842_MAC_ADDR_3_OFFSET + 1)
+#define KS8842_MAC_ADDR_5_OFFSET	KS8842_MACAR3_P
+#define KS8842_MAC_ADDR_4_OFFSET	(KS8842_MAC_ADDR_5_OFFSET + 1)
+
+/* TOSR1 */
+#define KS8842_TOSR1_P			0x0480
+#define KS8842_TOSR2_P			0x0482
+#define KS8842_TOSR3_P			0x0484
+#define KS8842_TOSR4_P			0x0486
+#define KS8842_TOSR5_P			0x0488
+#define KS8842_TOSR6_P			0x048A
+#define KS8842_TOSR7_P			0x0490
+#define KS8842_TOSR8_P			0x0492
+#define KS8842_TOS_1_OFFSET		KS8842_TOSR1_P
+#define KS8842_TOS_2_OFFSET		KS8842_TOSR2_P
+#define KS8842_TOS_3_OFFSET		KS8842_TOSR3_P
+#define KS8842_TOS_4_OFFSET		KS8842_TOSR4_P
+#define KS8842_TOS_5_OFFSET		KS8842_TOSR5_P
+#define KS8842_TOS_6_OFFSET		KS8842_TOSR6_P
+
+#define KS8842_TOS_7_OFFSET		KS8842_TOSR7_P
+#define KS8842_TOS_8_OFFSET		KS8842_TOSR8_P
+
+/* P1CR1 */
+#define KS8842_P1CR1_P			0x0500
+#define KS8842_P1CR2_P			0x0502
+#define KS8842_P1VIDR_P			0x0504
+#define KS8842_P1CR3_P			0x0506
+#define KS8842_P1IRCR_P			0x0508
+#define KS8842_P1ERCR_P			0x050A
+#define KS884X_P1SCSLMD_P		0x0510
+#define KS884X_P1CR4_P			0x0512
+#define KS884X_P1SR_P			0x0514
+
+/* P2CR1 */
+#define KS8842_P2CR1_P			0x0520
+#define KS8842_P2CR2_P			0x0522
+#define KS8842_P2VIDR_P			0x0524
+#define KS8842_P2CR3_P			0x0526
+#define KS8842_P2IRCR_P			0x0528
+#define KS8842_P2ERCR_P			0x052A
+#define KS884X_P2SCSLMD_P		0x0530
+#define KS884X_P2CR4_P			0x0532
+#define KS884X_P2SR_P			0x0534
+
+/* P3CR1 */
+#define KS8842_P3CR1_P			0x0540
+#define KS8842_P3CR2_P			0x0542
+#define KS8842_P3VIDR_P			0x0544
+#define KS8842_P3CR3_P			0x0546
+#define KS8842_P3IRCR_P			0x0548
+#define KS8842_P3ERCR_P			0x054A
+
+#define KS8842_PORT_1_CTRL_1		KS8842_P1CR1_P
+#define KS8842_PORT_2_CTRL_1		KS8842_P2CR1_P
+#define KS8842_PORT_3_CTRL_1		KS8842_P3CR1_P
+
+#define PORT_CTRL_ADDR(port, addr)		\
+	(addr = KS8842_PORT_1_CTRL_1 + (port) *	\
+		(KS8842_PORT_2_CTRL_1 - KS8842_PORT_1_CTRL_1))
+
+#define KS8842_PORT_CTRL_1_OFFSET	0x00
+
+#define PORT_BROADCAST_STORM		0x0080
+#define PORT_DIFFSERV_ENABLE		0x0040
+#define PORT_802_1P_ENABLE		0x0020
+#define PORT_BASED_PRIORITY_MASK	0x0018
+#define PORT_BASED_PRIORITY_BASE	0x0003
+#define PORT_BASED_PRIORITY_SHIFT	3
+#define PORT_BASED_PRIORITY_0		0x0000
+#define PORT_BASED_PRIORITY_1		0x0008
+#define PORT_BASED_PRIORITY_2		0x0010
+#define PORT_BASED_PRIORITY_3		0x0018
+#define PORT_INSERT_TAG			0x0004
+#define PORT_REMOVE_TAG			0x0002
+#define PORT_PRIO_QUEUE_ENABLE		0x0001
+
+#define KS8842_PORT_CTRL_2_OFFSET	0x02
+
+#define PORT_INGRESS_VLAN_FILTER	0x4000
+#define PORT_DISCARD_NON_VID		0x2000
+#define PORT_FORCE_FLOW_CTRL		0x1000
+#define PORT_BACK_PRESSURE		0x0800
+#define PORT_TX_ENABLE			0x0400
+#define PORT_RX_ENABLE			0x0200
+#define PORT_LEARN_DISABLE		0x0100
+#define PORT_MIRROR_SNIFFER		0x0080
+#define PORT_MIRROR_RX			0x0040
+#define PORT_MIRROR_TX			0x0020
+#define PORT_USER_PRIORITY_CEILING	0x0008
+#define PORT_VLAN_MEMBERSHIP		0x0007
+
+#define KS8842_PORT_CTRL_VID_OFFSET	0x04
+
+#define PORT_DEFAULT_VID		0x0001
+
+#define KS8842_PORT_CTRL_3_OFFSET	0x06
+
+#define PORT_INGRESS_LIMIT_MODE		0x000C
+#define PORT_INGRESS_ALL		0x0000
+#define PORT_INGRESS_UNICAST		0x0004
+#define PORT_INGRESS_MULTICAST		0x0008
+#define PORT_INGRESS_BROADCAST		0x000C
+#define PORT_COUNT_IFG			0x0002
+#define PORT_COUNT_PREAMBLE		0x0001
+
+#define KS8842_PORT_IN_RATE_OFFSET	0x08
+#define KS8842_PORT_OUT_RATE_OFFSET	0x0A
+
+#define PORT_PRIORITY_RATE		0x0F
+#define PORT_PRIORITY_RATE_SHIFT	4
+
+#define KS884X_PORT_LINK_MD		0x10
+
+#define PORT_CABLE_10M_SHORT		0x8000
+#define PORT_CABLE_DIAG_RESULT		0x6000
+#define PORT_CABLE_STAT_NORMAL		0x0000
+#define PORT_CABLE_STAT_OPEN		0x2000
+#define PORT_CABLE_STAT_SHORT		0x4000
+#define PORT_CABLE_STAT_FAILED		0x6000
+#define PORT_START_CABLE_DIAG		0x1000
+#define PORT_FORCE_LINK			0x0800
+#define PORT_POWER_SAVING_DISABLE	0x0400
+#define PORT_PHY_REMOTE_LOOPBACK	0x0200
+#define PORT_CABLE_FAULT_COUNTER	0x01FF
+
+#define KS884X_PORT_CTRL_4_OFFSET	0x12
+
+#define PORT_LED_OFF			0x8000
+#define PORT_TX_DISABLE			0x4000
+#define PORT_AUTO_NEG_RESTART		0x2000
+#define PORT_REMOTE_FAULT_DISABLE	0x1000
+#define PORT_POWER_DOWN			0x0800
+#define PORT_AUTO_MDIX_DISABLE		0x0400
+#define PORT_FORCE_MDIX			0x0200
+#define PORT_LOOPBACK			0x0100
+#define PORT_AUTO_NEG_ENABLE		0x0080
+#define PORT_FORCE_100_MBIT		0x0040
+#define PORT_FORCE_FULL_DUPLEX		0x0020
+#define PORT_AUTO_NEG_SYM_PAUSE		0x0010
+#define PORT_AUTO_NEG_100BTX_FD		0x0008
+#define PORT_AUTO_NEG_100BTX		0x0004
+#define PORT_AUTO_NEG_10BT_FD		0x0002
+#define PORT_AUTO_NEG_10BT		0x0001
+
+#define KS884X_PORT_STATUS_OFFSET	0x14
+
+#define PORT_HP_MDIX			0x8000
+#define PORT_REVERSED_POLARITY		0x2000
+#define PORT_RX_FLOW_CTRL		0x0800
+#define PORT_TX_FLOW_CTRL		0x1000
+#define PORT_STATUS_SPEED_100MBIT	0x0400
+#define PORT_STATUS_FULL_DUPLEX		0x0200
+#define PORT_REMOTE_FAULT		0x0100
+#define PORT_MDIX_STATUS		0x0080
+#define PORT_AUTO_NEG_COMPLETE		0x0040
+#define PORT_STATUS_LINK_GOOD		0x0020
+#define PORT_REMOTE_SYM_PAUSE		0x0010
+#define PORT_REMOTE_100BTX_FD		0x0008
+#define PORT_REMOTE_100BTX		0x0004
+#define PORT_REMOTE_10BT_FD		0x0002
+#define PORT_REMOTE_10BT		0x0001
+
+/*
+#define STATIC_MAC_TABLE_ADDR		00-0000FFFF-FFFFFFFF
+#define STATIC_MAC_TABLE_FWD_PORTS	00-00070000-00000000
+#define STATIC_MAC_TABLE_VALID		00-00080000-00000000
+#define STATIC_MAC_TABLE_OVERRIDE	00-00100000-00000000
+#define STATIC_MAC_TABLE_USE_FID	00-00200000-00000000
+#define STATIC_MAC_TABLE_FID		00-03C00000-00000000
+*/
+
+#define STATIC_MAC_TABLE_ADDR		0x0000FFFF
+#define STATIC_MAC_TABLE_FWD_PORTS	0x00070000
+#define STATIC_MAC_TABLE_VALID		0x00080000
+#define STATIC_MAC_TABLE_OVERRIDE	0x00100000
+#define STATIC_MAC_TABLE_USE_FID	0x00200000
+#define STATIC_MAC_TABLE_FID		0x03C00000
+
+#define STATIC_MAC_FWD_PORTS_SHIFT	16
+#define STATIC_MAC_FID_SHIFT		22
+
+/*
+#define VLAN_TABLE_VID			00-00000000-00000FFF
+#define VLAN_TABLE_FID			00-00000000-0000F000
+#define VLAN_TABLE_MEMBERSHIP		00-00000000-00070000
+#define VLAN_TABLE_VALID		00-00000000-00080000
+*/
+
+#define VLAN_TABLE_VID			0x00000FFF
+#define VLAN_TABLE_FID			0x0000F000
+#define VLAN_TABLE_MEMBERSHIP		0x00070000
+#define VLAN_TABLE_VALID		0x00080000
+
+#define VLAN_TABLE_FID_SHIFT		12
+#define VLAN_TABLE_MEMBERSHIP_SHIFT	16
+
+/*
+#define DYNAMIC_MAC_TABLE_ADDR		00-0000FFFF-FFFFFFFF
+#define DYNAMIC_MAC_TABLE_FID		00-000F0000-00000000
+#define DYNAMIC_MAC_TABLE_SRC_PORT	00-00300000-00000000
+#define DYNAMIC_MAC_TABLE_TIMESTAMP	00-00C00000-00000000
+#define DYNAMIC_MAC_TABLE_ENTRIES	03-FF000000-00000000
+#define DYNAMIC_MAC_TABLE_MAC_EMPTY	04-00000000-00000000
+#define DYNAMIC_MAC_TABLE_RESERVED	78-00000000-00000000
+#define DYNAMIC_MAC_TABLE_NOT_READY	80-00000000-00000000
+*/
+
+#define DYNAMIC_MAC_TABLE_ADDR		0x0000FFFF
+#define DYNAMIC_MAC_TABLE_FID		0x000F0000
+#define DYNAMIC_MAC_TABLE_SRC_PORT	0x00300000
+#define DYNAMIC_MAC_TABLE_TIMESTAMP	0x00C00000
+#define DYNAMIC_MAC_TABLE_ENTRIES	0xFF000000
+
+#define DYNAMIC_MAC_TABLE_ENTRIES_H	0x03
+#define DYNAMIC_MAC_TABLE_MAC_EMPTY	0x04
+#define DYNAMIC_MAC_TABLE_RESERVED	0x78
+#define DYNAMIC_MAC_TABLE_NOT_READY	0x80
+
+#define DYNAMIC_MAC_FID_SHIFT		16
+#define DYNAMIC_MAC_SRC_PORT_SHIFT	20
+#define DYNAMIC_MAC_TIMESTAMP_SHIFT	22
+#define DYNAMIC_MAC_ENTRIES_SHIFT	24
+#define DYNAMIC_MAC_ENTRIES_H_SHIFT	8
+
+/*
+#define MIB_COUNTER_VALUE		00-00000000-3FFFFFFF
+#define MIB_COUNTER_VALID		00-00000000-40000000
+#define MIB_COUNTER_OVERFLOW		00-00000000-80000000
+*/
+
+#define MIB_COUNTER_VALUE		0x3FFFFFFF
+#define MIB_COUNTER_VALID		0x40000000
+#define MIB_COUNTER_OVERFLOW		0x80000000
+
+#define MIB_PACKET_DROPPED		0x0000FFFF
+
+#define KS_MIB_PACKET_DROPPED_TX_0	0x100
+#define KS_MIB_PACKET_DROPPED_TX_1	0x101
+#define KS_MIB_PACKET_DROPPED_TX	0x102
+#define KS_MIB_PACKET_DROPPED_RX_0	0x103
+#define KS_MIB_PACKET_DROPPED_RX_1	0x104
+#define KS_MIB_PACKET_DROPPED_RX	0x105
+
+/* Change default LED mode. */
+#define SET_DEFAULT_LED			LED_SPEED_DUPLEX_ACT
+
+#define MAC_ADDR_LEN			6
+#define MAC_ADDR_ORDER(i)		(MAC_ADDR_LEN - 1 - (i))
+
+#define MAX_ETHERNET_BODY_SIZE		1500
+#define ETHERNET_HEADER_SIZE		14
+
+#define MAX_ETHERNET_PACKET_SIZE	\
+	(MAX_ETHERNET_BODY_SIZE + ETHERNET_HEADER_SIZE)
+
+#define REGULAR_RX_BUF_SIZE		(MAX_ETHERNET_PACKET_SIZE + 4)
+#define MAX_RX_BUF_SIZE			(1912 + 4)
+
+#define ADDITIONAL_ENTRIES		16
+#define MAX_MULTICAST_LIST		32
+
+#define HW_MULTICAST_SIZE		8
+
+#define HW_TO_DEV_PORT(port)		(port - 1)
+
+enum {
+	media_connected,
+	media_disconnected
+};
+
+enum {
+	OID_COUNTER_UNKOWN,
+
+	OID_COUNTER_FIRST,
+
+	/* total transmit errors */
+	OID_COUNTER_XMIT_ERROR,
+
+	/* total receive errors */
+	OID_COUNTER_RCV_ERROR,
+
+	OID_COUNTER_LAST
+};
+
+/*
+ * Hardware descriptor definitions
+ */
+
+#define DESC_ALIGNMENT			16
+#define BUFFER_ALIGNMENT		8
+
+#define NUM_OF_RX_DESC			64
+#define NUM_OF_TX_DESC			64
+
+#define KS_DESC_RX_FRAME_LEN		0x000007FF
+#define KS_DESC_RX_FRAME_TYPE		0x00008000
+#define KS_DESC_RX_ERROR_CRC		0x00010000
+#define KS_DESC_RX_ERROR_RUNT		0x00020000
+#define KS_DESC_RX_ERROR_TOO_LONG	0x00040000
+#define KS_DESC_RX_ERROR_PHY		0x00080000
+#define KS884X_DESC_RX_PORT_MASK	0x00300000
+#define KS_DESC_RX_MULTICAST		0x01000000
+#define KS_DESC_RX_ERROR		0x02000000
+#define KS_DESC_RX_ERROR_CSUM_UDP	0x04000000
+#define KS_DESC_RX_ERROR_CSUM_TCP	0x08000000
+#define KS_DESC_RX_ERROR_CSUM_IP	0x10000000
+#define KS_DESC_RX_LAST			0x20000000
+#define KS_DESC_RX_FIRST		0x40000000
+#define KS_DESC_RX_ERROR_COND		\
+	(KS_DESC_RX_ERROR_CRC |		\
+	KS_DESC_RX_ERROR_RUNT |		\
+	KS_DESC_RX_ERROR_PHY |		\
+	KS_DESC_RX_ERROR_TOO_LONG)
+
+#define KS_DESC_HW_OWNED		0x80000000
+
+#define KS_DESC_BUF_SIZE		0x000007FF
+#define KS884X_DESC_TX_PORT_MASK	0x00300000
+#define KS_DESC_END_OF_RING		0x02000000
+#define KS_DESC_TX_CSUM_GEN_UDP		0x04000000
+#define KS_DESC_TX_CSUM_GEN_TCP		0x08000000
+#define KS_DESC_TX_CSUM_GEN_IP		0x10000000
+#define KS_DESC_TX_LAST			0x20000000
+#define KS_DESC_TX_FIRST		0x40000000
+#define KS_DESC_TX_INTERRUPT		0x80000000
+
+#define KS_DESC_PORT_SHIFT		20
+
+#define KS_DESC_RX_MASK			(KS_DESC_BUF_SIZE)
+
+#define KS_DESC_TX_MASK			\
+	(KS_DESC_TX_INTERRUPT |		\
+	KS_DESC_TX_FIRST |		\
+	KS_DESC_TX_LAST |		\
+	KS_DESC_TX_CSUM_GEN_IP |	\
+	KS_DESC_TX_CSUM_GEN_TCP |	\
+	KS_DESC_TX_CSUM_GEN_UDP |	\
+	KS_DESC_BUF_SIZE)
+
+struct ksz_desc_rx_stat {
+#ifdef __BIG_ENDIAN_BITFIELD
+	u32 hw_owned:1;
+	u32 first_desc:1;
+	u32 last_desc:1;
+	u32 csum_err_ip:1;
+	u32 csum_err_tcp:1;
+	u32 csum_err_udp:1;
+	u32 error:1;
+	u32 multicast:1;
+	u32 src_port:4;
+	u32 err_phy:1;
+	u32 err_too_long:1;
+	u32 err_runt:1;
+	u32 err_crc:1;
+	u32 frame_type:1;
+	u32 reserved1:4;
+	u32 frame_len:11;
+#else
+	u32 frame_len:11;
+	u32 reserved1:4;
+	u32 frame_type:1;
+	u32 err_crc:1;
+	u32 err_runt:1;
+	u32 err_too_long:1;
+	u32 err_phy:1;
+	u32 src_port:4;
+	u32 multicast:1;
+	u32 error:1;
+	u32 csum_err_udp:1;
+	u32 csum_err_tcp:1;
+	u32 csum_err_ip:1;
+	u32 last_desc:1;
+	u32 first_desc:1;
+	u32 hw_owned:1;
+#endif
+};
+
+struct ksz_desc_tx_stat {
+#ifdef __BIG_ENDIAN_BITFIELD
+	u32 hw_owned:1;
+	u32 reserved1:31;
+#else
+	u32 reserved1:31;
+	u32 hw_owned:1;
+#endif
+};
+
+struct ksz_desc_rx_buf {
+#ifdef __BIG_ENDIAN_BITFIELD
+	u32 reserved4:6;
+	u32 end_of_ring:1;
+	u32 reserved3:14;
+	u32 buf_size:11;
+#else
+	u32 buf_size:11;
+	u32 reserved3:14;
+	u32 end_of_ring:1;
+	u32 reserved4:6;
+#endif
+};
+
+struct ksz_desc_tx_buf {
+#ifdef __BIG_ENDIAN_BITFIELD
+	u32 intr:1;
+	u32 first_seg:1;
+	u32 last_seg:1;
+	u32 csum_gen_ip:1;
+	u32 csum_gen_tcp:1;
+	u32 csum_gen_udp:1;
+	u32 end_of_ring:1;
+	u32 reserved4:1;
+	u32 dest_port:4;
+	u32 reserved3:9;
+	u32 buf_size:11;
+#else
+	u32 buf_size:11;
+	u32 reserved3:9;
+	u32 dest_port:4;
+	u32 reserved4:1;
+	u32 end_of_ring:1;
+	u32 csum_gen_udp:1;
+	u32 csum_gen_tcp:1;
+	u32 csum_gen_ip:1;
+	u32 last_seg:1;
+	u32 first_seg:1;
+	u32 intr:1;
+#endif
+};
+
+union desc_stat {
+	struct ksz_desc_rx_stat rx;
+	struct ksz_desc_tx_stat tx;
+	u32 data;
+};
+
+union desc_buf {
+	struct ksz_desc_rx_buf rx;
+	struct ksz_desc_tx_buf tx;
+	u32 data;
+};
+
+/**
+ * struct ksz_hw_desc - Hardware descriptor data structure
+ * @ctrl:	Descriptor control value.
+ * @buf:	Descriptor buffer value.
+ * @addr:	Physical address of memory buffer.
+ * @next:	Pointer to next hardware descriptor.
+ */
+struct ksz_hw_desc {
+	union desc_stat ctrl;
+	union desc_buf buf;
+	u32 addr;
+	u32 next;
+};
+
+/**
+ * struct ksz_sw_desc - Software descriptor data structure
+ * @ctrl:	Descriptor control value.
+ * @buf:	Descriptor buffer value.
+ * @buf_size:	Current buffers size value in hardware descriptor.
+ */
+struct ksz_sw_desc {
+	union desc_stat ctrl;
+	union desc_buf buf;
+	u32 buf_size;
+};
+
+/**
+ * struct ksz_dma_buf - OS dependent DMA buffer data structure
+ * @skb:	Associated socket buffer.
+ * @dma:	Associated physical DMA address.
+ * len:		Actual len used.
+ */
+struct ksz_dma_buf {
+	struct sk_buff *skb;
+	dma_addr_t dma;
+	int len;
+};
+
+/**
+ * struct ksz_desc - Descriptor structure
+ * @phw:	Hardware descriptor pointer to uncached physical memory.
+ * @sw:		Cached memory to hold hardware descriptor values for
+ * 		manipulation.
+ * @dma_buf:	Operating system dependent data structure to hold physical
+ * 		memory buffer allocation information.
+ */
+struct ksz_desc {
+	struct ksz_hw_desc *phw;
+	struct ksz_sw_desc sw;
+	struct ksz_dma_buf dma_buf;
+};
+
+#define DMA_BUFFER(desc)  ((struct ksz_dma_buf *)(&(desc)->dma_buf))
+
+/**
+ * struct ksz_desc_info - Descriptor information data structure
+ * @ring:	First descriptor in the ring.
+ * @cur:	Current descriptor being manipulated.
+ * @ring_virt:	First hardware descriptor in the ring.
+ * @ring_phys:	The physical address of the first descriptor of the ring.
+ * @size:	Size of hardware descriptor.
+ * @alloc:	Number of descriptors allocated.
+ * @avail:	Number of descriptors available for use.
+ * @last:	Index for last descriptor released to hardware.
+ * @next:	Index for next descriptor available for use.
+ * @mask:	Mask for index wrapping.
+ */
+struct ksz_desc_info {
+	struct ksz_desc *ring;
+	struct ksz_desc *cur;
+	struct ksz_hw_desc *ring_virt;
+	u32 ring_phys;
+	int size;
+	int alloc;
+	int avail;
+	int last;
+	int next;
+	int mask;
+};
+
+/*
+ * KSZ8842 switch definitions
+ */
+
+enum {
+	TABLE_STATIC_MAC = 0,
+	TABLE_VLAN,
+	TABLE_DYNAMIC_MAC,
+	TABLE_MIB
+};
+
+#define LEARNED_MAC_TABLE_ENTRIES	1024
+#define STATIC_MAC_TABLE_ENTRIES	8
+
+/**
+ * struct ksz_mac_table - Static MAC table data structure
+ * @mac_addr:	MAC address to filter.
+ * @vid:	VID value.
+ * @fid:	FID value.
+ * @ports:	Port membership.
+ * @override:	Override setting.
+ * @use_fid:	FID use setting.
+ * @valid:	Valid setting indicating the entry is being used.
+ */
+struct ksz_mac_table {
+	u8 mac_addr[MAC_ADDR_LEN];
+	u16 vid;
+	u8 fid;
+	u8 ports;
+	u8 override:1;
+	u8 use_fid:1;
+	u8 valid:1;
+};
+
+#define VLAN_TABLE_ENTRIES		16
+
+/**
+ * struct ksz_vlan_table - VLAN table data structure
+ * @vid:	VID value.
+ * @fid:	FID value.
+ * @member:	Port membership.
+ */
+struct ksz_vlan_table {
+	u16 vid;
+	u8 fid;
+	u8 member;
+};
+
+#define DIFFSERV_ENTRIES		64
+#define PRIO_802_1P_ENTRIES		8
+#define PRIO_QUEUES			4
+
+#define SWITCH_PORT_NUM			2
+#define TOTAL_PORT_NUM			(SWITCH_PORT_NUM + 1)
+#define HOST_MASK			(1 << SWITCH_PORT_NUM)
+#define PORT_MASK			7
+
+#define MAIN_PORT			0
+#define OTHER_PORT			1
+#define HOST_PORT			SWITCH_PORT_NUM
+
+#define PORT_COUNTER_NUM		0x20
+#define TOTAL_PORT_COUNTER_NUM		(PORT_COUNTER_NUM + 2)
+
+#define MIB_COUNTER_RX_LO_PRIORITY	0x00
+#define MIB_COUNTER_RX_HI_PRIORITY	0x01
+#define MIB_COUNTER_RX_UNDERSIZE	0x02
+#define MIB_COUNTER_RX_FRAGMENT		0x03
+#define MIB_COUNTER_RX_OVERSIZE		0x04
+#define MIB_COUNTER_RX_JABBER		0x05
+#define MIB_COUNTER_RX_SYMBOL_ERR	0x06
+#define MIB_COUNTER_RX_CRC_ERR		0x07
+#define MIB_COUNTER_RX_ALIGNMENT_ERR	0x08
+#define MIB_COUNTER_RX_CTRL_8808	0x09
+#define MIB_COUNTER_RX_PAUSE		0x0A
+#define MIB_COUNTER_RX_BROADCAST	0x0B
+#define MIB_COUNTER_RX_MULTICAST	0x0C
+#define MIB_COUNTER_RX_UNICAST		0x0D
+#define MIB_COUNTER_RX_OCTET_64		0x0E
+#define MIB_COUNTER_RX_OCTET_65_127	0x0F
+#define MIB_COUNTER_RX_OCTET_128_255	0x10
+#define MIB_COUNTER_RX_OCTET_256_511	0x11
+#define MIB_COUNTER_RX_OCTET_512_1023	0x12
+#define MIB_COUNTER_RX_OCTET_1024_1522	0x13
+#define MIB_COUNTER_TX_LO_PRIORITY	0x14
+#define MIB_COUNTER_TX_HI_PRIORITY	0x15
+#define MIB_COUNTER_TX_LATE_COLLISION	0x16
+#define MIB_COUNTER_TX_PAUSE		0x17
+#define MIB_COUNTER_TX_BROADCAST	0x18
+#define MIB_COUNTER_TX_MULTICAST	0x19
+#define MIB_COUNTER_TX_UNICAST		0x1A
+#define MIB_COUNTER_TX_DEFERRED		0x1B
+#define MIB_COUNTER_TX_TOTAL_COLLISION	0x1C
+#define MIB_COUNTER_TX_EXCESS_COLLISION	0x1D
+#define MIB_COUNTER_TX_SINGLE_COLLISION	0x1E
+#define MIB_COUNTER_TX_MULTI_COLLISION	0x1F
+
+#define MIB_COUNTER_RX_DROPPED_PACKET	0x20
+#define MIB_COUNTER_TX_DROPPED_PACKET	0x21
+
+/**
+ * struct ksz_port_mib - Port MIB data structure
+ * @cnt_ptr:	Current pointer to MIB counter index.
+ * @link_down:	Indication the link has just gone down.
+ * @state:	Connection status of the port.
+ * @mib_start:	The starting counter index.  Some ports do not start at 0.
+ * @counter:	64-bit MIB counter value.
+ * @dropped:	Temporary buffer to remember last read packet dropped values.
+ *
+ * MIB counters needs to be read periodically so that counters do not get
+ * overflowed and give incorrect values.  A right balance is needed to
+ * satisfy this condition and not waste too much CPU time.
+ *
+ * It is pointless to read MIB counters when the port is disconnected.  The
+ * @state provides the connection status so that MIB counters are read only
+ * when the port is connected.  The @link_down indicates the port is just
+ * disconnected so that all MIB counters are read one last time to update the
+ * information.
+ */
+struct ksz_port_mib {
+	u8 cnt_ptr;
+	u8 link_down;
+	u8 state;
+	u8 mib_start;
+
+	u64 counter[TOTAL_PORT_COUNTER_NUM];
+	u32 dropped[2];
+};
+
+/**
+ * struct ksz_port_cfg - Port configuration data structure
+ * @vid:	VID value.
+ * @member:	Port membership.
+ * @port_prio:	Port priority.
+ * @rx_rate:	Receive priority rate.
+ * @tx_rate:	Transmit priority rate.
+ * @stp_state:	Current Spanning Tree Protocol state.
+ */
+struct ksz_port_cfg {
+	u16 vid;
+	u8 member;
+	u8 port_prio;
+	u32 rx_rate[PRIO_QUEUES];
+	u32 tx_rate[PRIO_QUEUES];
+	int stp_state;
+};
+
+/**
+ * struct ksz_switch - KSZ8842 switch data structure
+ * @mac_table:	MAC table entries information.
+ * @vlan_table:	VLAN table entries information.
+ * @port_cfg:	Port configuration information.
+ * @diffserv:	DiffServ priority settings.  Possible values from 6-bit of ToS
+ * 		(bit7 ~ bit2) field.
+ * @p_802_1p:	802.1P priority settings.  Possible values from 3-bit of 802.1p
+ * 		Tag priority field.
+ * @br_addr:	Bridge address.  Used for STP.
+ * @other_addr:	Other MAC address.  Used for multiple network device mode.
+ * @broad_per:	Broadcast storm percentage.
+ * @member:	Current port membership.  Used for STP.
+ */
+struct ksz_switch {
+	struct ksz_mac_table mac_table[STATIC_MAC_TABLE_ENTRIES];
+	struct ksz_vlan_table vlan_table[VLAN_TABLE_ENTRIES];
+	struct ksz_port_cfg port_cfg[TOTAL_PORT_NUM];
+
+	u8 diffserv[DIFFSERV_ENTRIES];
+	u8 p_802_1p[PRIO_802_1P_ENTRIES];
+
+	u8 br_addr[MAC_ADDR_LEN];
+	u8 other_addr[MAC_ADDR_LEN];
+
+	u8 broad_per;
+	u8 member;
+};
+
+#define TX_RATE_UNIT			10000
+
+/**
+ * struct ksz_port_info - Port information data structure
+ * @state:	Connection status of the port.
+ * @tx_rate:	Transmit rate divided by 10000 to get Mbit.
+ * @duplex:	Duplex mode.
+ * @advertised:	Advertised auto-negotiation setting.  Used to determine link.
+ * @partner:	Auto-negotiation partner setting.  Used to determine link.
+ * @port_id:	Port index to access actual hardware register.
+ * @pdev:	Pointer to OS dependent network device.
+ */
+struct ksz_port_info {
+	uint state;
+	uint tx_rate;
+	u8 duplex;
+	u8 advertised;
+	u8 partner;
+	u8 port_id;
+	void *pdev;
+};
+
+#define MAX_TX_HELD_SIZE		52000
+
+/* Hardware features and bug fixes. */
+#define LINK_INT_WORKING		(1 << 0)
+#define SMALL_PACKET_TX_BUG		(1 << 1)
+#define HALF_DUPLEX_SIGNAL_BUG		(1 << 2)
+#define IPV6_CSUM_GEN_HACK		(1 << 3)
+#define RX_HUGE_FRAME			(1 << 4)
+#define STP_SUPPORT			(1 << 8)
+
+/* Software overrides. */
+#define PAUSE_FLOW_CTRL			(1 << 0)
+#define FAST_AGING			(1 << 1)
+
+/**
+ * struct ksz_hw - KSZ884X hardware data structure
+ * @io:			Virtual address assigned.
+ * @ksz_switch:		Pointer to KSZ8842 switch.
+ * @port_info:		Port information.
+ * @port_mib:		Port MIB information.
+ * @dev_count:		Number of network devices this hardware supports.
+ * @dst_ports:		Destination ports in switch for transmission.
+ * @id:			Hardware ID.  Used for display only.
+ * @mib_cnt:		Number of MIB counters this hardware has.
+ * @mib_port_cnt:	Number of ports with MIB counters.
+ * @tx_cfg:		Cached transmit control settings.
+ * @rx_cfg:		Cached receive control settings.
+ * @intr_mask:		Current interrupt mask.
+ * @intr_set:		Current interrup set.
+ * @intr_blocked:	Interrupt blocked.
+ * @rx_desc_info:	Receive descriptor information.
+ * @tx_desc_info:	Transmit descriptor information.
+ * @tx_int_cnt:		Transmit interrupt count.  Used for TX optimization.
+ * @tx_int_mask:	Transmit interrupt mask.  Used for TX optimization.
+ * @tx_size:		Transmit data size.  Used for TX optimization.
+ * 			The maximum is defined by MAX_TX_HELD_SIZE.
+ * @perm_addr:		Permanent MAC address.
+ * @override_addr:	Overrided MAC address.
+ * @address:		Additional MAC address entries.
+ * @addr_list_size:	Additional MAC address list size.
+ * @mac_override:	Indication of MAC address overrided.
+ * @promiscuous:	Counter to keep track of promiscuous mode set.
+ * @all_multi:		Counter to keep track of all multicast mode set.
+ * @multi_list:		Multicast address entries.
+ * @multi_bits:		Cached multicast hash table settings.
+ * @multi_list_size:	Multicast address list size.
+ * @enabled:		Indication of hardware enabled.
+ * @rx_stop:		Indication of receive process stop.
+ * @features:		Hardware features to enable.
+ * @overrides:		Hardware features to override.
+ * @parent:		Pointer to parent, network device private structure.
+ */
+struct ksz_hw {
+	void __iomem *io;
+
+	struct ksz_switch *ksz_switch;
+	struct ksz_port_info port_info[SWITCH_PORT_NUM];
+	struct ksz_port_mib port_mib[TOTAL_PORT_NUM];
+	int dev_count;
+	int dst_ports;
+	int id;
+	int mib_cnt;
+	int mib_port_cnt;
+
+	u32 tx_cfg;
+	u32 rx_cfg;
+	u32 intr_mask;
+	u32 intr_set;
+	uint intr_blocked;
+
+	struct ksz_desc_info rx_desc_info;
+	struct ksz_desc_info tx_desc_info;
+
+	int tx_int_cnt;
+	int tx_int_mask;
+	int tx_size;
+
+	u8 perm_addr[MAC_ADDR_LEN];
+	u8 override_addr[MAC_ADDR_LEN];
+	u8 address[ADDITIONAL_ENTRIES][MAC_ADDR_LEN];
+	u8 addr_list_size;
+	u8 mac_override;
+	u8 promiscuous;
+	u8 all_multi;
+	u8 multi_list[MAX_MULTICAST_LIST][MAC_ADDR_LEN];
+	u8 multi_bits[HW_MULTICAST_SIZE];
+	u8 multi_list_size;
+
+	u8 enabled;
+	u8 rx_stop;
+	u8 reserved2[1];
+
+	uint features;
+	uint overrides;
+
+	void *parent;
+};
+
+enum {
+	PHY_NO_FLOW_CTRL,
+	PHY_FLOW_CTRL,
+	PHY_TX_ONLY,
+	PHY_RX_ONLY
+};
+
+/**
+ * struct ksz_port - Virtual port data structure
+ * @duplex:		Duplex mode setting.  1 for half duplex, 2 for full
+ * 			duplex, and 0 for auto, which normally results in full
+ * 			duplex.
+ * @speed:		Speed setting.  10 for 10 Mbit, 100 for 100 Mbit, and
+ * 			0 for auto, which normally results in 100 Mbit.
+ * @force_link:		Force link setting.  0 for auto-negotiation, and 1 for
+ * 			force.
+ * @flow_ctrl:		Flow control setting.  PHY_NO_FLOW_CTRL for no flow
+ * 			control, and PHY_FLOW_CTRL for flow control.
+ * 			PHY_TX_ONLY and PHY_RX_ONLY are not supported for 100
+ * 			Mbit PHY.
+ * @first_port:		Index of first port this port supports.
+ * @mib_port_cnt:	Number of ports with MIB counters.
+ * @port_cnt:		Number of ports this port supports.
+ * @counter:		Port statistics counter.
+ * @hw:			Pointer to hardware structure.
+ * @linked:		Pointer to port information linked to this port.
+ */
+struct ksz_port {
+	u8 duplex;
+	u8 speed;
+	u8 force_link;
+	u8 flow_ctrl;
+
+	int first_port;
+	int mib_port_cnt;
+	int port_cnt;
+	u64 counter[OID_COUNTER_LAST];
+
+	struct ksz_hw *hw;
+	struct ksz_port_info *linked;
+};
+
+/**
+ * struct ksz_timer_info - Timer information data structure
+ * @timer:	Kernel timer.
+ * @cnt:	Running timer counter.
+ * @max:	Number of times to run timer; -1 for infinity.
+ * @period:	Timer period in jiffies.
+ */
+struct ksz_timer_info {
+	struct timer_list timer;
+	int cnt;
+	int max;
+	int period;
+};
+
+/**
+ * struct ksz_shared_mem - OS dependent shared memory data structure
+ * @dma_addr:	Physical DMA address allocated.
+ * @alloc_size:	Allocation size.
+ * @phys:	Actual physical address used.
+ * @alloc_virt:	Virtual address allocated.
+ * @virt:	Actual virtual address used.
+ */
+struct ksz_shared_mem {
+	dma_addr_t dma_addr;
+	uint alloc_size;
+	uint phys;
+	u8 *alloc_virt;
+	u8 *virt;
+};
+
+/**
+ * struct ksz_counter_info - OS dependent counter information data structure
+ * @counter:	Wait queue to wakeup after counters are read.
+ * @time:	Next time in jiffies to read counter.
+ * @read:	Indication of counters read in full or not.
+ */
+struct ksz_counter_info {
+	wait_queue_head_t counter;
+	unsigned long time;
+	int read;
+};
+
+/**
+ * struct dev_info - Network device information data structure
+ * @dev:		Pointer to network device.
+ * @pdev:		Pointer to PCI device.
+ * @hw:			Hardware structure.
+ * @desc_pool:		Physical memory used for descriptor pool.
+ * @hwlock:		Spinlock to prevent hardware from accessing.
+ * @lock:		Mutex lock to prevent device from accessing.
+ * @dev_rcv:		Receive process function used.
+ * @last_skb:		Socket buffer allocated for descriptor rx fragments.
+ * @skb_index:		Buffer index for receiving fragments.
+ * @skb_len:		Buffer length for receiving fragments.
+ * @mib_read:		Workqueue to read MIB counters.
+ * @mib_timer_info:	Timer to read MIB counters.
+ * @counter:		Used for MIB reading.
+ * @mtu:		Current MTU used.  The default is REGULAR_RX_BUF_SIZE;
+ * 			the maximum is MAX_RX_BUF_SIZE.
+ * @opened:		Counter to keep track of device open.
+ * @rx_tasklet:		Receive processing tasklet.
+ * @tx_tasklet:		Transmit processing tasklet.
+ * @wol_enable:		Wake-on-LAN enable set by ethtool.
+ * @wol_support:	Wake-on-LAN support used by ethtool.
+ * @pme_wait:		Used for KSZ8841 power management.
+ */
+struct dev_info {
+	struct net_device *dev;
+	struct pci_dev *pdev;
+
+	struct ksz_hw hw;
+	struct ksz_shared_mem desc_pool;
+
+	spinlock_t hwlock;
+	struct mutex lock;
+
+	int (*dev_rcv)(struct dev_info *);
+
+	struct sk_buff *last_skb;
+	int skb_index;
+	int skb_len;
+
+	struct work_struct mib_read;
+	struct ksz_timer_info mib_timer_info;
+	struct ksz_counter_info counter[TOTAL_PORT_NUM];
+
+	int mtu;
+	int opened;
+
+	struct tasklet_struct rx_tasklet;
+	struct tasklet_struct tx_tasklet;
+
+	int wol_enable;
+	int wol_support;
+	unsigned long pme_wait;
+};
+
+/**
+ * struct dev_priv - Network device private data structure
+ * @adapter:		Adapter device information.
+ * @port:		Port information.
+ * @monitor_time_info:	Timer to monitor ports.
+ * @stats:		Network statistics.
+ * @proc_sem:		Semaphore for proc accessing.
+ * @id:			Device ID.
+ * @mii_if:		MII interface information.
+ * @advertising:	Temporary variable to store advertised settings.
+ * @msg_enable:		The message flags controlling driver output.
+ * @media_state:	The connection status of the device.
+ * @multicast:		The all multicast state of the device.
+ * @promiscuous:	The promiscuous state of the device.
+ */
+struct dev_priv {
+	struct dev_info *adapter;
+	struct ksz_port port;
+	struct ksz_timer_info monitor_timer_info;
+	struct net_device_stats stats;
+
+	struct semaphore proc_sem;
+	int id;
+
+	struct mii_if_info mii_if;
+	u32 advertising;
+
+	u32 msg_enable;
+	int media_state;
+	int multicast;
+	int promiscuous;
+};
+
+#define ks_info(_ks, _msg...) dev_info(&(_ks)->pdev->dev, _msg)
+#define ks_warn(_ks, _msg...) dev_warn(&(_ks)->pdev->dev, _msg)
+#define ks_dbg(_ks, _msg...) dev_dbg(&(_ks)->pdev->dev, _msg)
+#define ks_err(_ks, _msg...) dev_err(&(_ks)->pdev->dev, _msg)
+
+#define DRV_NAME		"KSZ884X PCI"
+#define DEVICE_NAME		"KSZ884x PCI"
+#define DRV_VERSION		"1.0.0"
+#define DRV_RELDATE		"Feb 8, 2010"
+
+static char version[] __devinitdata =
+	"Micrel " DEVICE_NAME " " DRV_VERSION " (" DRV_RELDATE ")";
+
+static u8 DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x88, 0x42, 0x01 };
+
+/*
+ * Interrupt processing primary routines
+ */
+
+static inline void hw_ack_intr(struct ksz_hw *hw, uint interrupt)
+{
+	writel(interrupt, hw->io + KS884X_INTERRUPTS_STATUS);
+}
+
+static inline void hw_dis_intr(struct ksz_hw *hw)
+{
+	hw->intr_blocked = hw->intr_mask;
+	writel(0, hw->io + KS884X_INTERRUPTS_ENABLE);
+	hw->intr_set = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
+}
+
+static inline void hw_set_intr(struct ksz_hw *hw, uint interrupt)
+{
+	hw->intr_set = interrupt;
+	writel(interrupt, hw->io + KS884X_INTERRUPTS_ENABLE);
+}
+
+static inline void hw_ena_intr(struct ksz_hw *hw)
+{
+	hw->intr_blocked = 0;
+	hw_set_intr(hw, hw->intr_mask);
+}
+
+static inline void hw_dis_intr_bit(struct ksz_hw *hw, uint bit)
+{
+	hw->intr_mask &= ~(bit);
+}
+
+static inline void hw_turn_off_intr(struct ksz_hw *hw, uint interrupt)
+{
+	u32 read_intr;
+
+	read_intr = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
+	hw->intr_set = read_intr & ~interrupt;
+	writel(hw->intr_set, hw->io + KS884X_INTERRUPTS_ENABLE);
+	hw_dis_intr_bit(hw, interrupt);
+}
+
+/**
+ * hw_turn_on_intr - turn on specified interrupts
+ * @hw: 	The hardware instance.
+ * @bit:	The interrupt bits to be on.
+ *
+ * This routine turns on the specified interrupts in the interrupt mask so that
+ * those interrupts will be enabled.
+ */
+static void hw_turn_on_intr(struct ksz_hw *hw, u32 bit)
+{
+	hw->intr_mask |= bit;
+
+	if (!hw->intr_blocked)
+		hw_set_intr(hw, hw->intr_mask);
+}
+
+static inline void hw_ena_intr_bit(struct ksz_hw *hw, uint interrupt)
+{
+	u32 read_intr;
+
+	read_intr = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
+	hw->intr_set = read_intr | interrupt;
+	writel(hw->intr_set, hw->io + KS884X_INTERRUPTS_ENABLE);
+}
+
+static inline void hw_read_intr(struct ksz_hw *hw, uint *status)
+{
+	*status = readl(hw->io + KS884X_INTERRUPTS_STATUS);
+	*status = *status & hw->intr_set;
+}
+
+static inline void hw_restore_intr(struct ksz_hw *hw, uint interrupt)
+{
+	if (interrupt)
+		hw_ena_intr(hw);
+}
+
+/**
+ * hw_block_intr - block hardware interrupts
+ *
+ * This function blocks all interrupts of the hardware and returns the current
+ * interrupt enable mask so that interrupts can be restored later.
+ *
+ * Return the current interrupt enable mask.
+ */
+static uint hw_block_intr(struct ksz_hw *hw)
+{
+	uint interrupt = 0;
+
+	if (!hw->intr_blocked) {
+		hw_dis_intr(hw);
+		interrupt = hw->intr_blocked;
+	}
+	return interrupt;
+}
+
+/*
+ * Hardware descriptor routines
+ */
+
+static inline void reset_desc(struct ksz_desc *desc, union desc_stat status)
+{
+	status.rx.hw_owned = 0;
+	desc->phw->ctrl.data = cpu_to_le32(status.data);
+}
+
+static inline void release_desc(struct ksz_desc *desc)
+{
+	desc->sw.ctrl.tx.hw_owned = 1;
+	if (desc->sw.buf_size != desc->sw.buf.data) {
+		desc->sw.buf_size = desc->sw.buf.data;
+		desc->phw->buf.data = cpu_to_le32(desc->sw.buf.data);
+	}
+	desc->phw->ctrl.data = cpu_to_le32(desc->sw.ctrl.data);
+}
+
+static void get_rx_pkt(struct ksz_desc_info *info, struct ksz_desc **desc)
+{
+	*desc = &info->ring[info->last];
+	info->last++;
+	info->last &= info->mask;
+	info->avail--;
+	(*desc)->sw.buf.data &= ~KS_DESC_RX_MASK;
+}
+
+static inline void set_rx_buf(struct ksz_desc *desc, u32 addr)
+{
+	desc->phw->addr = cpu_to_le32(addr);
+}
+
+static inline void set_rx_len(struct ksz_desc *desc, u32 len)
+{
+	desc->sw.buf.rx.buf_size = len;
+}
+
+static inline void get_tx_pkt(struct ksz_desc_info *info,
+	struct ksz_desc **desc)
+{
+	*desc = &info->ring[info->next];
+	info->next++;
+	info->next &= info->mask;
+	info->avail--;
+	(*desc)->sw.buf.data &= ~KS_DESC_TX_MASK;
+}
+
+static inline void set_tx_buf(struct ksz_desc *desc, u32 addr)
+{
+	desc->phw->addr = cpu_to_le32(addr);
+}
+
+static inline void set_tx_len(struct ksz_desc *desc, u32 len)
+{
+	desc->sw.buf.tx.buf_size = len;
+}
+
+/* Switch functions */
+
+#define TABLE_READ			0x10
+#define TABLE_SEL_SHIFT			2
+
+#define HW_DELAY(hw, reg)			\
+	do {					\
+		u16 dummy;			\
+		dummy = readw(hw->io + reg);	\
+	} while (0)
+
+/**
+ * sw_r_table - read 4 bytes of data from switch table
+ * @hw:		The hardware instance.
+ * @table:	The table selector.
+ * @addr:	The address of the table entry.
+ * @data:	Buffer to store the read data.
+ *
+ * This routine reads 4 bytes of data from the table of the switch.
+ * Hardware interrupts are disabled to minimize corruption of read data.
+ */
+static void sw_r_table(struct ksz_hw *hw, int table, u16 addr, u32 *data)
+{
+	u16 ctrl_addr;
+	uint interrupt;
+
+	ctrl_addr = (((table << TABLE_SEL_SHIFT) | TABLE_READ) << 8) | addr;
+
+	interrupt = hw_block_intr(hw);
+
+	writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
+	HW_DELAY(hw, KS884X_IACR_OFFSET);
+	*data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
+
+	hw_restore_intr(hw, interrupt);
+}
+
+/**
+ * sw_w_table_64 - write 8 bytes of data to the switch table
+ * @hw:		The hardware instance.
+ * @table:	The table selector.
+ * @addr:	The address of the table entry.
+ * @data_hi:	The high part of data to be written (bit63 ~ bit32).
+ * @data_lo:	The low part of data to be written (bit31 ~ bit0).
+ *
+ * This routine writes 8 bytes of data to the table of the switch.
+ * Hardware interrupts are disabled to minimize corruption of written data.
+ */
+static void sw_w_table_64(struct ksz_hw *hw, int table, u16 addr, u32 data_hi,
+	u32 data_lo)
+{
+	u16 ctrl_addr;
+	uint interrupt;
+
+	ctrl_addr = ((table << TABLE_SEL_SHIFT) << 8) | addr;
+
+	interrupt = hw_block_intr(hw);
+
+	writel(data_hi, hw->io + KS884X_ACC_DATA_4_OFFSET);
+	writel(data_lo, hw->io + KS884X_ACC_DATA_0_OFFSET);
+
+	writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
+	HW_DELAY(hw, KS884X_IACR_OFFSET);
+
+	hw_restore_intr(hw, interrupt);
+}
+
+/**
+ * sw_w_sta_mac_table - write to the static MAC table
+ * @hw: 	The hardware instance.
+ * @addr:	The address of the table entry.
+ * @mac_addr:	The MAC address.
+ * @ports:	The port members.
+ * @override:	The flag to override the port receive/transmit settings.
+ * @valid:	The flag to indicate entry is valid.
+ * @use_fid:	The flag to indicate the FID is valid.
+ * @fid:	The FID value.
+ *
+ * This routine writes an entry of the static MAC table of the switch.  It
+ * calls sw_w_table_64() to write the data.
+ */
+static void sw_w_sta_mac_table(struct ksz_hw *hw, u16 addr, u8 *mac_addr,
+	u8 ports, int override, int valid, int use_fid, u8 fid)
+{
+	u32 data_hi;
+	u32 data_lo;
+
+	data_lo = ((u32) mac_addr[2] << 24) |
+		((u32) mac_addr[3] << 16) |
+		((u32) mac_addr[4] << 8) | mac_addr[5];
+	data_hi = ((u32) mac_addr[0] << 8) | mac_addr[1];
+	data_hi |= (u32) ports << STATIC_MAC_FWD_PORTS_SHIFT;
+
+	if (override)
+		data_hi |= STATIC_MAC_TABLE_OVERRIDE;
+	if (use_fid) {
+		data_hi |= STATIC_MAC_TABLE_USE_FID;
+		data_hi |= (u32) fid << STATIC_MAC_FID_SHIFT;
+	}
+	if (valid)
+		data_hi |= STATIC_MAC_TABLE_VALID;
+
+	sw_w_table_64(hw, TABLE_STATIC_MAC, addr, data_hi, data_lo);
+}
+
+/**
+ * sw_r_vlan_table - read from the VLAN table
+ * @hw: 	The hardware instance.
+ * @addr:	The address of the table entry.
+ * @vid:	Buffer to store the VID.
+ * @fid:	Buffer to store the VID.
+ * @member:	Buffer to store the port membership.
+ *
+ * This function reads an entry of the VLAN table of the switch.  It calls
+ * sw_r_table() to get the data.
+ *
+ * Return 0 if the entry is valid; otherwise -1.
+ */
+static int sw_r_vlan_table(struct ksz_hw *hw, u16 addr, u16 *vid, u8 *fid,
+	u8 *member)
+{
+	u32 data;
+
+	sw_r_table(hw, TABLE_VLAN, addr, &data);
+	if (data & VLAN_TABLE_VALID) {
+		*vid = (u16)(data & VLAN_TABLE_VID);
+		*fid = (u8)((data & VLAN_TABLE_FID) >> VLAN_TABLE_FID_SHIFT);
+		*member = (u8)((data & VLAN_TABLE_MEMBERSHIP) >>
+			VLAN_TABLE_MEMBERSHIP_SHIFT);
+		return 0;
+	}
+	return -1;
+}
+
+/**
+ * port_r_mib_cnt - read MIB counter
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @addr:	The address of the counter.
+ * @cnt:	Buffer to store the counter.
+ *
+ * This routine reads a MIB counter of the port.
+ * Hardware interrupts are disabled to minimize corruption of read data.
+ */
+static void port_r_mib_cnt(struct ksz_hw *hw, int port, u16 addr, u64 *cnt)
+{
+	u32 data;
+	u16 ctrl_addr;
+	uint interrupt;
+	int timeout;
+
+	ctrl_addr = addr + PORT_COUNTER_NUM * port;
+
+	interrupt = hw_block_intr(hw);
+
+	ctrl_addr |= (((TABLE_MIB << TABLE_SEL_SHIFT) | TABLE_READ) << 8);
+	writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
+	HW_DELAY(hw, KS884X_IACR_OFFSET);
+
+	for (timeout = 100; timeout > 0; timeout--) {
+		data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
+
+		if (data & MIB_COUNTER_VALID) {
+			if (data & MIB_COUNTER_OVERFLOW)
+				*cnt += MIB_COUNTER_VALUE + 1;
+			*cnt += data & MIB_COUNTER_VALUE;
+			break;
+		}
+	}
+
+	hw_restore_intr(hw, interrupt);
+}
+
+/**
+ * port_r_mib_pkt - read dropped packet counts
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @cnt:	Buffer to store the receive and transmit dropped packet counts.
+ *
+ * This routine reads the dropped packet counts of the port.
+ * Hardware interrupts are disabled to minimize corruption of read data.
+ */
+static void port_r_mib_pkt(struct ksz_hw *hw, int port, u32 *last, u64 *cnt)
+{
+	u32 cur;
+	u32 data;
+	u16 ctrl_addr;
+	uint interrupt;
+	int index;
+
+	index = KS_MIB_PACKET_DROPPED_RX_0 + port;
+	do {
+		interrupt = hw_block_intr(hw);
+
+		ctrl_addr = (u16) index;
+		ctrl_addr |= (((TABLE_MIB << TABLE_SEL_SHIFT) | TABLE_READ)
+			<< 8);
+		writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
+		HW_DELAY(hw, KS884X_IACR_OFFSET);
+		data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
+
+		hw_restore_intr(hw, interrupt);
+
+		data &= MIB_PACKET_DROPPED;
+		cur = *last;
+		if (data != cur) {
+			*last = data;
+			if (data < cur)
+				data += MIB_PACKET_DROPPED + 1;
+			data -= cur;
+			*cnt += data;
+		}
+		++last;
+		++cnt;
+		index -= KS_MIB_PACKET_DROPPED_TX -
+			KS_MIB_PACKET_DROPPED_TX_0 + 1;
+	} while (index >= KS_MIB_PACKET_DROPPED_TX_0 + port);
+}
+
+/**
+ * port_r_cnt - read MIB counters periodically
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ *
+ * This routine is used to read the counters of the port periodically to avoid
+ * counter overflow.  The hardware should be acquired first before calling this
+ * routine.
+ *
+ * Return non-zero when not all counters not read.
+ */
+static int port_r_cnt(struct ksz_hw *hw, int port)
+{
+	struct ksz_port_mib *mib = &hw->port_mib[port];
+
+	if (mib->mib_start < PORT_COUNTER_NUM)
+		while (mib->cnt_ptr < PORT_COUNTER_NUM) {
+			port_r_mib_cnt(hw, port, mib->cnt_ptr,
+				&mib->counter[mib->cnt_ptr]);
+			++mib->cnt_ptr;
+		}
+	if (hw->mib_cnt > PORT_COUNTER_NUM)
+		port_r_mib_pkt(hw, port, mib->dropped,
+			&mib->counter[PORT_COUNTER_NUM]);
+	mib->cnt_ptr = 0;
+	return 0;
+}
+
+/**
+ * port_init_cnt - initialize MIB counter values
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ *
+ * This routine is used to initialize all counters to zero if the hardware
+ * cannot do it after reset.
+ */
+static void port_init_cnt(struct ksz_hw *hw, int port)
+{
+	struct ksz_port_mib *mib = &hw->port_mib[port];
+
+	mib->cnt_ptr = 0;
+	if (mib->mib_start < PORT_COUNTER_NUM)
+		do {
+			port_r_mib_cnt(hw, port, mib->cnt_ptr,
+				&mib->counter[mib->cnt_ptr]);
+			++mib->cnt_ptr;
+		} while (mib->cnt_ptr < PORT_COUNTER_NUM);
+	if (hw->mib_cnt > PORT_COUNTER_NUM)
+		port_r_mib_pkt(hw, port, mib->dropped,
+			&mib->counter[PORT_COUNTER_NUM]);
+	memset((void *) mib->counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM);
+	mib->cnt_ptr = 0;
+}
+
+/*
+ * Port functions
+ */
+
+/**
+ * port_chk - check port register bits
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @offset:	The offset of the port register.
+ * @bits:	The data bits to check.
+ *
+ * This function checks whether the specified bits of the port register are set
+ * or not.
+ *
+ * Return 0 if the bits are not set.
+ */
+static int port_chk(struct ksz_hw *hw, int port, int offset, u16 bits)
+{
+	u32 addr;
+	u16 data;
+
+	PORT_CTRL_ADDR(port, addr);
+	addr += offset;
+	data = readw(hw->io + addr);
+	return (data & bits) == bits;
+}
+
+/**
+ * port_cfg - set port register bits
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @offset:	The offset of the port register.
+ * @bits:	The data bits to set.
+ * @set:	The flag indicating whether the bits are to be set or not.
+ *
+ * This routine sets or resets the specified bits of the port register.
+ */
+static void port_cfg(struct ksz_hw *hw, int port, int offset, u16 bits,
+	int set)
+{
+	u32 addr;
+	u16 data;
+
+	PORT_CTRL_ADDR(port, addr);
+	addr += offset;
+	data = readw(hw->io + addr);
+	if (set)
+		data |= bits;
+	else
+		data &= ~bits;
+	writew(data, hw->io + addr);
+}
+
+/**
+ * port_chk_shift - check port bit
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @offset:	The offset of the register.
+ * @shift:	Number of bits to shift.
+ *
+ * This function checks whether the specified port is set in the register or
+ * not.
+ *
+ * Return 0 if the port is not set.
+ */
+static int port_chk_shift(struct ksz_hw *hw, int port, u32 addr, int shift)
+{
+	u16 data;
+	u16 bit = 1 << port;
+
+	data = readw(hw->io + addr);
+	data >>= shift;
+	return (data & bit) == bit;
+}
+
+/**
+ * port_cfg_shift - set port bit
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @offset:	The offset of the register.
+ * @shift:	Number of bits to shift.
+ * @set:	The flag indicating whether the port is to be set or not.
+ *
+ * This routine sets or resets the specified port in the register.
+ */
+static void port_cfg_shift(struct ksz_hw *hw, int port, u32 addr, int shift,
+	int set)
+{
+	u16 data;
+	u16 bits = 1 << port;
+
+	data = readw(hw->io + addr);
+	bits <<= shift;
+	if (set)
+		data |= bits;
+	else
+		data &= ~bits;
+	writew(data, hw->io + addr);
+}
+
+/**
+ * port_r8 - read byte from port register
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @offset:	The offset of the port register.
+ * @data:	Buffer to store the data.
+ *
+ * This routine reads a byte from the port register.
+ */
+static void port_r8(struct ksz_hw *hw, int port, int offset, u8 *data)
+{
+	u32 addr;
+
+	PORT_CTRL_ADDR(port, addr);
+	addr += offset;
+	*data = readb(hw->io + addr);
+}
+
+/**
+ * port_r16 - read word from port register.
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @offset:	The offset of the port register.
+ * @data:	Buffer to store the data.
+ *
+ * This routine reads a word from the port register.
+ */
+static void port_r16(struct ksz_hw *hw, int port, int offset, u16 *data)
+{
+	u32 addr;
+
+	PORT_CTRL_ADDR(port, addr);
+	addr += offset;
+	*data = readw(hw->io + addr);
+}
+
+/**
+ * port_w16 - write word to port register.
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @offset:	The offset of the port register.
+ * @data:	Data to write.
+ *
+ * This routine writes a word to the port register.
+ */
+static void port_w16(struct ksz_hw *hw, int port, int offset, u16 data)
+{
+	u32 addr;
+
+	PORT_CTRL_ADDR(port, addr);
+	addr += offset;
+	writew(data, hw->io + addr);
+}
+
+/**
+ * sw_chk - check switch register bits
+ * @hw: 	The hardware instance.
+ * @addr:	The address of the switch register.
+ * @bits:	The data bits to check.
+ *
+ * This function checks whether the specified bits of the switch register are
+ * set or not.
+ *
+ * Return 0 if the bits are not set.
+ */
+static int sw_chk(struct ksz_hw *hw, u32 addr, u16 bits)
+{
+	u16 data;
+
+	data = readw(hw->io + addr);
+	return (data & bits) == bits;
+}
+
+/**
+ * sw_cfg - set switch register bits
+ * @hw: 	The hardware instance.
+ * @addr:	The address of the switch register.
+ * @bits:	The data bits to set.
+ * @set:	The flag indicating whether the bits are to be set or not.
+ *
+ * This function sets or resets the specified bits of the switch register.
+ */
+static void sw_cfg(struct ksz_hw *hw, u32 addr, u16 bits, int set)
+{
+	u16 data;
+
+	data = readw(hw->io + addr);
+	if (set)
+		data |= bits;
+	else
+		data &= ~bits;
+	writew(data, hw->io + addr);
+}
+
+/* Bandwidth */
+
+static inline void port_cfg_broad_storm(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_BROADCAST_STORM, set);
+}
+
+static inline int port_chk_broad_storm(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_BROADCAST_STORM);
+}
+
+/* Driver set switch broadcast storm protection at 10% rate. */
+#define BROADCAST_STORM_PROTECTION_RATE	10
+
+/* 148,800 frames * 67 ms / 100 */
+#define BROADCAST_STORM_VALUE		9969
+
+/**
+ * sw_cfg_broad_storm - configure broadcast storm threshold
+ * @hw: 	The hardware instance.
+ * @percent:	Broadcast storm threshold in percent of transmit rate.
+ *
+ * This routine configures the broadcast storm threshold of the switch.
+ */
+static void sw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
+{
+	u16 data;
+	u32 value = ((u32) BROADCAST_STORM_VALUE * (u32) percent / 100);
+
+	if (value > BROADCAST_STORM_RATE)
+		value = BROADCAST_STORM_RATE;
+
+	data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
+	data &= ~(BROADCAST_STORM_RATE_LO | BROADCAST_STORM_RATE_HI);
+	data |= ((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8);
+	writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
+}
+
+/**
+ * sw_get_board_storm - get broadcast storm threshold
+ * @hw: 	The hardware instance.
+ * @percent:	Buffer to store the broadcast storm threshold percentage.
+ *
+ * This routine retrieves the broadcast storm threshold of the switch.
+ */
+static void sw_get_broad_storm(struct ksz_hw *hw, u8 *percent)
+{
+	int num;
+	u16 data;
+
+	data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
+	num = (data & BROADCAST_STORM_RATE_HI);
+	num <<= 8;
+	num |= (data & BROADCAST_STORM_RATE_LO) >> 8;
+	num = (num * 100 + BROADCAST_STORM_VALUE / 2) / BROADCAST_STORM_VALUE;
+	*percent = (u8) num;
+}
+
+/**
+ * sw_dis_broad_storm - disable broadstorm
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ *
+ * This routine disables the broadcast storm limit function of the switch.
+ */
+static void sw_dis_broad_storm(struct ksz_hw *hw, int port)
+{
+	port_cfg_broad_storm(hw, port, 0);
+}
+
+/**
+ * sw_ena_broad_storm - enable broadcast storm
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ *
+ * This routine enables the broadcast storm limit function of the switch.
+ */
+static void sw_ena_broad_storm(struct ksz_hw *hw, int port)
+{
+	sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
+	port_cfg_broad_storm(hw, port, 1);
+}
+
+/**
+ * sw_init_broad_storm - initialize broadcast storm
+ * @hw: 	The hardware instance.
+ *
+ * This routine initializes the broadcast storm limit function of the switch.
+ */
+static void sw_init_broad_storm(struct ksz_hw *hw)
+{
+	int port;
+
+	hw->ksz_switch->broad_per = 1;
+	sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
+	for (port = 0; port < TOTAL_PORT_NUM; port++)
+		sw_dis_broad_storm(hw, port);
+	sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, MULTICAST_STORM_DISABLE, 1);
+}
+
+/**
+ * hw_cfg_broad_storm - configure broadcast storm
+ * @hw: 	The hardware instance.
+ * @percent:	Broadcast storm threshold in percent of transmit rate.
+ *
+ * This routine configures the broadcast storm threshold of the switch.
+ * It is called by user functions.  The hardware should be acquired first.
+ */
+static void hw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
+{
+	if (percent > 100)
+		percent = 100;
+
+	sw_cfg_broad_storm(hw, percent);
+	sw_get_broad_storm(hw, &percent);
+	hw->ksz_switch->broad_per = percent;
+}
+
+/**
+ * sw_dis_prio_rate - disable switch priority rate
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ *
+ * This routine disables the priority rate function of the switch.
+ */
+static void sw_dis_prio_rate(struct ksz_hw *hw, int port)
+{
+	u32 addr;
+
+	PORT_CTRL_ADDR(port, addr);
+	addr += KS8842_PORT_IN_RATE_OFFSET;
+	writel(0, hw->io + addr);
+}
+
+/**
+ * sw_init_prio_rate - initialize switch prioirty rate
+ * @hw: 	The hardware instance.
+ *
+ * This routine initializes the priority rate function of the switch.
+ */
+static void sw_init_prio_rate(struct ksz_hw *hw)
+{
+	int port;
+	int prio;
+	struct ksz_switch *sw = hw->ksz_switch;
+
+	for (port = 0; port < TOTAL_PORT_NUM; port++) {
+		for (prio = 0; prio < PRIO_QUEUES; prio++) {
+			sw->port_cfg[port].rx_rate[prio] =
+			sw->port_cfg[port].tx_rate[prio] = 0;
+		}
+		sw_dis_prio_rate(hw, port);
+	}
+}
+
+/* Communication */
+
+static inline void port_cfg_back_pressure(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_BACK_PRESSURE, set);
+}
+
+static inline void port_cfg_force_flow_ctrl(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_FORCE_FLOW_CTRL, set);
+}
+
+static inline int port_chk_back_pressure(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_BACK_PRESSURE);
+}
+
+static inline int port_chk_force_flow_ctrl(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_FORCE_FLOW_CTRL);
+}
+
+/* Spanning Tree */
+
+static inline void port_cfg_dis_learn(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_LEARN_DISABLE, set);
+}
+
+static inline void port_cfg_rx(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_RX_ENABLE, set);
+}
+
+static inline void port_cfg_tx(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_TX_ENABLE, set);
+}
+
+static inline void sw_cfg_fast_aging(struct ksz_hw *hw, int set)
+{
+	sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET, SWITCH_FAST_AGING, set);
+}
+
+static inline void sw_flush_dyn_mac_table(struct ksz_hw *hw)
+{
+	if (!(hw->overrides & FAST_AGING)) {
+		sw_cfg_fast_aging(hw, 1);
+		mdelay(1);
+		sw_cfg_fast_aging(hw, 0);
+	}
+}
+
+/* VLAN */
+
+static inline void port_cfg_ins_tag(struct ksz_hw *hw, int p, int insert)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_INSERT_TAG, insert);
+}
+
+static inline void port_cfg_rmv_tag(struct ksz_hw *hw, int p, int remove)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_REMOVE_TAG, remove);
+}
+
+static inline int port_chk_ins_tag(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_INSERT_TAG);
+}
+
+static inline int port_chk_rmv_tag(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_REMOVE_TAG);
+}
+
+static inline void port_cfg_dis_non_vid(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_DISCARD_NON_VID, set);
+}
+
+static inline void port_cfg_in_filter(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_INGRESS_VLAN_FILTER, set);
+}
+
+static inline int port_chk_dis_non_vid(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_DISCARD_NON_VID);
+}
+
+static inline int port_chk_in_filter(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_INGRESS_VLAN_FILTER);
+}
+
+/* Mirroring */
+
+static inline void port_cfg_mirror_sniffer(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_SNIFFER, set);
+}
+
+static inline void port_cfg_mirror_rx(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_RX, set);
+}
+
+static inline void port_cfg_mirror_tx(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_TX, set);
+}
+
+static inline void sw_cfg_mirror_rx_tx(struct ksz_hw *hw, int set)
+{
+	sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, SWITCH_MIRROR_RX_TX, set);
+}
+
+static void sw_init_mirror(struct ksz_hw *hw)
+{
+	int port;
+
+	for (port = 0; port < TOTAL_PORT_NUM; port++) {
+		port_cfg_mirror_sniffer(hw, port, 0);
+		port_cfg_mirror_rx(hw, port, 0);
+		port_cfg_mirror_tx(hw, port, 0);
+	}
+	sw_cfg_mirror_rx_tx(hw, 0);
+}
+
+static inline void sw_cfg_unk_def_deliver(struct ksz_hw *hw, int set)
+{
+	sw_cfg(hw, KS8842_SWITCH_CTRL_7_OFFSET,
+		SWITCH_UNK_DEF_PORT_ENABLE, set);
+}
+
+static inline int sw_cfg_chk_unk_def_deliver(struct ksz_hw *hw)
+{
+	return sw_chk(hw, KS8842_SWITCH_CTRL_7_OFFSET,
+		SWITCH_UNK_DEF_PORT_ENABLE);
+}
+
+static inline void sw_cfg_unk_def_port(struct ksz_hw *hw, int port, int set)
+{
+	port_cfg_shift(hw, port, KS8842_SWITCH_CTRL_7_OFFSET, 0, set);
+}
+
+static inline int sw_chk_unk_def_port(struct ksz_hw *hw, int port)
+{
+	return port_chk_shift(hw, port, KS8842_SWITCH_CTRL_7_OFFSET, 0);
+}
+
+/* Priority */
+
+static inline void port_cfg_diffserv(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_DIFFSERV_ENABLE, set);
+}
+
+static inline void port_cfg_802_1p(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_802_1P_ENABLE, set);
+}
+
+static inline void port_cfg_replace_vid(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_USER_PRIORITY_CEILING, set);
+}
+
+static inline void port_cfg_prio(struct ksz_hw *hw, int p, int set)
+{
+	port_cfg(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_PRIO_QUEUE_ENABLE, set);
+}
+
+static inline int port_chk_diffserv(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_DIFFSERV_ENABLE);
+}
+
+static inline int port_chk_802_1p(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_802_1P_ENABLE);
+}
+
+static inline int port_chk_replace_vid(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_2_OFFSET, PORT_USER_PRIORITY_CEILING);
+}
+
+static inline int port_chk_prio(struct ksz_hw *hw, int p)
+{
+	return port_chk(hw, p,
+		KS8842_PORT_CTRL_1_OFFSET, PORT_PRIO_QUEUE_ENABLE);
+}
+
+/**
+ * sw_dis_diffserv - disable switch DiffServ priority
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ *
+ * This routine disables the DiffServ priority function of the switch.
+ */
+static void sw_dis_diffserv(struct ksz_hw *hw, int port)
+{
+	port_cfg_diffserv(hw, port, 0);
+}
+
+/**
+ * sw_dis_802_1p - disable switch 802.1p priority
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ *
+ * This routine disables the 802.1p priority function of the switch.
+ */
+static void sw_dis_802_1p(struct ksz_hw *hw, int port)
+{
+	port_cfg_802_1p(hw, port, 0);
+}
+
+/**
+ * sw_cfg_replace_null_vid -
+ * @hw: 	The hardware instance.
+ * @set:	The flag to disable or enable.
+ *
+ */
+static void sw_cfg_replace_null_vid(struct ksz_hw *hw, int set)
+{
+	sw_cfg(hw, KS8842_SWITCH_CTRL_3_OFFSET, SWITCH_REPLACE_NULL_VID, set);
+}
+
+/**
+ * sw_cfg_replace_vid - enable switch 802.10 priority re-mapping
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @set:	The flag to disable or enable.
+ *
+ * This routine enables the 802.1p priority re-mapping function of the switch.
+ * That allows 802.1p priority field to be replaced with the port's default
+ * tag's priority value if the ingress packet's 802.1p priority has a higher
+ * priority than port's default tag's priority.
+ */
+static void sw_cfg_replace_vid(struct ksz_hw *hw, int port, int set)
+{
+	port_cfg_replace_vid(hw, port, set);
+}
+
+/**
+ * sw_cfg_port_based - configure switch port based priority
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @prio:	The priority to set.
+ *
+ * This routine configures the port based priority of the switch.
+ */
+static void sw_cfg_port_based(struct ksz_hw *hw, int port, u8 prio)
+{
+	u16 data;
+
+	if (prio > PORT_BASED_PRIORITY_BASE)
+		prio = PORT_BASED_PRIORITY_BASE;
+
+	hw->ksz_switch->port_cfg[port].port_prio = prio;
+
+	port_r16(hw, port, KS8842_PORT_CTRL_1_OFFSET, &data);
+	data &= ~PORT_BASED_PRIORITY_MASK;
+	data |= prio << PORT_BASED_PRIORITY_SHIFT;
+	port_w16(hw, port, KS8842_PORT_CTRL_1_OFFSET, data);
+}
+
+/**
+ * sw_dis_multi_queue - disable transmit multiple queues
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ *
+ * This routine disables the transmit multiple queues selection of the switch
+ * port.  Only single transmit queue on the port.
+ */
+static void sw_dis_multi_queue(struct ksz_hw *hw, int port)
+{
+	port_cfg_prio(hw, port, 0);
+}
+
+/**
+ * sw_init_prio - initialize switch priority
+ * @hw: 	The hardware instance.
+ *
+ * This routine initializes the switch QoS priority functions.
+ */
+static void sw_init_prio(struct ksz_hw *hw)
+{
+	int port;
+	int tos;
+	struct ksz_switch *sw = hw->ksz_switch;
+
+	/*
+	 * Init all the 802.1p tag priority value to be assigned to different
+	 * priority queue.
+	 */
+	sw->p_802_1p[0] = 0;
+	sw->p_802_1p[1] = 0;
+	sw->p_802_1p[2] = 1;
+	sw->p_802_1p[3] = 1;
+	sw->p_802_1p[4] = 2;
+	sw->p_802_1p[5] = 2;
+	sw->p_802_1p[6] = 3;
+	sw->p_802_1p[7] = 3;
+
+	/*
+	 * Init all the DiffServ priority value to be assigned to priority
+	 * queue 0.
+	 */
+	for (tos = 0; tos < DIFFSERV_ENTRIES; tos++)
+		sw->diffserv[tos] = 0;
+
+	/* All QoS functions disabled. */
+	for (port = 0; port < TOTAL_PORT_NUM; port++) {
+		sw_dis_multi_queue(hw, port);
+		sw_dis_diffserv(hw, port);
+		sw_dis_802_1p(hw, port);
+		sw_cfg_replace_vid(hw, port, 0);
+
+		sw->port_cfg[port].port_prio = 0;
+		sw_cfg_port_based(hw, port, sw->port_cfg[port].port_prio);
+	}
+	sw_cfg_replace_null_vid(hw, 0);
+}
+
+/**
+ * port_get_def_vid - get port default VID.
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @vid:	Buffer to store the VID.
+ *
+ * This routine retrieves the default VID of the port.
+ */
+static void port_get_def_vid(struct ksz_hw *hw, int port, u16 *vid)
+{
+	u32 addr;
+
+	PORT_CTRL_ADDR(port, addr);
+	addr += KS8842_PORT_CTRL_VID_OFFSET;
+	*vid = readw(hw->io + addr);
+}
+
+/**
+ * sw_init_vlan - initialize switch VLAN
+ * @hw: 	The hardware instance.
+ *
+ * This routine initializes the VLAN function of the switch.
+ */
+static void sw_init_vlan(struct ksz_hw *hw)
+{
+	int port;
+	int entry;
+	struct ksz_switch *sw = hw->ksz_switch;
+
+	/* Read 16 VLAN entries from device's VLAN table. */
+	for (entry = 0; entry < VLAN_TABLE_ENTRIES; entry++) {
+		sw_r_vlan_table(hw, entry,
+			&sw->vlan_table[entry].vid,
+			&sw->vlan_table[entry].fid,
+			&sw->vlan_table[entry].member);
+	}
+
+	for (port = 0; port < TOTAL_PORT_NUM; port++) {
+		port_get_def_vid(hw, port, &sw->port_cfg[port].vid);
+		sw->port_cfg[port].member = PORT_MASK;
+	}
+}
+
+/**
+ * sw_cfg_port_base_vlan - configure port-based VLAN membership
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @member:	The port-based VLAN membership.
+ *
+ * This routine configures the port-based VLAN membership of the port.
+ */
+static void sw_cfg_port_base_vlan(struct ksz_hw *hw, int port, u8 member)
+{
+	u32 addr;
+	u8 data;
+
+	PORT_CTRL_ADDR(port, addr);
+	addr += KS8842_PORT_CTRL_2_OFFSET;
+
+	data = readb(hw->io + addr);
+	data &= ~PORT_VLAN_MEMBERSHIP;
+	data |= (member & PORT_MASK);
+	writeb(data, hw->io + addr);
+
+	hw->ksz_switch->port_cfg[port].member = member;
+}
+
+/**
+ * sw_get_addr - get the switch MAC address.
+ * @hw: 	The hardware instance.
+ * @mac_addr:	Buffer to store the MAC address.
+ *
+ * This function retrieves the MAC address of the switch.
+ */
+static inline void sw_get_addr(struct ksz_hw *hw, u8 *mac_addr)
+{
+	int i;
+
+	for (i = 0; i < 6; i += 2) {
+		mac_addr[i] = readb(hw->io + KS8842_MAC_ADDR_0_OFFSET + i);
+		mac_addr[1 + i] = readb(hw->io + KS8842_MAC_ADDR_1_OFFSET + i);
+	}
+}
+
+/**
+ * sw_set_addr - configure switch MAC address
+ * @hw: 	The hardware instance.
+ * @mac_addr:	The MAC address.
+ *
+ * This function configures the MAC address of the switch.
+ */
+static void sw_set_addr(struct ksz_hw *hw, u8 *mac_addr)
+{
+	int i;
+
+	for (i = 0; i < 6; i += 2) {
+		writeb(mac_addr[i], hw->io + KS8842_MAC_ADDR_0_OFFSET + i);
+		writeb(mac_addr[1 + i], hw->io + KS8842_MAC_ADDR_1_OFFSET + i);
+	}
+}
+
+/**
+ * sw_set_global_ctrl - set switch global control
+ * @hw: 	The hardware instance.
+ *
+ * This routine sets the global control of the switch function.
+ */
+static void sw_set_global_ctrl(struct ksz_hw *hw)
+{
+	u16 data;
+
+	/* Enable switch MII flow control. */
+	data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
+	data |= SWITCH_FLOW_CTRL;
+	writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
+
+	data = readw(hw->io + KS8842_SWITCH_CTRL_1_OFFSET);
+
+	/* Enable aggressive back off algorithm in half duplex mode. */
+	data |= SWITCH_AGGR_BACKOFF;
+
+	/* Enable automatic fast aging when link changed detected. */
+	data |= SWITCH_AGING_ENABLE;
+	data |= SWITCH_LINK_AUTO_AGING;
+
+	if (hw->overrides & FAST_AGING)
+		data |= SWITCH_FAST_AGING;
+	else
+		data &= ~SWITCH_FAST_AGING;
+	writew(data, hw->io + KS8842_SWITCH_CTRL_1_OFFSET);
+
+	data = readw(hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
+
+	/* Enable no excessive collision drop. */
+	data |= NO_EXC_COLLISION_DROP;
+	writew(data, hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
+}
+
+enum {
+	STP_STATE_DISABLED = 0,
+	STP_STATE_LISTENING,
+	STP_STATE_LEARNING,
+	STP_STATE_FORWARDING,
+	STP_STATE_BLOCKED,
+	STP_STATE_SIMPLE
+};
+
+/**
+ * port_set_stp_state - configure port spanning tree state
+ * @hw: 	The hardware instance.
+ * @port:	The port index.
+ * @state:	The spanning tree state.
+ *
+ * This routine configures the spanning tree state of the port.
+ */
+static void port_set_stp_state(struct ksz_hw *hw, int port, int state)
+{
+	u16 data;
+
+	port_r16(hw, port, KS8842_PORT_CTRL_2_OFFSET, &data);
+	switch (state) {
+	case STP_STATE_DISABLED:
+		data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
+		data |= PORT_LEARN_DISABLE;
+		break;
+	case STP_STATE_LISTENING:
+/*
+ * No need to turn on transmit because of port direct mode.
+ * Turning on receive is required if static MAC table is not setup.
+ */
+		data &= ~PORT_TX_ENABLE;
+		data |= PORT_RX_ENABLE;
+		data |= PORT_LEARN_DISABLE;
+		break;
+	case STP_STATE_LEARNING:
+		data &= ~PORT_TX_ENABLE;
+		data |= PORT_RX_ENABLE;
+		data &= ~PORT_LEARN_DISABLE;
+		break;
+	case STP_STATE_FORWARDING:
+		data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
+		data &= ~PORT_LEARN_DISABLE;
+		break;
+	case STP_STATE_BLOCKED:
+/*
+ * Need to setup static MAC table with override to keep receiving BPDU
+ * messages.  See sw_init_stp routine.
+ */
+		data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
+		data |= PORT_LEARN_DISABLE;
+		break;
+	case STP_STATE_SIMPLE:
+		data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
+		data |= PORT_LEARN_DISABLE;
+		break;
+	}
+	port_w16(hw, port, KS8842_PORT_CTRL_2_OFFSET, data);
+	hw->ksz_switch->port_cfg[port].stp_state = state;
+}
+
+#define STP_ENTRY			0
+#define BROADCAST_ENTRY			1
+#define BRIDGE_ADDR_ENTRY		2
+#define IPV6_ADDR_ENTRY			3
+
+/**
+ * sw_clr_sta_mac_table - clear static MAC table
+ * @hw: 	The hardware instance.
+ *
+ * This routine clears the static MAC table.
+ */
+static void sw_clr_sta_mac_table(struct ksz_hw *hw)
+{
+	struct ksz_mac_table *entry;
+	int i;
+
+	for (i = 0; i < STATIC_MAC_TABLE_ENTRIES; i++) {
+		entry = &hw->ksz_switch->mac_table[i];
+		sw_w_sta_mac_table(hw, i,
+			entry->mac_addr, entry->ports,
+			entry->override, 0,
+			entry->use_fid, entry->fid);
+	}
+}
+
+/**
+ * sw_init_stp - initialize switch spanning tree support
+ * @hw: 	The hardware instance.
+ *
+ * This routine initializes the spanning tree support of the switch.
+ */
+static void sw_init_stp(struct ksz_hw *hw)
+{
+	struct ksz_mac_table *entry;
+
+	entry = &hw->ksz_switch->mac_table[STP_ENTRY];
+	entry->mac_addr[0] = 0x01;
+	entry->mac_addr[1] = 0x80;
+	entry->mac_addr[2] = 0xC2;
+	entry->mac_addr[3] = 0x00;
+	entry->mac_addr[4] = 0x00;
+	entry->mac_addr[5] = 0x00;
+	entry->ports = HOST_MASK;
+	entry->override = 1;
+	entry->valid = 1;
+	sw_w_sta_mac_table(hw, STP_ENTRY,
+		entry->mac_addr, entry->ports,
+		entry->override, entry->valid,
+		entry->use_fid, entry->fid);
+}
+
+/**
+ * sw_block_addr - block certain packets from the host port
+ * @hw: 	The hardware instance.
+ *
+ * This routine blocks certain packets from reaching to the host port.
+ */
+static void sw_block_addr(struct ksz_hw *hw)
+{
+	struct ksz_mac_table *entry;
+	int i;
+
+	for (i = BROADCAST_ENTRY; i <= IPV6_ADDR_ENTRY; i++) {
+		entry = &hw->ksz_switch->mac_table[i];
+		entry->valid = 0;
+		sw_w_sta_mac_table(hw, i,
+			entry->mac_addr, entry->ports,
+			entry->override, entry->valid,
+			entry->use_fid, entry->fid);
+	}
+}
+
+#define PHY_LINK_SUPPORT		\
+	(PHY_AUTO_NEG_ASYM_PAUSE |	\
+	PHY_AUTO_NEG_SYM_PAUSE |	\
+	PHY_AUTO_NEG_100BT4 |		\
+	PHY_AUTO_NEG_100BTX_FD |	\
+	PHY_AUTO_NEG_100BTX |		\
+	PHY_AUTO_NEG_10BT_FD |		\
+	PHY_AUTO_NEG_10BT)
+
+static inline void hw_r_phy_ctrl(struct ksz_hw *hw, int phy, u16 *data)
+{
+	*data = readw(hw->io + phy + KS884X_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_w_phy_ctrl(struct ksz_hw *hw, int phy, u16 data)
+{
+	writew(data, hw->io + phy + KS884X_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_r_phy_link_stat(struct ksz_hw *hw, int phy, u16 *data)
+{
+	*data = readw(hw->io + phy + KS884X_PHY_STATUS_OFFSET);
+}
+
+static inline void hw_r_phy_auto_neg(struct ksz_hw *hw, int phy, u16 *data)
+{
+	*data = readw(hw->io + phy + KS884X_PHY_AUTO_NEG_OFFSET);
+}
+
+static inline void hw_w_phy_auto_neg(struct ksz_hw *hw, int phy, u16 data)
+{
+	writew(data, hw->io + phy + KS884X_PHY_AUTO_NEG_OFFSET);
+}
+
+static inline void hw_r_phy_rem_cap(struct ksz_hw *hw, int phy, u16 *data)
+{
+	*data = readw(hw->io + phy + KS884X_PHY_REMOTE_CAP_OFFSET);
+}
+
+static inline void hw_r_phy_crossover(struct ksz_hw *hw, int phy, u16 *data)
+{
+	*data = readw(hw->io + phy + KS884X_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_w_phy_crossover(struct ksz_hw *hw, int phy, u16 data)
+{
+	writew(data, hw->io + phy + KS884X_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_r_phy_polarity(struct ksz_hw *hw, int phy, u16 *data)
+{
+	*data = readw(hw->io + phy + KS884X_PHY_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_w_phy_polarity(struct ksz_hw *hw, int phy, u16 data)
+{
+	writew(data, hw->io + phy + KS884X_PHY_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_r_phy_link_md(struct ksz_hw *hw, int phy, u16 *data)
+{
+	*data = readw(hw->io + phy + KS884X_PHY_LINK_MD_OFFSET);
+}
+
+static inline void hw_w_phy_link_md(struct ksz_hw *hw, int phy, u16 data)
+{
+	writew(data, hw->io + phy + KS884X_PHY_LINK_MD_OFFSET);
+}
+
+/**
+ * hw_r_phy - read data from PHY register
+ * @hw: 	The hardware instance.
+ * @port:	Port to read.
+ * @reg:	PHY register to read.
+ * @val:	Buffer to store the read data.
+ *
+ * This routine reads data from the PHY register.
+ */
+static void hw_r_phy(struct ksz_hw *hw, int port, u16 reg, u16 *val)
+{
+	int phy;
+
+	phy = KS884X_PHY_1_CTRL_OFFSET + port * PHY_CTRL_INTERVAL + reg;
+	*val = readw(hw->io + phy);
+}
+
+/**
+ * port_w_phy - write data to PHY register
+ * @hw: 	The hardware instance.
+ * @port:	Port to write.
+ * @reg:	PHY register to write.
+ * @val:	Word data to write.
+ *
+ * This routine writes data to the PHY register.
+ */
+static void hw_w_phy(struct ksz_hw *hw, int port, u16 reg, u16 val)
+{
+	int phy;
+
+	phy = KS884X_PHY_1_CTRL_OFFSET + port * PHY_CTRL_INTERVAL + reg;
+	writew(val, hw->io + phy);
+}
+
+/*
+ * EEPROM access functions
+ */
+
+#define AT93C_CODE			0
+#define AT93C_WR_OFF			0x00
+#define AT93C_WR_ALL			0x10
+#define AT93C_ER_ALL			0x20
+#define AT93C_WR_ON			0x30
+
+#define AT93C_WRITE			1
+#define AT93C_READ			2
+#define AT93C_ERASE			3
+
+#define EEPROM_DELAY			4
+
+static inline void drop_gpio(struct ksz_hw *hw, u8 gpio)
+{
+	u16 data;
+
+	data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
+	data &= ~gpio;
+	writew(data, hw->io + KS884X_EEPROM_CTRL_OFFSET);
+}
+
+static inline void raise_gpio(struct ksz_hw *hw, u8 gpio)
+{
+	u16 data;
+
+	data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
+	data |= gpio;
+	writew(data, hw->io + KS884X_EEPROM_CTRL_OFFSET);
+}
+
+static inline u8 state_gpio(struct ksz_hw *hw, u8 gpio)
+{
+	u16 data;
+
+	data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
+	return (u8)(data & gpio);
+}
+
+static void eeprom_clk(struct ksz_hw *hw)
+{
+	raise_gpio(hw, EEPROM_SERIAL_CLOCK);
+	udelay(EEPROM_DELAY);
+	drop_gpio(hw, EEPROM_SERIAL_CLOCK);
+	udelay(EEPROM_DELAY);
+}
+
+static u16 spi_r(struct ksz_hw *hw)
+{
+	int i;
+	u16 temp = 0;
+
+	for (i = 15; i >= 0; i--) {
+		raise_gpio(hw, EEPROM_SERIAL_CLOCK);
+		udelay(EEPROM_DELAY);
+
+		temp |= (state_gpio(hw, EEPROM_DATA_IN)) ? 1 << i : 0;
+
+		drop_gpio(hw, EEPROM_SERIAL_CLOCK);
+		udelay(EEPROM_DELAY);
+	}
+	return temp;
+}
+
+static void spi_w(struct ksz_hw *hw, u16 data)
+{
+	int i;
+
+	for (i = 15; i >= 0; i--) {
+		(data & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
+			drop_gpio(hw, EEPROM_DATA_OUT);
+		eeprom_clk(hw);
+	}
+}
+
+static void spi_reg(struct ksz_hw *hw, u8 data, u8 reg)
+{
+	int i;
+
+	/* Initial start bit */
+	raise_gpio(hw, EEPROM_DATA_OUT);
+	eeprom_clk(hw);
+
+	/* AT93C operation */
+	for (i = 1; i >= 0; i--) {
+		(data & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
+			drop_gpio(hw, EEPROM_DATA_OUT);
+		eeprom_clk(hw);
+	}
+
+	/* Address location */
+	for (i = 5; i >= 0; i--) {
+		(reg & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
+			drop_gpio(hw, EEPROM_DATA_OUT);
+		eeprom_clk(hw);
+	}
+}
+
+#define EEPROM_DATA_RESERVED		0
+#define EEPROM_DATA_MAC_ADDR_0		1
+#define EEPROM_DATA_MAC_ADDR_1		2
+#define EEPROM_DATA_MAC_ADDR_2		3
+#define EEPROM_DATA_SUBSYS_ID		4
+#define EEPROM_DATA_SUBSYS_VEN_ID	5
+#define EEPROM_DATA_PM_CAP		6
+
+/* User defined EEPROM data */
+#define EEPROM_DATA_OTHER_MAC_ADDR	9
+
+/**
+ * eeprom_read - read from AT93C46 EEPROM
+ * @hw: 	The hardware instance.
+ * @reg:	The register offset.
+ *
+ * This function reads a word from the AT93C46 EEPROM.
+ *
+ * Return the data value.
+ */
+static u16 eeprom_read(struct ksz_hw *hw, u8 reg)
+{
+	u16 data;
+
+	raise_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
+
+	spi_reg(hw, AT93C_READ, reg);
+	data = spi_r(hw);
+
+	drop_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
+
+	return data;
+}
+
+/**
+ * eeprom_write - write to AT93C46 EEPROM
+ * @hw: 	The hardware instance.
+ * @reg:	The register offset.
+ * @data:	The data value.
+ *
+ * This procedure writes a word to the AT93C46 EEPROM.
+ */
+static void eeprom_write(struct ksz_hw *hw, u8 reg, u16 data)
+{
+	int timeout;
+
+	raise_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
+
+	/* Enable write. */
+	spi_reg(hw, AT93C_CODE, AT93C_WR_ON);
+	drop_gpio(hw, EEPROM_CHIP_SELECT);
+	udelay(1);
+
+	/* Erase the register. */
+	raise_gpio(hw, EEPROM_CHIP_SELECT);
+	spi_reg(hw, AT93C_ERASE, reg);
+	drop_gpio(hw, EEPROM_CHIP_SELECT);
+	udelay(1);
+
+	/* Check operation complete. */
+	raise_gpio(hw, EEPROM_CHIP_SELECT);
+	timeout = 8;
+	mdelay(2);
+	do {
+		mdelay(1);
+	} while (!state_gpio(hw, EEPROM_DATA_IN) && --timeout);
+	drop_gpio(hw, EEPROM_CHIP_SELECT);
+	udelay(1);
+
+	/* Write the register. */
+	raise_gpio(hw, EEPROM_CHIP_SELECT);
+	spi_reg(hw, AT93C_WRITE, reg);
+	spi_w(hw, data);
+	drop_gpio(hw, EEPROM_CHIP_SELECT);
+	udelay(1);
+
+	/* Check operation complete. */
+	raise_gpio(hw, EEPROM_CHIP_SELECT);
+	timeout = 8;
+	mdelay(2);
+	do {
+		mdelay(1);
+	} while (!state_gpio(hw, EEPROM_DATA_IN) && --timeout);
+	drop_gpio(hw, EEPROM_CHIP_SELECT);
+	udelay(1);
+
+	/* Disable write. */
+	raise_gpio(hw, EEPROM_CHIP_SELECT);
+	spi_reg(hw, AT93C_CODE, AT93C_WR_OFF);
+
+	drop_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
+}
+
+/*
+ * Link detection routines
+ */
+
+static u16 advertised_flow_ctrl(struct ksz_port *port, u16 ctrl)
+{
+	ctrl &= ~PORT_AUTO_NEG_SYM_PAUSE;
+	switch (port->flow_ctrl) {
+	case PHY_FLOW_CTRL:
+		ctrl |= PORT_AUTO_NEG_SYM_PAUSE;
+		break;
+	/* Not supported. */
+	case PHY_TX_ONLY:
+	case PHY_RX_ONLY:
+	default:
+		break;
+	}
+	return ctrl;
+}
+
+static void set_flow_ctrl(struct ksz_hw *hw, int rx, int tx)
+{
+	u32 rx_cfg;
+	u32 tx_cfg;
+
+	rx_cfg = hw->rx_cfg;
+	tx_cfg = hw->tx_cfg;
+	if (rx)
+		hw->rx_cfg |= DMA_RX_FLOW_ENABLE;
+	else
+		hw->rx_cfg &= ~DMA_RX_FLOW_ENABLE;
+	if (tx)
+		hw->tx_cfg |= DMA_TX_FLOW_ENABLE;
+	else
+		hw->tx_cfg &= ~DMA_TX_FLOW_ENABLE;
+	if (hw->enabled) {
+		if (rx_cfg != hw->rx_cfg)
+			writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
+		if (tx_cfg != hw->tx_cfg)
+			writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
+	}
+}
+
+static void determine_flow_ctrl(struct ksz_hw *hw, struct ksz_port *port,
+	u16 local, u16 remote)
+{
+	int rx;
+	int tx;
+
+	if (hw->overrides & PAUSE_FLOW_CTRL)
+		return;
+
+	rx = tx = 0;
+	if (port->force_link)
+		rx = tx = 1;
+	if (remote & PHY_AUTO_NEG_SYM_PAUSE) {
+		if (local & PHY_AUTO_NEG_SYM_PAUSE) {
+			rx = tx = 1;
+		} else if ((remote & PHY_AUTO_NEG_ASYM_PAUSE) &&
+				(local & PHY_AUTO_NEG_PAUSE) ==
+				PHY_AUTO_NEG_ASYM_PAUSE) {
+			tx = 1;
+		}
+	} else if (remote & PHY_AUTO_NEG_ASYM_PAUSE) {
+		if ((local & PHY_AUTO_NEG_PAUSE) == PHY_AUTO_NEG_PAUSE)
+			rx = 1;
+	}
+	if (!hw->ksz_switch)
+		set_flow_ctrl(hw, rx, tx);
+}
+
+static inline void port_cfg_change(struct ksz_hw *hw, struct ksz_port *port,
+	struct ksz_port_info *info, u16 link_status)
+{
+	if ((hw->features & HALF_DUPLEX_SIGNAL_BUG) &&
+			!(hw->overrides & PAUSE_FLOW_CTRL)) {
+		u32 cfg = hw->tx_cfg;
+
+		/* Disable flow control in the half duplex mode. */
+		if (1 == info->duplex)
+			hw->tx_cfg &= ~DMA_TX_FLOW_ENABLE;
+		if (hw->enabled && cfg != hw->tx_cfg)
+			writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
+	}
+}
+
+/**
+ * port_get_link_speed - get current link status
+ * @port: 	The port instance.
+ *
+ * This routine reads PHY registers to determine the current link status of the
+ * switch ports.
+ */
+static void port_get_link_speed(struct ksz_port *port)
+{
+	uint interrupt;
+	struct ksz_port_info *info;
+	struct ksz_port_info *linked = NULL;
+	struct ksz_hw *hw = port->hw;
+	u16 data;
+	u16 status;
+	u8 local;
+	u8 remote;
+	int i;
+	int p;
+	int change = 0;
+
+	interrupt = hw_block_intr(hw);
+
+	for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
+		info = &hw->port_info[p];
+		port_r16(hw, p, KS884X_PORT_CTRL_4_OFFSET, &data);
+		port_r16(hw, p, KS884X_PORT_STATUS_OFFSET, &status);
+
+		/*
+		 * Link status is changing all the time even when there is no
+		 * cable connection!
+		 */
+		remote = status & (PORT_AUTO_NEG_COMPLETE |
+			PORT_STATUS_LINK_GOOD);
+		local = (u8) data;
+
+		/* No change to status. */
+		if (local == info->advertised && remote == info->partner)
+			continue;
+
+		info->advertised = local;
+		info->partner = remote;
+		if (status & PORT_STATUS_LINK_GOOD) {
+
+			/* Remember the first linked port. */
+			if (!linked)
+				linked = info;
+
+			info->tx_rate = 10 * TX_RATE_UNIT;
+			if (status & PORT_STATUS_SPEED_100MBIT)
+				info->tx_rate = 100 * TX_RATE_UNIT;
+
+			info->duplex = 1;
+			if (status & PORT_STATUS_FULL_DUPLEX)
+				info->duplex = 2;
+
+			if (media_connected != info->state) {
+				hw_r_phy(hw, p, KS884X_PHY_AUTO_NEG_OFFSET,
+					&data);
+				hw_r_phy(hw, p, KS884X_PHY_REMOTE_CAP_OFFSET,
+					&status);
+				determine_flow_ctrl(hw, port, data, status);
+				if (hw->ksz_switch) {
+					port_cfg_back_pressure(hw, p,
+						(1 == info->duplex));
+				}
+				change |= 1 << i;
+				port_cfg_change(hw, port, info, status);
+			}
+			info->state = media_connected;
+		} else {
+			if (media_disconnected != info->state) {
+				change |= 1 << i;
+
+				/* Indicate the link just goes down. */
+				hw->port_mib[p].link_down = 1;
+			}
+			info->state = media_disconnected;
+		}
+		hw->port_mib[p].state = (u8) info->state;
+	}
+
+	if (linked && media_disconnected == port->linked->state)
+		port->linked = linked;
+
+	hw_restore_intr(hw, interrupt);
+}
+
+#define PHY_RESET_TIMEOUT		10
+
+/**
+ * port_set_link_speed - set port speed
+ * @port: 	The port instance.
+ *
+ * This routine sets the link speed of the switch ports.
+ */
+static void port_set_link_speed(struct ksz_port *port)
+{
+	struct ksz_port_info *info;
+	struct ksz_hw *hw = port->hw;
+	u16 data;
+	u16 cfg;
+	u8 status;
+	int i;
+	int p;
+
+	for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
+		info = &hw->port_info[p];
+
+		port_r16(hw, p, KS884X_PORT_CTRL_4_OFFSET, &data);
+		port_r8(hw, p, KS884X_PORT_STATUS_OFFSET, &status);
+
+		cfg = 0;
+		if (status & PORT_STATUS_LINK_GOOD)
+			cfg = data;
+
+		data |= PORT_AUTO_NEG_ENABLE;
+		data = advertised_flow_ctrl(port, data);
+
+		data |= PORT_AUTO_NEG_100BTX_FD | PORT_AUTO_NEG_100BTX |
+			PORT_AUTO_NEG_10BT_FD | PORT_AUTO_NEG_10BT;
+
+		/* Check if manual configuration is specified by the user. */
+		if (port->speed || port->duplex) {
+			if (10 == port->speed)
+				data &= ~(PORT_AUTO_NEG_100BTX_FD |
+					PORT_AUTO_NEG_100BTX);
+			else if (100 == port->speed)
+				data &= ~(PORT_AUTO_NEG_10BT_FD |
+					PORT_AUTO_NEG_10BT);
+			if (1 == port->duplex)
+				data &= ~(PORT_AUTO_NEG_100BTX_FD |
+					PORT_AUTO_NEG_10BT_FD);
+			else if (2 == port->duplex)
+				data &= ~(PORT_AUTO_NEG_100BTX |
+					PORT_AUTO_NEG_10BT);
+		}
+		if (data != cfg) {
+			data |= PORT_AUTO_NEG_RESTART;
+			port_w16(hw, p, KS884X_PORT_CTRL_4_OFFSET, data);
+		}
+	}
+}
+
+/**
+ * port_force_link_speed - force port speed
+ * @port: 	The port instance.
+ *
+ * This routine forces the link speed of the switch ports.
+ */
+static void port_force_link_speed(struct ksz_port *port)
+{
+	struct ksz_hw *hw = port->hw;
+	u16 data;
+	int i;
+	int phy;
+	int p;
+
+	for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
+		phy = KS884X_PHY_1_CTRL_OFFSET + p * PHY_CTRL_INTERVAL;
+		hw_r_phy_ctrl(hw, phy, &data);
+
+		data &= ~PHY_AUTO_NEG_ENABLE;
+
+		if (10 == port->speed)
+			data &= ~PHY_SPEED_100MBIT;
+		else if (100 == port->speed)
+			data |= PHY_SPEED_100MBIT;
+		if (1 == port->duplex)
+			data &= ~PHY_FULL_DUPLEX;
+		else if (2 == port->duplex)
+			data |= PHY_FULL_DUPLEX;
+		hw_w_phy_ctrl(hw, phy, data);
+	}
+}
+
+static void port_set_power_saving(struct ksz_port *port, int enable)
+{
+	struct ksz_hw *hw = port->hw;
+	int i;
+	int p;
+
+	for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++)
+		port_cfg(hw, p,
+			KS884X_PORT_CTRL_4_OFFSET, PORT_POWER_DOWN, enable);
+}
+
+/*
+ * KSZ8841 power management functions
+ */
+
+/**
+ * hw_chk_wol_pme_status - check PMEN pin
+ * @hw: 	The hardware instance.
+ *
+ * This function is used to check PMEN pin is asserted.
+ *
+ * Return 1 if PMEN pin is asserted; otherwise, 0.
+ */
+static int hw_chk_wol_pme_status(struct ksz_hw *hw)
+{
+	struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
+	struct pci_dev *pdev = hw_priv->pdev;
+	u16 data;
+
+	if (!pdev->pm_cap)
+		return 0;
+	pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
+	return (data & PCI_PM_CTRL_PME_STATUS) == PCI_PM_CTRL_PME_STATUS;
+}
+
+/**
+ * hw_clr_wol_pme_status - clear PMEN pin
+ * @hw: 	The hardware instance.
+ *
+ * This routine is used to clear PME_Status to deassert PMEN pin.
+ */
+static void hw_clr_wol_pme_status(struct ksz_hw *hw)
+{
+	struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
+	struct pci_dev *pdev = hw_priv->pdev;
+	u16 data;
+
+	if (!pdev->pm_cap)
+		return;
+
+	/* Clear PME_Status to deassert PMEN pin. */
+	pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
+	data |= PCI_PM_CTRL_PME_STATUS;
+	pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, data);
+}
+
+/**
+ * hw_cfg_wol_pme - enable or disable Wake-on-LAN
+ * @hw: 	The hardware instance.
+ * @set:	The flag indicating whether to enable or disable.
+ *
+ * This routine is used to enable or disable Wake-on-LAN.
+ */
+static void hw_cfg_wol_pme(struct ksz_hw *hw, int set)
+{
+	struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
+	struct pci_dev *pdev = hw_priv->pdev;
+	u16 data;
+
+	if (!pdev->pm_cap)
+		return;
+	pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
+	data &= ~PCI_PM_CTRL_STATE_MASK;
+	if (set)
+		data |= PCI_PM_CTRL_PME_ENABLE | PCI_D3hot;
+	else
+		data &= ~PCI_PM_CTRL_PME_ENABLE;
+	pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, data);
+}
+
+/**
+ * hw_cfg_wol - configure Wake-on-LAN features
+ * @hw: 	The hardware instance.
+ * @frame:	The pattern frame bit.
+ * @set:	The flag indicating whether to enable or disable.
+ *
+ * This routine is used to enable or disable certain Wake-on-LAN features.
+ */
+static void hw_cfg_wol(struct ksz_hw *hw, u16 frame, int set)
+{
+	u16 data;
+
+	data = readw(hw->io + KS8841_WOL_CTRL_OFFSET);
+	if (set)
+		data |= frame;
+	else
+		data &= ~frame;
+	writew(data, hw->io + KS8841_WOL_CTRL_OFFSET);
+}
+
+/**
+ * hw_set_wol_frame - program Wake-on-LAN pattern
+ * @hw: 	The hardware instance.
+ * @i:		The frame index.
+ * @mask_size:	The size of the mask.
+ * @mask:	Mask to ignore certain bytes in the pattern.
+ * @frame_size:	The size of the frame.
+ * @pattern:	The frame data.
+ *
+ * This routine is used to program Wake-on-LAN pattern.
+ */
+static void hw_set_wol_frame(struct ksz_hw *hw, int i, uint mask_size,
+	u8 *mask, uint frame_size, u8 *pattern)
+{
+	int bits;
+	int from;
+	int len;
+	int to;
+	u32 crc;
+	u8 data[64];
+	u8 val = 0;
+
+	if (frame_size > mask_size * 8)
+		frame_size = mask_size * 8;
+	if (frame_size > 64)
+		frame_size = 64;
+
+	i *= 0x10;
+	writel(0, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i);
+	writel(0, hw->io + KS8841_WOL_FRAME_BYTE2_OFFSET + i);
+
+	bits = len = from = to = 0;
+	do {
+		if (bits) {
+			if ((val & 1))
+				data[to++] = pattern[from];
+			val >>= 1;
+			++from;
+			--bits;
+		} else {
+			val = mask[len];
+			writeb(val, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i
+				+ len);
+			++len;
+			if (val)
+				bits = 8;
+			else
+				from += 8;
+		}
+	} while (from < (int) frame_size);
+	if (val) {
+		bits = mask[len - 1];
+		val <<= (from % 8);
+		bits &= ~val;
+		writeb(bits, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i + len -
+			1);
+	}
+	crc = ether_crc(to, data);
+	writel(crc, hw->io + KS8841_WOL_FRAME_CRC_OFFSET + i);
+}
+
+/**
+ * hw_add_wol_arp - add ARP pattern
+ * @hw: 	The hardware instance.
+ * @ip_addr:	The IPv4 address assigned to the device.
+ *
+ * This routine is used to add ARP pattern for waking up the host.
+ */
+static void hw_add_wol_arp(struct ksz_hw *hw, u8 *ip_addr)
+{
+	u8 mask[6] = { 0x3F, 0xF0, 0x3F, 0x00, 0xC0, 0x03 };
+	u8 pattern[42] = {
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x08, 0x06,
+		0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00 };
+
+	memcpy(&pattern[38], ip_addr, 4);
+	hw_set_wol_frame(hw, 3, 6, mask, 42, pattern);
+}
+
+/**
+ * hw_add_wol_bcast - add broadcast pattern
+ * @hw: 	The hardware instance.
+ *
+ * This routine is used to add broadcast pattern for waking up the host.
+ */
+static void hw_add_wol_bcast(struct ksz_hw *hw)
+{
+	u8 mask[] = { 0x3F };
+	u8 pattern[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+	hw_set_wol_frame(hw, 2, 1, mask, MAC_ADDR_LEN, pattern);
+}
+
+/**
+ * hw_add_wol_mcast - add multicast pattern
+ * @hw: 	The hardware instance.
+ *
+ * This routine is used to add multicast pattern for waking up the host.
+ *
+ * It is assumed the multicast packet is the ICMPv6 neighbor solicitation used
+ * by IPv6 ping command.  Note that multicast packets are filtred through the
+ * multicast hash table, so not all multicast packets can wake up the host.
+ */
+static void hw_add_wol_mcast(struct ksz_hw *hw)
+{
+	u8 mask[] = { 0x3F };
+	u8 pattern[] = { 0x33, 0x33, 0xFF, 0x00, 0x00, 0x00 };
+
+	memcpy(&pattern[3], &hw->override_addr[3], 3);
+	hw_set_wol_frame(hw, 1, 1, mask, 6, pattern);
+}
+
+/**
+ * hw_add_wol_ucast - add unicast pattern
+ * @hw: 	The hardware instance.
+ *
+ * This routine is used to add unicast pattern to wakeup the host.
+ *
+ * It is assumed the unicast packet is directed to the device, as the hardware
+ * can only receive them in normal case.
+ */
+static void hw_add_wol_ucast(struct ksz_hw *hw)
+{
+	u8 mask[] = { 0x3F };
+
+	hw_set_wol_frame(hw, 0, 1, mask, MAC_ADDR_LEN, hw->override_addr);
+}
+
+/**
+ * hw_enable_wol - enable Wake-on-LAN
+ * @hw: 	The hardware instance.
+ * @wol_enable:	The Wake-on-LAN settings.
+ * @net_addr:	The IPv4 address assigned to the device.
+ *
+ * This routine is used to enable Wake-on-LAN depending on driver settings.
+ */
+static void hw_enable_wol(struct ksz_hw *hw, u32 wol_enable, u8 *net_addr)
+{
+	hw_cfg_wol(hw, KS8841_WOL_MAGIC_ENABLE, (wol_enable & WAKE_MAGIC));
+	hw_cfg_wol(hw, KS8841_WOL_FRAME0_ENABLE, (wol_enable & WAKE_UCAST));
+	hw_add_wol_ucast(hw);
+	hw_cfg_wol(hw, KS8841_WOL_FRAME1_ENABLE, (wol_enable & WAKE_MCAST));
+	hw_add_wol_mcast(hw);
+	hw_cfg_wol(hw, KS8841_WOL_FRAME2_ENABLE, (wol_enable & WAKE_BCAST));
+	hw_cfg_wol(hw, KS8841_WOL_FRAME3_ENABLE, (wol_enable & WAKE_ARP));
+	hw_add_wol_arp(hw, net_addr);
+}
+
+/**
+ * hw_init - check driver is correct for the hardware
+ * @hw: 	The hardware instance.
+ *
+ * This function checks the hardware is correct for this driver and sets the
+ * hardware up for proper initialization.
+ *
+ * Return number of ports or 0 if not right.
+ */
+static int hw_init(struct ksz_hw *hw)
+{
+	int rc = 0;
+	u16 data;
+	u16 revision;
+
+	/* Set bus speed to 125MHz. */
+	writew(BUS_SPEED_125_MHZ, hw->io + KS884X_BUS_CTRL_OFFSET);
+
+	/* Check KSZ884x chip ID. */
+	data = readw(hw->io + KS884X_CHIP_ID_OFFSET);
+
+	revision = (data & KS884X_REVISION_MASK) >> KS884X_REVISION_SHIFT;
+	data &= KS884X_CHIP_ID_MASK_41;
+	if (REG_CHIP_ID_41 == data)
+		rc = 1;
+	else if (REG_CHIP_ID_42 == data)
+		rc = 2;
+	else
+		return 0;
+
+	/* Setup hardware features or bug workarounds. */
+	if (revision <= 1) {
+		hw->features |= SMALL_PACKET_TX_BUG;
+		if (1 == rc)
+			hw->features |= HALF_DUPLEX_SIGNAL_BUG;
+	}
+	hw->features |= IPV6_CSUM_GEN_HACK;
+	return rc;
+}
+
+/**
+ * hw_reset - reset the hardware
+ * @hw: 	The hardware instance.
+ *
+ * This routine resets the hardware.
+ */
+static void hw_reset(struct ksz_hw *hw)
+{
+	writew(GLOBAL_SOFTWARE_RESET, hw->io + KS884X_GLOBAL_CTRL_OFFSET);
+
+	/* Wait for device to reset. */
+	mdelay(10);
+
+	/* Write 0 to clear device reset. */
+	writew(0, hw->io + KS884X_GLOBAL_CTRL_OFFSET);
+}
+
+/**
+ * hw_setup - setup the hardware
+ * @hw: 	The hardware instance.
+ *
+ * This routine setup the hardware for proper operation.
+ */
+static void hw_setup(struct ksz_hw *hw)
+{
+#if SET_DEFAULT_LED
+	u16 data;
+
+	/* Change default LED mode. */
+	data = readw(hw->io + KS8842_SWITCH_CTRL_5_OFFSET);
+	data &= ~LED_MODE;
+	data |= SET_DEFAULT_LED;
+	writew(data, hw->io + KS8842_SWITCH_CTRL_5_OFFSET);
+#endif
+
+	/* Setup transmit control. */
+	hw->tx_cfg = (DMA_TX_PAD_ENABLE | DMA_TX_CRC_ENABLE |
+		(DMA_BURST_DEFAULT << DMA_BURST_SHIFT) | DMA_TX_ENABLE);
+
+	/* Setup receive control. */
+	hw->rx_cfg = (DMA_RX_BROADCAST | DMA_RX_UNICAST |
+		(DMA_BURST_DEFAULT << DMA_BURST_SHIFT) | DMA_RX_ENABLE);
+	hw->rx_cfg |= KS884X_DMA_RX_MULTICAST;
+
+	/* Hardware cannot handle UDP packet in IP fragments. */
+	hw->rx_cfg |= (DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP);
+
+	if (hw->all_multi)
+		hw->rx_cfg |= DMA_RX_ALL_MULTICAST;
+	if (hw->promiscuous)
+		hw->rx_cfg |= DMA_RX_PROMISCUOUS;
+}
+
+/**
+ * hw_setup_intr - setup interrupt mask
+ * @hw: 	The hardware instance.
+ *
+ * This routine setup the interrupt mask for proper operation.
+ */
+static void hw_setup_intr(struct ksz_hw *hw)
+{
+	hw->intr_mask = KS884X_INT_MASK | KS884X_INT_RX_OVERRUN;
+}
+
+static void ksz_check_desc_num(struct ksz_desc_info *info)
+{
+#define MIN_DESC_SHIFT  2
+
+	int alloc = info->alloc;
+	int shift;
+
+	shift = 0;
+	while (!(alloc & 1)) {
+		shift++;
+		alloc >>= 1;
+	}
+	if (alloc != 1 || shift < MIN_DESC_SHIFT) {
+		printk(KERN_ALERT "Hardware descriptor numbers not right!\n");
+		while (alloc) {
+			shift++;
+			alloc >>= 1;
+		}
+		if (shift < MIN_DESC_SHIFT)
+			shift = MIN_DESC_SHIFT;
+		alloc = 1 << shift;
+		info->alloc = alloc;
+	}
+	info->mask = info->alloc - 1;
+}
+
+static void hw_init_desc(struct ksz_desc_info *desc_info, int transmit)
+{
+	int i;
+	u32 phys = desc_info->ring_phys;
+	struct ksz_hw_desc *desc = desc_info->ring_virt;
+	struct ksz_desc *cur = desc_info->ring;
+	struct ksz_desc *previous = NULL;
+
+	for (i = 0; i < desc_info->alloc; i++) {
+		cur->phw = desc++;
+		phys += desc_info->size;
+		previous = cur++;
+		previous->phw->next = cpu_to_le32(phys);
+	}
+	previous->phw->next = cpu_to_le32(desc_info->ring_phys);
+	previous->sw.buf.rx.end_of_ring = 1;
+	previous->phw->buf.data = cpu_to_le32(previous->sw.buf.data);
+
+	desc_info->avail = desc_info->alloc;
+	desc_info->last = desc_info->next = 0;
+
+	desc_info->cur = desc_info->ring;
+}
+
+/**
+ * hw_set_desc_base - set descriptor base addresses
+ * @hw: 	The hardware instance.
+ * @tx_addr:	The transmit descriptor base.
+ * @rx_addr:	The receive descriptor base.
+ *
+ * This routine programs the descriptor base addresses after reset.
+ */
+static void hw_set_desc_base(struct ksz_hw *hw, u32 tx_addr, u32 rx_addr)
+{
+	/* Set base address of Tx/Rx descriptors. */
+	writel(tx_addr, hw->io + KS_DMA_TX_ADDR);
+	writel(rx_addr, hw->io + KS_DMA_RX_ADDR);
+}
+
+static void hw_reset_pkts(struct ksz_desc_info *info)
+{
+	info->cur = info->ring;
+	info->avail = info->alloc;
+	info->last = info->next = 0;
+}
+
+static inline void hw_resume_rx(struct ksz_hw *hw)
+{
+	writel(DMA_START, hw->io + KS_DMA_RX_START);
+}
+
+/**
+ * hw_start_rx - start receiving
+ * @hw: 	The hardware instance.
+ *
+ * This routine starts the receive function of the hardware.
+ */
+static void hw_start_rx(struct ksz_hw *hw)
+{
+	writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
+
+	/* Notify when the receive stops. */
+	hw->intr_mask |= KS884X_INT_RX_STOPPED;
+
+	writel(DMA_START, hw->io + KS_DMA_RX_START);
+	hw_ack_intr(hw, KS884X_INT_RX_STOPPED);
+	hw->rx_stop++;
+
+	/* Variable overflows. */
+	if (0 == hw->rx_stop)
+		hw->rx_stop = 2;
+}
+
+/*
+ * hw_stop_rx - stop receiving
+ * @hw: 	The hardware instance.
+ *
+ * This routine stops the receive function of the hardware.
+ */
+static void hw_stop_rx(struct ksz_hw *hw)
+{
+	hw->rx_stop = 0;
+	hw_turn_off_intr(hw, KS884X_INT_RX_STOPPED);
+	writel((hw->rx_cfg & ~DMA_RX_ENABLE), hw->io + KS_DMA_RX_CTRL);
+}
+
+/**
+ * hw_start_tx - start transmitting
+ * @hw: 	The hardware instance.
+ *
+ * This routine starts the transmit function of the hardware.
+ */
+static void hw_start_tx(struct ksz_hw *hw)
+{
+	writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
+}
+
+/**
+ * hw_stop_tx - stop transmitting
+ * @hw: 	The hardware instance.
+ *
+ * This routine stops the transmit function of the hardware.
+ */
+static void hw_stop_tx(struct ksz_hw *hw)
+{
+	writel((hw->tx_cfg & ~DMA_TX_ENABLE), hw->io + KS_DMA_TX_CTRL);
+}
+
+/**
+ * hw_disable - disable hardware
+ * @hw: 	The hardware instance.
+ *
+ * This routine disables the hardware.
+ */
+static void hw_disable(struct ksz_hw *hw)
+{
+	hw_stop_rx(hw);
+	hw_stop_tx(hw);
+	hw->enabled = 0;
+}
+
+/**
+ * hw_enable - enable hardware
+ * @hw: 	The hardware instance.
+ *
+ * This routine enables the hardware.
+ */
+static void hw_enable(struct ksz_hw *hw)
+{
+	hw_start_tx(hw);
+	hw_start_rx(hw);
+	hw->enabled = 1;
+}
+
+/**
+ * hw_alloc_pkt - allocate enough descriptors for transmission
+ * @hw: 	The hardware instance.
+ * @length:	The length of the packet.
+ * @physical:	Number of descriptors required.
+ *
+ * This function allocates descriptors for transmission.
+ *
+ * Return 0 if not successful; 1 for buffer copy; or number of descriptors.
+ */
+static int hw_alloc_pkt(struct ksz_hw *hw, int length, int physical)
+{
+	/* Always leave one descriptor free. */
+	if (hw->tx_desc_info.avail <= 1)
+		return 0;
+
+	/* Allocate a descriptor for transmission and mark it current. */
+	get_tx_pkt(&hw->tx_desc_info, &hw->tx_desc_info.cur);
+	hw->tx_desc_info.cur->sw.buf.tx.first_seg = 1;
+
+	/* Keep track of number of transmit descriptors used so far. */
+	++hw->tx_int_cnt;
+	hw->tx_size += length;
+
+	/* Cannot hold on too much data. */
+	if (hw->tx_size >= MAX_TX_HELD_SIZE)
+		hw->tx_int_cnt = hw->tx_int_mask + 1;
+
+	if (physical > hw->tx_desc_info.avail)
+		return 1;
+
+	return hw->tx_desc_info.avail;
+}
+
+/**
+ * hw_send_pkt - mark packet for transmission
+ * @hw: 	The hardware instance.
+ *
+ * This routine marks the packet for transmission in PCI version.
+ */
+static void hw_send_pkt(struct ksz_hw *hw)
+{
+	struct ksz_desc *cur = hw->tx_desc_info.cur;
+
+	cur->sw.buf.tx.last_seg = 1;
+
+	/* Interrupt only after specified number of descriptors used. */
+	if (hw->tx_int_cnt > hw->tx_int_mask) {
+		cur->sw.buf.tx.intr = 1;
+		hw->tx_int_cnt = 0;
+		hw->tx_size = 0;
+	}
+
+	/* KSZ8842 supports port directed transmission. */
+	cur->sw.buf.tx.dest_port = hw->dst_ports;
+
+	release_desc(cur);
+
+	writel(0, hw->io + KS_DMA_TX_START);
+}
+
+static int empty_addr(u8 *addr)
+{
+	u32 *addr1 = (u32 *) addr;
+	u16 *addr2 = (u16 *) &addr[4];
+
+	return 0 == *addr1 && 0 == *addr2;
+}
+
+/**
+ * hw_set_addr - set MAC address
+ * @hw: 	The hardware instance.
+ *
+ * This routine programs the MAC address of the hardware when the address is
+ * overrided.
+ */
+static void hw_set_addr(struct ksz_hw *hw)
+{
+	int i;
+
+	for (i = 0; i < MAC_ADDR_LEN; i++)
+		writeb(hw->override_addr[MAC_ADDR_ORDER(i)],
+			hw->io + KS884X_ADDR_0_OFFSET + i);
+
+	sw_set_addr(hw, hw->override_addr);
+}
+
+/**
+ * hw_read_addr - read MAC address
+ * @hw: 	The hardware instance.
+ *
+ * This routine retrieves the MAC address of the hardware.
+ */
+static void hw_read_addr(struct ksz_hw *hw)
+{
+	int i;
+
+	for (i = 0; i < MAC_ADDR_LEN; i++)
+		hw->perm_addr[MAC_ADDR_ORDER(i)] = readb(hw->io +
+			KS884X_ADDR_0_OFFSET + i);
+
+	if (!hw->mac_override) {
+		memcpy(hw->override_addr, hw->perm_addr, MAC_ADDR_LEN);
+		if (empty_addr(hw->override_addr)) {
+			memcpy(hw->perm_addr, DEFAULT_MAC_ADDRESS,
+				MAC_ADDR_LEN);
+			memcpy(hw->override_addr, DEFAULT_MAC_ADDRESS,
+				MAC_ADDR_LEN);
+			hw->override_addr[5] += hw->id;
+			hw_set_addr(hw);
+		}
+	}
+}
+
+static void hw_ena_add_addr(struct ksz_hw *hw, int index, u8 *mac_addr)
+{
+	int i;
+	u32 mac_addr_lo;
+	u32 mac_addr_hi;
+
+	mac_addr_hi = 0;
+	for (i = 0; i < 2; i++) {
+		mac_addr_hi <<= 8;
+		mac_addr_hi |= mac_addr[i];
+	}
+	mac_addr_hi |= ADD_ADDR_ENABLE;
+	mac_addr_lo = 0;
+	for (i = 2; i < 6; i++) {
+		mac_addr_lo <<= 8;
+		mac_addr_lo |= mac_addr[i];
+	}
+	index *= ADD_ADDR_INCR;
+
+	writel(mac_addr_lo, hw->io + index + KS_ADD_ADDR_0_LO);
+	writel(mac_addr_hi, hw->io + index + KS_ADD_ADDR_0_HI);
+}
+
+static void hw_set_add_addr(struct ksz_hw *hw)
+{
+	int i;
+
+	for (i = 0; i < ADDITIONAL_ENTRIES; i++) {
+		if (empty_addr(hw->address[i]))
+			writel(0, hw->io + ADD_ADDR_INCR * i +
+				KS_ADD_ADDR_0_HI);
+		else
+			hw_ena_add_addr(hw, i, hw->address[i]);
+	}
+}
+
+static int hw_add_addr(struct ksz_hw *hw, u8 *mac_addr)
+{
+	int i;
+	int j = ADDITIONAL_ENTRIES;
+
+	if (!memcmp(hw->override_addr, mac_addr, MAC_ADDR_LEN))
+		return 0;
+	for (i = 0; i < hw->addr_list_size; i++) {
+		if (!memcmp(hw->address[i], mac_addr, MAC_ADDR_LEN))
+			return 0;
+		if (ADDITIONAL_ENTRIES == j && empty_addr(hw->address[i]))
+			j = i;
+	}
+	if (j < ADDITIONAL_ENTRIES) {
+		memcpy(hw->address[j], mac_addr, MAC_ADDR_LEN);
+		hw_ena_add_addr(hw, j, hw->address[j]);
+		return 0;
+	}
+	return -1;
+}
+
+static int hw_del_addr(struct ksz_hw *hw, u8 *mac_addr)
+{
+	int i;
+
+	for (i = 0; i < hw->addr_list_size; i++) {
+		if (!memcmp(hw->address[i], mac_addr, MAC_ADDR_LEN)) {
+			memset(hw->address[i], 0, MAC_ADDR_LEN);
+			writel(0, hw->io + ADD_ADDR_INCR * i +
+				KS_ADD_ADDR_0_HI);
+			return 0;
+		}
+	}
+	return -1;
+}
+
+/**
+ * hw_clr_multicast - clear multicast addresses
+ * @hw: 	The hardware instance.
+ *
+ * This routine removes all multicast addresses set in the hardware.
+ */
+static void hw_clr_multicast(struct ksz_hw *hw)
+{
+	int i;
+
+	for (i = 0; i < HW_MULTICAST_SIZE; i++) {
+		hw->multi_bits[i] = 0;
+
+		writeb(0, hw->io + KS884X_MULTICAST_0_OFFSET + i);
+	}
+}
+
+/**
+ * hw_set_grp_addr - set multicast addresses
+ * @hw: 	The hardware instance.
+ *
+ * This routine programs multicast addresses for the hardware to accept those
+ * addresses.
+ */
+static void hw_set_grp_addr(struct ksz_hw *hw)
+{
+	int i;
+	int index;
+	int position;
+	int value;
+
+	memset(hw->multi_bits, 0, sizeof(u8) * HW_MULTICAST_SIZE);
+
+	for (i = 0; i < hw->multi_list_size; i++) {
+		position = (ether_crc(6, hw->multi_list[i]) >> 26) & 0x3f;
+		index = position >> 3;
+		value = 1 << (position & 7);
+		hw->multi_bits[index] |= (u8) value;
+	}
+
+	for (i = 0; i < HW_MULTICAST_SIZE; i++)
+		writeb(hw->multi_bits[i], hw->io + KS884X_MULTICAST_0_OFFSET +
+			i);
+}
+
+/**
+ * hw_set_multicast - enable or disable all multicast receiving
+ * @hw: 	The hardware instance.
+ * @multicast:	To turn on or off the all multicast feature.
+ *
+ * This routine enables/disables the hardware to accept all multicast packets.
+ */
+static void hw_set_multicast(struct ksz_hw *hw, u8 multicast)
+{
+	/* Stop receiving for reconfiguration. */
+	hw_stop_rx(hw);
+
+	if (multicast)
+		hw->rx_cfg |= DMA_RX_ALL_MULTICAST;
+	else
+		hw->rx_cfg &= ~DMA_RX_ALL_MULTICAST;
+
+	if (hw->enabled)
+		hw_start_rx(hw);
+}
+
+/**
+ * hw_set_promiscuous - enable or disable promiscuous receiving
+ * @hw: 	The hardware instance.
+ * @prom:	To turn on or off the promiscuous feature.
+ *
+ * This routine enables/disables the hardware to accept all packets.
+ */
+static void hw_set_promiscuous(struct ksz_hw *hw, u8 prom)
+{
+	/* Stop receiving for reconfiguration. */
+	hw_stop_rx(hw);
+
+	if (prom)
+		hw->rx_cfg |= DMA_RX_PROMISCUOUS;
+	else
+		hw->rx_cfg &= ~DMA_RX_PROMISCUOUS;
+
+	if (hw->enabled)
+		hw_start_rx(hw);
+}
+
+/**
+ * sw_enable - enable the switch
+ * @hw: 	The hardware instance.
+ * @enable:	The flag to enable or disable the switch
+ *
+ * This routine is used to enable/disable the switch in KSZ8842.
+ */
+static void sw_enable(struct ksz_hw *hw, int enable)
+{
+	int port;
+
+	for (port = 0; port < SWITCH_PORT_NUM; port++) {
+		if (hw->dev_count > 1) {
+			/* Set port-base vlan membership with host port. */
+			sw_cfg_port_base_vlan(hw, port,
+				HOST_MASK | (1 << port));
+			port_set_stp_state(hw, port, STP_STATE_DISABLED);
+		} else {
+			sw_cfg_port_base_vlan(hw, port, PORT_MASK);
+			port_set_stp_state(hw, port, STP_STATE_FORWARDING);
+		}
+	}
+	if (hw->dev_count > 1)
+		port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_SIMPLE);
+	else
+		port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_FORWARDING);
+
+	if (enable)
+		enable = KS8842_START;
+	writew(enable, hw->io + KS884X_CHIP_ID_OFFSET);
+}
+
+/**
+ * sw_setup - setup the switch
+ * @hw: 	The hardware instance.
+ *
+ * This routine setup the hardware switch engine for default operation.
+ */
+static void sw_setup(struct ksz_hw *hw)
+{
+	int port;
+
+	sw_set_global_ctrl(hw);
+
+	/* Enable switch broadcast storm protection at 10% percent rate. */
+	sw_init_broad_storm(hw);
+	hw_cfg_broad_storm(hw, BROADCAST_STORM_PROTECTION_RATE);
+	for (port = 0; port < SWITCH_PORT_NUM; port++)
+		sw_ena_broad_storm(hw, port);
+
+	sw_init_prio(hw);
+
+	sw_init_mirror(hw);
+
+	sw_init_prio_rate(hw);
+
+	sw_init_vlan(hw);
+
+	if (hw->features & STP_SUPPORT)
+		sw_init_stp(hw);
+	if (!sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+			SWITCH_TX_FLOW_CTRL | SWITCH_RX_FLOW_CTRL))
+		hw->overrides |= PAUSE_FLOW_CTRL;
+	sw_enable(hw, 1);
+}
+
+/**
+ * ksz_start_timer - start kernel timer
+ * @info:	Kernel timer information.
+ * @time:	The time tick.
+ *
+ * This routine starts the kernel timer after the specified time tick.
+ */
+static void ksz_start_timer(struct ksz_timer_info *info, int time)
+{
+	info->cnt = 0;
+	info->timer.expires = jiffies + time;
+	add_timer(&info->timer);
+
+	/* infinity */
+	info->max = -1;
+}
+
+/**
+ * ksz_stop_timer - stop kernel timer
+ * @info:	Kernel timer information.
+ *
+ * This routine stops the kernel timer.
+ */
+static void ksz_stop_timer(struct ksz_timer_info *info)
+{
+	if (info->max) {
+		info->max = 0;
+		del_timer_sync(&info->timer);
+	}
+}
+
+static void ksz_init_timer(struct ksz_timer_info *info, int period,
+	void (*function)(unsigned long), void *data)
+{
+	info->max = 0;
+	info->period = period;
+	init_timer(&info->timer);
+	info->timer.function = function;
+	info->timer.data = (unsigned long) data;
+}
+
+static void ksz_update_timer(struct ksz_timer_info *info)
+{
+	++info->cnt;
+	if (info->max > 0) {
+		if (info->cnt < info->max) {
+			info->timer.expires = jiffies + info->period;
+			add_timer(&info->timer);
+		} else
+			info->max = 0;
+	} else if (info->max < 0) {
+		info->timer.expires = jiffies + info->period;
+		add_timer(&info->timer);
+	}
+}
+
+/**
+ * ksz_alloc_soft_desc - allocate software descriptors
+ * @desc_info:	Descriptor information structure.
+ * @transmit:	Indication that descriptors are for transmit.
+ *
+ * This local function allocates software descriptors for manipulation in
+ * memory.
+ *
+ * Return 0 if successful.
+ */
+static int ksz_alloc_soft_desc(struct ksz_desc_info *desc_info, int transmit)
+{
+	desc_info->ring = kmalloc(sizeof(struct ksz_desc) * desc_info->alloc,
+		GFP_KERNEL);
+	if (!desc_info->ring)
+		return 1;
+	memset((void *) desc_info->ring, 0,
+		sizeof(struct ksz_desc) * desc_info->alloc);
+	hw_init_desc(desc_info, transmit);
+	return 0;
+}
+
+/**
+ * ksz_alloc_desc - allocate hardware descriptors
+ * @adapter:	Adapter information structure.
+ *
+ * This local function allocates hardware descriptors for receiving and
+ * transmitting.
+ *
+ * Return 0 if successful.
+ */
+static int ksz_alloc_desc(struct dev_info *adapter)
+{
+	struct ksz_hw *hw = &adapter->hw;
+	int offset;
+
+	/* Allocate memory for RX & TX descriptors. */
+	adapter->desc_pool.alloc_size =
+		hw->rx_desc_info.size * hw->rx_desc_info.alloc +
+		hw->tx_desc_info.size * hw->tx_desc_info.alloc +
+		DESC_ALIGNMENT;
+
+	adapter->desc_pool.alloc_virt =
+		pci_alloc_consistent(
+			adapter->pdev, adapter->desc_pool.alloc_size,
+			&adapter->desc_pool.dma_addr);
+	if (adapter->desc_pool.alloc_virt == NULL) {
+		adapter->desc_pool.alloc_size = 0;
+		return 1;
+	}
+	memset(adapter->desc_pool.alloc_virt, 0, adapter->desc_pool.alloc_size);
+
+	/* Align to the next cache line boundary. */
+	offset = (((ulong) adapter->desc_pool.alloc_virt % DESC_ALIGNMENT) ?
+		(DESC_ALIGNMENT -
+		((ulong) adapter->desc_pool.alloc_virt % DESC_ALIGNMENT)) : 0);
+	adapter->desc_pool.virt = adapter->desc_pool.alloc_virt + offset;
+	adapter->desc_pool.phys = adapter->desc_pool.dma_addr + offset;
+
+	/* Allocate receive/transmit descriptors. */
+	hw->rx_desc_info.ring_virt = (struct ksz_hw_desc *)
+		adapter->desc_pool.virt;
+	hw->rx_desc_info.ring_phys = adapter->desc_pool.phys;
+	offset = hw->rx_desc_info.alloc * hw->rx_desc_info.size;
+	hw->tx_desc_info.ring_virt = (struct ksz_hw_desc *)
+		(adapter->desc_pool.virt + offset);
+	hw->tx_desc_info.ring_phys = adapter->desc_pool.phys + offset;
+
+	if (ksz_alloc_soft_desc(&hw->rx_desc_info, 0))
+		return 1;
+	if (ksz_alloc_soft_desc(&hw->tx_desc_info, 1))
+		return 1;
+
+	return 0;
+}
+
+/**
+ * free_dma_buf - release DMA buffer resources
+ * @adapter:	Adapter information structure.
+ *
+ * This routine is just a helper function to release the DMA buffer resources.
+ */
+static void free_dma_buf(struct dev_info *adapter, struct ksz_dma_buf *dma_buf,
+	int direction)
+{
+	pci_unmap_single(adapter->pdev, dma_buf->dma, dma_buf->len, direction);
+	dev_kfree_skb(dma_buf->skb);
+	dma_buf->skb = NULL;
+	dma_buf->dma = 0;
+}
+
+/**
+ * ksz_init_rx_buffers - initialize receive descriptors
+ * @adapter:	Adapter information structure.
+ *
+ * This routine initializes DMA buffers for receiving.
+ */
+static void ksz_init_rx_buffers(struct dev_info *adapter)
+{
+	int i;
+	struct ksz_desc *desc;
+	struct ksz_dma_buf *dma_buf;
+	struct ksz_hw *hw = &adapter->hw;
+	struct ksz_desc_info *info = &hw->rx_desc_info;
+
+	for (i = 0; i < hw->rx_desc_info.alloc; i++) {
+		get_rx_pkt(info, &desc);
+
+		dma_buf = DMA_BUFFER(desc);
+		if (dma_buf->skb && dma_buf->len != adapter->mtu)
+			free_dma_buf(adapter, dma_buf, PCI_DMA_FROMDEVICE);
+		dma_buf->len = adapter->mtu;
+		if (!dma_buf->skb)
+			dma_buf->skb = alloc_skb(dma_buf->len, GFP_ATOMIC);
+		if (dma_buf->skb && !dma_buf->dma) {
+			dma_buf->skb->dev = adapter->dev;
+			dma_buf->dma = pci_map_single(
+				adapter->pdev,
+				skb_tail_pointer(dma_buf->skb),
+				dma_buf->len,
+				PCI_DMA_FROMDEVICE);
+		}
+
+		/* Set descriptor. */
+		set_rx_buf(desc, dma_buf->dma);
+		set_rx_len(desc, dma_buf->len);
+		release_desc(desc);
+	}
+}
+
+/**
+ * ksz_alloc_mem - allocate memory for hardware descriptors
+ * @adapter:	Adapter information structure.
+ *
+ * This function allocates memory for use by hardware descriptors for receiving
+ * and transmitting.
+ *
+ * Return 0 if successful.
+ */
+static int ksz_alloc_mem(struct dev_info *adapter)
+{
+	struct ksz_hw *hw = &adapter->hw;
+
+	/* Determine the number of receive and transmit descriptors. */
+	hw->rx_desc_info.alloc = NUM_OF_RX_DESC;
+	hw->tx_desc_info.alloc = NUM_OF_TX_DESC;
+
+	/* Determine how many descriptors to skip transmit interrupt. */
+	hw->tx_int_cnt = 0;
+	hw->tx_int_mask = NUM_OF_TX_DESC / 4;
+	if (hw->tx_int_mask > 8)
+		hw->tx_int_mask = 8;
+	while (hw->tx_int_mask) {
+		hw->tx_int_cnt++;
+		hw->tx_int_mask >>= 1;
+	}
+	if (hw->tx_int_cnt) {
+		hw->tx_int_mask = (1 << (hw->tx_int_cnt - 1)) - 1;
+		hw->tx_int_cnt = 0;
+	}
+
+	/* Determine the descriptor size. */
+	hw->rx_desc_info.size =
+		(((sizeof(struct ksz_hw_desc) + DESC_ALIGNMENT - 1) /
+		DESC_ALIGNMENT) * DESC_ALIGNMENT);
+	hw->tx_desc_info.size =
+		(((sizeof(struct ksz_hw_desc) + DESC_ALIGNMENT - 1) /
+		DESC_ALIGNMENT) * DESC_ALIGNMENT);
+	if (hw->rx_desc_info.size != sizeof(struct ksz_hw_desc))
+		printk(KERN_ALERT
+			"Hardware descriptor size not right!\n");
+	ksz_check_desc_num(&hw->rx_desc_info);
+	ksz_check_desc_num(&hw->tx_desc_info);
+
+	/* Allocate descriptors. */
+	if (ksz_alloc_desc(adapter))
+		return 1;
+
+	return 0;
+}
+
+/**
+ * ksz_free_desc - free software and hardware descriptors
+ * @adapter:	Adapter information structure.
+ *
+ * This local routine frees the software and hardware descriptors allocated by
+ * ksz_alloc_desc().
+ */
+static void ksz_free_desc(struct dev_info *adapter)
+{
+	struct ksz_hw *hw = &adapter->hw;
+
+	/* Reset descriptor. */
+	hw->rx_desc_info.ring_virt = NULL;
+	hw->tx_desc_info.ring_virt = NULL;
+	hw->rx_desc_info.ring_phys = 0;
+	hw->tx_desc_info.ring_phys = 0;
+
+	/* Free memory. */
+	if (adapter->desc_pool.alloc_virt)
+		pci_free_consistent(
+			adapter->pdev,
+			adapter->desc_pool.alloc_size,
+			adapter->desc_pool.alloc_virt,
+			adapter->desc_pool.dma_addr);
+
+	/* Reset resource pool. */
+	adapter->desc_pool.alloc_size = 0;
+	adapter->desc_pool.alloc_virt = NULL;
+
+	kfree(hw->rx_desc_info.ring);
+	hw->rx_desc_info.ring = NULL;
+	kfree(hw->tx_desc_info.ring);
+	hw->tx_desc_info.ring = NULL;
+}
+
+/**
+ * ksz_free_buffers - free buffers used in the descriptors
+ * @adapter:	Adapter information structure.
+ * @desc_info:	Descriptor information structure.
+ *
+ * This local routine frees buffers used in the DMA buffers.
+ */
+static void ksz_free_buffers(struct dev_info *adapter,
+	struct ksz_desc_info *desc_info, int direction)
+{
+	int i;
+	struct ksz_dma_buf *dma_buf;
+	struct ksz_desc *desc = desc_info->ring;
+
+	for (i = 0; i < desc_info->alloc; i++) {
+		dma_buf = DMA_BUFFER(desc);
+		if (dma_buf->skb)
+			free_dma_buf(adapter, dma_buf, direction);
+		desc++;
+	}
+}
+
+/**
+ * ksz_free_mem - free all resources used by descriptors
+ * @adapter:	Adapter information structure.
+ *
+ * This local routine frees all the resources allocated by ksz_alloc_mem().
+ */
+static void ksz_free_mem(struct dev_info *adapter)
+{
+	/* Free transmit buffers. */
+	ksz_free_buffers(adapter, &adapter->hw.tx_desc_info,
+		PCI_DMA_TODEVICE);
+
+	/* Free receive buffers. */
+	ksz_free_buffers(adapter, &adapter->hw.rx_desc_info,
+		PCI_DMA_FROMDEVICE);
+
+	/* Free descriptors. */
+	ksz_free_desc(adapter);
+}
+
+static void get_mib_counters(struct ksz_hw *hw, int first, int cnt,
+	u64 *counter)
+{
+	int i;
+	int mib;
+	int port;
+	struct ksz_port_mib *port_mib;
+
+	memset(counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM);
+	for (i = 0, port = first; i < cnt; i++, port++) {
+		port_mib = &hw->port_mib[port];
+		for (mib = port_mib->mib_start; mib < hw->mib_cnt; mib++)
+			counter[mib] += port_mib->counter[mib];
+	}
+}
+
+/**
+ * send_packet - send packet
+ * @skb:	Socket buffer.
+ * @dev:	Network device.
+ *
+ * This routine is used to send a packet out to the network.
+ */
+static void send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ksz_desc *desc;
+	struct ksz_desc *first;
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct ksz_desc_info *info = &hw->tx_desc_info;
+	struct ksz_dma_buf *dma_buf;
+	int len;
+	int last_frag = skb_shinfo(skb)->nr_frags;
+
+	/*
+	 * KSZ8842 with multiple device interfaces needs to be told which port
+	 * to send.
+	 */
+	if (hw->dev_count > 1)
+		hw->dst_ports = 1 << priv->port.first_port;
+
+	/* Hardware will pad the length to 60. */
+	len = skb->len;
+
+	/* Remember the very first descriptor. */
+	first = info->cur;
+	desc = first;
+
+	dma_buf = DMA_BUFFER(desc);
+	if (last_frag) {
+		int frag;
+		skb_frag_t *this_frag;
+
+		dma_buf->len = skb->len - skb->data_len;
+
+		dma_buf->dma = pci_map_single(
+			hw_priv->pdev, skb->data, dma_buf->len,
+			PCI_DMA_TODEVICE);
+		set_tx_buf(desc, dma_buf->dma);
+		set_tx_len(desc, dma_buf->len);
+
+		frag = 0;
+		do {
+			this_frag = &skb_shinfo(skb)->frags[frag];
+
+			/* Get a new descriptor. */
+			get_tx_pkt(info, &desc);
+
+			/* Keep track of descriptors used so far. */
+			++hw->tx_int_cnt;
+
+			dma_buf = DMA_BUFFER(desc);
+			dma_buf->len = this_frag->size;
+
+			dma_buf->dma = pci_map_single(
+				hw_priv->pdev,
+				page_address(this_frag->page) +
+				this_frag->page_offset,
+				dma_buf->len,
+				PCI_DMA_TODEVICE);
+			set_tx_buf(desc, dma_buf->dma);
+			set_tx_len(desc, dma_buf->len);
+
+			frag++;
+			if (frag == last_frag)
+				break;
+
+			/* Do not release the last descriptor here. */
+			release_desc(desc);
+		} while (1);
+
+		/* current points to the last descriptor. */
+		info->cur = desc;
+
+		/* Release the first descriptor. */
+		release_desc(first);
+	} else {
+		dma_buf->len = len;
+
+		dma_buf->dma = pci_map_single(
+			hw_priv->pdev, skb->data, dma_buf->len,
+			PCI_DMA_TODEVICE);
+		set_tx_buf(desc, dma_buf->dma);
+		set_tx_len(desc, dma_buf->len);
+	}
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		(desc)->sw.buf.tx.csum_gen_tcp = 1;
+		(desc)->sw.buf.tx.csum_gen_udp = 1;
+	}
+
+	/*
+	 * The last descriptor holds the packet so that it can be returned to
+	 * network subsystem after all descriptors are transmitted.
+	 */
+	dma_buf->skb = skb;
+
+	hw_send_pkt(hw);
+
+	/* Update transmit statistics. */
+	priv->stats.tx_packets++;
+	priv->stats.tx_bytes += len;
+}
+
+/**
+ * transmit_cleanup - clean up transmit descriptors
+ * @dev:	Network device.
+ *
+ * This routine is called to clean up the transmitted buffers.
+ */
+static void transmit_cleanup(struct dev_info *hw_priv, int normal)
+{
+	int last;
+	union desc_stat status;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct ksz_desc_info *info = &hw->tx_desc_info;
+	struct ksz_desc *desc;
+	struct ksz_dma_buf *dma_buf;
+	struct net_device *dev = NULL;
+
+	spin_lock(&hw_priv->hwlock);
+	last = info->last;
+
+	while (info->avail < info->alloc) {
+		/* Get next descriptor which is not hardware owned. */
+		desc = &info->ring[last];
+		status.data = le32_to_cpu(desc->phw->ctrl.data);
+		if (status.tx.hw_owned) {
+			if (normal)
+				break;
+			else
+				reset_desc(desc, status);
+		}
+
+		dma_buf = DMA_BUFFER(desc);
+		pci_unmap_single(
+			hw_priv->pdev, dma_buf->dma, dma_buf->len,
+			PCI_DMA_TODEVICE);
+
+		/* This descriptor contains the last buffer in the packet. */
+		if (dma_buf->skb) {
+			dev = dma_buf->skb->dev;
+
+			/* Release the packet back to network subsystem. */
+			dev_kfree_skb_irq(dma_buf->skb);
+			dma_buf->skb = NULL;
+		}
+
+		/* Free the transmitted descriptor. */
+		last++;
+		last &= info->mask;
+		info->avail++;
+	}
+	info->last = last;
+	spin_unlock(&hw_priv->hwlock);
+
+	/* Notify the network subsystem that the packet has been sent. */
+	if (dev)
+		dev->trans_start = jiffies;
+}
+
+/**
+ * transmit_done - transmit done processing
+ * @dev:	Network device.
+ *
+ * This routine is called when the transmit interrupt is triggered, indicating
+ * either a packet is sent successfully or there are transmit errors.
+ */
+static void tx_done(struct dev_info *hw_priv)
+{
+	struct ksz_hw *hw = &hw_priv->hw;
+	int port;
+
+	transmit_cleanup(hw_priv, 1);
+
+	for (port = 0; port < hw->dev_count; port++) {
+		struct net_device *dev = hw->port_info[port].pdev;
+
+		if (netif_running(dev) && netif_queue_stopped(dev))
+			netif_wake_queue(dev);
+	}
+}
+
+static inline void copy_old_skb(struct sk_buff *old, struct sk_buff *skb)
+{
+	skb->dev = old->dev;
+	skb->protocol = old->protocol;
+	skb->ip_summed = old->ip_summed;
+	skb->csum = old->csum;
+	skb_set_network_header(skb, ETH_HLEN);
+
+	dev_kfree_skb(old);
+}
+
+/**
+ * netdev_tx - send out packet
+ * @skb:	Socket buffer.
+ * @dev:	Network device.
+ *
+ * This function is used by the upper network layer to send out a packet.
+ *
+ * Return 0 if successful; otherwise an error code indicating failure.
+ */
+static int netdev_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	int left;
+	int num = 1;
+	int rc = 0;
+
+	if (hw->features & SMALL_PACKET_TX_BUG) {
+		struct sk_buff *org_skb = skb;
+
+		if (skb->len <= 48) {
+			if (skb_end_pointer(skb) - skb->data >= 50) {
+				memset(&skb->data[skb->len], 0, 50 - skb->len);
+				skb->len = 50;
+			} else {
+				skb = dev_alloc_skb(50);
+				if (!skb)
+					return NETDEV_TX_BUSY;
+				memcpy(skb->data, org_skb->data, org_skb->len);
+				memset(&skb->data[org_skb->len], 0,
+					50 - org_skb->len);
+				skb->len = 50;
+				copy_old_skb(org_skb, skb);
+			}
+		}
+	}
+
+	spin_lock_irq(&hw_priv->hwlock);
+
+	num = skb_shinfo(skb)->nr_frags + 1;
+	left = hw_alloc_pkt(hw, skb->len, num);
+	if (left) {
+		if (left < num ||
+				((hw->features & IPV6_CSUM_GEN_HACK) &&
+				(CHECKSUM_PARTIAL == skb->ip_summed) &&
+				(ETH_P_IPV6 == htons(skb->protocol)))) {
+			struct sk_buff *org_skb = skb;
+
+			skb = dev_alloc_skb(org_skb->len);
+			if (!skb)
+				return NETDEV_TX_BUSY;
+			skb_copy_and_csum_dev(org_skb, skb->data);
+			org_skb->ip_summed = 0;
+			skb->len = org_skb->len;
+			copy_old_skb(org_skb, skb);
+		}
+		send_packet(skb, dev);
+		if (left <= num)
+			netif_stop_queue(dev);
+	} else {
+		/* Stop the transmit queue until packet is allocated. */
+		netif_stop_queue(dev);
+		rc = NETDEV_TX_BUSY;
+	}
+
+	spin_unlock_irq(&hw_priv->hwlock);
+
+	return rc;
+}
+
+/**
+ * netdev_tx_timeout - transmit timeout processing
+ * @dev:	Network device.
+ *
+ * This routine is called when the transmit timer expires.  That indicates the
+ * hardware is not running correctly because transmit interrupts are not
+ * triggered to free up resources so that the transmit routine can continue
+ * sending out packets.  The hardware is reset to correct the problem.
+ */
+static void netdev_tx_timeout(struct net_device *dev)
+{
+	static unsigned long last_reset;
+
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	int port;
+
+	if (hw->dev_count > 1) {
+		/*
+		 * Only reset the hardware if time between calls is long
+		 * enough.
+		 */
+		if (jiffies - last_reset <= dev->watchdog_timeo)
+			hw_priv = NULL;
+	}
+
+	last_reset = jiffies;
+	if (hw_priv) {
+		hw_dis_intr(hw);
+		hw_disable(hw);
+
+		transmit_cleanup(hw_priv, 0);
+		hw_reset_pkts(&hw->rx_desc_info);
+		hw_reset_pkts(&hw->tx_desc_info);
+		ksz_init_rx_buffers(hw_priv);
+
+		hw_reset(hw);
+
+		hw_set_desc_base(hw,
+			hw->tx_desc_info.ring_phys,
+			hw->rx_desc_info.ring_phys);
+		hw_set_addr(hw);
+		if (hw->all_multi)
+			hw_set_multicast(hw, hw->all_multi);
+		else if (hw->multi_list_size)
+			hw_set_grp_addr(hw);
+
+		if (hw->dev_count > 1) {
+			hw_set_add_addr(hw);
+			for (port = 0; port < SWITCH_PORT_NUM; port++) {
+				struct net_device *port_dev;
+
+				port_set_stp_state(hw, port,
+					STP_STATE_DISABLED);
+
+				port_dev = hw->port_info[port].pdev;
+				if (netif_running(port_dev))
+					port_set_stp_state(hw, port,
+						STP_STATE_SIMPLE);
+			}
+		}
+
+		hw_enable(hw);
+		hw_ena_intr(hw);
+	}
+
+	dev->trans_start = jiffies;
+	netif_wake_queue(dev);
+}
+
+static inline void csum_verified(struct sk_buff *skb)
+{
+	unsigned short protocol;
+	struct iphdr *iph;
+
+	protocol = skb->protocol;
+	skb_reset_network_header(skb);
+	iph = (struct iphdr *) skb_network_header(skb);
+	if (protocol == htons(ETH_P_8021Q)) {
+		protocol = iph->tot_len;
+		skb_set_network_header(skb, VLAN_HLEN);
+		iph = (struct iphdr *) skb_network_header(skb);
+	}
+	if (protocol == htons(ETH_P_IP)) {
+		if (iph->protocol == IPPROTO_TCP)
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+	}
+}
+
+static inline int rx_proc(struct net_device *dev, struct ksz_hw* hw,
+	struct ksz_desc *desc, union desc_stat status)
+{
+	int packet_len;
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_dma_buf *dma_buf;
+	struct sk_buff *skb;
+	int rx_status;
+
+	/* Received length includes 4-byte CRC. */
+	packet_len = status.rx.frame_len - 4;
+
+	dma_buf = DMA_BUFFER(desc);
+	pci_dma_sync_single_for_cpu(
+		hw_priv->pdev, dma_buf->dma, packet_len + 4,
+		PCI_DMA_FROMDEVICE);
+
+	do {
+		/* skb->data != skb->head */
+		skb = dev_alloc_skb(packet_len + 2);
+		if (!skb) {
+			priv->stats.rx_dropped++;
+			return -ENOMEM;
+		}
+
+		/*
+		 * Align socket buffer in 4-byte boundary for better
+		 * performance.
+		 */
+		skb_reserve(skb, 2);
+
+		memcpy(skb_put(skb, packet_len),
+			dma_buf->skb->data, packet_len);
+	} while (0);
+
+	skb->dev = dev;
+
+	skb->protocol = eth_type_trans(skb, dev);
+
+	if (hw->rx_cfg & (DMA_RX_CSUM_UDP | DMA_RX_CSUM_TCP))
+		csum_verified(skb);
+
+	/* Update receive statistics. */
+	priv->stats.rx_packets++;
+	priv->stats.rx_bytes += packet_len;
+
+	/* Notify upper layer for received packet. */
+	dev->last_rx = jiffies;
+
+	rx_status = netif_rx(skb);
+
+	return 0;
+}
+
+static int dev_rcv_packets(struct dev_info *hw_priv)
+{
+	int next;
+	union desc_stat status;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct net_device *dev = hw->port_info[0].pdev;
+	struct ksz_desc_info *info = &hw->rx_desc_info;
+	int left = info->alloc;
+	struct ksz_desc *desc;
+	int received = 0;
+
+	next = info->next;
+	while (left--) {
+		/* Get next descriptor which is not hardware owned. */
+		desc = &info->ring[next];
+		status.data = le32_to_cpu(desc->phw->ctrl.data);
+		if (status.rx.hw_owned)
+			break;
+
+		/* Status valid only when last descriptor bit is set. */
+		if (status.rx.last_desc && status.rx.first_desc) {
+			if (rx_proc(dev, hw, desc, status))
+				goto release_packet;
+			received++;
+		}
+
+release_packet:
+		release_desc(desc);
+		next++;
+		next &= info->mask;
+	}
+	info->next = next;
+
+	return received;
+}
+
+static int port_rcv_packets(struct dev_info *hw_priv)
+{
+	int next;
+	union desc_stat status;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct net_device *dev = hw->port_info[0].pdev;
+	struct ksz_desc_info *info = &hw->rx_desc_info;
+	int left = info->alloc;
+	struct ksz_desc *desc;
+	int received = 0;
+
+	next = info->next;
+	while (left--) {
+		/* Get next descriptor which is not hardware owned. */
+		desc = &info->ring[next];
+		status.data = le32_to_cpu(desc->phw->ctrl.data);
+		if (status.rx.hw_owned)
+			break;
+
+		if (hw->dev_count > 1) {
+			/* Get received port number. */
+			int p = HW_TO_DEV_PORT(status.rx.src_port);
+
+			dev = hw->port_info[p].pdev;
+			if (!netif_running(dev))
+				goto release_packet;
+		}
+
+		/* Status valid only when last descriptor bit is set. */
+		if (status.rx.last_desc && status.rx.first_desc) {
+			if (rx_proc(dev, hw, desc, status))
+				goto release_packet;
+			received++;
+		}
+
+release_packet:
+		release_desc(desc);
+		next++;
+		next &= info->mask;
+	}
+	info->next = next;
+
+	return received;
+}
+
+static int dev_rcv_special(struct dev_info *hw_priv)
+{
+	int next;
+	union desc_stat status;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct net_device *dev = hw->port_info[0].pdev;
+	struct ksz_desc_info *info = &hw->rx_desc_info;
+	int left = info->alloc;
+	struct ksz_desc *desc;
+	int received = 0;
+
+	next = info->next;
+	while (left--) {
+		/* Get next descriptor which is not hardware owned. */
+		desc = &info->ring[next];
+		status.data = le32_to_cpu(desc->phw->ctrl.data);
+		if (status.rx.hw_owned)
+			break;
+
+		if (hw->dev_count > 1) {
+			/* Get received port number. */
+			int p = HW_TO_DEV_PORT(status.rx.src_port);
+
+			dev = hw->port_info[p].pdev;
+			if (!netif_running(dev))
+				goto release_packet;
+		}
+
+		/* Status valid only when last descriptor bit is set. */
+		if (status.rx.last_desc && status.rx.first_desc) {
+			/*
+			 * Receive without error.  With receive errors
+			 * disabled, packets with receive errors will be
+			 * dropped, so no need to check the error bit.
+			 */
+			if (!status.rx.error || (status.data &
+					KS_DESC_RX_ERROR_COND) ==
+					KS_DESC_RX_ERROR_TOO_LONG) {
+				if (rx_proc(dev, hw, desc, status))
+					goto release_packet;
+				received++;
+			} else {
+				struct dev_priv *priv = netdev_priv(dev);
+
+				/* Update receive error statistics. */
+				priv->port.counter[OID_COUNTER_RCV_ERROR]++;
+			}
+		}
+
+release_packet:
+		release_desc(desc);
+		next++;
+		next &= info->mask;
+	}
+	info->next = next;
+
+	return received;
+}
+
+static void rx_proc_task(unsigned long data)
+{
+	struct dev_info *hw_priv = (struct dev_info *) data;
+	struct ksz_hw *hw = &hw_priv->hw;
+
+	if (!hw->enabled)
+		return;
+	if (unlikely(!hw_priv->dev_rcv(hw_priv))) {
+
+		/* In case receive process is suspended because of overrun. */
+		hw_resume_rx(hw);
+
+		/* tasklets are interruptible. */
+		spin_lock_irq(&hw_priv->hwlock);
+		hw_turn_on_intr(hw, KS884X_INT_RX_MASK);
+		spin_unlock_irq(&hw_priv->hwlock);
+	} else {
+		hw_ack_intr(hw, KS884X_INT_RX);
+		tasklet_schedule(&hw_priv->rx_tasklet);
+	}
+}
+
+static void tx_proc_task(unsigned long data)
+{
+	struct dev_info *hw_priv = (struct dev_info *) data;
+	struct ksz_hw *hw = &hw_priv->hw;
+
+	hw_ack_intr(hw, KS884X_INT_TX_MASK);
+
+	tx_done(hw_priv);
+
+	/* tasklets are interruptible. */
+	spin_lock_irq(&hw_priv->hwlock);
+	hw_turn_on_intr(hw, KS884X_INT_TX);
+	spin_unlock_irq(&hw_priv->hwlock);
+}
+
+static inline void handle_rx_stop(struct ksz_hw *hw)
+{
+	/* Receive just has been stopped. */
+	if (0 == hw->rx_stop)
+		hw->intr_mask &= ~KS884X_INT_RX_STOPPED;
+	else if (hw->rx_stop > 1) {
+		if (hw->enabled && (hw->rx_cfg & DMA_RX_ENABLE)) {
+			hw_start_rx(hw);
+		} else {
+			hw->intr_mask &= ~KS884X_INT_RX_STOPPED;
+			hw->rx_stop = 0;
+		}
+	} else
+		/* Receive just has been started. */
+		hw->rx_stop++;
+}
+
+/**
+ * netdev_intr - interrupt handling
+ * @irq:	Interrupt number.
+ * @dev_id:	Network device.
+ *
+ * This function is called by upper network layer to signal interrupt.
+ *
+ * Return IRQ_HANDLED if interrupt is handled.
+ */
+static irqreturn_t netdev_intr(int irq, void *dev_id)
+{
+	uint int_enable = 0;
+	struct net_device *dev = (struct net_device *) dev_id;
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+
+	hw_read_intr(hw, &int_enable);
+
+	/* Not our interrupt! */
+	if (!int_enable)
+		return IRQ_NONE;
+
+	do {
+		hw_ack_intr(hw, int_enable);
+		int_enable &= hw->intr_mask;
+
+		if (unlikely(int_enable & KS884X_INT_TX_MASK)) {
+			hw_dis_intr_bit(hw, KS884X_INT_TX_MASK);
+			tasklet_schedule(&hw_priv->tx_tasklet);
+		}
+
+		if (likely(int_enable & KS884X_INT_RX)) {
+			hw_dis_intr_bit(hw, KS884X_INT_RX);
+			tasklet_schedule(&hw_priv->rx_tasklet);
+		}
+
+		if (unlikely(int_enable & KS884X_INT_RX_OVERRUN)) {
+			priv->stats.rx_fifo_errors++;
+			hw_resume_rx(hw);
+		}
+
+		if (unlikely(int_enable & KS884X_INT_PHY)) {
+			struct ksz_port *port = &priv->port;
+
+			hw->features |= LINK_INT_WORKING;
+			port_get_link_speed(port);
+		}
+
+		if (unlikely(int_enable & KS884X_INT_RX_STOPPED)) {
+			handle_rx_stop(hw);
+			break;
+		}
+
+		if (unlikely(int_enable & KS884X_INT_TX_STOPPED)) {
+			u32 data;
+
+			hw->intr_mask &= ~KS884X_INT_TX_STOPPED;
+			printk(KERN_INFO "Tx stopped\n");
+			data = readl(hw->io + KS_DMA_TX_CTRL);
+			if (!(data & DMA_TX_ENABLE))
+				printk(KERN_INFO "Tx disabled\n");
+			break;
+		}
+	} while (0);
+
+	hw_ena_intr(hw);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Linux network device functions
+ */
+
+static unsigned long next_jiffies;
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void netdev_netpoll(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+
+	hw_dis_intr(&hw_priv->hw);
+	netdev_intr(dev->irq, dev);
+}
+#endif
+
+static void bridge_change(struct ksz_hw *hw)
+{
+	int port;
+	u8  member;
+	struct ksz_switch *sw = hw->ksz_switch;
+
+	/* No ports in forwarding state. */
+	if (!sw->member) {
+		port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_SIMPLE);
+		sw_block_addr(hw);
+	}
+	for (port = 0; port < SWITCH_PORT_NUM; port++) {
+		if (STP_STATE_FORWARDING == sw->port_cfg[port].stp_state)
+			member = HOST_MASK | sw->member;
+		else
+			member = HOST_MASK | (1 << port);
+		if (member != sw->port_cfg[port].member)
+			sw_cfg_port_base_vlan(hw, port, member);
+	}
+}
+
+/**
+ * netdev_close - close network device
+ * @dev:	Network device.
+ *
+ * This function process the close operation of network device.  This is caused
+ * by the user command "ifconfig ethX down."
+ *
+ * Return 0 if successful; otherwise an error code indicating failure.
+ */
+static int netdev_close(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_port *port = &priv->port;
+	struct ksz_hw *hw = &hw_priv->hw;
+	int pi;
+
+	netif_stop_queue(dev);
+
+	ksz_stop_timer(&priv->monitor_timer_info);
+
+	/* Need to shut the port manually in multiple device interfaces mode. */
+	if (hw->dev_count > 1) {
+		port_set_stp_state(hw, port->first_port, STP_STATE_DISABLED);
+
+		/* Port is closed.  Need to change bridge setting. */
+		if (hw->features & STP_SUPPORT) {
+			pi = 1 << port->first_port;
+			if (hw->ksz_switch->member & pi) {
+				hw->ksz_switch->member &= ~pi;
+				bridge_change(hw);
+			}
+		}
+	}
+	if (port->first_port > 0)
+		hw_del_addr(hw, dev->dev_addr);
+	if (!hw_priv->wol_enable)
+		port_set_power_saving(port, true);
+
+	if (priv->multicast)
+		--hw->all_multi;
+	if (priv->promiscuous)
+		--hw->promiscuous;
+
+	hw_priv->opened--;
+	if (!(hw_priv->opened)) {
+		ksz_stop_timer(&hw_priv->mib_timer_info);
+		flush_work(&hw_priv->mib_read);
+
+		hw_dis_intr(hw);
+		hw_disable(hw);
+		hw_clr_multicast(hw);
+
+		/* Delay for receive task to stop scheduling itself. */
+		msleep(2000 / HZ);
+
+		tasklet_disable(&hw_priv->rx_tasklet);
+		tasklet_disable(&hw_priv->tx_tasklet);
+		free_irq(dev->irq, hw_priv->dev);
+
+		transmit_cleanup(hw_priv, 0);
+		hw_reset_pkts(&hw->rx_desc_info);
+		hw_reset_pkts(&hw->tx_desc_info);
+
+		/* Clean out static MAC table when the switch is shutdown. */
+		if (hw->features & STP_SUPPORT)
+			sw_clr_sta_mac_table(hw);
+	}
+
+	return 0;
+}
+
+static void hw_cfg_huge_frame(struct dev_info *hw_priv, struct ksz_hw *hw)
+{
+	if (hw->ksz_switch) {
+		u32 data;
+
+		data = readw(hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
+		if (hw->features & RX_HUGE_FRAME)
+			data |= SWITCH_HUGE_PACKET;
+		else
+			data &= ~SWITCH_HUGE_PACKET;
+		writew(data, hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
+	}
+	if (hw->features & RX_HUGE_FRAME) {
+		hw->rx_cfg |= DMA_RX_ERROR;
+		hw_priv->dev_rcv = dev_rcv_special;
+	} else {
+		hw->rx_cfg &= ~DMA_RX_ERROR;
+		if (hw->dev_count > 1)
+			hw_priv->dev_rcv = port_rcv_packets;
+		else
+			hw_priv->dev_rcv = dev_rcv_packets;
+	}
+}
+
+static int prepare_hardware(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	int rc = 0;
+
+	/* Remember the network device that requests interrupts. */
+	hw_priv->dev = dev;
+	rc = request_irq(dev->irq, netdev_intr, IRQF_SHARED, dev->name, dev);
+	if (rc)
+		return rc;
+	tasklet_enable(&hw_priv->rx_tasklet);
+	tasklet_enable(&hw_priv->tx_tasklet);
+
+	hw->promiscuous = 0;
+	hw->all_multi = 0;
+	hw->multi_list_size = 0;
+
+	hw_reset(hw);
+
+	hw_set_desc_base(hw,
+		hw->tx_desc_info.ring_phys, hw->rx_desc_info.ring_phys);
+	hw_set_addr(hw);
+	hw_cfg_huge_frame(hw_priv, hw);
+	ksz_init_rx_buffers(hw_priv);
+	return 0;
+}
+
+/**
+ * netdev_open - open network device
+ * @dev:	Network device.
+ *
+ * This function process the open operation of network device.  This is caused
+ * by the user command "ifconfig ethX up."
+ *
+ * Return 0 if successful; otherwise an error code indicating failure.
+ */
+static int netdev_open(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct ksz_port *port = &priv->port;
+	int i;
+	int p;
+	int rc = 0;
+
+	priv->multicast = 0;
+	priv->promiscuous = 0;
+
+	/* Reset device statistics. */
+	memset(&priv->stats, 0, sizeof(struct net_device_stats));
+	memset((void *) port->counter, 0,
+		(sizeof(u64) * OID_COUNTER_LAST));
+
+	if (!(hw_priv->opened)) {
+		rc = prepare_hardware(dev);
+		if (rc)
+			return rc;
+		for (i = 0; i < hw->mib_port_cnt; i++) {
+			if (next_jiffies < jiffies)
+				next_jiffies = jiffies + HZ * 2;
+			else
+				next_jiffies += HZ * 1;
+			hw_priv->counter[i].time = next_jiffies;
+			hw->port_mib[i].state = media_disconnected;
+			port_init_cnt(hw, i);
+		}
+		if (hw->ksz_switch)
+			hw->port_mib[HOST_PORT].state = media_connected;
+		else {
+			hw_add_wol_bcast(hw);
+			hw_cfg_wol_pme(hw, 0);
+			hw_clr_wol_pme_status(&hw_priv->hw);
+		}
+	}
+	port_set_power_saving(port, false);
+
+	for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
+		/*
+		 * Initialize to invalid value so that link detection
+		 * is done.
+		 */
+		hw->port_info[p].partner = 0xFF;
+		hw->port_info[p].state = media_disconnected;
+	}
+
+	/* Need to open the port in multiple device interfaces mode. */
+	if (hw->dev_count > 1) {
+		port_set_stp_state(hw, port->first_port, STP_STATE_SIMPLE);
+		if (port->first_port > 0)
+			hw_add_addr(hw, dev->dev_addr);
+	}
+
+	port_get_link_speed(port);
+	if (port->force_link)
+		port_force_link_speed(port);
+	else
+		port_set_link_speed(port);
+
+	if (!(hw_priv->opened)) {
+		hw_setup_intr(hw);
+		hw_enable(hw);
+		hw_ena_intr(hw);
+
+		if (hw->mib_port_cnt)
+			ksz_start_timer(&hw_priv->mib_timer_info,
+				hw_priv->mib_timer_info.period);
+	}
+
+	hw_priv->opened++;
+
+	ksz_start_timer(&priv->monitor_timer_info,
+		priv->monitor_timer_info.period);
+
+	priv->media_state = port->linked->state;
+
+	if (media_connected == priv->media_state)
+		netif_carrier_on(dev);
+	else
+		netif_carrier_off(dev);
+	if (netif_msg_link(priv))
+		printk(KERN_INFO "%s link %s\n", dev->name,
+			(media_connected == priv->media_state ?
+			"on" : "off"));
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+/* RX errors = rx_errors */
+/* RX dropped = rx_dropped */
+/* RX overruns = rx_fifo_errors */
+/* RX frame = rx_crc_errors + rx_frame_errors + rx_length_errors */
+/* TX errors = tx_errors */
+/* TX dropped = tx_dropped */
+/* TX overruns = tx_fifo_errors */
+/* TX carrier = tx_aborted_errors + tx_carrier_errors + tx_window_errors */
+/* collisions = collisions */
+
+/**
+ * netdev_query_statistics - query network device statistics
+ * @dev:	Network device.
+ *
+ * This function returns the statistics of the network device.  The device
+ * needs not be opened.
+ *
+ * Return network device statistics.
+ */
+static struct net_device_stats *netdev_query_statistics(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct ksz_port *port = &priv->port;
+	struct ksz_hw *hw = &priv->adapter->hw;
+	struct ksz_port_mib *mib;
+	int i;
+	int p;
+
+	priv->stats.rx_errors = port->counter[OID_COUNTER_RCV_ERROR];
+	priv->stats.tx_errors = port->counter[OID_COUNTER_XMIT_ERROR];
+
+	/* Reset to zero to add count later. */
+	priv->stats.multicast = 0;
+	priv->stats.collisions = 0;
+	priv->stats.rx_length_errors = 0;
+	priv->stats.rx_crc_errors = 0;
+	priv->stats.rx_frame_errors = 0;
+	priv->stats.tx_window_errors = 0;
+
+	for (i = 0, p = port->first_port; i < port->mib_port_cnt; i++, p++) {
+		mib = &hw->port_mib[p];
+
+		priv->stats.multicast += (unsigned long)
+			mib->counter[MIB_COUNTER_RX_MULTICAST];
+
+		priv->stats.collisions += (unsigned long)
+			mib->counter[MIB_COUNTER_TX_TOTAL_COLLISION];
+
+		priv->stats.rx_length_errors += (unsigned long)(
+			mib->counter[MIB_COUNTER_RX_UNDERSIZE] +
+			mib->counter[MIB_COUNTER_RX_FRAGMENT] +
+			mib->counter[MIB_COUNTER_RX_OVERSIZE] +
+			mib->counter[MIB_COUNTER_RX_JABBER]);
+		priv->stats.rx_crc_errors += (unsigned long)
+			mib->counter[MIB_COUNTER_RX_CRC_ERR];
+		priv->stats.rx_frame_errors += (unsigned long)(
+			mib->counter[MIB_COUNTER_RX_ALIGNMENT_ERR] +
+			mib->counter[MIB_COUNTER_RX_SYMBOL_ERR]);
+
+		priv->stats.tx_window_errors += (unsigned long)
+			mib->counter[MIB_COUNTER_TX_LATE_COLLISION];
+	}
+
+	return &priv->stats;
+}
+
+/**
+ * netdev_set_mac_address - set network device MAC address
+ * @dev:	Network device.
+ * @addr:	Buffer of MAC address.
+ *
+ * This function is used to set the MAC address of the network device.
+ *
+ * Return 0 to indicate success.
+ */
+static int netdev_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct sockaddr *mac = addr;
+	uint interrupt;
+
+	if (priv->port.first_port > 0)
+		hw_del_addr(hw, dev->dev_addr);
+	else {
+		hw->mac_override = 1;
+		memcpy(hw->override_addr, mac->sa_data, MAC_ADDR_LEN);
+	}
+
+	memcpy(dev->dev_addr, mac->sa_data, MAX_ADDR_LEN);
+
+	interrupt = hw_block_intr(hw);
+
+	if (priv->port.first_port > 0)
+		hw_add_addr(hw, dev->dev_addr);
+	else
+		hw_set_addr(hw);
+	hw_restore_intr(hw, interrupt);
+
+	return 0;
+}
+
+static void dev_set_promiscuous(struct net_device *dev, struct dev_priv *priv,
+	struct ksz_hw *hw, int promiscuous)
+{
+	if (promiscuous != priv->promiscuous) {
+		u8 prev_state = hw->promiscuous;
+
+		if (promiscuous)
+			++hw->promiscuous;
+		else
+			--hw->promiscuous;
+		priv->promiscuous = promiscuous;
+
+		/* Turn on/off promiscuous mode. */
+		if (hw->promiscuous <= 1 && prev_state <= 1)
+			hw_set_promiscuous(hw, hw->promiscuous);
+
+		/*
+		 * Port is not in promiscuous mode, meaning it is released
+		 * from the bridge.
+		 */
+		if ((hw->features & STP_SUPPORT) && !promiscuous &&
+				dev->br_port) {
+			struct ksz_switch *sw = hw->ksz_switch;
+			int port = priv->port.first_port;
+
+			port_set_stp_state(hw, port, STP_STATE_DISABLED);
+			port = 1 << port;
+			if (sw->member & port) {
+				sw->member &= ~port;
+				bridge_change(hw);
+			}
+		}
+	}
+}
+
+static void dev_set_multicast(struct dev_priv *priv, struct ksz_hw *hw,
+	int multicast)
+{
+	if (multicast != priv->multicast) {
+		u8 all_multi = hw->all_multi;
+
+		if (multicast)
+			++hw->all_multi;
+		else
+			--hw->all_multi;
+		priv->multicast = multicast;
+
+		/* Turn on/off all multicast mode. */
+		if (hw->all_multi <= 1 && all_multi <= 1)
+			hw_set_multicast(hw, hw->all_multi);
+	}
+}
+
+/**
+ * netdev_set_rx_mode
+ * @dev:	Network device.
+ *
+ * This routine is used to set multicast addresses or put the network device
+ * into promiscuous mode.
+ */
+static void netdev_set_rx_mode(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct dev_mc_list *mc_ptr;
+	int multicast = (dev->flags & IFF_ALLMULTI);
+
+	dev_set_promiscuous(dev, priv, hw, (dev->flags & IFF_PROMISC));
+
+	if (hw_priv->hw.dev_count > 1)
+		multicast |= (dev->flags & IFF_MULTICAST);
+	dev_set_multicast(priv, hw, multicast);
+
+	/* Cannot use different hashes in multiple device interfaces mode. */
+	if (hw_priv->hw.dev_count > 1)
+		return;
+
+	if ((dev->flags & IFF_MULTICAST) && dev->mc_count) {
+		int i = 0;
+
+		/* List too big to support so turn on all multicast mode. */
+		if (dev->mc_count > MAX_MULTICAST_LIST) {
+			if (MAX_MULTICAST_LIST != hw->multi_list_size) {
+				hw->multi_list_size = MAX_MULTICAST_LIST;
+				++hw->all_multi;
+				hw_set_multicast(hw, hw->all_multi);
+			}
+			return;
+		}
+
+		for (mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) {
+			if (!(*mc_ptr->dmi_addr & 1))
+				continue;
+			if (i >= MAX_MULTICAST_LIST)
+				break;
+			memcpy(hw->multi_list[i++], mc_ptr->dmi_addr,
+				MAC_ADDR_LEN);
+		}
+		hw->multi_list_size = (u8) i;
+		hw_set_grp_addr(hw);
+	} else {
+		if (MAX_MULTICAST_LIST == hw->multi_list_size) {
+			--hw->all_multi;
+			hw_set_multicast(hw, hw->all_multi);
+		}
+		hw->multi_list_size = 0;
+		hw_clr_multicast(hw);
+	}
+}
+
+static int netdev_change_mtu(struct net_device *dev, int new_mtu)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	int hw_mtu;
+
+	if (netif_running(dev))
+		return -EBUSY;
+
+	/* Cannot use different MTU in multiple device interfaces mode. */
+	if (hw->dev_count > 1)
+		if (dev != hw_priv->dev)
+			return 0;
+	if (new_mtu < 60)
+		return -EINVAL;
+
+	if (dev->mtu != new_mtu) {
+		hw_mtu = new_mtu + ETHERNET_HEADER_SIZE + 4;
+		if (hw_mtu > MAX_RX_BUF_SIZE)
+			return -EINVAL;
+		if (hw_mtu > REGULAR_RX_BUF_SIZE) {
+			hw->features |= RX_HUGE_FRAME;
+			hw_mtu = MAX_RX_BUF_SIZE;
+		} else {
+			hw->features &= ~RX_HUGE_FRAME;
+			hw_mtu = REGULAR_RX_BUF_SIZE;
+		}
+		hw_mtu = (hw_mtu + 3) & ~3;
+		hw_priv->mtu = hw_mtu;
+		dev->mtu = new_mtu;
+	}
+	return 0;
+}
+
+/**
+ * netdev_ioctl - I/O control processing
+ * @dev:	Network device.
+ * @ifr:	Interface request structure.
+ * @cmd:	I/O control code.
+ *
+ * This function is used to process I/O control calls.
+ *
+ * Return 0 to indicate success.
+ */
+static int netdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct ksz_port *port = &priv->port;
+	int rc;
+	int result = 0;
+	struct mii_ioctl_data *data = if_mii(ifr);
+
+	if (down_interruptible(&priv->proc_sem))
+		return -ERESTARTSYS;
+
+	/* assume success */
+	rc = 0;
+	switch (cmd) {
+	/* Get address of MII PHY in use. */
+	case SIOCGMIIPHY:
+		data->phy_id = priv->id;
+
+		/* Fallthrough... */
+
+	/* Read MII PHY register. */
+	case SIOCGMIIREG:
+		if (data->phy_id != priv->id || data->reg_num >= 6)
+			result = -EIO;
+		else
+			hw_r_phy(hw, port->linked->port_id, data->reg_num,
+				&data->val_out);
+		break;
+
+	/* Write MII PHY register. */
+	case SIOCSMIIREG:
+		if (!capable(CAP_NET_ADMIN))
+			result = -EPERM;
+		else if (data->phy_id != priv->id || data->reg_num >= 6)
+			result = -EIO;
+		else
+			hw_w_phy(hw, port->linked->port_id, data->reg_num,
+				data->val_in);
+		break;
+
+	default:
+		result = -EOPNOTSUPP;
+	}
+
+	up(&priv->proc_sem);
+
+	return result;
+}
+
+/*
+ * MII support
+ */
+
+/**
+ * mdio_read - read PHY register
+ * @dev:	Network device.
+ * @phy_id:	The PHY id.
+ * @reg_num:	The register number.
+ *
+ * This function returns the PHY register value.
+ *
+ * Return the register value.
+ */
+static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct ksz_port *port = &priv->port;
+	struct ksz_hw *hw = port->hw;
+	u16 val_out;
+
+	hw_r_phy(hw, port->linked->port_id, reg_num << 1, &val_out);
+	return val_out;
+}
+
+/**
+ * mdio_write - set PHY register
+ * @dev:	Network device.
+ * @phy_id:	The PHY id.
+ * @reg_num:	The register number.
+ * @val:	The register value.
+ *
+ * This procedure sets the PHY register value.
+ */
+static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct ksz_port *port = &priv->port;
+	struct ksz_hw *hw = port->hw;
+	int i;
+	int pi;
+
+	for (i = 0, pi = port->first_port; i < port->port_cnt; i++, pi++)
+		hw_w_phy(hw, pi, reg_num << 1, val);
+}
+
+/*
+ * ethtool support
+ */
+
+#define EEPROM_SIZE			0x40
+
+static u16 eeprom_data[EEPROM_SIZE] = { 0 };
+
+#define ADVERTISED_ALL			\
+	(ADVERTISED_10baseT_Half |	\
+	ADVERTISED_10baseT_Full |	\
+	ADVERTISED_100baseT_Half |	\
+	ADVERTISED_100baseT_Full)
+
+/* These functions use the MII functions in mii.c. */
+
+/**
+ * netdev_get_settings - get network device settings
+ * @dev:	Network device.
+ * @cmd:	Ethtool command.
+ *
+ * This function queries the PHY and returns its state in the ethtool command.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+
+	mutex_lock(&hw_priv->lock);
+	mii_ethtool_gset(&priv->mii_if, cmd);
+	cmd->advertising |= SUPPORTED_TP;
+	mutex_unlock(&hw_priv->lock);
+
+	/* Save advertised settings for workaround in next function. */
+	priv->advertising = cmd->advertising;
+	return 0;
+}
+
+/**
+ * netdev_set_settings - set network device settings
+ * @dev:	Network device.
+ * @cmd:	Ethtool command.
+ *
+ * This function sets the PHY according to the ethtool command.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_port *port = &priv->port;
+	int rc;
+
+	/*
+	 * ethtool utility does not change advertised setting if auto
+	 * negotiation is not specified explicitly.
+	 */
+	if (cmd->autoneg && priv->advertising == cmd->advertising) {
+		cmd->advertising |= ADVERTISED_ALL;
+		if (10 == cmd->speed)
+			cmd->advertising &=
+				~(ADVERTISED_100baseT_Full |
+				ADVERTISED_100baseT_Half);
+		else if (100 == cmd->speed)
+			cmd->advertising &=
+				~(ADVERTISED_10baseT_Full |
+				ADVERTISED_10baseT_Half);
+		if (0 == cmd->duplex)
+			cmd->advertising &=
+				~(ADVERTISED_100baseT_Full |
+				ADVERTISED_10baseT_Full);
+		else if (1 == cmd->duplex)
+			cmd->advertising &=
+				~(ADVERTISED_100baseT_Half |
+				ADVERTISED_10baseT_Half);
+	}
+	mutex_lock(&hw_priv->lock);
+	if (cmd->autoneg &&
+			(cmd->advertising & ADVERTISED_ALL) ==
+			ADVERTISED_ALL) {
+		port->duplex = 0;
+		port->speed = 0;
+		port->force_link = 0;
+	} else {
+		port->duplex = cmd->duplex + 1;
+		if (cmd->speed != 1000)
+			port->speed = cmd->speed;
+		if (cmd->autoneg)
+			port->force_link = 0;
+		else
+			port->force_link = 1;
+	}
+	rc = mii_ethtool_sset(&priv->mii_if, cmd);
+	mutex_unlock(&hw_priv->lock);
+	return rc;
+}
+
+/**
+ * netdev_nway_reset - restart auto-negotiation
+ * @dev:	Network device.
+ *
+ * This function restarts the PHY for auto-negotiation.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_nway_reset(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	int rc;
+
+	mutex_lock(&hw_priv->lock);
+	rc = mii_nway_restart(&priv->mii_if);
+	mutex_unlock(&hw_priv->lock);
+	return rc;
+}
+
+/**
+ * netdev_get_link - get network device link status
+ * @dev:	Network device.
+ *
+ * This function gets the link status from the PHY.
+ *
+ * Return true if PHY is linked and false otherwise.
+ */
+static u32 netdev_get_link(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	int rc;
+
+	rc = mii_link_ok(&priv->mii_if);
+	return rc;
+}
+
+/**
+ * netdev_get_drvinfo - get network driver information
+ * @dev:	Network device.
+ * @info:	Ethtool driver info data structure.
+ *
+ * This procedure returns the driver information.
+ */
+static void netdev_get_drvinfo(struct net_device *dev,
+	struct ethtool_drvinfo *info)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	strcpy(info->bus_info, pci_name(hw_priv->pdev));
+}
+
+/**
+ * netdev_get_regs_len - get length of register dump
+ * @dev:	Network device.
+ *
+ * This function returns the length of the register dump.
+ *
+ * Return length of the register dump.
+ */
+static struct hw_regs {
+	int start;
+	int end;
+} hw_regs_range[] = {
+	{ KS_DMA_TX_CTRL,	KS884X_INTERRUPTS_STATUS },
+	{ KS_ADD_ADDR_0_LO,	KS_ADD_ADDR_F_HI },
+	{ KS884X_ADDR_0_OFFSET,	KS8841_WOL_FRAME_BYTE2_OFFSET },
+	{ KS884X_SIDER_P,	KS8842_SGCR7_P },
+	{ KS8842_MACAR1_P,	KS8842_TOSR8_P },
+	{ KS884X_P1MBCR_P,	KS8842_P3ERCR_P },
+	{ 0, 0 }
+};
+
+static int netdev_get_regs_len(struct net_device *dev)
+{
+	struct hw_regs *range = hw_regs_range;
+	int regs_len = 0x10 * sizeof(u32);
+
+	while (range->end > range->start) {
+		regs_len += (range->end - range->start + 3) / 4 * 4;
+		range++;
+	}
+	return regs_len;
+}
+
+/**
+ * netdev_get_regs - get register dump
+ * @dev:	Network device.
+ * @regs:	Ethtool registers data structure.
+ * @ptr:	Buffer to store the register values.
+ *
+ * This procedure dumps the register values in the provided buffer.
+ */
+static void netdev_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+	void *ptr)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	int *buf = (int *) ptr;
+	struct hw_regs *range = hw_regs_range;
+	int len;
+
+	mutex_lock(&hw_priv->lock);
+	regs->version = 0;
+	for (len = 0; len < 0x40; len += 4) {
+		pci_read_config_dword(hw_priv->pdev, len, buf);
+		buf++;
+	}
+	while (range->end > range->start) {
+		for (len = range->start; len < range->end; len += 4) {
+			*buf = readl(hw->io + len);
+			buf++;
+		}
+		range++;
+	}
+	mutex_unlock(&hw_priv->lock);
+}
+
+#define WOL_SUPPORT			\
+	(WAKE_PHY | WAKE_MAGIC |	\
+	WAKE_UCAST | WAKE_MCAST |	\
+	WAKE_BCAST | WAKE_ARP)
+
+/**
+ * netdev_get_wol - get Wake-on-LAN support
+ * @dev:	Network device.
+ * @wol:	Ethtool Wake-on-LAN data structure.
+ *
+ * This procedure returns Wake-on-LAN support.
+ */
+static void netdev_get_wol(struct net_device *dev,
+	struct ethtool_wolinfo *wol)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+
+	wol->supported = hw_priv->wol_support;
+	wol->wolopts = hw_priv->wol_enable;
+	memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+
+/**
+ * netdev_set_wol - set Wake-on-LAN support
+ * @dev:	Network device.
+ * @wol:	Ethtool Wake-on-LAN data structure.
+ *
+ * This function sets Wake-on-LAN support.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_set_wol(struct net_device *dev,
+	struct ethtool_wolinfo *wol)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+
+	/* Need to find a way to retrieve the device IP address. */
+	u8 net_addr[] = { 192, 168, 1, 1 };
+
+	if (wol->wolopts & ~hw_priv->wol_support)
+		return -EINVAL;
+
+	hw_priv->wol_enable = wol->wolopts;
+
+	/* Link wakeup cannot really be disabled. */
+	if (wol->wolopts)
+		hw_priv->wol_enable |= WAKE_PHY;
+	hw_enable_wol(&hw_priv->hw, hw_priv->wol_enable, net_addr);
+	return 0;
+}
+
+/**
+ * netdev_get_msglevel - get debug message level
+ * @dev:	Network device.
+ *
+ * This function returns current debug message level.
+ *
+ * Return current debug message flags.
+ */
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+
+	return priv->msg_enable;
+}
+
+/**
+ * netdev_set_msglevel - set debug message level
+ * @dev:	Network device.
+ * @value:	Debug message flags.
+ *
+ * This procedure sets debug message level.
+ */
+static void netdev_set_msglevel(struct net_device *dev, u32 value)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+
+	priv->msg_enable = value;
+}
+
+/**
+ * netdev_get_eeprom_len - get EEPROM length
+ * @dev:	Network device.
+ *
+ * This function returns the length of the EEPROM.
+ *
+ * Return length of the EEPROM.
+ */
+static int netdev_get_eeprom_len(struct net_device *dev)
+{
+	return EEPROM_SIZE * 2;
+}
+
+/**
+ * netdev_get_eeprom - get EEPROM data
+ * @dev:	Network device.
+ * @eeprom:	Ethtool EEPROM data structure.
+ * @data:	Buffer to store the EEPROM data.
+ *
+ * This function dumps the EEPROM data in the provided buffer.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+#define EEPROM_MAGIC			0x10A18842
+
+static int netdev_get_eeprom(struct net_device *dev,
+	struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	u8 *eeprom_byte = (u8 *) eeprom_data;
+	int i;
+	int len;
+
+	len = (eeprom->offset + eeprom->len + 1) / 2;
+	for (i = eeprom->offset / 2; i < len; i++)
+		eeprom_data[i] = eeprom_read(&hw_priv->hw, i);
+	eeprom->magic = EEPROM_MAGIC;
+	memcpy(data, &eeprom_byte[eeprom->offset], eeprom->len);
+
+	return 0;
+}
+
+/**
+ * netdev_set_eeprom - write EEPROM data
+ * @dev:	Network device.
+ * @eeprom:	Ethtool EEPROM data structure.
+ * @data:	Data buffer.
+ *
+ * This function modifies the EEPROM data one byte at a time.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_set_eeprom(struct net_device *dev,
+	struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	u16 eeprom_word[EEPROM_SIZE];
+	u8 *eeprom_byte = (u8 *) eeprom_word;
+	int i;
+	int len;
+
+	if (eeprom->magic != EEPROM_MAGIC)
+		return 1;
+
+	len = (eeprom->offset + eeprom->len + 1) / 2;
+	for (i = eeprom->offset / 2; i < len; i++)
+		eeprom_data[i] = eeprom_read(&hw_priv->hw, i);
+	memcpy(eeprom_word, eeprom_data, EEPROM_SIZE * 2);
+	memcpy(&eeprom_byte[eeprom->offset], data, eeprom->len);
+	for (i = 0; i < EEPROM_SIZE; i++)
+		if (eeprom_word[i] != eeprom_data[i]) {
+			eeprom_data[i] = eeprom_word[i];
+			eeprom_write(&hw_priv->hw, i, eeprom_data[i]);
+	}
+
+	return 0;
+}
+
+/**
+ * netdev_get_pauseparam - get flow control parameters
+ * @dev:	Network device.
+ * @pause:	Ethtool PAUSE settings data structure.
+ *
+ * This procedure returns the PAUSE control flow settings.
+ */
+static void netdev_get_pauseparam(struct net_device *dev,
+	struct ethtool_pauseparam *pause)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+
+	pause->autoneg = (hw->overrides & PAUSE_FLOW_CTRL) ? 0 : 1;
+	if (!hw->ksz_switch) {
+		pause->rx_pause =
+			(hw->rx_cfg & DMA_RX_FLOW_ENABLE) ? 1 : 0;
+		pause->tx_pause =
+			(hw->tx_cfg & DMA_TX_FLOW_ENABLE) ? 1 : 0;
+	} else {
+		pause->rx_pause =
+			(sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+				SWITCH_RX_FLOW_CTRL)) ? 1 : 0;
+		pause->tx_pause =
+			(sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+				SWITCH_TX_FLOW_CTRL)) ? 1 : 0;
+	}
+}
+
+/**
+ * netdev_set_pauseparam - set flow control parameters
+ * @dev:	Network device.
+ * @pause:	Ethtool PAUSE settings data structure.
+ *
+ * This function sets the PAUSE control flow settings.
+ * Not implemented yet.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_set_pauseparam(struct net_device *dev,
+	struct ethtool_pauseparam *pause)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct ksz_port *port = &priv->port;
+
+	mutex_lock(&hw_priv->lock);
+	if (pause->autoneg) {
+		if (!pause->rx_pause && !pause->tx_pause)
+			port->flow_ctrl = PHY_NO_FLOW_CTRL;
+		else
+			port->flow_ctrl = PHY_FLOW_CTRL;
+		hw->overrides &= ~PAUSE_FLOW_CTRL;
+		port->force_link = 0;
+		if (hw->ksz_switch) {
+			sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+				SWITCH_RX_FLOW_CTRL, 1);
+			sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+				SWITCH_TX_FLOW_CTRL, 1);
+		}
+		port_set_link_speed(port);
+	} else {
+		hw->overrides |= PAUSE_FLOW_CTRL;
+		if (hw->ksz_switch) {
+			sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+				SWITCH_RX_FLOW_CTRL, pause->rx_pause);
+			sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+				SWITCH_TX_FLOW_CTRL, pause->tx_pause);
+		} else
+			set_flow_ctrl(hw, pause->rx_pause, pause->tx_pause);
+	}
+	mutex_unlock(&hw_priv->lock);
+
+	return 0;
+}
+
+/**
+ * netdev_get_ringparam - get tx/rx ring parameters
+ * @dev:	Network device.
+ * @pause:	Ethtool RING settings data structure.
+ *
+ * This procedure returns the TX/RX ring settings.
+ */
+static void netdev_get_ringparam(struct net_device *dev,
+	struct ethtool_ringparam *ring)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+
+	ring->tx_max_pending = (1 << 9);
+	ring->tx_pending = hw->tx_desc_info.alloc;
+	ring->rx_max_pending = (1 << 9);
+	ring->rx_pending = hw->rx_desc_info.alloc;
+}
+
+#define STATS_LEN			(TOTAL_PORT_COUNTER_NUM)
+
+static struct {
+	char string[ETH_GSTRING_LEN];
+} ethtool_stats_keys[STATS_LEN] = {
+	{ "rx_lo_priority_octets" },
+	{ "rx_hi_priority_octets" },
+	{ "rx_undersize_packets" },
+	{ "rx_fragments" },
+	{ "rx_oversize_packets" },
+	{ "rx_jabbers" },
+	{ "rx_symbol_errors" },
+	{ "rx_crc_errors" },
+	{ "rx_align_errors" },
+	{ "rx_mac_ctrl_packets" },
+	{ "rx_pause_packets" },
+	{ "rx_bcast_packets" },
+	{ "rx_mcast_packets" },
+	{ "rx_ucast_packets" },
+	{ "rx_64_or_less_octet_packets" },
+	{ "rx_65_to_127_octet_packets" },
+	{ "rx_128_to_255_octet_packets" },
+	{ "rx_256_to_511_octet_packets" },
+	{ "rx_512_to_1023_octet_packets" },
+	{ "rx_1024_to_1522_octet_packets" },
+
+	{ "tx_lo_priority_octets" },
+	{ "tx_hi_priority_octets" },
+	{ "tx_late_collisions" },
+	{ "tx_pause_packets" },
+	{ "tx_bcast_packets" },
+	{ "tx_mcast_packets" },
+	{ "tx_ucast_packets" },
+	{ "tx_deferred" },
+	{ "tx_total_collisions" },
+	{ "tx_excessive_collisions" },
+	{ "tx_single_collisions" },
+	{ "tx_mult_collisions" },
+
+	{ "rx_discards" },
+	{ "tx_discards" },
+};
+
+/**
+ * netdev_get_strings - get statistics identity strings
+ * @dev:	Network device.
+ * @stringset:	String set identifier.
+ * @buf:	Buffer to store the strings.
+ *
+ * This procedure returns the strings used to identify the statistics.
+ */
+static void netdev_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+
+	if (ETH_SS_STATS == stringset)
+		memcpy(buf, &ethtool_stats_keys,
+			ETH_GSTRING_LEN * hw->mib_cnt);
+}
+
+/**
+ * netdev_get_sset_count - get statistics size
+ * @dev:	Network device.
+ * @sset:	The statistics set number.
+ *
+ * This function returns the size of the statistics to be reported.
+ *
+ * Return size of the statistics to be reported.
+ */
+static int netdev_get_sset_count(struct net_device *dev, int sset)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+
+	switch (sset) {
+	case ETH_SS_STATS:
+		return hw->mib_cnt;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+/**
+ * netdev_get_ethtool_stats - get network device statistics
+ * @dev:	Network device.
+ * @stats:	Ethtool statistics data structure.
+ * @data:	Buffer to store the statistics.
+ *
+ * This procedure returns the statistics.
+ */
+static void netdev_get_ethtool_stats(struct net_device *dev,
+	struct ethtool_stats *stats, u64 *data)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct ksz_port *port = &priv->port;
+	int n_stats = stats->n_stats;
+	int i;
+	int n;
+	int p;
+	int rc;
+	u64 counter[TOTAL_PORT_COUNTER_NUM];
+
+	mutex_lock(&hw_priv->lock);
+	n = SWITCH_PORT_NUM;
+	for (i = 0, p = port->first_port; i < port->mib_port_cnt; i++, p++) {
+		if (media_connected == hw->port_mib[p].state) {
+			hw_priv->counter[p].read = 1;
+
+			/* Remember first port that requests read. */
+			if (n == SWITCH_PORT_NUM)
+				n = p;
+		}
+	}
+	mutex_unlock(&hw_priv->lock);
+
+	if (n < SWITCH_PORT_NUM)
+		schedule_work(&hw_priv->mib_read);
+
+	if (1 == port->mib_port_cnt && n < SWITCH_PORT_NUM) {
+		p = n;
+		rc = wait_event_interruptible_timeout(
+			hw_priv->counter[p].counter,
+			2 == hw_priv->counter[p].read,
+			HZ * 1);
+	} else
+		for (i = 0, p = n; i < port->mib_port_cnt - n; i++, p++) {
+			if (0 == i) {
+				rc = wait_event_interruptible_timeout(
+					hw_priv->counter[p].counter,
+					2 == hw_priv->counter[p].read,
+					HZ * 2);
+			} else if (hw->port_mib[p].cnt_ptr) {
+				rc = wait_event_interruptible_timeout(
+					hw_priv->counter[p].counter,
+					2 == hw_priv->counter[p].read,
+					HZ * 1);
+			}
+		}
+
+	get_mib_counters(hw, port->first_port, port->mib_port_cnt, counter);
+	n = hw->mib_cnt;
+	if (n > n_stats)
+		n = n_stats;
+	n_stats -= n;
+	for (i = 0; i < n; i++)
+		*data++ = counter[i];
+}
+
+/**
+ * netdev_get_rx_csum - get receive checksum support
+ * @dev:	Network device.
+ *
+ * This function gets receive checksum support setting.
+ *
+ * Return true if receive checksum is enabled; false otherwise.
+ */
+static u32 netdev_get_rx_csum(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+
+	return hw->rx_cfg &
+		(DMA_RX_CSUM_UDP |
+		DMA_RX_CSUM_TCP |
+		DMA_RX_CSUM_IP);
+}
+
+/**
+ * netdev_set_rx_csum - set receive checksum support
+ * @dev:	Network device.
+ * @data:	Zero to disable receive checksum support.
+ *
+ * This function sets receive checksum support setting.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_set_rx_csum(struct net_device *dev, u32 data)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	u32 new_setting = hw->rx_cfg;
+
+	if (data)
+		new_setting |=
+			(DMA_RX_CSUM_UDP | DMA_RX_CSUM_TCP |
+			DMA_RX_CSUM_IP);
+	else
+		new_setting &=
+			~(DMA_RX_CSUM_UDP | DMA_RX_CSUM_TCP |
+			DMA_RX_CSUM_IP);
+	new_setting &= ~DMA_RX_CSUM_UDP;
+	mutex_lock(&hw_priv->lock);
+	if (new_setting != hw->rx_cfg) {
+		hw->rx_cfg = new_setting;
+		if (hw->enabled)
+			writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
+	}
+	mutex_unlock(&hw_priv->lock);
+	return 0;
+}
+
+static struct ethtool_ops netdev_ethtool_ops = {
+	.get_settings		= netdev_get_settings,
+	.set_settings		= netdev_set_settings,
+	.nway_reset		= netdev_nway_reset,
+	.get_link		= netdev_get_link,
+	.get_drvinfo		= netdev_get_drvinfo,
+	.get_regs_len		= netdev_get_regs_len,
+	.get_regs		= netdev_get_regs,
+	.get_wol		= netdev_get_wol,
+	.set_wol		= netdev_set_wol,
+	.get_msglevel		= netdev_get_msglevel,
+	.set_msglevel		= netdev_set_msglevel,
+	.get_eeprom_len		= netdev_get_eeprom_len,
+	.get_eeprom		= netdev_get_eeprom,
+	.set_eeprom		= netdev_set_eeprom,
+	.get_pauseparam		= netdev_get_pauseparam,
+	.set_pauseparam		= netdev_set_pauseparam,
+	.get_ringparam		= netdev_get_ringparam,
+	.get_strings		= netdev_get_strings,
+	.get_sset_count		= netdev_get_sset_count,
+	.get_ethtool_stats	= netdev_get_ethtool_stats,
+	.get_rx_csum		= netdev_get_rx_csum,
+	.set_rx_csum		= netdev_set_rx_csum,
+	.get_tx_csum		= ethtool_op_get_tx_csum,
+	.set_tx_csum		= ethtool_op_set_tx_csum,
+	.get_sg			= ethtool_op_get_sg,
+	.set_sg			= ethtool_op_set_sg,
+};
+
+/*
+ * Hardware monitoring
+ */
+
+static void update_link(struct net_device *dev, struct dev_priv *priv,
+	struct ksz_port *port)
+{
+	if (priv->media_state != port->linked->state) {
+		priv->media_state = port->linked->state;
+		if (netif_running(dev)) {
+			if (media_connected == priv->media_state)
+				netif_carrier_on(dev);
+			else
+				netif_carrier_off(dev);
+			if (netif_msg_link(priv))
+				printk(KERN_INFO "%s link %s\n", dev->name,
+					(media_connected == priv->media_state ?
+					"on" : "off"));
+		}
+	}
+}
+
+static void mib_read_work(struct work_struct *work)
+{
+	struct dev_info *hw_priv =
+		container_of(work, struct dev_info, mib_read);
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct ksz_port_mib *mib;
+	int i;
+
+	next_jiffies = jiffies;
+	for (i = 0; i < hw->mib_port_cnt; i++) {
+		mib = &hw->port_mib[i];
+
+		/* Reading MIB counters or requested to read. */
+		if (mib->cnt_ptr || 1 == hw_priv->counter[i].read) {
+
+			/* Need to process receive interrupt. */
+			if (port_r_cnt(hw, i))
+				break;
+			hw_priv->counter[i].read = 0;
+
+			/* Finish reading counters. */
+			if (0 == mib->cnt_ptr) {
+				hw_priv->counter[i].read = 2;
+				wake_up_interruptible(
+					&hw_priv->counter[i].counter);
+			}
+		} else if (jiffies >= hw_priv->counter[i].time) {
+			/* Only read MIB counters when the port is connected. */
+			if (media_connected == mib->state)
+				hw_priv->counter[i].read = 1;
+			next_jiffies += HZ * 1 * hw->mib_port_cnt;
+			hw_priv->counter[i].time = next_jiffies;
+
+		/* Port is just disconnected. */
+		} else if (mib->link_down) {
+			mib->link_down = 0;
+
+			/* Read counters one last time after link is lost. */
+			hw_priv->counter[i].read = 1;
+		}
+	}
+}
+
+static void mib_monitor(unsigned long ptr)
+{
+	struct dev_info *hw_priv = (struct dev_info *) ptr;
+
+	mib_read_work(&hw_priv->mib_read);
+
+	/* This is used to verify Wake-on-LAN is working. */
+	if (hw_priv->pme_wait) {
+		if (hw_priv->pme_wait <= jiffies) {
+			hw_clr_wol_pme_status(&hw_priv->hw);
+			hw_priv->pme_wait = 0;
+		}
+	} else if (hw_chk_wol_pme_status(&hw_priv->hw)) {
+
+		/* PME is asserted.  Wait 2 seconds to clear it. */
+		hw_priv->pme_wait = jiffies + HZ * 2;
+	}
+
+	ksz_update_timer(&hw_priv->mib_timer_info);
+}
+
+/**
+ * dev_monitor - periodic monitoring
+ * @ptr:	Network device pointer.
+ *
+ * This routine is run in a kernel timer to monitor the network device.
+ */
+static void dev_monitor(unsigned long ptr)
+{
+	struct net_device *dev = (struct net_device *) ptr;
+	struct dev_priv *priv = netdev_priv(dev);
+	struct dev_info *hw_priv = priv->adapter;
+	struct ksz_hw *hw = &hw_priv->hw;
+	struct ksz_port *port = &priv->port;
+
+	if (!(hw->features & LINK_INT_WORKING))
+		port_get_link_speed(port);
+	update_link(dev, priv, port);
+
+	ksz_update_timer(&priv->monitor_timer_info);
+}
+
+/*
+ * Linux network device interface functions
+ */
+
+/* Driver exported variables */
+
+static int msg_enable;
+
+static char *macaddr = ":";
+static char *mac1addr = ":";
+
+/*
+ * This enables multiple network device mode for KSZ8842, which contains a
+ * switch with two physical ports.  Some users like to take control of the
+ * ports for running Spanning Tree Protocol.  The driver will create an
+ * additional eth? device for the other port.
+ *
+ * Some limitations are the network devices cannot have different MTU and
+ * multicast hash tables.
+ */
+static int multi_dev;
+
+/*
+ * As most users select multiple network device mode to use Spanning Tree
+ * Protocol, this enables a feature in which most unicast and multicast packets
+ * are forwarded inside the switch and not passed to the host.  Only packets
+ * that need the host's attention are passed to it.  This prevents the host
+ * wasting CPU time to examine each and every incoming packets and do the
+ * forwarding itself.
+ *
+ * As the hack requires the private bridge header, the driver cannot compile
+ * with just the kernel headers.
+ *
+ * Enabling STP support also turns on multiple network device mode.
+ */
+static int stp;
+
+/*
+ * This enables fast aging in the KSZ8842 switch.  Not sure what situation
+ * needs that.  However, fast aging is used to flush the dynamic MAC table when
+ * STP suport is enabled.
+ */
+static int fast_aging;
+
+/**
+ * netdev_init - initalize network device.
+ * @dev:	Network device.
+ *
+ * This function initializes the network device.
+ *
+ * Return 0 if successful; otherwise an error code indicating failure.
+ */
+static int __init netdev_init(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+
+	/* 500 ms timeout */
+	ksz_init_timer(&priv->monitor_timer_info, 500 * HZ / 1000,
+		dev_monitor, dev);
+
+	/* 500 ms timeout */
+	dev->watchdog_timeo = HZ / 2;
+
+	dev->features |= NETIF_F_IP_CSUM;
+
+	/*
+	 * Hardware does not really support IPv6 checksum generation, but
+	 * driver actually runs faster with this on.  Refer IPV6_CSUM_GEN_HACK.
+	 */
+	dev->features |= NETIF_F_IPV6_CSUM;
+	dev->features |= NETIF_F_SG;
+
+	sema_init(&priv->proc_sem, 1);
+
+	priv->mii_if.phy_id_mask = 0x1;
+	priv->mii_if.reg_num_mask = 0x7;
+	priv->mii_if.dev = dev;
+	priv->mii_if.mdio_read = mdio_read;
+	priv->mii_if.mdio_write = mdio_write;
+	priv->mii_if.phy_id = priv->port.first_port + 1;
+
+	priv->msg_enable = netif_msg_init(msg_enable,
+		(NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK));
+
+	return 0;
+}
+
+static const struct net_device_ops netdev_ops = {
+	.ndo_init		= netdev_init,
+	.ndo_open		= netdev_open,
+	.ndo_stop		= netdev_close,
+	.ndo_get_stats		= netdev_query_statistics,
+	.ndo_start_xmit		= netdev_tx,
+	.ndo_tx_timeout		= netdev_tx_timeout,
+	.ndo_change_mtu		= netdev_change_mtu,
+	.ndo_set_mac_address	= netdev_set_mac_address,
+	.ndo_do_ioctl		= netdev_ioctl,
+	.ndo_set_rx_mode	= netdev_set_rx_mode,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= netdev_netpoll,
+#endif
+};
+
+static void netdev_free(struct net_device *dev)
+{
+	if (dev->watchdog_timeo)
+		unregister_netdev(dev);
+
+	free_netdev(dev);
+}
+
+struct platform_info {
+	struct dev_info dev_info;
+	struct net_device *netdev[SWITCH_PORT_NUM];
+};
+
+static int net_device_present;
+
+static void get_mac_addr(struct dev_info *hw_priv, u8 *macaddr, int port)
+{
+	int i;
+	int j;
+	int got_num;
+	int num;
+
+	i = j = num = got_num = 0;
+	while (j < MAC_ADDR_LEN) {
+		if (macaddr[i]) {
+			got_num = 1;
+			if ('0' <= macaddr[i] && macaddr[i] <= '9')
+				num = num * 16 + macaddr[i] - '0';
+			else if ('A' <= macaddr[i] && macaddr[i] <= 'F')
+				num = num * 16 + 10 + macaddr[i] - 'A';
+			else if ('a' <= macaddr[i] && macaddr[i] <= 'f')
+				num = num * 16 + 10 + macaddr[i] - 'a';
+			else if (':' == macaddr[i])
+				got_num = 2;
+			else
+				break;
+		} else if (got_num)
+			got_num = 2;
+		else
+			break;
+		if (2 == got_num) {
+			if (MAIN_PORT == port) {
+				hw_priv->hw.override_addr[j++] = (u8) num;
+				hw_priv->hw.override_addr[5] +=
+					hw_priv->hw.id;
+			} else {
+				hw_priv->hw.ksz_switch->other_addr[j++] =
+					(u8) num;
+				hw_priv->hw.ksz_switch->other_addr[5] +=
+					hw_priv->hw.id;
+			}
+			num = got_num = 0;
+		}
+		i++;
+	}
+	if (MAC_ADDR_LEN == j) {
+		if (MAIN_PORT == port)
+			hw_priv->hw.mac_override = 1;
+	}
+}
+
+#define KS884X_DMA_MASK			(~0x0UL)
+
+static void read_other_addr(struct ksz_hw *hw)
+{
+	int i;
+	u16 data[3];
+	struct ksz_switch *sw = hw->ksz_switch;
+
+	for (i = 0; i < 3; i++)
+		data[i] = eeprom_read(hw, i + EEPROM_DATA_OTHER_MAC_ADDR);
+	if ((data[0] || data[1] || data[2]) && data[0] != 0xffff) {
+		sw->other_addr[5] = (u8) data[0];
+		sw->other_addr[4] = (u8)(data[0] >> 8);
+		sw->other_addr[3] = (u8) data[1];
+		sw->other_addr[2] = (u8)(data[1] >> 8);
+		sw->other_addr[1] = (u8) data[2];
+		sw->other_addr[0] = (u8)(data[2] >> 8);
+	}
+}
+
+#ifndef PCI_VENDOR_ID_MICREL_KS
+#define PCI_VENDOR_ID_MICREL_KS		0x16c6
+#endif
+
+static int __init pcidev_init(struct pci_dev *pdev,
+	const struct pci_device_id *id)
+{
+	struct net_device *dev;
+	struct dev_priv *priv;
+	struct dev_info *hw_priv;
+	struct ksz_hw *hw;
+	struct platform_info *info;
+	struct ksz_port *port;
+	unsigned long reg_base;
+	unsigned long reg_len;
+	int cnt;
+	int i;
+	int mib_port_count;
+	int pi;
+	int port_count;
+	int result;
+	char banner[80];
+	struct ksz_switch *sw = NULL;
+
+	result = pci_enable_device(pdev);
+	if (result)
+		return result;
+
+	result = -ENODEV;
+
+	if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) ||
+			pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))
+		return result;
+
+	reg_base = pci_resource_start(pdev, 0);
+	reg_len = pci_resource_len(pdev, 0);
+	if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO) != 0)
+		return result;
+
+	if (!request_mem_region(reg_base, reg_len, DRV_NAME))
+		return result;
+	pci_set_master(pdev);
+
+	result = -ENOMEM;
+
+	info = kmalloc(sizeof(struct platform_info), GFP_KERNEL);
+	if (!info)
+		goto pcidev_init_dev_err;
+	memset(info, 0, sizeof(struct platform_info));
+
+	hw_priv = &info->dev_info;
+	hw_priv->pdev = pdev;
+
+	hw = &hw_priv->hw;
+
+	hw->io = ioremap(reg_base, reg_len);
+	if (!hw->io)
+		goto pcidev_init_io_err;
+
+	cnt = hw_init(hw);
+	if (!cnt) {
+		if (msg_enable & NETIF_MSG_PROBE)
+			printk(KERN_ALERT "chip not detected\n");
+		result = -ENODEV;
+		goto pcidev_init_alloc_err;
+	}
+
+	sprintf(banner,	"%s\n", version);
+	banner[13] = cnt + '0';
+	ks_info(hw_priv, "%s", banner);
+	ks_dbg(hw_priv, "Mem = %p; IRQ = %d\n", hw->io, pdev->irq);
+
+	/* Assume device is KSZ8841. */
+	hw->dev_count = 1;
+	port_count = 1;
+	mib_port_count = 1;
+	hw->addr_list_size = 0;
+	hw->mib_cnt = PORT_COUNTER_NUM;
+	hw->mib_port_cnt = 1;
+
+	/* KSZ8842 has a switch with multiple ports. */
+	if (2 == cnt) {
+		if (fast_aging)
+			hw->overrides |= FAST_AGING;
+
+		hw->mib_cnt = TOTAL_PORT_COUNTER_NUM;
+
+		/* Multiple network device interfaces are required. */
+		if (multi_dev) {
+			hw->dev_count = SWITCH_PORT_NUM;
+			hw->addr_list_size = SWITCH_PORT_NUM - 1;
+		}
+
+		/* Single network device has multiple ports. */
+		if (1 == hw->dev_count) {
+			port_count = SWITCH_PORT_NUM;
+			mib_port_count = SWITCH_PORT_NUM;
+		}
+		hw->mib_port_cnt = TOTAL_PORT_NUM;
+		hw->ksz_switch = kmalloc(sizeof(struct ksz_switch), GFP_KERNEL);
+		if (!hw->ksz_switch)
+			goto pcidev_init_alloc_err;
+		memset(hw->ksz_switch, 0, sizeof(struct ksz_switch));
+
+		sw = hw->ksz_switch;
+	}
+	for (i = 0; i < hw->mib_port_cnt; i++)
+		hw->port_mib[i].mib_start = 0;
+
+	hw->parent = hw_priv;
+
+	/* Default MTU is 1500. */
+	hw_priv->mtu = (REGULAR_RX_BUF_SIZE + 3) & ~3;
+
+	if (ksz_alloc_mem(hw_priv))
+		goto pcidev_init_mem_err;
+
+	hw_priv->hw.id = net_device_present;
+
+	spin_lock_init(&hw_priv->hwlock);
+	mutex_init(&hw_priv->lock);
+
+	/* tasklet is enabled. */
+	tasklet_init(&hw_priv->rx_tasklet, rx_proc_task,
+		(unsigned long) hw_priv);
+	tasklet_init(&hw_priv->tx_tasklet, tx_proc_task,
+		(unsigned long) hw_priv);
+
+	/* tasklet_enable will decrement the atomic counter. */
+	tasklet_disable(&hw_priv->rx_tasklet);
+	tasklet_disable(&hw_priv->tx_tasklet);
+
+	for (i = 0; i < TOTAL_PORT_NUM; i++)
+		init_waitqueue_head(&hw_priv->counter[i].counter);
+
+	if (macaddr[0] != ':')
+		get_mac_addr(hw_priv, macaddr, MAIN_PORT);
+
+	/* Read MAC address and initialize override address if not overrided. */
+	hw_read_addr(hw);
+
+	/* Multiple device interfaces mode requires a second MAC address. */
+	if (hw->dev_count > 1) {
+		memcpy(sw->other_addr, hw->override_addr, MAC_ADDR_LEN);
+		read_other_addr(hw);
+		if (mac1addr[0] != ':')
+			get_mac_addr(hw_priv, mac1addr, OTHER_PORT);
+	}
+
+	hw_setup(hw);
+	if (hw->ksz_switch)
+		sw_setup(hw);
+	else {
+		hw_priv->wol_support = WOL_SUPPORT;
+		hw_priv->wol_enable = 0;
+	}
+
+	INIT_WORK(&hw_priv->mib_read, mib_read_work);
+
+	/* 500 ms timeout */
+	ksz_init_timer(&hw_priv->mib_timer_info, 500 * HZ / 1000,
+		mib_monitor, hw_priv);
+
+	for (i = 0; i < hw->dev_count; i++) {
+		dev = alloc_etherdev(sizeof(struct dev_priv));
+		if (!dev)
+			goto pcidev_init_reg_err;
+		info->netdev[i] = dev;
+
+		priv = netdev_priv(dev);
+		priv->adapter = hw_priv;
+		priv->id = net_device_present++;
+
+		port = &priv->port;
+		port->port_cnt = port_count;
+		port->mib_port_cnt = mib_port_count;
+		port->first_port = i;
+		port->flow_ctrl = PHY_FLOW_CTRL;
+
+		port->hw = hw;
+		port->linked = &hw->port_info[port->first_port];
+
+		for (cnt = 0, pi = i; cnt < port_count; cnt++, pi++) {
+			hw->port_info[pi].port_id = pi;
+			hw->port_info[pi].pdev = dev;
+			hw->port_info[pi].state = media_disconnected;
+		}
+
+		dev->mem_start = (unsigned long) hw->io;
+		dev->mem_end = dev->mem_start + reg_len - 1;
+		dev->irq = pdev->irq;
+		if (MAIN_PORT == i)
+			memcpy(dev->dev_addr, hw_priv->hw.override_addr,
+				MAC_ADDR_LEN);
+		else {
+			memcpy(dev->dev_addr, sw->other_addr,
+				MAC_ADDR_LEN);
+			if (!memcmp(sw->other_addr, hw->override_addr,
+					MAC_ADDR_LEN))
+				dev->dev_addr[5] += port->first_port;
+		}
+
+		dev->netdev_ops = &netdev_ops;
+		SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+		if (register_netdev(dev))
+			goto pcidev_init_reg_err;
+		port_set_power_saving(port, true);
+	}
+
+	pci_dev_get(hw_priv->pdev);
+	pci_set_drvdata(pdev, info);
+	return 0;
+
+pcidev_init_reg_err:
+	for (i = 0; i < hw->dev_count; i++) {
+		if (info->netdev[i]) {
+			netdev_free(info->netdev[i]);
+			info->netdev[i] = NULL;
+		}
+	}
+
+pcidev_init_mem_err:
+	ksz_free_mem(hw_priv);
+	kfree(hw->ksz_switch);
+
+pcidev_init_alloc_err:
+	iounmap(hw->io);
+
+pcidev_init_io_err:
+	kfree(info);
+
+pcidev_init_dev_err:
+	release_mem_region(reg_base, reg_len);
+
+	return result;
+}
+
+static void pcidev_exit(struct pci_dev *pdev)
+{
+	int i;
+	struct platform_info *info = pci_get_drvdata(pdev);
+	struct dev_info *hw_priv = &info->dev_info;
+
+	pci_set_drvdata(pdev, NULL);
+
+	release_mem_region(pci_resource_start(pdev, 0),
+		pci_resource_len(pdev, 0));
+	for (i = 0; i < hw_priv->hw.dev_count; i++) {
+		if (info->netdev[i])
+			netdev_free(info->netdev[i]);
+	}
+	if (hw_priv->hw.io)
+		iounmap(hw_priv->hw.io);
+	ksz_free_mem(hw_priv);
+	kfree(hw_priv->hw.ksz_switch);
+	pci_dev_put(hw_priv->pdev);
+	kfree(info);
+}
+
+#ifdef CONFIG_PM
+static int pcidev_resume(struct pci_dev *pdev)
+{
+	int i;
+	struct platform_info *info = pci_get_drvdata(pdev);
+	struct dev_info *hw_priv = &info->dev_info;
+	struct ksz_hw *hw = &hw_priv->hw;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	pci_enable_wake(pdev, PCI_D0, 0);
+
+	if (hw_priv->wol_enable)
+		hw_cfg_wol_pme(hw, 0);
+	for (i = 0; i < hw->dev_count; i++) {
+		if (info->netdev[i]) {
+			struct net_device *dev = info->netdev[i];
+
+			if (netif_running(dev)) {
+				netdev_open(dev);
+				netif_device_attach(dev);
+			}
+		}
+	}
+	return 0;
+}
+
+static int pcidev_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int i;
+	struct platform_info *info = pci_get_drvdata(pdev);
+	struct dev_info *hw_priv = &info->dev_info;
+	struct ksz_hw *hw = &hw_priv->hw;
+
+	/* Need to find a way to retrieve the device IP address. */
+	u8 net_addr[] = { 192, 168, 1, 1 };
+
+	for (i = 0; i < hw->dev_count; i++) {
+		if (info->netdev[i]) {
+			struct net_device *dev = info->netdev[i];
+
+			if (netif_running(dev)) {
+				netif_device_detach(dev);
+				netdev_close(dev);
+			}
+		}
+	}
+	if (hw_priv->wol_enable) {
+		hw_enable_wol(hw, hw_priv->wol_enable, net_addr);
+		hw_cfg_wol_pme(hw, 1);
+	}
+
+	pci_save_state(pdev);
+	pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	return 0;
+}
+#endif
+
+static char pcidev_name[] = "ksz884xp";
+
+static struct pci_device_id pcidev_table[] = {
+	{ PCI_VENDOR_ID_MICREL_KS, 0x8841,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ PCI_VENDOR_ID_MICREL_KS, 0x8842,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, pcidev_table);
+
+static struct pci_driver pci_device_driver = {
+#ifdef CONFIG_PM
+	.suspend	= pcidev_suspend,
+	.resume		= pcidev_resume,
+#endif
+	.name		= pcidev_name,
+	.id_table	= pcidev_table,
+	.probe		= pcidev_init,
+	.remove		= pcidev_exit
+};
+
+static int __init ksz884x_init_module(void)
+{
+	return pci_register_driver(&pci_device_driver);
+}
+
+static void __exit ksz884x_cleanup_module(void)
+{
+	pci_unregister_driver(&pci_device_driver);
+}
+
+module_init(ksz884x_init_module);
+module_exit(ksz884x_cleanup_module);
+
+MODULE_DESCRIPTION("KSZ8841/2 PCI network driver");
+MODULE_AUTHOR("Tristram Ha <Tristram.Ha@micrel.com>");
+MODULE_LICENSE("GPL");
+
+module_param_named(message, msg_enable, int, 0);
+MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)");
+
+module_param(macaddr, charp, 0);
+module_param(mac1addr, charp, 0);
+module_param(fast_aging, int, 0);
+module_param(multi_dev, int, 0);
+module_param(stp, int, 0);
+MODULE_PARM_DESC(macaddr, "MAC address");
+MODULE_PARM_DESC(mac1addr, "Second MAC address");
+MODULE_PARM_DESC(fast_aging, "Fast aging");
+MODULE_PARM_DESC(multi_dev, "Multiple device interfaces");
+MODULE_PARM_DESC(stp, "STP support");

^ permalink raw reply

* netconsole dumbs the system (in initramfs with bonding and vlan)
From: Ferenc Wagner @ 2010-02-08 21:01 UTC (permalink / raw)
  To: netdev

Hi,

I set up networking from the initramfs like this:

----------------------------------------
ifup()
{
    ip addr add $2 dev $1
    ip link set up dev $1
    echo "Configured $1 with address $2"
}

BOND=bond0

modprobe bonding
cd /sys/class/net/$BOND/bonding

echo active-backup >mode
echo +eth0 >slaves
echo +eth1 >slaves
echo eth0 >primary
echo +10.0.0.1 >arp_ip_target
echo +10.0.0.2 >arp_ip_target
echo 1000 >arp_interval

vconfig set_name_type VLAN_PLUS_VID_NO_PAD
vconfig add $BOND 39

ifup $BOND 10.0.0.6/24
ifup vlan39 10.253.1.103/24
----------------------------------------

which mostly works, apart from the occasional flood of "bonding: bond0:
doing slave updates when interface is down" messages as detailed in my
previous mail.  However, if I afterwards issue

# modprobe netconsole netconsole=@10.0.0.6/eth0,@10.0.0.7/

that kills off the consoles: past kernel messages start pouring over all
consoles, the new network console included, then it suddenly dies off in
the middle, with one more line on the VC than sent to the network.  At
this point, the machine still responds to ping, the keyboard leds work,
but otherwise it's impossible to interact with it.  No SysRq, even.
Being in the initramfs, there are no network services to test.  If I
load netconsole in the running system, there isn't much backlog to pour
out, and the problem doesn't appear.  What could be wrong here?
-- 
Thanks,
Feri.

^ permalink raw reply

* Re: [net-next PATCH v2 2/3] net: TCP thin linear timeouts
From: Damian Lukowski @ 2010-02-08 20:50 UTC (permalink / raw)
  To: Andreas Petlund
  Cc: netdev@vger.kernel.org, Ilpo Järvinen, Eric Dumazet,
	Arnd Hannemann, LKML, shemminger, David Miller,
	william.allen.simpson
In-Reply-To: <4B701EE2.1000006@simula.no>

Andreas Petlund schrieb:
> Major change: Limit number of thin linear timeout tries to TCP_THIN_LT_RETRIES (currently 6).
> 
>>From ec71404702149bc9197c749e5d1d68656c87f98f Mon Sep 17 00:00:00 2001
> From: Andreas Petlund <apetlund@simula.no>
> Date: Mon, 8 Feb 2010 14:05:53 +0100
> Subject: [PATCH 2/3] net: TCP thin linear timeouts
> 
> 
> Signed-off-by: Andreas Petlund <apetlund@simula.no>
> ---
>  include/linux/sysctl.h     |    1 +
>  include/linux/tcp.h        |    3 +++
>  include/net/tcp.h          |    4 ++++
>  net/ipv4/sysctl_net_ipv4.c |    7 +++++++
>  net/ipv4/tcp.c             |    5 +++++
>  net/ipv4/tcp_timer.c       |   19 ++++++++++++++++++-
>  6 files changed, 38 insertions(+), 1 deletions(-)
> 
> diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
> index 9f236cd..d840d75 100644
> --- a/include/linux/sysctl.h
> +++ b/include/linux/sysctl.h
> @@ -425,6 +425,7 @@ enum
>  	NET_TCP_ALLOWED_CONG_CONTROL=123,
>  	NET_TCP_MAX_SSTHRESH=124,
>  	NET_TCP_FRTO_RESPONSE=125,
> +	NET_TCP_FORCE_THIN_LINEAR_TIMEOUTS=126,
>  };
>  
>  enum {
> diff --git a/include/linux/tcp.h b/include/linux/tcp.h
> index 7fee8a4..67da706 100644
> --- a/include/linux/tcp.h
> +++ b/include/linux/tcp.h
> @@ -103,6 +103,7 @@ enum {
>  #define TCP_CONGESTION		13	/* Congestion control algorithm */
>  #define TCP_MD5SIG		14	/* TCP MD5 Signature (RFC2385) */
>  #define TCP_COOKIE_TRANSACTIONS	15	/* TCP Cookie Transactions */
> +#define TCP_THIN_LT             16      /* Use linear timeouts for thin streams*/
>  
>  /* for TCP_INFO socket option */
>  #define TCPI_OPT_TIMESTAMPS	1
> @@ -341,6 +342,8 @@ struct tcp_sock {
>  	u16	advmss;		/* Advertised MSS			*/
>  	u8	frto_counter;	/* Number of new acks after RTO */
>  	u8	nonagle;	/* Disable Nagle algorithm?             */
> +	u8      thin_lt     : 1,/* Use linear timeouts for thin streams */
> +		thin_undef  : 7;
>  
>  /* RTT measurement */
>  	u32	srtt;		/* smoothed round trip time << 3	*/
> diff --git a/include/net/tcp.h b/include/net/tcp.h
> index e5e2056..bc5856a 100644
> --- a/include/net/tcp.h
> +++ b/include/net/tcp.h
> @@ -196,6 +196,9 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
>  #define TCP_NAGLE_CORK		2	/* Socket is corked	    */
>  #define TCP_NAGLE_PUSH		4	/* Cork is overridden for already queued data */
>  
> +/* TCP thin-stream limits */
> +#define TCP_THIN_LT_RETRIES     6       /* After 6 linear retries, do exp. backoff */
> +
>  extern struct inet_timewait_death_row tcp_death_row;
>  
>  /* sysctl variables for tcp */
> @@ -241,6 +244,7 @@ extern int sysctl_tcp_workaround_signed_windows;
>  extern int sysctl_tcp_slow_start_after_idle;
>  extern int sysctl_tcp_max_ssthresh;
>  extern int sysctl_tcp_cookie_size;
> +extern int sysctl_tcp_force_thin_linear_timeouts;
>  
>  extern atomic_t tcp_memory_allocated;
>  extern struct percpu_counter tcp_sockets_allocated;
> diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
> index 7e3712c..cb2ed35 100644
> --- a/net/ipv4/sysctl_net_ipv4.c
> +++ b/net/ipv4/sysctl_net_ipv4.c
> @@ -576,6 +576,13 @@ static struct ctl_table ipv4_table[] = {
>  		.proc_handler	= proc_dointvec
>  	},
>  	{
> +		.procname       = "tcp_force_thin_linear_timeouts",
> +		.data           = &sysctl_tcp_force_thin_linear_timeouts,
> +		.maxlen         = sizeof(int),
> +		.mode           = 0644,
> +		.proc_handler   = proc_dointvec
> +	},
> +	{
>  		.procname	= "udp_mem",
>  		.data		= &sysctl_udp_mem,
>  		.maxlen		= sizeof(sysctl_udp_mem),
> diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> index d5d69ea..cbc1ee3 100644
> --- a/net/ipv4/tcp.c
> +++ b/net/ipv4/tcp.c
> @@ -2229,6 +2229,11 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
>  		}
>  		break;
>  
> +	case TCP_THIN_LT:
> +		if (val)
> +			tp->thin_lt = 1;
> +		break;
> +
>  	case TCP_CORK:
>  		/* When set indicates to always queue non-full frames.
>  		 * Later the user clears this option and we transmit
> diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
> index de7d1bf..f01a585 100644
> --- a/net/ipv4/tcp_timer.c
> +++ b/net/ipv4/tcp_timer.c
> @@ -29,6 +29,7 @@ int sysctl_tcp_keepalive_intvl __read_mostly = TCP_KEEPALIVE_INTVL;
>  int sysctl_tcp_retries1 __read_mostly = TCP_RETR1;
>  int sysctl_tcp_retries2 __read_mostly = TCP_RETR2;
>  int sysctl_tcp_orphan_retries __read_mostly;
> +int sysctl_tcp_force_thin_linear_timeouts __read_mostly;
>  
>  static void tcp_write_timer(unsigned long);
>  static void tcp_delack_timer(unsigned long);
> @@ -415,7 +416,23 @@ void tcp_retransmit_timer(struct sock *sk)
>  	icsk->icsk_retransmits++;
>  
>  out_reset_timer:
> -	icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
> +	/* If stream is thin, use linear timeouts. Since 'icsk_backoff' is
> +	 * used to reset timer, set to 0. Recalculate 'icsk_rto' as this
> +	 * might be increased if the stream oscillates between thin and thick,
> +	 * thus the old value might already be too high compared to the value
> +	 * set by 'tcp_set_rto' in tcp_input.c which resets the rto without
> +	 * backoff. Limit to TCP_THIN_LT_RETRIES before initiating exponential
> +	 * backoff behaviour to avoid continue hammering linear-timeout 
> +	 * retransmissions into a black hole*/
> +	if ((tp->thin_lt || sysctl_tcp_force_thin_linear_timeouts) &&
> +	    tcp_stream_is_thin(sk) && sk->sk_state == TCP_ESTABLISHED &&
> +	    icsk->icsk_retransmits <= TCP_THIN_LT_RETRIES) {
> +		icsk->icsk_backoff = 0;

Hi,
I think, this value should be at least 1, as icsk_backoff
might be decreased to -1 and used for bit-shifting in tcp_v4_err().
A lower boundary check might be even better.

Regards
 Damian


> +		icsk->icsk_rto = min(__tcp_set_rto(tp), TCP_RTO_MAX);
> +	} else {
> +		/* Use normal (exponential) backoff */
> +		icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
> +	}
>  	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
>  	if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1))
>  		__sk_dst_reset(sk);

^ permalink raw reply

* Re: [Bug #15196] kmem_cache_create: duplicate cache ccid2_h
From: David Miller @ 2010-02-08 20:34 UTC (permalink / raw)
  To: rjw-KKrjLPT3xs0
  Cc: htd-HjJ2MNWy62to6+H+lsi3Gti2O/JbrIOy,
	xtfeng-Re5JQEeQqe8AvxtiuMwx3w,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kernel-testers-u79uwXL29TY76Z2rM5mHXA,
	maciej.rutecki-Re5JQEeQqe8AvxtiuMwx3w,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <201002081204.28039.rjw-KKrjLPT3xs0@public.gmane.org>

From: "Rafael J. Wysocki" <rjw-KKrjLPT3xs0@public.gmane.org>
Date: Mon, 8 Feb 2010 12:04:27 +0100

> Hmm.  The bug is in net/dccp/ccid.c, so it looks like something for the
> networking guys to merge.
> 
> Any chance to send a singed-off version of the patch do David?

Fixes for this are already in my net-2.6 tree, will send them
off to Linus later today, I was waiting for some netfilter
fixes which I just received:

commit 1386be55e32a3c5d8ef4a2b243c530a7b664c02c
Author: Gerrit Renker <gerrit-VsoRXqaLlyfQzY9nttDBhA@public.gmane.org>
Date:   Tue Feb 2 20:16:56 2010 +0000

    dccp: fix auto-loading of dccp(_probe)
    
    This fixes commit (38ff3e6bb987ec583268da8eb22628293095d43b) ("dccp_probe:
    Fix module load dependencies between dccp and dccp_probe", from 15 Jan).
    
    It fixes the construction of the first argument of try_then_request_module(),
    where only valid return codes from the first argument should be returned.
    
    What we do now is assign the result of register_jprobe() to ret, without
    the side effect of the comparison.
    
    Acked-by: Gerrit Renker <gerrit-VsoRXqaLlyfQzY9nttDBhA@public.gmane.org>
    Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>
    Signed-off-by: David S. Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>

commit 8ed030dd0aa400d18c63861c2c6deb7c38f4edde
Author: Gerrit Renker <gerrit-VsoRXqaLlyfQzY9nttDBhA@public.gmane.org>
Date:   Mon Feb 1 02:12:19 2010 +0000

    dccp: fix bug in cache allocation
    
    This fixes a bug introduced in commit de4ef86cfce60d2250111f34f8a084e769f23b16
    ("dccp: fix dccp rmmod when kernel configured to use slub", 17 Jan): the
    vsnprintf used sizeof(slab_name_fmt), which became truncated to 4 bytes, since
    slab_name_fmt is now a 4-byte pointer and no longer a 32-character array.
    
    This lead to error messages such as
     FATAL: Error inserting dccp: No buffer space available
    
     >> kernel: [ 1456.341501] kmem_cache_create: duplicate cache cci
    generated due to the truncation after the 3rd character.
    
    Fixed for the moment by introducing a symbolic constant. Tested to fix the bug.
    
    Signed-off-by: Gerrit Renker <gerrit-VsoRXqaLlyfQzY9nttDBhA@public.gmane.org>
    Acked-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>
    Signed-off-by: David S. Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>

^ permalink raw reply

* [PATCH v2 41/41] CAPI: Remove experimental tag from middleware feature
From: Jan Kiszka @ 2010-02-08 20:12 UTC (permalink / raw)
  To: David Miller, Karsten Keil
  Cc: linux-kernel, i4ldeveloper, isdn4linux, netdev, Alan Cox,
	Marcel Holtmann
In-Reply-To: <cover.1265659933.git.jan.kiszka@web.de>

Despite all its bugs, the middleware support of our CAPI stack was
already in use for many, many moons. And after going through its code,
fixing all issues I found, I feel it deserves to officially become a
non-experimental feature.

Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
 drivers/isdn/capi/Kconfig |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig
index d607d46..a168e8a 100644
--- a/drivers/isdn/capi/Kconfig
+++ b/drivers/isdn/capi/Kconfig
@@ -17,8 +17,7 @@ config CAPI_TRACE
 	  If unsure, say Y.
 
 config ISDN_CAPI_MIDDLEWARE
-	bool "CAPI2.0 Middleware support (EXPERIMENTAL)"
-	depends on EXPERIMENTAL
+	bool "CAPI2.0 Middleware support"
 	help
 	  This option will enhance the capabilities of the /dev/capi20
 	  interface.  It will provide a means of moving a data connection,
-- 
1.6.0.2


^ permalink raw reply related

* [PATCH v2 37/41] CAPI: Clean up capiminor_*_ack
From: Jan Kiszka @ 2010-02-08 20:12 UTC (permalink / raw)
  To: David Miller, Karsten Keil
  Cc: linux-kernel, i4ldeveloper, isdn4linux, netdev, Alan Cox,
	Marcel Holtmann
In-Reply-To: <cover.1265659933.git.jan.kiszka@web.de>

No need for irqsave acquisition of acklock, bh-safe is sufficient.
Moverover, move kfree out of the lock and do not take acklock at all
in capiminor_del_all_ack as we are the last user of the list here.

Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
 drivers/isdn/capi/capi.c |   19 +++++++------------
 1 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 074496f..40b81b4 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -149,7 +149,6 @@ static struct tty_driver *capinc_tty_driver;
 static int capiminor_add_ack(struct capiminor *mp, u16 datahandle)
 {
 	struct ackqueue_entry *n;
-	unsigned long flags;
 
 	n = kmalloc(sizeof(*n), GFP_ATOMIC);
 	if (unlikely(!n)) {
@@ -158,44 +157,40 @@ static int capiminor_add_ack(struct capiminor *mp, u16 datahandle)
 	}
 	n->datahandle = datahandle;
 	INIT_LIST_HEAD(&n->list);
-	spin_lock_irqsave(&mp->ackqlock, flags);
+	spin_lock_bh(&mp->ackqlock);
 	list_add_tail(&n->list, &mp->ackqueue);
 	mp->nack++;
-	spin_unlock_irqrestore(&mp->ackqlock, flags);
+	spin_unlock_bh(&mp->ackqlock);
 	return 0;
 }
 
 static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
 {
 	struct ackqueue_entry *p, *tmp;
-	unsigned long flags;
 
-	spin_lock_irqsave(&mp->ackqlock, flags);
+	spin_lock_bh(&mp->ackqlock);
 	list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
  		if (p->datahandle == datahandle) {
 			list_del(&p->list);
-			kfree(p);
 			mp->nack--;
-			spin_unlock_irqrestore(&mp->ackqlock, flags);
+			spin_unlock_bh(&mp->ackqlock);
+			kfree(p);
 			return 0;
 		}
 	}
-	spin_unlock_irqrestore(&mp->ackqlock, flags);
+	spin_unlock_bh(&mp->ackqlock);
 	return -1;
 }
 
 static void capiminor_del_all_ack(struct capiminor *mp)
 {
 	struct ackqueue_entry *p, *tmp;
-	unsigned long flags;
 
-	spin_lock_irqsave(&mp->ackqlock, flags);
 	list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
 		list_del(&p->list);
 		kfree(p);
 		mp->nack--;
 	}
-	spin_unlock_irqrestore(&mp->ackqlock, flags);
 }
 
 
@@ -676,7 +671,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
 				CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2));
 #endif
 		kfree_skb(skb);
-		(void)capiminor_del_ack(mp, datahandle);
+		capiminor_del_ack(mp, datahandle);
 		tty = tty_port_tty_get(&mp->port);
 		if (tty) {
 			tty_wakeup(tty);
-- 
1.6.0.2


^ permalink raw reply related

* [PATCH v2 35/41] CAPI: Drop capiminor's unused inbytes counter
From: Jan Kiszka @ 2010-02-08 20:12 UTC (permalink / raw)
  To: David Miller, Karsten Keil
  Cc: linux-kernel, i4ldeveloper, isdn4linux, netdev, Alan Cox,
	Marcel Holtmann
In-Reply-To: <cover.1265659933.git.jan.kiszka@web.de>

The inbytes counter was only updated but never read.

Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
 drivers/isdn/capi/capi.c |   10 +++-------
 1 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 08d5a8a..be85c8c 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -98,7 +98,6 @@ struct capiminor {
 	struct sk_buff    *ttyskb;
 
 	struct sk_buff_head inqueue;
-	int                 inbytes;
 	struct sk_buff_head outqueue;
 	int                 outbytes;
 
@@ -520,15 +519,12 @@ deref_tty:
 static void handle_minor_recv(struct capiminor *mp)
 {
 	struct sk_buff *skb;
-	while ((skb = skb_dequeue(&mp->inqueue)) != NULL) {
-		unsigned int len = skb->len;
-		mp->inbytes -= len;
+
+	while ((skb = skb_dequeue(&mp->inqueue)) != NULL)
 		if (handle_recv_skb(mp, skb) < 0) {
 			skb_queue_head(&mp->inqueue, skb);
-			mp->inbytes += len;
 			return;
 		}
-	}
 }
 
 static int handle_minor_send(struct capiminor *mp)
@@ -659,7 +655,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
 				datahandle, skb->len-CAPIMSG_LEN(skb->data));
 #endif
 		skb_queue_tail(&mp->inqueue, skb);
-		mp->inbytes += skb->len;
+
 		handle_minor_recv(mp);
 
 	} else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) {
-- 
1.6.0.2


^ permalink raw reply related

* [PATCH v2 32/41] CAPI: Rework capiminor RX handler
From: Jan Kiszka @ 2010-02-08 20:12 UTC (permalink / raw)
  To: David Miller, Karsten Keil
  Cc: linux-kernel, i4ldeveloper, isdn4linux, netdev, Alan Cox,
	Marcel Holtmann
In-Reply-To: <cover.1265659933.git.jan.kiszka@web.de>

Avoid re-queuing skbs unless the error detected in handle_recv_skb is
expected to be recoverable such as lacking memory, a full CAPI queue, a
full TTY input buffer, or a not yet existing TTY.

Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
 drivers/isdn/capi/capi.c |   60 +++++++++++++++++++++++++++++-----------------
 1 files changed, 38 insertions(+), 22 deletions(-)

diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 554fa1b..c5c54fa 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -436,15 +436,13 @@ gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
 
 static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
 {
+	unsigned int datalen = skb->len - CAPIMSG_LEN(skb->data);
 	struct tty_struct *tty;
 	struct sk_buff *nskb;
-	int datalen;
 	u16 errcode, datahandle;
 	struct tty_ldisc *ld;
 	int ret = -1;
 
-	datalen = skb->len - CAPIMSG_LEN(skb->data);
-
 	tty = tty_port_tty_get(&mp->port);
 	if (!tty) {
 #ifdef _DEBUG_DATAFLOW
@@ -454,50 +452,68 @@ static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
 	}
 	
 	ld = tty_ldisc_ref(tty);
-	if (!ld)
-		goto out1;
+	if (!ld) {
+		/* fatal error, do not requeue */
+		ret = 0;
+		kfree_skb(skb);
+		goto deref_tty;
+	}
 
 	if (ld->ops->receive_buf == NULL) {
 #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
 		printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n");
 #endif
-		goto out2;
+		/* fatal error, do not requeue */
+		goto free_skb;
 	}
 	if (mp->ttyinstop) {
 #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
 		printk(KERN_DEBUG "capi: recv tty throttled\n");
 #endif
-		goto out2;
+		goto deref_ldisc;
 	}
+
 	if (tty->receive_room < datalen) {
 #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
 		printk(KERN_DEBUG "capi: no room in tty\n");
 #endif
-		goto out2;
+		goto deref_ldisc;
 	}
-	if ((nskb = gen_data_b3_resp_for(mp, skb)) == NULL) {
+
+	nskb = gen_data_b3_resp_for(mp, skb);
+	if (!nskb) {
 		printk(KERN_ERR "capi: gen_data_b3_resp failed\n");
-		goto out2;
+		goto deref_ldisc;
 	}
-	datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4);
+
+	datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4);
+
 	errcode = capi20_put_message(mp->ap, nskb);
-	if (errcode != CAPI_NOERROR) {
+
+	if (errcode == CAPI_NOERROR) {
+		skb_pull(skb, CAPIMSG_LEN(skb->data));
+#ifdef _DEBUG_DATAFLOW
+		printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n",
+					datahandle, skb->len);
+#endif
+		ld->ops->receive_buf(tty, skb->data, NULL, skb->len);
+	} else {
 		printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n",
 				errcode);
 		kfree_skb(nskb);
-		goto out2;
+
+		if (errcode == CAPI_SENDQUEUEFULL)
+			goto deref_ldisc;
 	}
-	(void)skb_pull(skb, CAPIMSG_LEN(skb->data));
-#ifdef _DEBUG_DATAFLOW
-	printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n",
-				datahandle, skb->len);
-#endif
-	ld->ops->receive_buf(tty, skb->data, NULL, skb->len);
-	kfree_skb(skb);
+
+free_skb:
 	ret = 0;
-out2:
+	kfree_skb(skb);
+
+deref_ldisc:
 	tty_ldisc_deref(ld);
-out1:
+
+deref_tty:
 	tty_kref_put(tty);
 	return ret;
 }
-- 
1.6.0.2


^ permalink raw reply related

* [PATCH v2 33/41] CAPI: Rename datahandle_queue -> ackqueue_entry
From: Jan Kiszka @ 2010-02-08 20:12 UTC (permalink / raw)
  To: David Miller, Karsten Keil
  Cc: linux-kernel, i4ldeveloper, isdn4linux, netdev, Alan Cox,
	Marcel Holtmann
In-Reply-To: <cover.1265659933.git.jan.kiszka@web.de>

This struct is describing a queue entry, not the queue itself.

Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
 drivers/isdn/capi/capi.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index c5c54fa..9d4750a 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -76,7 +76,7 @@ struct capidev;
 struct capincci;
 struct capiminor;
 
-struct datahandle_queue {
+struct ackqueue_entry {
 	struct list_head	list;
 	u16			datahandle;
 };
@@ -156,7 +156,7 @@ static struct tty_driver *capinc_tty_driver;
 
 static int capiminor_add_ack(struct capiminor *mp, u16 datahandle)
 {
-	struct datahandle_queue *n;
+	struct ackqueue_entry *n;
 	unsigned long flags;
 
 	n = kmalloc(sizeof(*n), GFP_ATOMIC);
@@ -175,7 +175,7 @@ static int capiminor_add_ack(struct capiminor *mp, u16 datahandle)
 
 static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
 {
-	struct datahandle_queue *p, *tmp;
+	struct ackqueue_entry *p, *tmp;
 	unsigned long flags;
 
 	spin_lock_irqsave(&mp->ackqlock, flags);
@@ -194,7 +194,7 @@ static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
 
 static void capiminor_del_all_ack(struct capiminor *mp)
 {
-	struct datahandle_queue *p, *tmp;
+	struct ackqueue_entry *p, *tmp;
 	unsigned long flags;
 
 	spin_lock_irqsave(&mp->ackqlock, flags);
-- 
1.6.0.2


^ permalink raw reply related

* [PATCH v2 30/41] CAPI: Drop atomic ttyopencount
From: Jan Kiszka @ 2010-02-08 20:12 UTC (permalink / raw)
  To: David Miller, Karsten Keil
  Cc: linux-kernel, i4ldeveloper, isdn4linux, netdev, Alan Cox,
	Marcel Holtmann
In-Reply-To: <cover.1265659933.git.jan.kiszka@web.de>

Not needed, tty->count keeps track of this information. At this chance,
drop traces of ancient attempts to debug this logic via _DEBUG_REFCOUNT.

Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
 drivers/isdn/capi/capi.c |   30 ++++++++++--------------------
 1 files changed, 10 insertions(+), 20 deletions(-)

diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 0b264b4..867589a 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -45,7 +45,6 @@ MODULE_DESCRIPTION("CAPI4Linux: Userspace /dev/capi20 interface");
 MODULE_AUTHOR("Carsten Paeth");
 MODULE_LICENSE("GPL");
 
-#undef _DEBUG_REFCOUNT		/* alloc/free and open/close debug */
 #undef _DEBUG_TTYFUNCS		/* call to tty_driver */
 #undef _DEBUG_DATAFLOW		/* data flow */
 
@@ -97,7 +96,6 @@ struct capiminor {
 	int                ttyinstop;
 	int                ttyoutstop;
 	struct sk_buff    *ttyskb;
-	atomic_t           ttyopencount;
 
 	struct sk_buff_head inqueue;
 	int                 inbytes;
@@ -230,7 +228,6 @@ static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci)
 	mp->ap = ap;
 	mp->ncci = ncci;
 	mp->msgid = 0;
-	atomic_set(&mp->ttyopencount,0);
 	INIT_LIST_HEAD(&mp->ackqueue);
 	spin_lock_init(&mp->ackqlock);
 
@@ -350,8 +347,17 @@ static void capincci_free_minor(struct capincci *np)
 static inline unsigned int capincci_minor_opencount(struct capincci *np)
 {
 	struct capiminor *mp = np->minorp;
+	unsigned int count = 0;
+	struct tty_struct *tty;
 
-	return mp ? atomic_read(&mp->ttyopencount) : 0;
+	if (mp) {
+		tty = tty_port_tty_get(&mp->port);
+		if (tty) {
+			count = tty->count;
+			tty_kref_put(tty);
+		}
+	}
+	return count;
 }
 
 #else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */
@@ -1054,10 +1060,6 @@ static int capinc_tty_open(struct tty_struct *tty, struct file *filp)
 		return err;
 
 	spin_lock_irqsave(&workaround_lock, flags);
-	atomic_inc(&mp->ttyopencount);
-#ifdef _DEBUG_REFCOUNT
-	printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount));
-#endif
 	handle_minor_recv(mp);
 	spin_unlock_irqrestore(&workaround_lock, flags);
 	return 0;
@@ -1067,18 +1069,6 @@ static void capinc_tty_close(struct tty_struct *tty, struct file *filp)
 {
 	struct capiminor *mp = tty->driver_data;
 
-		if (atomic_dec_and_test(&mp->ttyopencount)) {
-#ifdef _DEBUG_REFCOUNT
-			printk(KERN_DEBUG "capinc_tty_close lastclose\n");
-#endif
-		}
-#ifdef _DEBUG_REFCOUNT
-		printk(KERN_DEBUG "capinc_tty_close ocount=%d\n", atomic_read(&mp->ttyopencount));
-#endif
-
-#ifdef _DEBUG_REFCOUNT
-	printk(KERN_DEBUG "capinc_tty_close\n");
-#endif
 	tty_port_close(&mp->port, tty, filp);
 }
 
-- 
1.6.0.2


^ permalink raw reply related

* [PATCH v2 27/41] CAPI: Issue synchronous hangup on capincci_free_minor
From: Jan Kiszka @ 2010-02-08 20:12 UTC (permalink / raw)
  To: David Miller, Karsten Keil
  Cc: linux-kernel, i4ldeveloper, isdn4linux, netdev, Alan Cox,
	Marcel Holtmann
In-Reply-To: <cover.1265659933.git.jan.kiszka@web.de>

capincci_free and, thus, capincci_free_minor runs in process context, so
we can issue the hangup of the associated TTY synchronously.

Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
 drivers/isdn/capi/capi.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index acc811b..b382ede 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -351,7 +351,7 @@ static void capincci_free_minor(struct capincci *np)
 #ifdef _DEBUG_REFCOUNT
 			printk(KERN_DEBUG "reset mp->nccip\n");
 #endif
-			tty_hangup(tty);
+			tty_vhangup(tty);
 			tty_kref_put(tty);
 		}
 
-- 
1.6.0.2


^ permalink raw reply related

* [PATCH v2 24/41] CAPI: Establish install/cleanup handlers for capiminor TTYs
From: Jan Kiszka @ 2010-02-08 20:12 UTC (permalink / raw)
  To: David Miller, Karsten Keil
  Cc: linux-kernel, i4ldeveloper, isdn4linux, netdev, Alan Cox,
	Marcel Holtmann
In-Reply-To: <cover.1265659933.git.jan.kiszka@web.de>

Properly associate/disassociate a capiminor object with its TTY via the
install/cleanup handlers instead of trying to guess first open and last
close.

Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
 drivers/isdn/capi/capi.c |   42 ++++++++++++++++++++++++++++--------------
 1 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 732cdb5..3e4997a 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -1005,16 +1005,34 @@ static const struct file_operations capi_fops =
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
 /* -------- tty_operations for capincci ----------------------------- */
 
-static int capinc_tty_open(struct tty_struct * tty, struct file * file)
+static int
+capinc_tty_install(struct tty_driver *driver, struct tty_struct *tty)
 {
-	struct capiminor *mp;
-	unsigned long flags;
+	int idx = tty->index;
+	struct capiminor *mp = capiminor_get(idx);
+	int ret = tty_init_termios(tty);
+
+	if (ret == 0) {
+		tty_driver_kref_get(driver);
+		tty->count++;
+		tty->driver_data = mp;
+		driver->ttys[idx] = tty;
+	} else
+		capiminor_put(mp);
+	return ret;
+}
 
-	mp = capiminor_get(iminor(file->f_path.dentry->d_inode));
-	if (mp->nccip == NULL)
-		return -ENXIO;
+static void capinc_tty_cleanup(struct tty_struct *tty)
+{
+	struct capiminor *mp = tty->driver_data;
+	tty->driver_data = NULL;
+	capiminor_put(mp);
+}
 
-	tty->driver_data = (void *)mp;
+static int capinc_tty_open(struct tty_struct * tty, struct file * file)
+{
+	struct capiminor *mp = tty->driver_data;
+	unsigned long flags;
 
 	spin_lock_irqsave(&workaround_lock, flags);
 	if (atomic_read(&mp->ttyopencount) == 0)
@@ -1030,15 +1048,12 @@ static int capinc_tty_open(struct tty_struct * tty, struct file * file)
 
 static void capinc_tty_close(struct tty_struct * tty, struct file * file)
 {
-	struct capiminor *mp;
+	struct capiminor *mp = tty->driver_data;
 
-	mp = (struct capiminor *)tty->driver_data;
-	if (mp)	{
 		if (atomic_dec_and_test(&mp->ttyopencount)) {
 #ifdef _DEBUG_REFCOUNT
 			printk(KERN_DEBUG "capinc_tty_close lastclose\n");
 #endif
-			tty->driver_data = NULL;
 			mp->tty = NULL;
 		}
 #ifdef _DEBUG_REFCOUNT
@@ -1047,9 +1062,6 @@ static void capinc_tty_close(struct tty_struct * tty, struct file * file)
 		if (mp->nccip == NULL)
 			capiminor_free(mp);
 
-		capiminor_put(mp);
-	}
-
 #ifdef _DEBUG_REFCOUNT
 	printk(KERN_DEBUG "capinc_tty_close\n");
 #endif
@@ -1333,6 +1345,8 @@ static const struct tty_operations capinc_ops = {
 	.flush_buffer = capinc_tty_flush_buffer,
 	.set_ldisc = capinc_tty_set_ldisc,
 	.send_xchar = capinc_tty_send_xchar,
+	.install = capinc_tty_install,
+	.cleanup = capinc_tty_cleanup,
 };
 
 static int __init capinc_tty_init(void)
-- 
1.6.0.2


^ permalink raw reply related

* [PATCH v2 22/41] CAPI: Use dynamic major for NCCI TTYs by default
From: Jan Kiszka @ 2010-02-08 20:12 UTC (permalink / raw)
  To: David Miller, Karsten Keil
  Cc: linux-kernel, i4ldeveloper, isdn4linux, netdev, Alan Cox,
	Marcel Holtmann
In-Reply-To: <cover.1265659933.git.jan.kiszka@web.de>

No need to allocate a fixed major for this TTY, both capifs and udev
make this transparent to the user.

Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
---
 drivers/isdn/capi/capi.c |   10 ++++------
 1 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 901b79b..b1de0cb 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -60,10 +60,8 @@ module_param_named(major, capi_major, uint, 0);
 #define CAPINC_NR_PORTS		32
 #define CAPINC_MAX_PORTS	256
 
-static int capi_ttymajor = 191;
 static int capi_ttyminors = CAPINC_NR_PORTS;
 
-module_param_named(ttymajor, capi_ttymajor, uint, 0);
 module_param_named(ttyminors, capi_ttyminors, uint, 0);
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 
@@ -301,6 +299,7 @@ static struct capiminor *capiminor_get(unsigned int minor)
 static void capincci_alloc_minor(struct capidev *cdev, struct capincci *np)
 {
 	struct capiminor *mp;
+	dev_t device;
 
 	if (!(cdev->userflags & CAPIFLAG_HIGHJACKING))
 		return;
@@ -311,9 +310,8 @@ static void capincci_alloc_minor(struct capidev *cdev, struct capincci *np)
 #ifdef _DEBUG_REFCOUNT
 		printk(KERN_DEBUG "set mp->nccip\n");
 #endif
-		mp->capifs_dentry =
-			capifs_new_ncci(mp->minor,
-					MKDEV(capi_ttymajor, mp->minor));
+		device = MKDEV(capinc_tty_driver->major, mp->minor);
+		mp->capifs_dentry = capifs_new_ncci(mp->minor, device);
 	}
 }
 
@@ -1341,7 +1339,7 @@ static int __init capinc_tty_init(void)
 	drv->owner = THIS_MODULE;
 	drv->driver_name = "capi_nc";
 	drv->name = "capi";
-	drv->major = capi_ttymajor;
+	drv->major = 0;
 	drv->minor_start = 0;
 	drv->type = TTY_DRIVER_TYPE_SERIAL;
 	drv->subtype = SERIAL_TYPE_NORMAL;
-- 
1.6.0.2


^ 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