* Business Proposal.
From: Edward Lampl @ 2014-08-29 12:04 UTC (permalink / raw)
Good Day,
My name is Edward Lampl, i have gone through your profile and found out
that you are capable of
handling this project, I work with an INTESTATE & HEIR ESTATE BENEFICIARY
TRACING company in united states.
I have an important business proposal that would be of mutual benefit to
us, all I need is your TRUST and CONFIDENCE. It is risk free as long as
you would follow every instruction.
If interested, contact me on: edward@admin.in.th
Regards,
Ed Lampl
^ permalink raw reply
* [PATCH] brcmfmac: BRCMFMAC should depend on HAS_DMA
From: Geert Uytterhoeven @ 2014-08-29 16:24 UTC (permalink / raw)
To: Brett Rudley, John W. Linville, David S. Miller
Cc: linux-wireless, brcm80211-dev-list, netdev, linux-kernel,
Geert Uytterhoeven
If NO_DMA=y:
drivers/built-in.o: In function `brcmf_msgbuf_release_array':
msgbuf.c:(.text+0x34dbbe): undefined reference to `dma_unmap_single'
drivers/built-in.o: In function `brcmf_proto_msgbuf_detach':
(.text+0x34dca4): undefined reference to `dma_free_coherent'
drivers/built-in.o: In function `brcmf_msgbuf_get_pktid':
msgbuf.c:(.text+0x34dd2a): undefined reference to `dma_unmap_single'
drivers/built-in.o: In function `brcmf_msgbuf_alloc_pktid':
msgbuf.c:(.text+0x34de12): undefined reference to `dma_map_single'
msgbuf.c:(.text+0x34de20): undefined reference to `dma_mapping_error'
drivers/built-in.o: In function `brcmf_msgbuf_remove_flowring':
msgbuf.c:(.text+0x34e3d6): undefined reference to `dma_free_coherent'
drivers/built-in.o: In function `brcmf_msgbuf_flowring_create':
msgbuf.c:(.text+0x34e4f8): undefined reference to `dma_alloc_coherent'
drivers/built-in.o: In function `brcmf_proto_msgbuf_attach':
(.text+0x34f5fe): undefined reference to `dma_alloc_coherent'
drivers/built-in.o: In function `brcmf_proto_msgbuf_attach':
(.text+0x34f798): undefined reference to `dma_free_coherent'
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
drivers/net/wireless/brcm80211/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig
index b8e2561ea645..2e0da8925834 100644
--- a/drivers/net/wireless/brcm80211/Kconfig
+++ b/drivers/net/wireless/brcm80211/Kconfig
@@ -19,7 +19,7 @@ config BRCMSMAC
config BRCMFMAC
tristate "Broadcom IEEE802.11n embedded FullMAC WLAN driver"
- depends on CFG80211
+ depends on CFG80211A && HAS_DMA
select BRCMUTIL
---help---
This module adds support for embedded wireless adapters based on
--
1.9.1
^ permalink raw reply related
* [PATCH] drivers: net: NET_XGENE should depend on HAS_DMA
From: Geert Uytterhoeven @ 2014-08-29 16:21 UTC (permalink / raw)
To: Iyappan Subramanian, David S. Miller
Cc: netdev, linux-kernel, Geert Uytterhoeven
If NO_DMA=y:
drivers/built-in.o: In function `xgene_enet_delete_ring':
xgene_enet_main.c:(.text+0x28755a): undefined reference to `dma_free_coherent'
drivers/built-in.o: In function `xgene_enet_setup_tx_desc':
xgene_enet_main.c:(.text+0x287774): undefined reference to `dma_map_single'
xgene_enet_main.c:(.text+0x287780): undefined reference to `dma_mapping_error'
drivers/built-in.o: In function `xgene_enet_tx_completion':
xgene_enet_main.c:(.text+0x2878e6): undefined reference to `dma_unmap_single'
drivers/built-in.o: In function `xgene_enet_refill_bufpool':
xgene_enet_main.c:(.text+0x2879d4): undefined reference to `dma_map_single'
xgene_enet_main.c:(.text+0x2879e0): undefined reference to `dma_mapping_error'
drivers/built-in.o: In function `xgene_enet_rx_frame':
xgene_enet_main.c:(.text+0x287aaa): undefined reference to `dma_unmap_single'
drivers/built-in.o: In function `xgene_enet_free_desc_ring':
xgene_enet_main.c:(.text+0x287f98): undefined reference to `dma_free_coherent'
drivers/built-in.o: In function `xgene_enet_create_desc_ring':
xgene_enet_main.c:(.text+0x28808e): undefined reference to `dma_alloc_coherent'
drivers/built-in.o: In function `xgene_enet_probe':
xgene_enet_main.c:(.text+0x2883d4): undefined reference to `dma_set_mask'
xgene_enet_main.c:(.text+0x2883ec): undefined reference to `dma_supported'
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
drivers/net/ethernet/apm/xgene/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/ethernet/apm/xgene/Kconfig b/drivers/net/ethernet/apm/xgene/Kconfig
index 616dff6d3f5f..f4054d242f3c 100644
--- a/drivers/net/ethernet/apm/xgene/Kconfig
+++ b/drivers/net/ethernet/apm/xgene/Kconfig
@@ -1,5 +1,6 @@
config NET_XGENE
tristate "APM X-Gene SoC Ethernet Driver"
+ depends on HAS_DMA
select PHYLIB
help
This is the Ethernet driver for the on-chip ethernet interface on the
--
1.9.1
^ permalink raw reply related
* Re: RTNL: assertion failed at net/ipv6/addrconf.c (1699)
From: Vlad Yasevich @ 2014-08-29 16:17 UTC (permalink / raw)
To: Tommi Rantala, David S. Miller, Alexey Kuznetsov, James Morris,
Hideaki YOSHIFUJI, Patrick McHardy, Hannes Frederic Sowa
Cc: netdev, LKML, trinity, Dave Jones
In-Reply-To: <CA+ydwtoDwQVg_BWrxxdpUyJ7Up60GtxTCLYe_5vFdV2td_kGhQ@mail.gmail.com>
On 08/29/2014 11:26 AM, Tommi Rantala wrote:
> Hi,
>
> Was fuzzing Linus v3.17-rc2-89-g59753a8 with Trinity as the root user
> in qemu, when I hit the following assertion failures.
>
> Tommi
>
>
> [init] Started watchdog process, PID is 4841
> [main] Main thread is alive.
> [ 77.229699] sctp: [Deprecated]: trinity-main (pid 4842) Use of int
> in max_burst socket option deprecated.
> [ 77.229699] Use struct sctp_assoc_value instead
> [ 77.297196] RTNL: assertion failed at net/ipv6/addrconf.c (1699)
> [ 77.298080] CPU: 0 PID: 4842 Comm: trinity-main Not tainted 3.17.0-rc2+ #30
> [ 77.299039] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
> [ 77.299789] ffff88003d76a618 ffff880026133c50 ffffffff8238ba79
> ffff880037c84520
> [ 77.300829] ffff880026133c90 ffffffff820bd52b 0000000000000000
> ffffffff82d86c40
> [ 77.301869] 0000000000000000 00000000f76fd1e1 ffff8800382d8000
> ffff8800382d8220
> [ 77.302906] Call Trace:
> [ 77.303246] [<ffffffff8238ba79>] dump_stack+0x4d/0x66
> [ 77.303928] [<ffffffff820bd52b>] addrconf_join_solict+0x4b/0xb0
> [ 77.304731] [<ffffffff820b031b>] ipv6_dev_ac_inc+0x2bb/0x330
> [ 77.305498] [<ffffffff820b0060>] ? ac6_seq_start+0x260/0x260
> [ 77.306257] [<ffffffff820b05fe>] ipv6_sock_ac_join+0x26e/0x360
> [ 77.307046] [<ffffffff820b0429>] ? ipv6_sock_ac_join+0x99/0x360
> [ 77.307798] [<ffffffff820cdd60>] do_ipv6_setsockopt.isra.5+0xa70/0xf20
> [ 77.308570] [<ffffffff8117097d>] ? sched_clock_local+0x1d/0x80
> [ 77.309260] [<ffffffff810a8a27>] ? kvm_clock_read+0x27/0x40
> [ 77.309915] [<ffffffff810736d9>] ? sched_clock+0x9/0x10
> [ 77.310537] [<ffffffff815afff8>] ? sock_has_perm+0x168/0x1e0
> [ 77.311204] [<ffffffff81170bb8>] ? sched_clock_cpu+0xa8/0xf0
> [ 77.311866] [<ffffffff81170d1b>] ? local_clock+0x1b/0x30
> [ 77.312501] [<ffffffff811872cd>] ? lock_release_holdtime+0x1d/0x170
> [ 77.313241] [<ffffffff815b0010>] ? sock_has_perm+0x180/0x1e0
> [ 77.313905] [<ffffffff815afe90>] ?
> selinux_msg_queue_alloc_security+0xa0/0xa0
> [ 77.314746] [<ffffffff820ce263>] ipv6_setsockopt+0x53/0xb0
> [ 77.315397] [<ffffffff820d3135>] udpv6_setsockopt+0x25/0x30
> [ 77.316058] [<ffffffff81f9930f>] sock_common_setsockopt+0xf/0x20
> [ 77.316764] [<ffffffff81f9305e>] SyS_setsockopt+0x8e/0xd0
> [ 77.317406] [<ffffffff823a47e9>] system_call_fastpath+0x16/0x1b
> [main] 375 sockets created based on info from socket cachefile.
> [main] Generating file descriptors
> [main] Added 129 filenames from /dev
> [main] Added 44048 filenames from /proc
> [main] Added 18192 filenames from /sys
> [main] Enabled 9 fd providers.
> [watchdog] Watchdog is alive. (pid:4841)
> [child3:4846] finit_module (313) returned ENOSYS, marking as inactive.
> [child1:4844] kcmp (312) returned ENOSYS, marking as inactive.
> [child2:4845] uselib (134) returned ENOSYS, marking as inactive.
> [child1:4844] nfsservctl (180) returned ENOSYS, marking as inactive.
> [child2:4845] delete_module (129:[32BIT]) returned ENOSYS, marking as inactive.
> [child2:4845] init_module (175) returned ENOSYS, marking as inactive.
> [ 84.126609] trinity-c7: vm86 mode not supported on 64 bit kernel
> [child7:4850] vm86 (166:[32BIT]) returned ENOSYS, marking as inactive.
> [main] Bailing main loop because ctrl-c.
> [ 84.345840] RTNL: assertion failed at net/ipv6/addrconf.c (1712)
> [ 84.346615] CPU: 0 PID: 4842 Comm: trinity-main Not tainted 3.17.0-rc2+ #30
> [ 84.347426] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
> [ 84.348102] ffff88003d76a618 ffff880026133d10 ffffffff8238ba79
> ffff8800382d8000
> [ 84.349018] ffff880026133d50 ffffffff820bd5db ffffffff81141555
> ffff8800382d8220
> [ 84.349935] ffff8800382d8000 00000000f76fd1e1 ffff88003d76a618
> ffff8800382d8000
> [ 84.350848] Call Trace:
> [ 84.351149] [<ffffffff8238ba79>] dump_stack+0x4d/0x66
> [ 84.351751] [<ffffffff820bd5db>] addrconf_leave_solict+0x4b/0xb0
> [ 84.352574] [<ffffffff81141555>] ? __local_bh_enable_ip+0xa5/0xf0
> [ 84.353315] [<ffffffff820b07b3>] __ipv6_dev_ac_dec+0xc3/0x140
> [ 84.354019] [<ffffffff820b08c8>] ipv6_dev_ac_dec+0x98/0xb0
> [ 84.354687] [<ffffffff820b0bcd>] ipv6_sock_ac_close+0x10d/0x1a0
> [ 84.355410] [<ffffffff820b0aee>] ? ipv6_sock_ac_close+0x2e/0x1a0
> [ 84.356147] [<ffffffff820ae9d3>] inet6_release+0x23/0x40
> [ 84.356789] [<ffffffff81f91834>] sock_release+0x14/0x80
> [ 84.357410] [<ffffffff81f918ad>] sock_close+0xd/0x20
> [ 84.358042] [<ffffffff8127fa91>] __fput+0x111/0x1e0
> [ 84.358622] [<ffffffff8127fba9>] ____fput+0x9/0x10
> [ 84.359196] [<ffffffff8115e3ee>] task_work_run+0x9e/0xd0
> [ 84.359825] [<ffffffff8113f4b6>] do_exit+0x456/0xb30
> [ 84.360419] [<ffffffff823a541c>] ? retint_swapgs+0x13/0x1b
> [ 84.361075] [<ffffffff8113fc54>] do_group_exit+0x84/0xd0
> [ 84.361705] [<ffffffff8113fcaf>] SyS_exit_group+0xf/0x10
> [ 84.362338] [<ffffffff823a47e9>] system_call_fastpath+0x16/0x1b
> [watchdog] [4841] Watchdog exiting because ctrl-c.
> [init] Ran 775 syscalls. Successes: 179 Failures: 596
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
Yep, looks like ipv6_dev_ac_inc() and __ipv6_dev_ac_dec() are called
without RNTL in the socket option path and with RTNL in the address
configuration path. So it look like this this can actually trigger
list corruptions.
-vlad
^ permalink raw reply
* Re: [net-next PATCH] pktgen: add flag NO_TIMESTAMP to disable timestamping
From: Eric Dumazet @ 2014-08-29 16:17 UTC (permalink / raw)
To: Jesper Dangaard Brouer; +Cc: David S. Miller, netdev, Ben Greear, Robert Olsson
In-Reply-To: <20140828161447.6652.59388.stgit@dragon>
On Thu, 2014-08-28 at 18:14 +0200, Jesper Dangaard Brouer wrote:
> Then testing the TX limits of the stack, then it is useful to
> be-able to disable the do_gettimeofday() timetamping on every packet.
>
> This implements a pktgen flag NO_TIMESTAMP which will disable this
> call to do_gettimeofday().
>
> The performance change on (my system E5-2695) with skb_clone=0, goes
> from TX 2,423,751 pps to 2,567,165 pps with flag NO_TIMESTAMP. Thus,
> the cost of do_gettimeofday() or saving is approx 23 nanosec.
I guess using local_clock() would provide an accurate and less expensive
timestamp, if a timestamp is needed.
^ permalink raw reply
* RTNL: assertion failed at net/ipv6/addrconf.c (1699)
From: Tommi Rantala @ 2014-08-29 15:26 UTC (permalink / raw)
To: David S. Miller, Alexey Kuznetsov, James Morris,
Hideaki YOSHIFUJI, Patrick McHardy, Hannes Frederic Sowa
Cc: netdev, LKML, trinity, Dave Jones
Hi,
Was fuzzing Linus v3.17-rc2-89-g59753a8 with Trinity as the root user
in qemu, when I hit the following assertion failures.
Tommi
[init] Started watchdog process, PID is 4841
[main] Main thread is alive.
[ 77.229699] sctp: [Deprecated]: trinity-main (pid 4842) Use of int
in max_burst socket option deprecated.
[ 77.229699] Use struct sctp_assoc_value instead
[ 77.297196] RTNL: assertion failed at net/ipv6/addrconf.c (1699)
[ 77.298080] CPU: 0 PID: 4842 Comm: trinity-main Not tainted 3.17.0-rc2+ #30
[ 77.299039] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[ 77.299789] ffff88003d76a618 ffff880026133c50 ffffffff8238ba79
ffff880037c84520
[ 77.300829] ffff880026133c90 ffffffff820bd52b 0000000000000000
ffffffff82d86c40
[ 77.301869] 0000000000000000 00000000f76fd1e1 ffff8800382d8000
ffff8800382d8220
[ 77.302906] Call Trace:
[ 77.303246] [<ffffffff8238ba79>] dump_stack+0x4d/0x66
[ 77.303928] [<ffffffff820bd52b>] addrconf_join_solict+0x4b/0xb0
[ 77.304731] [<ffffffff820b031b>] ipv6_dev_ac_inc+0x2bb/0x330
[ 77.305498] [<ffffffff820b0060>] ? ac6_seq_start+0x260/0x260
[ 77.306257] [<ffffffff820b05fe>] ipv6_sock_ac_join+0x26e/0x360
[ 77.307046] [<ffffffff820b0429>] ? ipv6_sock_ac_join+0x99/0x360
[ 77.307798] [<ffffffff820cdd60>] do_ipv6_setsockopt.isra.5+0xa70/0xf20
[ 77.308570] [<ffffffff8117097d>] ? sched_clock_local+0x1d/0x80
[ 77.309260] [<ffffffff810a8a27>] ? kvm_clock_read+0x27/0x40
[ 77.309915] [<ffffffff810736d9>] ? sched_clock+0x9/0x10
[ 77.310537] [<ffffffff815afff8>] ? sock_has_perm+0x168/0x1e0
[ 77.311204] [<ffffffff81170bb8>] ? sched_clock_cpu+0xa8/0xf0
[ 77.311866] [<ffffffff81170d1b>] ? local_clock+0x1b/0x30
[ 77.312501] [<ffffffff811872cd>] ? lock_release_holdtime+0x1d/0x170
[ 77.313241] [<ffffffff815b0010>] ? sock_has_perm+0x180/0x1e0
[ 77.313905] [<ffffffff815afe90>] ?
selinux_msg_queue_alloc_security+0xa0/0xa0
[ 77.314746] [<ffffffff820ce263>] ipv6_setsockopt+0x53/0xb0
[ 77.315397] [<ffffffff820d3135>] udpv6_setsockopt+0x25/0x30
[ 77.316058] [<ffffffff81f9930f>] sock_common_setsockopt+0xf/0x20
[ 77.316764] [<ffffffff81f9305e>] SyS_setsockopt+0x8e/0xd0
[ 77.317406] [<ffffffff823a47e9>] system_call_fastpath+0x16/0x1b
[main] 375 sockets created based on info from socket cachefile.
[main] Generating file descriptors
[main] Added 129 filenames from /dev
[main] Added 44048 filenames from /proc
[main] Added 18192 filenames from /sys
[main] Enabled 9 fd providers.
[watchdog] Watchdog is alive. (pid:4841)
[child3:4846] finit_module (313) returned ENOSYS, marking as inactive.
[child1:4844] kcmp (312) returned ENOSYS, marking as inactive.
[child2:4845] uselib (134) returned ENOSYS, marking as inactive.
[child1:4844] nfsservctl (180) returned ENOSYS, marking as inactive.
[child2:4845] delete_module (129:[32BIT]) returned ENOSYS, marking as inactive.
[child2:4845] init_module (175) returned ENOSYS, marking as inactive.
[ 84.126609] trinity-c7: vm86 mode not supported on 64 bit kernel
[child7:4850] vm86 (166:[32BIT]) returned ENOSYS, marking as inactive.
[main] Bailing main loop because ctrl-c.
[ 84.345840] RTNL: assertion failed at net/ipv6/addrconf.c (1712)
[ 84.346615] CPU: 0 PID: 4842 Comm: trinity-main Not tainted 3.17.0-rc2+ #30
[ 84.347426] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[ 84.348102] ffff88003d76a618 ffff880026133d10 ffffffff8238ba79
ffff8800382d8000
[ 84.349018] ffff880026133d50 ffffffff820bd5db ffffffff81141555
ffff8800382d8220
[ 84.349935] ffff8800382d8000 00000000f76fd1e1 ffff88003d76a618
ffff8800382d8000
[ 84.350848] Call Trace:
[ 84.351149] [<ffffffff8238ba79>] dump_stack+0x4d/0x66
[ 84.351751] [<ffffffff820bd5db>] addrconf_leave_solict+0x4b/0xb0
[ 84.352574] [<ffffffff81141555>] ? __local_bh_enable_ip+0xa5/0xf0
[ 84.353315] [<ffffffff820b07b3>] __ipv6_dev_ac_dec+0xc3/0x140
[ 84.354019] [<ffffffff820b08c8>] ipv6_dev_ac_dec+0x98/0xb0
[ 84.354687] [<ffffffff820b0bcd>] ipv6_sock_ac_close+0x10d/0x1a0
[ 84.355410] [<ffffffff820b0aee>] ? ipv6_sock_ac_close+0x2e/0x1a0
[ 84.356147] [<ffffffff820ae9d3>] inet6_release+0x23/0x40
[ 84.356789] [<ffffffff81f91834>] sock_release+0x14/0x80
[ 84.357410] [<ffffffff81f918ad>] sock_close+0xd/0x20
[ 84.358042] [<ffffffff8127fa91>] __fput+0x111/0x1e0
[ 84.358622] [<ffffffff8127fba9>] ____fput+0x9/0x10
[ 84.359196] [<ffffffff8115e3ee>] task_work_run+0x9e/0xd0
[ 84.359825] [<ffffffff8113f4b6>] do_exit+0x456/0xb30
[ 84.360419] [<ffffffff823a541c>] ? retint_swapgs+0x13/0x1b
[ 84.361075] [<ffffffff8113fc54>] do_group_exit+0x84/0xd0
[ 84.361705] [<ffffffff8113fcaf>] SyS_exit_group+0xf/0x10
[ 84.362338] [<ffffffff823a47e9>] system_call_fastpath+0x16/0x1b
[watchdog] [4841] Watchdog exiting because ctrl-c.
[init] Ran 775 syscalls. Successes: 179 Failures: 596
^ permalink raw reply
* [PATCH net-next v1 1/3] amd-xgbe: Move phy driver support into the network driver
From: Tom Lendacky @ 2014-08-29 15:21 UTC (permalink / raw)
To: netdev; +Cc: davem
In-Reply-To: <20140829152148.13783.23789.stgit@tlendack-t1.amdoffice.net>
The phy driver support is not available as a generic phy device for
use by other drivers, it is integral to the amd-xgbe device. For
this reason, remove it from drivers/net/phy directory and make it
part of the amd-xgbe driver. The driver will still make use of the
phy library support for tracking state, etc.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
drivers/net/ethernet/amd/Kconfig | 1
drivers/net/ethernet/amd/xgbe/Makefile | 2
drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 16
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 10
drivers/net/ethernet/amd/xgbe/xgbe-main.c | 137 ++
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 39 -
drivers/net/ethernet/amd/xgbe/xgbe-phy.c | 1128 ++++++++++++++++++++
drivers/net/ethernet/amd/xgbe/xgbe.h | 59 +
drivers/net/phy/Kconfig | 6
drivers/net/phy/Makefile | 1
drivers/net/phy/amd-xgbe-phy.c | 1457 --------------------------
11 files changed, 1320 insertions(+), 1536 deletions(-)
create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-phy.c
delete mode 100644 drivers/net/phy/amd-xgbe-phy.c
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index 8319c99..dbf4446 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -181,7 +181,6 @@ config AMD_XGBE
tristate "AMD 10GbE Ethernet driver"
depends on OF_NET
select PHYLIB
- select AMD_XGBE_PHY
select BITREVERSE
select CRC32
select PTP_1588_CLOCK
diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile
index 171a7e6..861cf15 100644
--- a/drivers/net/ethernet/amd/xgbe/Makefile
+++ b/drivers/net/ethernet/amd/xgbe/Makefile
@@ -2,7 +2,7 @@ obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o
amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \
xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \
- xgbe-ptp.o
+ xgbe-ptp.o xgbe-phy.o
amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o
amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index dc84f71..61c0975 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -525,7 +525,7 @@ static void xgbe_adjust_link(struct net_device *netdev)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
struct xgbe_hw_if *hw_if = &pdata->hw_if;
- struct phy_device *phydev = pdata->phydev;
+ struct phy_device *phydev = pdata->phy.phydev;
int new_state = 0;
if (phydev == NULL)
@@ -590,7 +590,7 @@ static void xgbe_adjust_link(struct net_device *netdev)
static int xgbe_phy_init(struct xgbe_prv_data *pdata)
{
struct net_device *netdev = pdata->netdev;
- struct phy_device *phydev = pdata->phydev;
+ struct phy_device *phydev = pdata->phy.phydev;
int ret;
pdata->phy_link = -1;
@@ -623,10 +623,10 @@ err_phy_connect:
static void xgbe_phy_exit(struct xgbe_prv_data *pdata)
{
- if (!pdata->phydev)
+ if (!pdata->phy.phydev)
return;
- phy_disconnect(pdata->phydev);
+ phy_disconnect(pdata->phy.phydev);
}
int xgbe_powerdown(struct net_device *netdev, unsigned int caller)
@@ -644,7 +644,7 @@ int xgbe_powerdown(struct net_device *netdev, unsigned int caller)
return -EINVAL;
}
- phy_stop(pdata->phydev);
+ phy_stop(pdata->phy.phydev);
spin_lock_irqsave(&pdata->lock, flags);
@@ -686,7 +686,7 @@ int xgbe_powerup(struct net_device *netdev, unsigned int caller)
pdata->power_down = 0;
- phy_start(pdata->phydev);
+ phy_start(pdata->phy.phydev);
/* Enable Tx/Rx */
hw_if->powerup_tx(pdata);
@@ -716,7 +716,7 @@ static int xgbe_start(struct xgbe_prv_data *pdata)
hw_if->init(pdata);
- phy_start(pdata->phydev);
+ phy_start(pdata->phy.phydev);
hw_if->enable_tx(pdata);
hw_if->enable_rx(pdata);
@@ -738,7 +738,7 @@ static void xgbe_stop(struct xgbe_prv_data *pdata)
DBGPR("-->xgbe_stop\n");
- phy_stop(pdata->phydev);
+ phy_stop(pdata->phy.phydev);
netif_tx_stop_all_queues(netdev);
xgbe_napi_disable(pdata, 1);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
index a076aca..033ed55 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
@@ -250,7 +250,7 @@ static int xgbe_set_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
- struct phy_device *phydev = pdata->phydev;
+ struct phy_device *phydev = pdata->phy.phydev;
int ret = 0;
DBGPR("-->xgbe_set_pauseparam\n");
@@ -287,10 +287,10 @@ static int xgbe_get_settings(struct net_device *netdev,
DBGPR("-->xgbe_get_settings\n");
- if (!pdata->phydev)
+ if (!pdata->phy.phydev)
return -ENODEV;
- ret = phy_ethtool_gset(pdata->phydev, cmd);
+ ret = phy_ethtool_gset(pdata->phy.phydev, cmd);
cmd->transceiver = XCVR_EXTERNAL;
DBGPR("<--xgbe_get_settings\n");
@@ -302,13 +302,13 @@ static int xgbe_set_settings(struct net_device *netdev,
struct ethtool_cmd *cmd)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
- struct phy_device *phydev = pdata->phydev;
+ struct phy_device *phydev = pdata->phy.phydev;
u32 speed;
int ret;
DBGPR("-->xgbe_set_settings\n");
- if (!pdata->phydev)
+ if (!pdata->phy.phydev)
return -ENODEV;
speed = ethtool_cmd_speed(cmd);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
index 8aa6a93..f9ffbfc 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
@@ -122,6 +122,8 @@
#include <linux/etherdevice.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
#include <linux/of_net.h>
#include <linux/clk.h>
@@ -223,7 +225,10 @@ static int xgbe_probe(struct platform_device *pdev)
struct xgbe_desc_if *desc_if;
struct net_device *netdev;
struct device *dev = &pdev->dev;
+ struct device_node *phy_node;
+ struct platform_device *phy_pdev;
struct resource *res;
+ const __be32 *property;
const u8 *mac_addr;
int ret;
@@ -247,6 +252,21 @@ static int xgbe_probe(struct platform_device *pdev)
mutex_init(&pdata->xpcs_mutex);
spin_lock_init(&pdata->tstamp_lock);
+ /* Obtain a reference the phy device */
+ phy_node = of_parse_phandle(dev->of_node, "phy-handle", 0);
+ if (!phy_node) {
+ dev_err(dev, "unable to parse phy-handle\n");
+ ret = -EINVAL;
+ goto err_put;
+ }
+
+ phy_pdev = of_find_device_by_node(phy_node);
+ if (!phy_pdev) {
+ dev_err(dev, "unable to locate phy device\n");
+ ret = -EINVAL;
+ goto err_io;
+ }
+
/* Set and validate the number of descriptors for a ring */
BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_TX_DESC_CNT);
pdata->tx_desc_count = XGBE_TX_DESC_CNT;
@@ -300,12 +320,65 @@ static int xgbe_probe(struct platform_device *pdev)
}
DBGPR(" xpcs_regs = %p\n", pdata->xpcs_regs);
- /* Set the DMA mask */
- if (!dev->dma_mask)
- dev->dma_mask = &dev->coherent_dma_mask;
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
- if (ret) {
- dev_err(dev, "dma_set_mask_and_coherent failed\n");
+ /* Obtain the mmio areas for the phy device */
+ res = platform_get_resource(phy_pdev, IORESOURCE_MEM, 0);
+ pdata->rxtx_regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pdata->rxtx_regs)) {
+ dev_err(dev, "rxtx ioremap failed\n");
+ ret = PTR_ERR(pdata->rxtx_regs);
+ goto err_io;
+ }
+ DBGPR(" rxtx_regs = %p\n", pdata->rxtx_regs);
+
+ res = platform_get_resource(phy_pdev, IORESOURCE_MEM, 1);
+ pdata->sir0_regs = devm_ioremap_nocache(dev, res->start,
+ resource_size(res));
+ if (IS_ERR(pdata->sir0_regs)) {
+ dev_err(dev, "sir0 ioremap failed\n");
+ ret = PTR_ERR(pdata->sir0_regs);
+ goto err_io;
+ }
+ DBGPR(" sir0_regs = %p\n", pdata->sir0_regs);
+
+ res = platform_get_resource(phy_pdev, IORESOURCE_MEM, 2);
+ pdata->sir1_regs = devm_ioremap_nocache(dev, res->start,
+ resource_size(res));
+ if (IS_ERR(pdata->sir1_regs)) {
+ dev_err(dev, "sir1 ioremap failed\n");
+ ret = PTR_ERR(pdata->sir1_regs);
+ goto err_io;
+ }
+ DBGPR(" sir1_regs = %p\n", pdata->sir1_regs);
+
+ /* Get the device speed set property */
+ property = of_get_property(phy_node, XGBE_PHY_SPEEDSET_PROPERTY, NULL);
+ if (property)
+ pdata->speed_set = be32_to_cpu(*property);
+
+ switch (pdata->speed_set) {
+ case XGBE_PHY_SPEEDSET_1000_10000:
+ case XGBE_PHY_SPEEDSET_2500_10000:
+ break;
+ default:
+ dev_err(dev, "invalid amd,speed-set property\n");
+ ret = -EINVAL;
+ goto err_io;
+ }
+
+ /* Retrieve the MAC address */
+ mac_addr = of_get_mac_address(dev->of_node);
+ if (!mac_addr) {
+ dev_err(dev, "invalid mac address for this device\n");
+ ret = -EINVAL;
+ goto err_io;
+ }
+ memcpy(netdev->dev_addr, mac_addr, netdev->addr_len);
+
+ /* Retrieve the PHY mode - it must be "xgmii" */
+ pdata->phy_mode = of_get_phy_mode(dev->of_node);
+ if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) {
+ dev_err(dev, "invalid phy-mode specified for this device\n");
+ ret = -EINVAL;
goto err_io;
}
@@ -319,6 +392,16 @@ static int xgbe_probe(struct platform_device *pdev)
pdata->awcache = XGBE_DMA_SYS_AWCACHE;
}
+ /* Set the DMA mask */
+ if (!dev->dma_mask)
+ dev->dma_mask = &dev->coherent_dma_mask;
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+ if (ret) {
+ dev_err(dev, "dma_set_mask_and_coherent failed\n");
+ goto err_io;
+ }
+
+ /* Get the device interrupt */
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(dev, "platform_get_irq failed\n");
@@ -338,23 +421,6 @@ static int xgbe_probe(struct platform_device *pdev)
/* Populate the hardware features */
xgbe_get_all_hw_features(pdata);
- /* Retrieve the MAC address */
- mac_addr = of_get_mac_address(dev->of_node);
- if (!mac_addr) {
- dev_err(dev, "invalid mac address for this device\n");
- ret = -EINVAL;
- goto err_io;
- }
- memcpy(netdev->dev_addr, mac_addr, netdev->addr_len);
-
- /* Retrieve the PHY mode - it must be "xgmii" */
- pdata->phy_mode = of_get_phy_mode(dev->of_node);
- if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) {
- dev_err(dev, "invalid phy-mode specified for this device\n");
- ret = -EINVAL;
- goto err_io;
- }
-
/* Set default configuration data */
xgbe_default_config(pdata);
@@ -447,6 +513,8 @@ static int xgbe_probe(struct platform_device *pdev)
xgbe_debugfs_init(pdata);
+ of_node_put(phy_node);
+
netdev_notice(netdev, "net device enabled\n");
DBGPR("<-- xgbe_probe\n");
@@ -460,6 +528,9 @@ err_bus_id:
kfree(pdata->mii_bus_id);
err_io:
+ of_node_put(phy_node);
+
+err_put:
free_netdev(netdev);
err_alloc:
@@ -550,4 +621,22 @@ static struct platform_driver xgbe_driver = {
.remove = xgbe_remove,
};
-module_platform_driver(xgbe_driver);
+static int __init xgbe_driver_init(void)
+{
+ int ret;
+
+ ret = xgbe_phy_register();
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&xgbe_driver);
+}
+module_init(xgbe_driver_init);
+
+static void __exit xgbe_driver_exit(void)
+{
+ platform_driver_unregister(&xgbe_driver);
+
+ xgbe_phy_unregister();
+}
+module_exit(xgbe_driver_exit);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
index 6d2221e..11c4912 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
@@ -206,25 +206,16 @@ void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata)
int xgbe_mdio_register(struct xgbe_prv_data *pdata)
{
- struct device_node *phy_node;
struct mii_bus *mii;
struct phy_device *phydev;
int ret = 0;
DBGPR("-->xgbe_mdio_register\n");
- /* Retrieve the phy-handle */
- phy_node = of_parse_phandle(pdata->dev->of_node, "phy-handle", 0);
- if (!phy_node) {
- dev_err(pdata->dev, "unable to parse phy-handle\n");
- return -EINVAL;
- }
-
mii = mdiobus_alloc();
if (mii == NULL) {
dev_err(pdata->dev, "mdiobus_alloc failed\n");
- ret = -ENOMEM;
- goto err_node_get;
+ return -ENOMEM;
}
/* Register on the MDIO bus (don't probe any PHYs) */
@@ -250,24 +241,18 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata)
ret = phydev ? PTR_ERR(phydev) : -ENOLINK;
goto err_mdiobus_register;
}
- request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
- MDIO_ID_ARGS(phydev->c45_ids.device_ids[MDIO_MMD_PCS]));
+ phydev->priv = pdata;
- of_node_get(phy_node);
- phydev->dev.of_node = phy_node;
ret = phy_device_register(phydev);
if (ret) {
dev_err(pdata->dev, "phy_device_register failed\n");
- of_node_put(phy_node);
goto err_phy_device;
}
- /* Add a reference to the PHY driver so it can't be unloaded */
- pdata->phy_module = phydev->dev.driver ?
- phydev->dev.driver->owner : NULL;
- if (!try_module_get(pdata->phy_module)) {
- dev_err(pdata->dev, "try_module_get failed\n");
- ret = -EIO;
+ /* Be sure the phy support was probed successfully */
+ if (!pdata->phy.probed) {
+ dev_err(pdata->dev, "phy support not probed successfully\n");
+ ret = -EINVAL;
goto err_phy_device;
}
@@ -282,9 +267,7 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata)
phydev->advertising &= ~ADVERTISED_Autoneg;
}
- pdata->phydev = phydev;
-
- of_node_put(phy_node);
+ pdata->phy.phydev = phydev;
DBGPHY_REGS(pdata);
@@ -301,9 +284,6 @@ err_mdiobus_register:
err_mdiobus_alloc:
mdiobus_free(mii);
-err_node_get:
- of_node_put(phy_node);
-
return ret;
}
@@ -311,10 +291,7 @@ void xgbe_mdio_unregister(struct xgbe_prv_data *pdata)
{
DBGPR("-->xgbe_mdio_unregister\n");
- pdata->phydev = NULL;
-
- module_put(pdata->phy_module);
- pdata->phy_module = NULL;
+ pdata->phy.phydev = NULL;
mdiobus_unregister(pdata->mii);
pdata->mii->priv = NULL;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy.c
new file mode 100644
index 0000000..7aca21d
--- /dev/null
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy.c
@@ -0,0 +1,1128 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Advanced Micro Devices, Inc. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/mm.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/mdio.h>
+#include <linux/io.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+#define XGBE_PHY_ID 0x000162d0
+#define XGBE_PHY_MASK 0xfffffff0
+
+#define XGBE_AN_INT_CMPLT 0x01
+#define XGBE_AN_INC_LINK 0x02
+#define XGBE_AN_PG_RCV 0x04
+
+#define XNP_MCF_NULL_MESSAGE 0x001
+#define XNP_ACK_PROCESSED (1 << 12)
+#define XNP_MP_FORMATTED (1 << 13)
+#define XNP_NP_EXCHANGE (1 << 15)
+
+#define XGBE_PHY_RATECHANGE_COUNT 500
+
+#ifndef MDIO_PMA_10GBR_PMD_CTRL
+#define MDIO_PMA_10GBR_PMD_CTRL 0x0096
+#endif
+
+#ifndef MDIO_PMA_10GBR_FEC_CTRL
+#define MDIO_PMA_10GBR_FEC_CTRL 0x00ab
+#endif
+
+#ifndef MDIO_AN_XNP
+#define MDIO_AN_XNP 0x0016
+#endif
+
+#ifndef MDIO_AN_INTMASK
+#define MDIO_AN_INTMASK 0x8001
+#endif
+
+#ifndef MDIO_AN_INT
+#define MDIO_AN_INT 0x8002
+#endif
+
+#ifndef MDIO_CTRL1_SPEED1G
+#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
+#endif
+
+#ifndef MDIO_PMA_10GBR_KR_TRAIN_START
+#define MDIO_PMA_10GBR_KR_TRAIN_START 0x01
+#endif
+
+#ifndef MDIO_PMA_10GBR_KR_TRAIN_ENABLE
+#define MDIO_PMA_10GBR_KR_TRAIN_ENABLE 0x02
+#endif
+
+/* SerDes integration register offsets */
+#define SIR0_KR_RT_1 0x002c
+#define SIR0_STATUS 0x0040
+#define SIR1_SPEED 0x0000
+
+/* SerDes integration register entry bit positions and sizes */
+#define SIR0_KR_RT_1_RESET_INDEX 11
+#define SIR0_KR_RT_1_RESET_WIDTH 1
+#define SIR0_STATUS_RX_READY_INDEX 0
+#define SIR0_STATUS_RX_READY_WIDTH 1
+#define SIR0_STATUS_TX_READY_INDEX 8
+#define SIR0_STATUS_TX_READY_WIDTH 1
+#define SIR1_SPEED_DATARATE_INDEX 4
+#define SIR1_SPEED_DATARATE_WIDTH 2
+#define SIR1_SPEED_PI_SPD_SEL_INDEX 12
+#define SIR1_SPEED_PI_SPD_SEL_WIDTH 4
+#define SIR1_SPEED_PLLSEL_INDEX 3
+#define SIR1_SPEED_PLLSEL_WIDTH 1
+#define SIR1_SPEED_RATECHANGE_INDEX 6
+#define SIR1_SPEED_RATECHANGE_WIDTH 1
+#define SIR1_SPEED_TXAMP_INDEX 8
+#define SIR1_SPEED_TXAMP_WIDTH 4
+#define SIR1_SPEED_WORDMODE_INDEX 0
+#define SIR1_SPEED_WORDMODE_WIDTH 3
+
+#define SPEED_10000_CDR 0x7
+#define SPEED_10000_PLL 0x1
+#define SPEED_10000_RATE 0x0
+#define SPEED_10000_TXAMP 0xa
+#define SPEED_10000_WORD 0x7
+
+#define SPEED_2500_CDR 0x2
+#define SPEED_2500_PLL 0x0
+#define SPEED_2500_RATE 0x1
+#define SPEED_2500_TXAMP 0xf
+#define SPEED_2500_WORD 0x1
+
+#define SPEED_1000_CDR 0x2
+#define SPEED_1000_PLL 0x0
+#define SPEED_1000_RATE 0x3
+#define SPEED_1000_TXAMP 0xf
+#define SPEED_1000_WORD 0x1
+
+/* SerDes RxTx register offsets */
+#define RXTX_REG20 0x0050
+#define RXTX_REG114 0x01c8
+
+/* SerDes RxTx register entry bit positions and sizes */
+#define RXTX_REG20_BLWC_ENA_INDEX 2
+#define RXTX_REG20_BLWC_ENA_WIDTH 1
+#define RXTX_REG114_PQ_REG_INDEX 9
+#define RXTX_REG114_PQ_REG_WIDTH 7
+
+#define RXTX_10000_BLWC 0
+#define RXTX_10000_PQ 0x1e
+
+#define RXTX_2500_BLWC 1
+#define RXTX_2500_PQ 0xa
+
+#define RXTX_1000_BLWC 1
+#define RXTX_1000_PQ 0xa
+
+/* Bit setting and getting macros
+ * The get macro will extract the current bit field value from within
+ * the variable
+ *
+ * The set macro will clear the current bit field value within the
+ * variable and then set the bit field of the variable to the
+ * specified value
+ */
+#define GET_BITS(_var, _index, _width) \
+ (((_var) >> (_index)) & ((0x1 << (_width)) - 1))
+
+#define SET_BITS(_var, _index, _width, _val) \
+do { \
+ (_var) &= ~(((0x1 << (_width)) - 1) << (_index)); \
+ (_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index)); \
+} while (0)
+
+#define XSIR_GET_BITS(_var, _prefix, _field) \
+ GET_BITS((_var), \
+ _prefix##_##_field##_INDEX, \
+ _prefix##_##_field##_WIDTH)
+
+#define XSIR_SET_BITS(_var, _prefix, _field, _val) \
+ SET_BITS((_var), \
+ _prefix##_##_field##_INDEX, \
+ _prefix##_##_field##_WIDTH, (_val))
+
+/* Macros for reading or writing SerDes integration registers
+ * The ioread macros will get bit fields or full values using the
+ * register definitions formed using the input names
+ *
+ * The iowrite macros will set bit fields or full values using the
+ * register definitions formed using the input names
+ */
+#define XSIR0_IOREAD(_pdata, _reg) \
+ ioread16((_pdata)->sir0_regs + _reg)
+
+#define XSIR0_IOREAD_BITS(_pdata, _reg, _field) \
+ GET_BITS(XSIR0_IOREAD((_pdata), _reg), \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH)
+
+#define XSIR0_IOWRITE(_pdata, _reg, _val) \
+ iowrite16((_val), (_pdata)->sir0_regs + _reg)
+
+#define XSIR0_IOWRITE_BITS(_pdata, _reg, _field, _val) \
+do { \
+ u16 reg_val = XSIR0_IOREAD((_pdata), _reg); \
+ SET_BITS(reg_val, \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH, (_val)); \
+ XSIR0_IOWRITE((_pdata), _reg, reg_val); \
+} while (0)
+
+#define XSIR1_IOREAD(_pdata, _reg) \
+ ioread16((_pdata)->sir1_regs + _reg)
+
+#define XSIR1_IOREAD_BITS(_pdata, _reg, _field) \
+ GET_BITS(XSIR1_IOREAD((_pdata), _reg), \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH)
+
+#define XSIR1_IOWRITE(_pdata, _reg, _val) \
+ iowrite16((_val), (_pdata)->sir1_regs + _reg)
+
+#define XSIR1_IOWRITE_BITS(_pdata, _reg, _field, _val) \
+do { \
+ u16 reg_val = XSIR1_IOREAD((_pdata), _reg); \
+ SET_BITS(reg_val, \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH, (_val)); \
+ XSIR1_IOWRITE((_pdata), _reg, reg_val); \
+} while (0)
+
+/* Macros for reading or writing SerDes RxTx registers
+ * The ioread macros will get bit fields or full values using the
+ * register definitions formed using the input names
+ *
+ * The iowrite macros will set bit fields or full values using the
+ * register definitions formed using the input names
+ */
+#define XRXTX_IOREAD(_pdata, _reg) \
+ ioread16((_pdata)->rxtx_regs + _reg)
+
+#define XRXTX_IOREAD_BITS(_pdata, _reg, _field) \
+ GET_BITS(XRXTX_IOREAD((_pdata), _reg), \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH)
+
+#define XRXTX_IOWRITE(_pdata, _reg, _val) \
+ iowrite16((_val), (_pdata)->rxtx_regs + _reg)
+
+#define XRXTX_IOWRITE_BITS(_pdata, _reg, _field, _val) \
+do { \
+ u16 reg_val = XRXTX_IOREAD((_pdata), _reg); \
+ SET_BITS(reg_val, \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH, (_val)); \
+ XRXTX_IOWRITE((_pdata), _reg, reg_val); \
+} while (0)
+
+static void xgbe_phy_an_enable_kr_training(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL,
+ MDIO_PMA_10GBR_KR_TRAIN_ENABLE,
+ MDIO_PMA_10GBR_KR_TRAIN_ENABLE);
+}
+
+static void xgbe_phy_an_disable_kr_training(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL,
+ MDIO_PMA_10GBR_KR_TRAIN_ENABLE, 0);
+}
+
+static void xgbe_phy_pcs_power_cycle(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ unsigned int val;
+
+ val = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
+
+ val |= MDIO_CTRL1_LPOWER;
+ XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, val);
+
+ usleep_range(75, 100);
+
+ val &= ~MDIO_CTRL1_LPOWER;
+ XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, val);
+}
+
+static void xgbe_phy_serdes_start_ratechange(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ /* Assert Rx and Tx ratechange */
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 1);
+}
+
+static void xgbe_phy_serdes_complete_ratechange(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ unsigned int wait;
+ u16 status;
+
+ /* Release Rx and Tx ratechange */
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 0);
+
+ /* Wait for Rx and Tx ready */
+ wait = XGBE_PHY_RATECHANGE_COUNT;
+ while (wait--) {
+ usleep_range(50, 75);
+
+ status = XSIR0_IOREAD(pdata, SIR0_STATUS);
+ if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) &&
+ XSIR_GET_BITS(status, SIR0_STATUS, TX_READY))
+ return;
+ }
+
+ netdev_dbg(phydev->attached_dev, "SerDes rx/tx not ready (%#hx)\n",
+ status);
+}
+
+static void xgbe_phy_xgmii_mode(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ /* Enable KR training */
+ xgbe_phy_an_enable_kr_training(phydev);
+
+ /* Set PCS to KR/10G speed */
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_TYPE,
+ MDIO_PCS_CTRL2_10GBR);
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_SPEEDSEL,
+ MDIO_CTRL1_SPEED10G);
+
+ xgbe_phy_pcs_power_cycle(phydev);
+
+ /* Set SerDes to 10G speed */
+ xgbe_phy_serdes_start_ratechange(phydev);
+
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, SPEED_10000_RATE);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, SPEED_10000_WORD);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, SPEED_10000_TXAMP);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, SPEED_10000_PLL);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PI_SPD_SEL, SPEED_10000_CDR);
+
+ XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, RXTX_10000_BLWC);
+ XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, RXTX_10000_PQ);
+
+ xgbe_phy_serdes_complete_ratechange(phydev);
+}
+
+static void xgbe_phy_gmii_2500_mode(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ /* Disable KR training */
+ xgbe_phy_an_disable_kr_training(phydev);
+
+ /* Set PCS to KX/1G speed */
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_TYPE,
+ MDIO_PCS_CTRL2_10GBX);
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_SPEEDSEL,
+ MDIO_CTRL1_SPEED1G);
+
+ xgbe_phy_pcs_power_cycle(phydev);
+
+ /* Set SerDes to 2.5G speed */
+ xgbe_phy_serdes_start_ratechange(phydev);
+
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, SPEED_2500_RATE);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, SPEED_2500_WORD);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, SPEED_2500_TXAMP);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, SPEED_2500_PLL);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PI_SPD_SEL, SPEED_2500_CDR);
+
+ XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, RXTX_2500_BLWC);
+ XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, RXTX_2500_PQ);
+
+ xgbe_phy_serdes_complete_ratechange(phydev);
+}
+
+static void xgbe_phy_gmii_mode(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ /* Disable KR training */
+ xgbe_phy_an_disable_kr_training(phydev);
+
+ /* Set PCS to KX/1G speed */
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_TYPE,
+ MDIO_PCS_CTRL2_10GBX);
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_SPEEDSEL,
+ MDIO_CTRL1_SPEED1G);
+
+ xgbe_phy_pcs_power_cycle(phydev);
+
+ /* Set SerDes to 1G speed */
+ xgbe_phy_serdes_start_ratechange(phydev);
+
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, SPEED_1000_RATE);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, SPEED_1000_WORD);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, SPEED_1000_TXAMP);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, SPEED_1000_PLL);
+ XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PI_SPD_SEL, SPEED_1000_CDR);
+
+ XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, RXTX_1000_BLWC);
+ XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, RXTX_1000_PQ);
+
+ xgbe_phy_serdes_complete_ratechange(phydev);
+}
+
+static enum xgbe_speed_mode xgbe_phy_cur_mode(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ unsigned int val;
+
+ val = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2);
+ if ((val & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
+ return XGBE_SPEED_MODE_KR;
+
+ return XGBE_SPEED_MODE_KX;
+}
+
+static bool xgbe_phy_in_kr_mode(struct phy_device *phydev)
+{
+ return (xgbe_phy_cur_mode(phydev) == XGBE_SPEED_MODE_KR);
+}
+
+static bool xgbe_phy_in_kx_mode(struct phy_device *phydev)
+{
+ return (xgbe_phy_cur_mode(phydev) == XGBE_SPEED_MODE_KX);
+}
+
+static void xgbe_phy_switch_mode(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ /* If we are in KR switch to KX, and vice-versa */
+ if (xgbe_phy_in_kr_mode(phydev)) {
+ if (pdata->speed_set == XGBE_PHY_SPEEDSET_1000_10000)
+ xgbe_phy_gmii_mode(phydev);
+ else
+ xgbe_phy_gmii_2500_mode(phydev);
+ } else {
+ xgbe_phy_xgmii_mode(phydev);
+ }
+}
+
+static void xgbe_phy_set_mode(struct phy_device *phydev,
+ enum xgbe_speed_mode mode)
+{
+ if (mode != xgbe_phy_cur_mode(phydev))
+ xgbe_phy_switch_mode(phydev);
+}
+
+static enum xgbe_an xgbe_phy_an_tx_training(struct phy_device *phydev,
+ enum xgbe_an_rx *state)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ int ad_val, lp_val, val;
+
+ *state = XGBE_AN_RX_COMPLETE;
+
+ /* If we're in KX mode then we're done */
+ if (xgbe_phy_in_kx_mode(phydev))
+ return XGBE_AN_EVENT;
+
+ /* Enable/Disable FEC */
+ ad_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
+ lp_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2);
+
+ val = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL);
+
+ if ((ad_val & 0xc000) && (lp_val & 0xc000))
+ val |= 0x01;
+ else
+ val &= ~0x01;
+
+ XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL, val);
+
+ /* Start KR training */
+ XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 1);
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL,
+ MDIO_PMA_10GBR_KR_TRAIN_START,
+ MDIO_PMA_10GBR_KR_TRAIN_START);
+
+ XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 0);
+
+ return XGBE_AN_EVENT;
+}
+
+static enum xgbe_an xgbe_phy_an_tx_xnp(struct phy_device *phydev,
+ enum xgbe_an_rx *state)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ u16 msg;
+
+ *state = XGBE_AN_RX_XNP;
+
+ msg = XNP_MCF_NULL_MESSAGE;
+ msg |= XNP_MP_FORMATTED;
+
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 2, 0);
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0);
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP, msg);
+
+ return XGBE_AN_EVENT;
+}
+
+static enum xgbe_an xgbe_phy_an_rx_bpa(struct phy_device *phydev,
+ enum xgbe_an_rx *state)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ unsigned int link_support;
+ int val, ad_val, lp_val;
+
+ /* Read Base Ability register 2 first */
+ val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1);
+
+ /* Check for a supported mode, otherwise restart in a different one */
+ link_support = (xgbe_phy_in_kr_mode(phydev)) ? 0x80 : 0x20;
+ if (!(val & link_support))
+ return XGBE_AN_INCOMPAT_LINK;
+
+ /* Check Extended Next Page support */
+ ad_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+ lp_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA);
+
+ return ((ad_val & XNP_NP_EXCHANGE) || (lp_val & XNP_NP_EXCHANGE))
+ ? xgbe_phy_an_tx_xnp(phydev, state)
+ : xgbe_phy_an_tx_training(phydev, state);
+}
+
+static enum xgbe_an xgbe_phy_an_rx_xnp(struct phy_device *phydev,
+ enum xgbe_an_rx *state)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ int ad_val, lp_val;
+
+ /* Check Extended Next Page support */
+ ad_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+ lp_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA);
+
+ return ((ad_val & XNP_NP_EXCHANGE) || (lp_val & XNP_NP_EXCHANGE))
+ ? xgbe_phy_an_tx_xnp(phydev, state)
+ : xgbe_phy_an_tx_training(phydev, state);
+}
+
+static enum xgbe_an xgbe_phy_an_start(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ unsigned int val;
+
+ /* Be sure we aren't looping trying to negotiate */
+ if (xgbe_phy_in_kr_mode(phydev)) {
+ if (pdata->phy.kr_state != XGBE_AN_RX_READY)
+ return XGBE_AN_NO_LINK;
+ pdata->phy.kr_state = XGBE_AN_RX_BPA;
+ } else {
+ if (pdata->phy.kx_state != XGBE_AN_RX_READY)
+ return XGBE_AN_NO_LINK;
+ pdata->phy.kx_state = XGBE_AN_RX_BPA;
+ }
+
+ /* Set up Advertisement register 3 first */
+ val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
+
+ if (phydev->supported & SUPPORTED_10000baseR_FEC)
+ val |= 0xc000;
+ else
+ val &= ~0xc000;
+
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, val);
+
+ /* Set up Advertisement register 2 next */
+ val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
+
+ if (phydev->supported & SUPPORTED_10000baseKR_Full)
+ val |= 0x80;
+ else
+ val &= ~0x80;
+
+ if ((phydev->supported & SUPPORTED_1000baseKX_Full) ||
+ (phydev->supported & SUPPORTED_2500baseX_Full))
+ val |= 0x20;
+ else
+ val &= ~0x20;
+
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, val);
+
+ /* Set up Advertisement register 1 last */
+ val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+
+ if (phydev->supported & SUPPORTED_Pause)
+ val |= 0x400;
+ else
+ val &= ~0x400;
+
+ if (phydev->supported & SUPPORTED_Asym_Pause)
+ val |= 0x800;
+ else
+ val &= ~0x800;
+
+ /* We don't intend to perform XNP */
+ val &= ~XNP_NP_EXCHANGE;
+
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE, val);
+
+ /* Enable and start auto-negotiation */
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+ val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1);
+ val |= MDIO_AN_CTRL1_ENABLE;
+ val |= MDIO_AN_CTRL1_RESTART;
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_CTRL1, val);
+
+ return XGBE_AN_EVENT;
+}
+
+static enum xgbe_an xgbe_phy_an_event(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ enum xgbe_an new_state;
+ unsigned int val;
+
+ val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INT);
+
+ new_state = XGBE_AN_EVENT;
+ if (val & XGBE_AN_PG_RCV)
+ new_state = XGBE_AN_PAGE_RECEIVED;
+ else if (val & XGBE_AN_INC_LINK)
+ new_state = XGBE_AN_INCOMPAT_LINK;
+ else if (val & XGBE_AN_INT_CMPLT)
+ new_state = XGBE_AN_COMPLETE;
+
+ if (new_state != XGBE_AN_EVENT)
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+ return new_state;
+}
+
+static enum xgbe_an xgbe_phy_an_page_received(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ enum xgbe_an_rx *state;
+ enum xgbe_an new_state;
+
+ state = xgbe_phy_in_kr_mode(phydev) ? &pdata->phy.kr_state
+ : &pdata->phy.kx_state;
+
+ switch (*state) {
+ case XGBE_AN_RX_BPA:
+ new_state = xgbe_phy_an_rx_bpa(phydev, state);
+ break;
+
+ case XGBE_AN_RX_XNP:
+ new_state = xgbe_phy_an_rx_xnp(phydev, state);
+ break;
+
+ default:
+ new_state = XGBE_AN_ERROR;
+ }
+
+ return new_state;
+}
+
+static enum xgbe_an xgbe_phy_an_incompat_link(struct phy_device *phydev)
+{
+ xgbe_phy_switch_mode(phydev);
+
+ return XGBE_AN_START;
+}
+
+static void xgbe_phy_an_state_machine(struct work_struct *work)
+{
+ struct xgbe_prv_data *pdata = container_of(work,
+ struct xgbe_prv_data,
+ phy.an_work);
+ struct phy_device *phydev = pdata->phy.phydev;
+ enum xgbe_an cur_state;
+ unsigned int sleep;
+ unsigned int an_supported = 0;
+
+ while (1) {
+ mutex_lock(&pdata->phy.an_mutex);
+
+ cur_state = pdata->phy.an_state;
+
+ switch (pdata->phy.an_state) {
+ case XGBE_AN_START:
+ an_supported = 0;
+ pdata->phy.an_state = xgbe_phy_an_start(phydev);
+ break;
+
+ case XGBE_AN_EVENT:
+ pdata->phy.an_state = xgbe_phy_an_event(phydev);
+ break;
+
+ case XGBE_AN_PAGE_RECEIVED:
+ an_supported++;
+ pdata->phy.an_state = xgbe_phy_an_page_received(phydev);
+ break;
+
+ case XGBE_AN_INCOMPAT_LINK:
+ pdata->phy.an_state = xgbe_phy_an_incompat_link(phydev);
+ break;
+
+ case XGBE_AN_COMPLETE:
+ netdev_info(phydev->attached_dev, "%s successful\n",
+ an_supported ? "Auto negotiation"
+ : "Parallel detection");
+ /* fall through */
+
+ case XGBE_AN_NO_LINK:
+ case XGBE_AN_EXIT:
+ goto exit_unlock;
+
+ default:
+ pdata->phy.an_state = XGBE_AN_ERROR;
+ }
+
+ if (pdata->phy.an_state == XGBE_AN_ERROR) {
+ netdev_err(phydev->attached_dev,
+ "error during auto-negotiation, state=%u\n",
+ cur_state);
+ goto exit_unlock;
+ }
+
+ sleep = (pdata->phy.an_state == XGBE_AN_EVENT) ? 1 : 0;
+
+ mutex_unlock(&pdata->phy.an_mutex);
+
+ if (sleep)
+ usleep_range(20, 50);
+ }
+
+exit_unlock:
+ pdata->phy.an_result = pdata->phy.an_state;
+ pdata->phy.an_state = XGBE_AN_READY;
+
+ mutex_unlock(&pdata->phy.an_mutex);
+}
+
+static int xgbe_phy_soft_reset(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ unsigned int count, val;
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_RESET,
+ MDIO_CTRL1_RESET);
+
+ count = 50;
+ do {
+ msleep(20);
+ val = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
+ } while ((val & MDIO_CTRL1_RESET) && --count);
+
+ if (val & MDIO_CTRL1_RESET)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int xgbe_phy_config_init(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ /* Initialize supported features */
+ phydev->supported = SUPPORTED_Autoneg;
+ phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+ phydev->supported |= SUPPORTED_Backplane;
+ phydev->supported |= SUPPORTED_10000baseKR_Full |
+ SUPPORTED_10000baseR_FEC;
+ switch (pdata->speed_set) {
+ case XGBE_PHY_SPEEDSET_1000_10000:
+ phydev->supported |= SUPPORTED_1000baseKX_Full;
+ break;
+ case XGBE_PHY_SPEEDSET_2500_10000:
+ phydev->supported |= SUPPORTED_2500baseX_Full;
+ break;
+ }
+ phydev->advertising = phydev->supported;
+
+ /* Turn off and clear interrupts */
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0);
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+ return 0;
+}
+
+static int xgbe_phy_setup_forced(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ /* Disable auto-negotiation */
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_AN, MDIO_CTRL1, MDIO_AN_CTRL1_ENABLE,
+ 0);
+
+ /* Validate/Set specified speed */
+ switch (phydev->speed) {
+ case SPEED_10000:
+ xgbe_phy_xgmii_mode(phydev);
+ break;
+
+ case SPEED_2500:
+ xgbe_phy_gmii_2500_mode(phydev);
+ break;
+
+ case SPEED_1000:
+ xgbe_phy_gmii_mode(phydev);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Validate duplex mode */
+ if (phydev->duplex != DUPLEX_FULL)
+ return -EINVAL;
+
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+
+ return 0;
+}
+
+static int xgbe_phy_config_aneg(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ u32 mmd_mask = phydev->c45_ids.devices_in_package;
+
+ if (phydev->autoneg != AUTONEG_ENABLE)
+ return xgbe_phy_setup_forced(phydev);
+
+ /* Make sure we have the AN MMD present */
+ if (!(mmd_mask & MDIO_DEVS_AN))
+ return -EINVAL;
+
+ /* Start/Restart the auto-negotiation state machine */
+ mutex_lock(&pdata->phy.an_mutex);
+ pdata->phy.an_result = XGBE_AN_READY;
+ pdata->phy.an_state = XGBE_AN_START;
+ pdata->phy.kr_state = XGBE_AN_RX_READY;
+ pdata->phy.kx_state = XGBE_AN_RX_READY;
+ mutex_unlock(&pdata->phy.an_mutex);
+
+ queue_work(pdata->phy.an_workqueue, &pdata->phy.an_work);
+
+ return 0;
+}
+
+static int xgbe_phy_aneg_done(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ enum xgbe_an state;
+
+ mutex_lock(&pdata->phy.an_mutex);
+ state = pdata->phy.an_result;
+ mutex_unlock(&pdata->phy.an_mutex);
+
+ return (state == XGBE_AN_COMPLETE);
+}
+
+static int xgbe_phy_update_link(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ enum xgbe_an state;
+ unsigned int check_again, autoneg, val;
+ int ret = 0;
+
+ /* If we're doing auto-negotiation don't report link down */
+ mutex_lock(&pdata->phy.an_mutex);
+ state = pdata->phy.an_state;
+ mutex_unlock(&pdata->phy.an_mutex);
+
+ if (state != XGBE_AN_READY) {
+ phydev->link = 1;
+ return 0;
+ }
+
+ /* Since the device can be in the wrong mode when a link is
+ * (re-)established (cable connected after the interface is
+ * up, etc.), the link status may report no link. If there
+ * is no link, try switching modes and checking the status
+ * again if auto negotiation is enabled.
+ */
+ check_again = (phydev->autoneg == AUTONEG_ENABLE) ? 1 : 0;
+again:
+ /* Link status is latched low, so read once to clear
+ * and then read again to get current state
+ */
+ val = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1);
+ val = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1);
+
+ phydev->link = (val & MDIO_STAT1_LSTATUS) ? 1 : 0;
+
+ if (!phydev->link) {
+ if (check_again) {
+ xgbe_phy_switch_mode(phydev);
+ check_again = 0;
+ goto again;
+ }
+ }
+
+ autoneg = (phydev->link && !pdata->phy.link) ? 1 : 0;
+ pdata->phy.link = phydev->link;
+
+ /* If link is (back) up, re-start auto-negotiation */
+ if (autoneg)
+ ret = xgbe_phy_config_aneg(phydev);
+
+ return ret;
+}
+
+static int xgbe_phy_read_status(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ u32 mmd_mask = phydev->c45_ids.devices_in_package;
+ unsigned int ad_val, lp_val;
+ int ret;
+
+ ret = xgbe_phy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+ if (!(mmd_mask & MDIO_DEVS_AN))
+ return -EINVAL;
+
+ if (!xgbe_phy_aneg_done(phydev))
+ return 0;
+
+ /* Compare Advertisement and Link Partner register 1 */
+ ad_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+ lp_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA);
+
+ ad_val &= lp_val;
+ phydev->pause = (ad_val & 0x400) ? 1 : 0;
+ phydev->asym_pause = (ad_val & 0x800) ? 1 : 0;
+
+ /* Compare Advertisement and Link Partner register 2 */
+ ad_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
+ lp_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1);
+
+ ad_val &= lp_val;
+ if (ad_val & 0x80) {
+ phydev->speed = SPEED_10000;
+ xgbe_phy_set_mode(phydev, XGBE_SPEED_MODE_KR);
+ } else {
+ switch (pdata->speed_set) {
+ case XGBE_PHY_SPEEDSET_1000_10000:
+ phydev->speed = SPEED_1000;
+ break;
+
+ case XGBE_PHY_SPEEDSET_2500_10000:
+ phydev->speed = SPEED_2500;
+ break;
+ }
+ xgbe_phy_set_mode(phydev, XGBE_SPEED_MODE_KX);
+ }
+
+ phydev->duplex = DUPLEX_FULL;
+ } else {
+ if (xgbe_phy_in_kr_mode(phydev)) {
+ phydev->speed = SPEED_10000;
+ } else {
+ switch (pdata->speed_set) {
+ case XGBE_PHY_SPEEDSET_1000_10000:
+ phydev->speed = SPEED_1000;
+ break;
+
+ case XGBE_PHY_SPEEDSET_2500_10000:
+ phydev->speed = SPEED_2500;
+ break;
+ }
+ }
+ phydev->duplex = DUPLEX_FULL;
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+ }
+
+ return 0;
+}
+
+static int xgbe_phy_suspend(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ mutex_lock(&phydev->lock);
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_LPOWER,
+ MDIO_CTRL1_LPOWER);
+
+ mutex_unlock(&phydev->lock);
+
+ return 0;
+}
+
+static int xgbe_phy_resume(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ mutex_lock(&phydev->lock);
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_LPOWER,
+ 0);
+
+ mutex_unlock(&phydev->lock);
+
+ return 0;
+}
+
+static int xgbe_phy_probe(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+ char *wq_name;
+
+ if (!pdata)
+ return -EINVAL;
+
+ wq_name = kasprintf(GFP_KERNEL, "%s-amd-xgbe-phy", pdata->pdev->name);
+ if (!wq_name)
+ return -ENOMEM;
+
+ pdata->phy.link = 1;
+
+ mutex_init(&pdata->phy.an_mutex);
+ INIT_WORK(&pdata->phy.an_work, xgbe_phy_an_state_machine);
+ pdata->phy.an_workqueue = create_singlethread_workqueue(wq_name);
+ kfree(wq_name);
+ if (!pdata->phy.an_workqueue)
+ return -ENOMEM;
+
+ pdata->phy.probed = 1;
+
+ return 0;
+}
+
+static void xgbe_phy_remove(struct phy_device *phydev)
+{
+ struct xgbe_prv_data *pdata = phydev->priv;
+
+ /* Stop any in process auto-negotiation */
+ mutex_lock(&pdata->phy.an_mutex);
+ pdata->phy.an_state = XGBE_AN_EXIT;
+ mutex_unlock(&pdata->phy.an_mutex);
+
+ flush_workqueue(pdata->phy.an_workqueue);
+ destroy_workqueue(pdata->phy.an_workqueue);
+}
+
+static int xgbe_phy_match_device(struct phy_device *phydev)
+{
+ return phydev->c45_ids.device_ids[MDIO_MMD_PCS] == XGBE_PHY_ID;
+}
+
+static struct phy_driver xgbe_phy_driver[] = {
+ {
+ .phy_id = XGBE_PHY_ID,
+ .phy_id_mask = XGBE_PHY_MASK,
+ .name = "AMD XGBE PHY",
+ .features = 0,
+ .probe = xgbe_phy_probe,
+ .remove = xgbe_phy_remove,
+ .soft_reset = xgbe_phy_soft_reset,
+ .config_init = xgbe_phy_config_init,
+ .suspend = xgbe_phy_suspend,
+ .resume = xgbe_phy_resume,
+ .config_aneg = xgbe_phy_config_aneg,
+ .aneg_done = xgbe_phy_aneg_done,
+ .read_status = xgbe_phy_read_status,
+ .match_phy_device = xgbe_phy_match_device,
+ .driver = {
+ .owner = THIS_MODULE,
+ },
+ },
+};
+
+int xgbe_phy_register(void)
+{
+ return phy_drivers_register(xgbe_phy_driver,
+ ARRAY_SIZE(xgbe_phy_driver));
+}
+
+void xgbe_phy_unregister(void)
+{
+ phy_drivers_unregister(xgbe_phy_driver,
+ ARRAY_SIZE(xgbe_phy_driver));
+}
+
+static struct mdio_device_id __maybe_unused xgbe_phy_ids[] = {
+ { XGBE_PHY_ID, XGBE_PHY_MASK },
+ { }
+};
+MODULE_DEVICE_TABLE(mdio, xgbe_phy_ids);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index 07bf70a..36f8717 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -173,6 +173,11 @@
#define XGBE_DMA_CLOCK "dma_clk"
#define XGBE_PTP_CLOCK "ptp_clk"
+/* Device-tree property names/values */
+#define XGBE_PHY_SPEEDSET_PROPERTY "amd,speed-set"
+#define XGBE_PHY_SPEEDSET_1000_10000 0
+#define XGBE_PHY_SPEEDSET_2500_10000 1
+
/* Timestamp support - values based on 50MHz PTP clock
* 50MHz => 20 nsec
*/
@@ -419,6 +424,48 @@ struct xgbe_mmc_stats {
u64 rxwatchdogerror;
};
+enum xgbe_an {
+ XGBE_AN_READY = 0,
+ XGBE_AN_START,
+ XGBE_AN_EVENT,
+ XGBE_AN_PAGE_RECEIVED,
+ XGBE_AN_INCOMPAT_LINK,
+ XGBE_AN_COMPLETE,
+ XGBE_AN_NO_LINK,
+ XGBE_AN_EXIT,
+ XGBE_AN_ERROR,
+};
+
+enum xgbe_an_rx {
+ XGBE_AN_RX_READY = 0,
+ XGBE_AN_RX_BPA,
+ XGBE_AN_RX_XNP,
+ XGBE_AN_RX_COMPLETE,
+};
+
+enum xgbe_speed_mode {
+ XGBE_SPEED_MODE_KR,
+ XGBE_SPEED_MODE_KX,
+};
+
+struct xgbe_phy {
+ struct phy_device *phydev;
+
+ unsigned int probed;
+
+ /* Maintain link status for re-starting auto-negotiation */
+ unsigned int link;
+
+ /* Auto-negotiation state machine support */
+ struct mutex an_mutex;
+ enum xgbe_an an_result;
+ enum xgbe_an an_state;
+ enum xgbe_an_rx kr_state;
+ enum xgbe_an_rx kx_state;
+ struct work_struct an_work;
+ struct workqueue_struct *an_workqueue;
+};
+
struct xgbe_hw_if {
int (*tx_complete)(struct xgbe_ring_desc *);
@@ -573,6 +620,13 @@ struct xgbe_prv_data {
void __iomem *xgmac_regs; /* XGMAC CSRs */
void __iomem *xpcs_regs; /* XPCS MMD registers */
+ /* SerDes related mmio registers */
+ void __iomem *rxtx_regs; /* SerDes Rx/Tx CSRs */
+ void __iomem *sir0_regs; /* SerDes integration registers (1/2) */
+ void __iomem *sir1_regs; /* SerDes integration registers (2/2) */
+
+ unsigned int speed_set;
+
/* Overall device lock */
spinlock_t lock;
@@ -631,11 +685,10 @@ struct xgbe_prv_data {
unsigned int rx_pause;
/* MDIO settings */
- struct module *phy_module;
char *mii_bus_id;
struct mii_bus *mii;
int mdio_mmd;
- struct phy_device *phydev;
+ struct xgbe_phy phy;
int default_autoneg;
int default_speed;
@@ -705,6 +758,8 @@ struct ethtool_ops *xgbe_get_ethtool_ops(void);
const struct dcbnl_rtnl_ops *xgbe_get_dcbnl_ops(void);
#endif
+int xgbe_phy_register(void);
+void xgbe_phy_unregister(void);
int xgbe_mdio_register(struct xgbe_prv_data *);
void xgbe_mdio_unregister(struct xgbe_prv_data *);
void xgbe_dump_phy_registers(struct xgbe_prv_data *);
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 28437ab..60a53bb 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -24,12 +24,6 @@ config AMD_PHY
---help---
Currently supports the am79c874
-config AMD_XGBE_PHY
- tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs"
- depends on OF
- ---help---
- Currently supports the AMD 10GbE PHY
-
config MARVELL_PHY
tristate "Drivers for Marvell PHYs"
---help---
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index eb3b18b..21813d1 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -33,5 +33,4 @@ obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o
obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
-obj-$(CONFIG_AMD_XGBE_PHY) += amd-xgbe-phy.o
obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o
diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c
deleted file mode 100644
index f3230ee..0000000
--- a/drivers/net/phy/amd-xgbe-phy.c
+++ /dev/null
@@ -1,1457 +0,0 @@
-/*
- * AMD 10Gb Ethernet PHY driver
- *
- * This file is available to you under your choice of the following two
- * licenses:
- *
- * License 1: GPLv2
- *
- * Copyright (c) 2014 Advanced Micro Devices, Inc.
- *
- * This file is free software; you may copy, redistribute and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or (at
- * your option) any later version.
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- *
- * License 2: Modified BSD
- *
- * Copyright (c) 2014 Advanced Micro Devices, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Advanced Micro Devices, Inc. nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/mii.h>
-#include <linux/ethtool.h>
-#include <linux/phy.h>
-#include <linux/mdio.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/of_device.h>
-#include <linux/uaccess.h>
-
-
-MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_VERSION("1.0.0-a");
-MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
-
-#define XGBE_PHY_ID 0x000162d0
-#define XGBE_PHY_MASK 0xfffffff0
-
-#define XGBE_PHY_SPEEDSET_PROPERTY "amd,speed-set"
-
-#define XGBE_AN_INT_CMPLT 0x01
-#define XGBE_AN_INC_LINK 0x02
-#define XGBE_AN_PG_RCV 0x04
-
-#define XNP_MCF_NULL_MESSAGE 0x001
-#define XNP_ACK_PROCESSED (1 << 12)
-#define XNP_MP_FORMATTED (1 << 13)
-#define XNP_NP_EXCHANGE (1 << 15)
-
-#define XGBE_PHY_RATECHANGE_COUNT 500
-
-#ifndef MDIO_PMA_10GBR_PMD_CTRL
-#define MDIO_PMA_10GBR_PMD_CTRL 0x0096
-#endif
-#ifndef MDIO_PMA_10GBR_FEC_CTRL
-#define MDIO_PMA_10GBR_FEC_CTRL 0x00ab
-#endif
-#ifndef MDIO_AN_XNP
-#define MDIO_AN_XNP 0x0016
-#endif
-
-#ifndef MDIO_AN_INTMASK
-#define MDIO_AN_INTMASK 0x8001
-#endif
-#ifndef MDIO_AN_INT
-#define MDIO_AN_INT 0x8002
-#endif
-
-#ifndef MDIO_CTRL1_SPEED1G
-#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
-#endif
-
-/* SerDes integration register offsets */
-#define SIR0_KR_RT_1 0x002c
-#define SIR0_STATUS 0x0040
-#define SIR1_SPEED 0x0000
-
-/* SerDes integration register entry bit positions and sizes */
-#define SIR0_KR_RT_1_RESET_INDEX 11
-#define SIR0_KR_RT_1_RESET_WIDTH 1
-#define SIR0_STATUS_RX_READY_INDEX 0
-#define SIR0_STATUS_RX_READY_WIDTH 1
-#define SIR0_STATUS_TX_READY_INDEX 8
-#define SIR0_STATUS_TX_READY_WIDTH 1
-#define SIR1_SPEED_DATARATE_INDEX 4
-#define SIR1_SPEED_DATARATE_WIDTH 2
-#define SIR1_SPEED_PI_SPD_SEL_INDEX 12
-#define SIR1_SPEED_PI_SPD_SEL_WIDTH 4
-#define SIR1_SPEED_PLLSEL_INDEX 3
-#define SIR1_SPEED_PLLSEL_WIDTH 1
-#define SIR1_SPEED_RATECHANGE_INDEX 6
-#define SIR1_SPEED_RATECHANGE_WIDTH 1
-#define SIR1_SPEED_TXAMP_INDEX 8
-#define SIR1_SPEED_TXAMP_WIDTH 4
-#define SIR1_SPEED_WORDMODE_INDEX 0
-#define SIR1_SPEED_WORDMODE_WIDTH 3
-
-#define SPEED_10000_CDR 0x7
-#define SPEED_10000_PLL 0x1
-#define SPEED_10000_RATE 0x0
-#define SPEED_10000_TXAMP 0xa
-#define SPEED_10000_WORD 0x7
-
-#define SPEED_2500_CDR 0x2
-#define SPEED_2500_PLL 0x0
-#define SPEED_2500_RATE 0x1
-#define SPEED_2500_TXAMP 0xf
-#define SPEED_2500_WORD 0x1
-
-#define SPEED_1000_CDR 0x2
-#define SPEED_1000_PLL 0x0
-#define SPEED_1000_RATE 0x3
-#define SPEED_1000_TXAMP 0xf
-#define SPEED_1000_WORD 0x1
-
-
-/* SerDes RxTx register offsets */
-#define RXTX_REG20 0x0050
-#define RXTX_REG114 0x01c8
-
-/* SerDes RxTx register entry bit positions and sizes */
-#define RXTX_REG20_BLWC_ENA_INDEX 2
-#define RXTX_REG20_BLWC_ENA_WIDTH 1
-#define RXTX_REG114_PQ_REG_INDEX 9
-#define RXTX_REG114_PQ_REG_WIDTH 7
-
-#define RXTX_10000_BLWC 0
-#define RXTX_10000_PQ 0x1e
-
-#define RXTX_2500_BLWC 1
-#define RXTX_2500_PQ 0xa
-
-#define RXTX_1000_BLWC 1
-#define RXTX_1000_PQ 0xa
-
-/* Bit setting and getting macros
- * The get macro will extract the current bit field value from within
- * the variable
- *
- * The set macro will clear the current bit field value within the
- * variable and then set the bit field of the variable to the
- * specified value
- */
-#define GET_BITS(_var, _index, _width) \
- (((_var) >> (_index)) & ((0x1 << (_width)) - 1))
-
-#define SET_BITS(_var, _index, _width, _val) \
-do { \
- (_var) &= ~(((0x1 << (_width)) - 1) << (_index)); \
- (_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index)); \
-} while (0)
-
-#define XSIR_GET_BITS(_var, _prefix, _field) \
- GET_BITS((_var), \
- _prefix##_##_field##_INDEX, \
- _prefix##_##_field##_WIDTH)
-
-#define XSIR_SET_BITS(_var, _prefix, _field, _val) \
- SET_BITS((_var), \
- _prefix##_##_field##_INDEX, \
- _prefix##_##_field##_WIDTH, (_val))
-
-/* Macros for reading or writing SerDes integration registers
- * The ioread macros will get bit fields or full values using the
- * register definitions formed using the input names
- *
- * The iowrite macros will set bit fields or full values using the
- * register definitions formed using the input names
- */
-#define XSIR0_IOREAD(_priv, _reg) \
- ioread16((_priv)->sir0_regs + _reg)
-
-#define XSIR0_IOREAD_BITS(_priv, _reg, _field) \
- GET_BITS(XSIR0_IOREAD((_priv), _reg), \
- _reg##_##_field##_INDEX, \
- _reg##_##_field##_WIDTH)
-
-#define XSIR0_IOWRITE(_priv, _reg, _val) \
- iowrite16((_val), (_priv)->sir0_regs + _reg)
-
-#define XSIR0_IOWRITE_BITS(_priv, _reg, _field, _val) \
-do { \
- u16 reg_val = XSIR0_IOREAD((_priv), _reg); \
- SET_BITS(reg_val, \
- _reg##_##_field##_INDEX, \
- _reg##_##_field##_WIDTH, (_val)); \
- XSIR0_IOWRITE((_priv), _reg, reg_val); \
-} while (0)
-
-#define XSIR1_IOREAD(_priv, _reg) \
- ioread16((_priv)->sir1_regs + _reg)
-
-#define XSIR1_IOREAD_BITS(_priv, _reg, _field) \
- GET_BITS(XSIR1_IOREAD((_priv), _reg), \
- _reg##_##_field##_INDEX, \
- _reg##_##_field##_WIDTH)
-
-#define XSIR1_IOWRITE(_priv, _reg, _val) \
- iowrite16((_val), (_priv)->sir1_regs + _reg)
-
-#define XSIR1_IOWRITE_BITS(_priv, _reg, _field, _val) \
-do { \
- u16 reg_val = XSIR1_IOREAD((_priv), _reg); \
- SET_BITS(reg_val, \
- _reg##_##_field##_INDEX, \
- _reg##_##_field##_WIDTH, (_val)); \
- XSIR1_IOWRITE((_priv), _reg, reg_val); \
-} while (0)
-
-
-/* Macros for reading or writing SerDes RxTx registers
- * The ioread macros will get bit fields or full values using the
- * register definitions formed using the input names
- *
- * The iowrite macros will set bit fields or full values using the
- * register definitions formed using the input names
- */
-#define XRXTX_IOREAD(_priv, _reg) \
- ioread16((_priv)->rxtx_regs + _reg)
-
-#define XRXTX_IOREAD_BITS(_priv, _reg, _field) \
- GET_BITS(XRXTX_IOREAD((_priv), _reg), \
- _reg##_##_field##_INDEX, \
- _reg##_##_field##_WIDTH)
-
-#define XRXTX_IOWRITE(_priv, _reg, _val) \
- iowrite16((_val), (_priv)->rxtx_regs + _reg)
-
-#define XRXTX_IOWRITE_BITS(_priv, _reg, _field, _val) \
-do { \
- u16 reg_val = XRXTX_IOREAD((_priv), _reg); \
- SET_BITS(reg_val, \
- _reg##_##_field##_INDEX, \
- _reg##_##_field##_WIDTH, (_val)); \
- XRXTX_IOWRITE((_priv), _reg, reg_val); \
-} while (0)
-
-
-enum amd_xgbe_phy_an {
- AMD_XGBE_AN_READY = 0,
- AMD_XGBE_AN_START,
- AMD_XGBE_AN_EVENT,
- AMD_XGBE_AN_PAGE_RECEIVED,
- AMD_XGBE_AN_INCOMPAT_LINK,
- AMD_XGBE_AN_COMPLETE,
- AMD_XGBE_AN_NO_LINK,
- AMD_XGBE_AN_EXIT,
- AMD_XGBE_AN_ERROR,
-};
-
-enum amd_xgbe_phy_rx {
- AMD_XGBE_RX_READY = 0,
- AMD_XGBE_RX_BPA,
- AMD_XGBE_RX_XNP,
- AMD_XGBE_RX_COMPLETE,
-};
-
-enum amd_xgbe_phy_mode {
- AMD_XGBE_MODE_KR,
- AMD_XGBE_MODE_KX,
-};
-
-enum amd_xgbe_phy_speedset {
- AMD_XGBE_PHY_SPEEDSET_1000_10000,
- AMD_XGBE_PHY_SPEEDSET_2500_10000,
-};
-
-struct amd_xgbe_phy_priv {
- struct platform_device *pdev;
- struct device *dev;
-
- struct phy_device *phydev;
-
- /* SerDes related mmio resources */
- struct resource *rxtx_res;
- struct resource *sir0_res;
- struct resource *sir1_res;
-
- /* SerDes related mmio registers */
- void __iomem *rxtx_regs; /* SerDes Rx/Tx CSRs */
- void __iomem *sir0_regs; /* SerDes integration registers (1/2) */
- void __iomem *sir1_regs; /* SerDes integration registers (2/2) */
-
- /* Maintain link status for re-starting auto-negotiation */
- unsigned int link;
- enum amd_xgbe_phy_mode mode;
- unsigned int speed_set;
-
- /* Auto-negotiation state machine support */
- struct mutex an_mutex;
- enum amd_xgbe_phy_an an_result;
- enum amd_xgbe_phy_an an_state;
- enum amd_xgbe_phy_rx kr_state;
- enum amd_xgbe_phy_rx kx_state;
- struct work_struct an_work;
- struct workqueue_struct *an_workqueue;
-};
-
-static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev)
-{
- int ret;
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
- if (ret < 0)
- return ret;
-
- ret |= 0x02;
- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
-
- return 0;
-}
-
-static int amd_xgbe_an_disable_kr_training(struct phy_device *phydev)
-{
- int ret;
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
- if (ret < 0)
- return ret;
-
- ret &= ~0x02;
- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
-
- return 0;
-}
-
-static int amd_xgbe_phy_pcs_power_cycle(struct phy_device *phydev)
-{
- int ret;
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
- if (ret < 0)
- return ret;
-
- ret |= MDIO_CTRL1_LPOWER;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
-
- usleep_range(75, 100);
-
- ret &= ~MDIO_CTRL1_LPOWER;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
-
- return 0;
-}
-
-static void amd_xgbe_phy_serdes_start_ratechange(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
-
- /* Assert Rx and Tx ratechange */
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 1);
-}
-
-static void amd_xgbe_phy_serdes_complete_ratechange(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- unsigned int wait;
- u16 status;
-
- /* Release Rx and Tx ratechange */
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 0);
-
- /* Wait for Rx and Tx ready */
- wait = XGBE_PHY_RATECHANGE_COUNT;
- while (wait--) {
- usleep_range(50, 75);
-
- status = XSIR0_IOREAD(priv, SIR0_STATUS);
- if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) &&
- XSIR_GET_BITS(status, SIR0_STATUS, TX_READY))
- return;
- }
-
- netdev_dbg(phydev->attached_dev, "SerDes rx/tx not ready (%#hx)\n",
- status);
-}
-
-static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- int ret;
-
- /* Enable KR training */
- ret = amd_xgbe_an_enable_kr_training(phydev);
- if (ret < 0)
- return ret;
-
- /* Set PCS to KR/10G speed */
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
- if (ret < 0)
- return ret;
-
- ret &= ~MDIO_PCS_CTRL2_TYPE;
- ret |= MDIO_PCS_CTRL2_10GBR;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret);
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
- if (ret < 0)
- return ret;
-
- ret &= ~MDIO_CTRL1_SPEEDSEL;
- ret |= MDIO_CTRL1_SPEED10G;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
-
- ret = amd_xgbe_phy_pcs_power_cycle(phydev);
- if (ret < 0)
- return ret;
-
- /* Set SerDes to 10G speed */
- amd_xgbe_phy_serdes_start_ratechange(phydev);
-
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_10000_RATE);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_10000_WORD);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_10000_TXAMP);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_10000_PLL);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_10000_CDR);
-
- XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_10000_BLWC);
- XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10000_PQ);
-
- amd_xgbe_phy_serdes_complete_ratechange(phydev);
-
- priv->mode = AMD_XGBE_MODE_KR;
-
- return 0;
-}
-
-static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- int ret;
-
- /* Disable KR training */
- ret = amd_xgbe_an_disable_kr_training(phydev);
- if (ret < 0)
- return ret;
-
- /* Set PCS to KX/1G speed */
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
- if (ret < 0)
- return ret;
-
- ret &= ~MDIO_PCS_CTRL2_TYPE;
- ret |= MDIO_PCS_CTRL2_10GBX;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret);
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
- if (ret < 0)
- return ret;
-
- ret &= ~MDIO_CTRL1_SPEEDSEL;
- ret |= MDIO_CTRL1_SPEED1G;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
-
- ret = amd_xgbe_phy_pcs_power_cycle(phydev);
- if (ret < 0)
- return ret;
-
- /* Set SerDes to 2.5G speed */
- amd_xgbe_phy_serdes_start_ratechange(phydev);
-
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_2500_RATE);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_2500_WORD);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_2500_TXAMP);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_2500_PLL);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_2500_CDR);
-
- XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_2500_BLWC);
- XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_2500_PQ);
-
- amd_xgbe_phy_serdes_complete_ratechange(phydev);
-
- priv->mode = AMD_XGBE_MODE_KX;
-
- return 0;
-}
-
-static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- int ret;
-
- /* Disable KR training */
- ret = amd_xgbe_an_disable_kr_training(phydev);
- if (ret < 0)
- return ret;
-
- /* Set PCS to KX/1G speed */
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
- if (ret < 0)
- return ret;
-
- ret &= ~MDIO_PCS_CTRL2_TYPE;
- ret |= MDIO_PCS_CTRL2_10GBX;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret);
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
- if (ret < 0)
- return ret;
-
- ret &= ~MDIO_CTRL1_SPEEDSEL;
- ret |= MDIO_CTRL1_SPEED1G;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
-
- ret = amd_xgbe_phy_pcs_power_cycle(phydev);
- if (ret < 0)
- return ret;
-
- /* Set SerDes to 1G speed */
- amd_xgbe_phy_serdes_start_ratechange(phydev);
-
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_1000_RATE);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_1000_WORD);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_1000_TXAMP);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_1000_PLL);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_1000_CDR);
-
- XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_1000_BLWC);
- XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1000_PQ);
-
- amd_xgbe_phy_serdes_complete_ratechange(phydev);
-
- priv->mode = AMD_XGBE_MODE_KX;
-
- return 0;
-}
-
-static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- int ret;
-
- /* If we are in KR switch to KX, and vice-versa */
- if (priv->mode == AMD_XGBE_MODE_KR) {
- if (priv->speed_set == AMD_XGBE_PHY_SPEEDSET_1000_10000)
- ret = amd_xgbe_phy_gmii_mode(phydev);
- else
- ret = amd_xgbe_phy_gmii_2500_mode(phydev);
- } else {
- ret = amd_xgbe_phy_xgmii_mode(phydev);
- }
-
- return ret;
-}
-
-static enum amd_xgbe_phy_an amd_xgbe_an_switch_mode(struct phy_device *phydev)
-{
- int ret;
-
- ret = amd_xgbe_phy_switch_mode(phydev);
- if (ret < 0)
- return AMD_XGBE_AN_ERROR;
-
- return AMD_XGBE_AN_START;
-}
-
-static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
- enum amd_xgbe_phy_rx *state)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- int ad_reg, lp_reg, ret;
-
- *state = AMD_XGBE_RX_COMPLETE;
-
- /* If we're in KX mode then we're done */
- if (priv->mode == AMD_XGBE_MODE_KX)
- return AMD_XGBE_AN_EVENT;
-
- /* Enable/Disable FEC */
- ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
- if (ad_reg < 0)
- return AMD_XGBE_AN_ERROR;
-
- lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 2);
- if (lp_reg < 0)
- return AMD_XGBE_AN_ERROR;
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL);
- if (ret < 0)
- return AMD_XGBE_AN_ERROR;
-
- if ((ad_reg & 0xc000) && (lp_reg & 0xc000))
- ret |= 0x01;
- else
- ret &= ~0x01;
-
- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL, ret);
-
- /* Start KR training */
- ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
- if (ret < 0)
- return AMD_XGBE_AN_ERROR;
-
- XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 1);
-
- ret |= 0x01;
- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
-
- XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 0);
-
- return AMD_XGBE_AN_EVENT;
-}
-
-static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev,
- enum amd_xgbe_phy_rx *state)
-{
- u16 msg;
-
- *state = AMD_XGBE_RX_XNP;
-
- msg = XNP_MCF_NULL_MESSAGE;
- msg |= XNP_MP_FORMATTED;
-
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 2, 0);
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0);
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP, msg);
-
- return AMD_XGBE_AN_EVENT;
-}
-
-static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
- enum amd_xgbe_phy_rx *state)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- unsigned int link_support;
- int ret, ad_reg, lp_reg;
-
- /* Read Base Ability register 2 first */
- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 1);
- if (ret < 0)
- return AMD_XGBE_AN_ERROR;
-
- /* Check for a supported mode, otherwise restart in a different one */
- link_support = (priv->mode == AMD_XGBE_MODE_KR) ? 0x80 : 0x20;
- if (!(ret & link_support))
- return amd_xgbe_an_switch_mode(phydev);
-
- /* Check Extended Next Page support */
- ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
- if (ad_reg < 0)
- return AMD_XGBE_AN_ERROR;
-
- lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
- if (lp_reg < 0)
- return AMD_XGBE_AN_ERROR;
-
- return ((ad_reg & XNP_NP_EXCHANGE) || (lp_reg & XNP_NP_EXCHANGE)) ?
- amd_xgbe_an_tx_xnp(phydev, state) :
- amd_xgbe_an_tx_training(phydev, state);
-}
-
-static enum amd_xgbe_phy_an amd_xgbe_an_rx_xnp(struct phy_device *phydev,
- enum amd_xgbe_phy_rx *state)
-{
- int ad_reg, lp_reg;
-
- /* Check Extended Next Page support */
- ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
- if (ad_reg < 0)
- return AMD_XGBE_AN_ERROR;
-
- lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
- if (lp_reg < 0)
- return AMD_XGBE_AN_ERROR;
-
- return ((ad_reg & XNP_NP_EXCHANGE) || (lp_reg & XNP_NP_EXCHANGE)) ?
- amd_xgbe_an_tx_xnp(phydev, state) :
- amd_xgbe_an_tx_training(phydev, state);
-}
-
-static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- int ret;
-
- /* Be sure we aren't looping trying to negotiate */
- if (priv->mode == AMD_XGBE_MODE_KR) {
- if (priv->kr_state != AMD_XGBE_RX_READY)
- return AMD_XGBE_AN_NO_LINK;
- priv->kr_state = AMD_XGBE_RX_BPA;
- } else {
- if (priv->kx_state != AMD_XGBE_RX_READY)
- return AMD_XGBE_AN_NO_LINK;
- priv->kx_state = AMD_XGBE_RX_BPA;
- }
-
- /* Set up Advertisement register 3 first */
- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
- if (ret < 0)
- return AMD_XGBE_AN_ERROR;
-
- if (phydev->supported & SUPPORTED_10000baseR_FEC)
- ret |= 0xc000;
- else
- ret &= ~0xc000;
-
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, ret);
-
- /* Set up Advertisement register 2 next */
- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
- if (ret < 0)
- return AMD_XGBE_AN_ERROR;
-
- if (phydev->supported & SUPPORTED_10000baseKR_Full)
- ret |= 0x80;
- else
- ret &= ~0x80;
-
- if ((phydev->supported & SUPPORTED_1000baseKX_Full) ||
- (phydev->supported & SUPPORTED_2500baseX_Full))
- ret |= 0x20;
- else
- ret &= ~0x20;
-
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, ret);
-
- /* Set up Advertisement register 1 last */
- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
- if (ret < 0)
- return AMD_XGBE_AN_ERROR;
-
- if (phydev->supported & SUPPORTED_Pause)
- ret |= 0x400;
- else
- ret &= ~0x400;
-
- if (phydev->supported & SUPPORTED_Asym_Pause)
- ret |= 0x800;
- else
- ret &= ~0x800;
-
- /* We don't intend to perform XNP */
- ret &= ~XNP_NP_EXCHANGE;
-
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, ret);
-
- /* Enable and start auto-negotiation */
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
-
- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
- if (ret < 0)
- return AMD_XGBE_AN_ERROR;
-
- ret |= MDIO_AN_CTRL1_ENABLE;
- ret |= MDIO_AN_CTRL1_RESTART;
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret);
-
- return AMD_XGBE_AN_EVENT;
-}
-
-static enum amd_xgbe_phy_an amd_xgbe_an_event(struct phy_device *phydev)
-{
- enum amd_xgbe_phy_an new_state;
- int ret;
-
- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT);
- if (ret < 0)
- return AMD_XGBE_AN_ERROR;
-
- new_state = AMD_XGBE_AN_EVENT;
- if (ret & XGBE_AN_PG_RCV)
- new_state = AMD_XGBE_AN_PAGE_RECEIVED;
- else if (ret & XGBE_AN_INC_LINK)
- new_state = AMD_XGBE_AN_INCOMPAT_LINK;
- else if (ret & XGBE_AN_INT_CMPLT)
- new_state = AMD_XGBE_AN_COMPLETE;
-
- if (new_state != AMD_XGBE_AN_EVENT)
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
-
- return new_state;
-}
-
-static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- enum amd_xgbe_phy_rx *state;
- int ret;
-
- state = (priv->mode == AMD_XGBE_MODE_KR) ? &priv->kr_state
- : &priv->kx_state;
-
- switch (*state) {
- case AMD_XGBE_RX_BPA:
- ret = amd_xgbe_an_rx_bpa(phydev, state);
- break;
-
- case AMD_XGBE_RX_XNP:
- ret = amd_xgbe_an_rx_xnp(phydev, state);
- break;
-
- default:
- ret = AMD_XGBE_AN_ERROR;
- }
-
- return ret;
-}
-
-static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev)
-{
- return amd_xgbe_an_switch_mode(phydev);
-}
-
-static void amd_xgbe_an_state_machine(struct work_struct *work)
-{
- struct amd_xgbe_phy_priv *priv = container_of(work,
- struct amd_xgbe_phy_priv,
- an_work);
- struct phy_device *phydev = priv->phydev;
- enum amd_xgbe_phy_an cur_state;
- int sleep;
- unsigned int an_supported = 0;
-
- while (1) {
- mutex_lock(&priv->an_mutex);
-
- cur_state = priv->an_state;
-
- switch (priv->an_state) {
- case AMD_XGBE_AN_START:
- priv->an_state = amd_xgbe_an_start(phydev);
- an_supported = 0;
- break;
-
- case AMD_XGBE_AN_EVENT:
- priv->an_state = amd_xgbe_an_event(phydev);
- break;
-
- case AMD_XGBE_AN_PAGE_RECEIVED:
- priv->an_state = amd_xgbe_an_page_received(phydev);
- an_supported++;
- break;
-
- case AMD_XGBE_AN_INCOMPAT_LINK:
- priv->an_state = amd_xgbe_an_incompat_link(phydev);
- break;
-
- case AMD_XGBE_AN_COMPLETE:
- netdev_info(phydev->attached_dev, "%s successful\n",
- an_supported ? "Auto negotiation"
- : "Parallel detection");
- /* fall through */
-
- case AMD_XGBE_AN_NO_LINK:
- case AMD_XGBE_AN_EXIT:
- goto exit_unlock;
-
- default:
- priv->an_state = AMD_XGBE_AN_ERROR;
- }
-
- if (priv->an_state == AMD_XGBE_AN_ERROR) {
- netdev_err(phydev->attached_dev,
- "error during auto-negotiation, state=%u\n",
- cur_state);
- goto exit_unlock;
- }
-
- sleep = (priv->an_state == AMD_XGBE_AN_EVENT) ? 1 : 0;
-
- mutex_unlock(&priv->an_mutex);
-
- if (sleep)
- usleep_range(20, 50);
- }
-
-exit_unlock:
- priv->an_result = priv->an_state;
- priv->an_state = AMD_XGBE_AN_READY;
-
- mutex_unlock(&priv->an_mutex);
-}
-
-static int amd_xgbe_phy_soft_reset(struct phy_device *phydev)
-{
- int count, ret;
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
- if (ret < 0)
- return ret;
-
- ret |= MDIO_CTRL1_RESET;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
-
- count = 50;
- do {
- msleep(20);
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
- if (ret < 0)
- return ret;
- } while ((ret & MDIO_CTRL1_RESET) && --count);
-
- if (ret & MDIO_CTRL1_RESET)
- return -ETIMEDOUT;
-
- return 0;
-}
-
-static int amd_xgbe_phy_config_init(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
-
- /* Initialize supported features */
- phydev->supported = SUPPORTED_Autoneg;
- phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
- phydev->supported |= SUPPORTED_Backplane;
- phydev->supported |= SUPPORTED_10000baseKR_Full |
- SUPPORTED_10000baseR_FEC;
- switch (priv->speed_set) {
- case AMD_XGBE_PHY_SPEEDSET_1000_10000:
- phydev->supported |= SUPPORTED_1000baseKX_Full;
- break;
- case AMD_XGBE_PHY_SPEEDSET_2500_10000:
- phydev->supported |= SUPPORTED_2500baseX_Full;
- break;
- }
- phydev->advertising = phydev->supported;
-
- /* Turn off and clear interrupts */
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INTMASK, 0);
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
-
- return 0;
-}
-
-static int amd_xgbe_phy_setup_forced(struct phy_device *phydev)
-{
- int ret;
-
- /* Disable auto-negotiation */
- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
- if (ret < 0)
- return ret;
-
- ret &= ~MDIO_AN_CTRL1_ENABLE;
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret);
-
- /* Validate/Set specified speed */
- switch (phydev->speed) {
- case SPEED_10000:
- ret = amd_xgbe_phy_xgmii_mode(phydev);
- break;
-
- case SPEED_2500:
- ret = amd_xgbe_phy_gmii_2500_mode(phydev);
- break;
-
- case SPEED_1000:
- ret = amd_xgbe_phy_gmii_mode(phydev);
- break;
-
- default:
- ret = -EINVAL;
- }
-
- if (ret < 0)
- return ret;
-
- /* Validate duplex mode */
- if (phydev->duplex != DUPLEX_FULL)
- return -EINVAL;
-
- phydev->pause = 0;
- phydev->asym_pause = 0;
-
- return 0;
-}
-
-static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- u32 mmd_mask = phydev->c45_ids.devices_in_package;
- int ret;
-
- if (phydev->autoneg != AUTONEG_ENABLE)
- return amd_xgbe_phy_setup_forced(phydev);
-
- /* Make sure we have the AN MMD present */
- if (!(mmd_mask & MDIO_DEVS_AN))
- return -EINVAL;
-
- /* Get the current speed mode */
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
- if (ret < 0)
- return ret;
-
- /* Start/Restart the auto-negotiation state machine */
- mutex_lock(&priv->an_mutex);
- priv->an_result = AMD_XGBE_AN_READY;
- priv->an_state = AMD_XGBE_AN_START;
- priv->kr_state = AMD_XGBE_RX_READY;
- priv->kx_state = AMD_XGBE_RX_READY;
- mutex_unlock(&priv->an_mutex);
-
- queue_work(priv->an_workqueue, &priv->an_work);
-
- return 0;
-}
-
-static int amd_xgbe_phy_aneg_done(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- enum amd_xgbe_phy_an state;
-
- mutex_lock(&priv->an_mutex);
- state = priv->an_result;
- mutex_unlock(&priv->an_mutex);
-
- return (state == AMD_XGBE_AN_COMPLETE);
-}
-
-static int amd_xgbe_phy_update_link(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- enum amd_xgbe_phy_an state;
- unsigned int check_again, autoneg;
- int ret;
-
- /* If we're doing auto-negotiation don't report link down */
- mutex_lock(&priv->an_mutex);
- state = priv->an_state;
- mutex_unlock(&priv->an_mutex);
-
- if (state != AMD_XGBE_AN_READY) {
- phydev->link = 1;
- return 0;
- }
-
- /* Since the device can be in the wrong mode when a link is
- * (re-)established (cable connected after the interface is
- * up, etc.), the link status may report no link. If there
- * is no link, try switching modes and checking the status
- * again if auto negotiation is enabled.
- */
- check_again = (phydev->autoneg == AUTONEG_ENABLE) ? 1 : 0;
-again:
- /* Link status is latched low, so read once to clear
- * and then read again to get current state
- */
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
- if (ret < 0)
- return ret;
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
- if (ret < 0)
- return ret;
-
- phydev->link = (ret & MDIO_STAT1_LSTATUS) ? 1 : 0;
-
- if (!phydev->link) {
- if (check_again) {
- ret = amd_xgbe_phy_switch_mode(phydev);
- if (ret < 0)
- return ret;
- check_again = 0;
- goto again;
- }
- }
-
- autoneg = (phydev->link && !priv->link) ? 1 : 0;
- priv->link = phydev->link;
- if (autoneg) {
- /* Link is (back) up, re-start auto-negotiation */
- ret = amd_xgbe_phy_config_aneg(phydev);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-static int amd_xgbe_phy_read_status(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- u32 mmd_mask = phydev->c45_ids.devices_in_package;
- int ret, mode, ad_ret, lp_ret;
-
- ret = amd_xgbe_phy_update_link(phydev);
- if (ret)
- return ret;
-
- mode = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
- if (mode < 0)
- return mode;
- mode &= MDIO_PCS_CTRL2_TYPE;
-
- if (phydev->autoneg == AUTONEG_ENABLE) {
- if (!(mmd_mask & MDIO_DEVS_AN))
- return -EINVAL;
-
- if (!amd_xgbe_phy_aneg_done(phydev))
- return 0;
-
- /* Compare Advertisement and Link Partner register 1 */
- ad_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
- if (ad_ret < 0)
- return ad_ret;
- lp_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
- if (lp_ret < 0)
- return lp_ret;
-
- ad_ret &= lp_ret;
- phydev->pause = (ad_ret & 0x400) ? 1 : 0;
- phydev->asym_pause = (ad_ret & 0x800) ? 1 : 0;
-
- /* Compare Advertisement and Link Partner register 2 */
- ad_ret = phy_read_mmd(phydev, MDIO_MMD_AN,
- MDIO_AN_ADVERTISE + 1);
- if (ad_ret < 0)
- return ad_ret;
- lp_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 1);
- if (lp_ret < 0)
- return lp_ret;
-
- ad_ret &= lp_ret;
- if (ad_ret & 0x80) {
- phydev->speed = SPEED_10000;
- if (mode != MDIO_PCS_CTRL2_10GBR) {
- ret = amd_xgbe_phy_xgmii_mode(phydev);
- if (ret < 0)
- return ret;
- }
- } else {
- int (*mode_fcn)(struct phy_device *);
-
- if (priv->speed_set ==
- AMD_XGBE_PHY_SPEEDSET_1000_10000) {
- phydev->speed = SPEED_1000;
- mode_fcn = amd_xgbe_phy_gmii_mode;
- } else {
- phydev->speed = SPEED_2500;
- mode_fcn = amd_xgbe_phy_gmii_2500_mode;
- }
-
- if (mode == MDIO_PCS_CTRL2_10GBR) {
- ret = mode_fcn(phydev);
- if (ret < 0)
- return ret;
- }
- }
-
- phydev->duplex = DUPLEX_FULL;
- } else {
- if (mode == MDIO_PCS_CTRL2_10GBR) {
- phydev->speed = SPEED_10000;
- } else {
- if (priv->speed_set ==
- AMD_XGBE_PHY_SPEEDSET_1000_10000)
- phydev->speed = SPEED_1000;
- else
- phydev->speed = SPEED_2500;
- }
- phydev->duplex = DUPLEX_FULL;
- phydev->pause = 0;
- phydev->asym_pause = 0;
- }
-
- return 0;
-}
-
-static int amd_xgbe_phy_suspend(struct phy_device *phydev)
-{
- int ret;
-
- mutex_lock(&phydev->lock);
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
- if (ret < 0)
- goto unlock;
-
- ret |= MDIO_CTRL1_LPOWER;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
-
- ret = 0;
-
-unlock:
- mutex_unlock(&phydev->lock);
-
- return ret;
-}
-
-static int amd_xgbe_phy_resume(struct phy_device *phydev)
-{
- int ret;
-
- mutex_lock(&phydev->lock);
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
- if (ret < 0)
- goto unlock;
-
- ret &= ~MDIO_CTRL1_LPOWER;
- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
-
- ret = 0;
-
-unlock:
- mutex_unlock(&phydev->lock);
-
- return ret;
-}
-
-static int amd_xgbe_phy_probe(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv;
- struct platform_device *pdev;
- struct device *dev;
- char *wq_name;
- const __be32 *property;
- unsigned int speed_set;
- int ret;
-
- if (!phydev->dev.of_node)
- return -EINVAL;
-
- pdev = of_find_device_by_node(phydev->dev.of_node);
- if (!pdev)
- return -EINVAL;
- dev = &pdev->dev;
-
- wq_name = kasprintf(GFP_KERNEL, "%s-amd-xgbe-phy", phydev->bus->name);
- if (!wq_name) {
- ret = -ENOMEM;
- goto err_pdev;
- }
-
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- ret = -ENOMEM;
- goto err_name;
- }
-
- priv->pdev = pdev;
- priv->dev = dev;
- priv->phydev = phydev;
-
- /* Get the device mmio areas */
- priv->rxtx_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->rxtx_regs = devm_ioremap_resource(dev, priv->rxtx_res);
- if (IS_ERR(priv->rxtx_regs)) {
- dev_err(dev, "rxtx ioremap failed\n");
- ret = PTR_ERR(priv->rxtx_regs);
- goto err_priv;
- }
-
- priv->sir0_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- priv->sir0_regs = devm_ioremap_resource(dev, priv->sir0_res);
- if (IS_ERR(priv->sir0_regs)) {
- dev_err(dev, "sir0 ioremap failed\n");
- ret = PTR_ERR(priv->sir0_regs);
- goto err_rxtx;
- }
-
- priv->sir1_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- priv->sir1_regs = devm_ioremap_resource(dev, priv->sir1_res);
- if (IS_ERR(priv->sir1_regs)) {
- dev_err(dev, "sir1 ioremap failed\n");
- ret = PTR_ERR(priv->sir1_regs);
- goto err_sir0;
- }
-
- /* Get the device speed set property */
- speed_set = 0;
- property = of_get_property(dev->of_node, XGBE_PHY_SPEEDSET_PROPERTY,
- NULL);
- if (property)
- speed_set = be32_to_cpu(*property);
-
- switch (speed_set) {
- case 0:
- priv->speed_set = AMD_XGBE_PHY_SPEEDSET_1000_10000;
- break;
- case 1:
- priv->speed_set = AMD_XGBE_PHY_SPEEDSET_2500_10000;
- break;
- default:
- dev_err(dev, "invalid amd,speed-set property\n");
- ret = -EINVAL;
- goto err_sir1;
- }
-
- priv->link = 1;
-
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
- if (ret < 0)
- goto err_sir1;
- if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
- priv->mode = AMD_XGBE_MODE_KR;
- else
- priv->mode = AMD_XGBE_MODE_KX;
-
- mutex_init(&priv->an_mutex);
- INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine);
- priv->an_workqueue = create_singlethread_workqueue(wq_name);
- if (!priv->an_workqueue) {
- ret = -ENOMEM;
- goto err_sir1;
- }
-
- phydev->priv = priv;
-
- kfree(wq_name);
- of_dev_put(pdev);
-
- return 0;
-
-err_sir1:
- devm_iounmap(dev, priv->sir1_regs);
- devm_release_mem_region(dev, priv->sir1_res->start,
- resource_size(priv->sir1_res));
-
-err_sir0:
- devm_iounmap(dev, priv->sir0_regs);
- devm_release_mem_region(dev, priv->sir0_res->start,
- resource_size(priv->sir0_res));
-
-err_rxtx:
- devm_iounmap(dev, priv->rxtx_regs);
- devm_release_mem_region(dev, priv->rxtx_res->start,
- resource_size(priv->rxtx_res));
-
-err_priv:
- devm_kfree(dev, priv);
-
-err_name:
- kfree(wq_name);
-
-err_pdev:
- of_dev_put(pdev);
-
- return ret;
-}
-
-static void amd_xgbe_phy_remove(struct phy_device *phydev)
-{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
- struct device *dev = priv->dev;
-
- /* Stop any in process auto-negotiation */
- mutex_lock(&priv->an_mutex);
- priv->an_state = AMD_XGBE_AN_EXIT;
- mutex_unlock(&priv->an_mutex);
-
- flush_workqueue(priv->an_workqueue);
- destroy_workqueue(priv->an_workqueue);
-
- /* Release resources */
- devm_iounmap(dev, priv->sir1_regs);
- devm_release_mem_region(dev, priv->sir1_res->start,
- resource_size(priv->sir1_res));
-
- devm_iounmap(dev, priv->sir0_regs);
- devm_release_mem_region(dev, priv->sir0_res->start,
- resource_size(priv->sir0_res));
-
- devm_iounmap(dev, priv->rxtx_regs);
- devm_release_mem_region(dev, priv->rxtx_res->start,
- resource_size(priv->rxtx_res));
-
- devm_kfree(dev, priv);
-}
-
-static int amd_xgbe_match_phy_device(struct phy_device *phydev)
-{
- return phydev->c45_ids.device_ids[MDIO_MMD_PCS] == XGBE_PHY_ID;
-}
-
-static struct phy_driver amd_xgbe_phy_driver[] = {
- {
- .phy_id = XGBE_PHY_ID,
- .phy_id_mask = XGBE_PHY_MASK,
- .name = "AMD XGBE PHY",
- .features = 0,
- .probe = amd_xgbe_phy_probe,
- .remove = amd_xgbe_phy_remove,
- .soft_reset = amd_xgbe_phy_soft_reset,
- .config_init = amd_xgbe_phy_config_init,
- .suspend = amd_xgbe_phy_suspend,
- .resume = amd_xgbe_phy_resume,
- .config_aneg = amd_xgbe_phy_config_aneg,
- .aneg_done = amd_xgbe_phy_aneg_done,
- .read_status = amd_xgbe_phy_read_status,
- .match_phy_device = amd_xgbe_match_phy_device,
- .driver = {
- .owner = THIS_MODULE,
- },
- },
-};
-
-static int __init amd_xgbe_phy_init(void)
-{
- return phy_drivers_register(amd_xgbe_phy_driver,
- ARRAY_SIZE(amd_xgbe_phy_driver));
-}
-
-static void __exit amd_xgbe_phy_exit(void)
-{
- phy_drivers_unregister(amd_xgbe_phy_driver,
- ARRAY_SIZE(amd_xgbe_phy_driver));
-}
-
-module_init(amd_xgbe_phy_init);
-module_exit(amd_xgbe_phy_exit);
-
-static struct mdio_device_id __maybe_unused amd_xgbe_phy_ids[] = {
- { XGBE_PHY_ID, XGBE_PHY_MASK },
- { }
-};
-MODULE_DEVICE_TABLE(mdio, amd_xgbe_phy_ids);
^ permalink raw reply related
* [PATCH net-next v1 3/3] amd-xgbe: Enhance auto-negotiation parallel detection
From: Tom Lendacky @ 2014-08-29 15:22 UTC (permalink / raw)
To: netdev; +Cc: davem
In-Reply-To: <20140829152148.13783.23789.stgit@tlendack-t1.amdoffice.net>
Add support for parallel detection during auto negotiation while in
KR speed mode. Additionally, don't use auto negotiation registers
to determine state if parallel detection was used.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
drivers/net/ethernet/amd/xgbe/xgbe-phy.c | 19 ++++++++++++++++++-
drivers/net/ethernet/amd/xgbe/xgbe.h | 1 +
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy.c
index d072201..d95c2a8 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-phy.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy.c
@@ -108,6 +108,10 @@
#define MDIO_AN_INT 0x8002
#endif
+#ifndef MDIO_AN_KR_CTRL
+#define MDIO_AN_KR_CTRL 0x8003
+#endif
+
#ifndef MDIO_CTRL1_SPEED1G
#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
#endif
@@ -120,6 +124,10 @@
#define MDIO_PMA_10GBR_KR_TRAIN_ENABLE 0x02
#endif
+#ifndef MDIO_KR_CTRL_PDETECT
+#define MDIO_KR_CTRL_PDETECT 1
+#endif
+
/* SerDes integration register offsets */
#define SIR0_KR_RT_1 0x002c
#define SIR0_STATUS 0x0040
@@ -642,6 +650,9 @@ static enum xgbe_an xgbe_phy_an_start(struct phy_device *phydev)
/* Enable and start auto-negotiation */
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0);
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_AN, MDIO_AN_KR_CTRL,
+ MDIO_KR_CTRL_PDETECT, MDIO_KR_CTRL_PDETECT);
+
val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1);
val |= MDIO_AN_CTRL1_ENABLE;
val |= MDIO_AN_CTRL1_RESTART;
@@ -714,6 +725,9 @@ static void xgbe_phy_an_state_machine(struct work_struct *work)
unsigned int sleep;
unsigned int an_supported = 0;
+ /* Start in KX mode */
+ xgbe_phy_set_mode(phydev, XGBE_SPEED_MODE_KX);
+
while (1) {
mutex_lock(&pdata->phy.an_mutex);
@@ -722,6 +736,7 @@ static void xgbe_phy_an_state_machine(struct work_struct *work)
switch (pdata->phy.an_state) {
case XGBE_AN_START:
an_supported = 0;
+ pdata->phy.parallel_detect = 0;
pdata->phy.an_state = xgbe_phy_an_start(phydev);
break;
@@ -739,6 +754,7 @@ static void xgbe_phy_an_state_machine(struct work_struct *work)
break;
case XGBE_AN_COMPLETE:
+ pdata->phy.parallel_detect = an_supported ? 0 : 1;
netdev_info(phydev->attached_dev, "%s successful\n",
an_supported ? "Auto negotiation"
: "Parallel detection");
@@ -956,7 +972,8 @@ static int xgbe_phy_read_status(struct phy_device *phydev)
if (ret)
return ret;
- if (phydev->autoneg == AUTONEG_ENABLE) {
+ if ((phydev->autoneg == AUTONEG_ENABLE) &&
+ !pdata->phy.parallel_detect) {
if (!(mmd_mask & MDIO_DEVS_AN))
return -EINVAL;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index 2d057cf..a964266 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -464,6 +464,7 @@ struct xgbe_phy {
enum xgbe_an_rx kx_state;
struct work_struct an_work;
struct workqueue_struct *an_workqueue;
+ unsigned int parallel_detect;
};
struct xgbe_hw_if {
^ permalink raw reply related
* [PATCH net-next v1 2/3] amd-xgbe: Move the phy device resources into network device
From: Tom Lendacky @ 2014-08-29 15:22 UTC (permalink / raw)
To: netdev; +Cc: davem
In-Reply-To: <20140829152148.13783.23789.stgit@tlendack-t1.amdoffice.net>
As the second step of moving the phy driver into the network driver,
move the phy device resources and settings into the network device.
Creating a new compatible id string for this format allows for backward
compatibility with old device trees.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
.../devicetree/bindings/net/amd-xgbe-phy.txt | 23 --------
Documentation/devicetree/bindings/net/amd-xgbe.txt | 18 ++++--
drivers/net/ethernet/amd/xgbe/xgbe-main.c | 60 ++++++++++++++------
drivers/net/ethernet/amd/xgbe/xgbe-phy.c | 14 ++---
drivers/net/ethernet/amd/xgbe/xgbe.h | 13 +++-
5 files changed, 73 insertions(+), 55 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/net/amd-xgbe-phy.txt
diff --git a/Documentation/devicetree/bindings/net/amd-xgbe-phy.txt b/Documentation/devicetree/bindings/net/amd-xgbe-phy.txt
deleted file mode 100644
index 42409bf..0000000
--- a/Documentation/devicetree/bindings/net/amd-xgbe-phy.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-* AMD 10GbE PHY driver (amd-xgbe-phy)
-
-Required properties:
-- compatible: Should be "amd,xgbe-phy-seattle-v1a" and
- "ethernet-phy-ieee802.3-c45"
-- reg: Address and length of the register sets for the device
- - SerDes Rx/Tx registers
- - SerDes integration registers (1/2)
- - SerDes integration registers (2/2)
-
-Optional properties:
-- amd,speed-set: Speed capabilities of the device
- 0 - 1GbE and 10GbE (default)
- 1 - 2.5GbE and 10GbE
-
-Example:
- xgbe_phy@e1240800 {
- compatible = "amd,xgbe-phy-seattle-v1a", "ethernet-phy-ieee802.3-c45";
- reg = <0 0xe1240800 0 0x00400>,
- <0 0xe1250000 0 0x00060>,
- <0 0xe1250080 0 0x00004>;
- amd,speed-set = <0>;
- };
diff --git a/Documentation/devicetree/bindings/net/amd-xgbe.txt b/Documentation/devicetree/bindings/net/amd-xgbe.txt
index 41354f7..259482c 100644
--- a/Documentation/devicetree/bindings/net/amd-xgbe.txt
+++ b/Documentation/devicetree/bindings/net/amd-xgbe.txt
@@ -1,10 +1,13 @@
* AMD 10GbE driver (amd-xgbe)
Required properties:
-- compatible: Should be "amd,xgbe-seattle-v1a"
+- compatible: Should be "amd,xgbe-seattle-v1b"
- reg: Address and length of the register sets for the device
- MAC registers
- PCS registers
+ - SerDes Rx/Tx registers
+ - SerDes integration registers (1/2)
+ - SerDes integration registers (2/2)
- interrupt-parent: Should be the phandle for the interrupt controller
that services interrupts for this device
- interrupts: Should contain the amd-xgbe interrupt
@@ -16,24 +19,29 @@ Required properties:
- clock-names: Should be the names of the clocks
- "dma_clk" for the DMA clock
- "ptp_clk" for the PTP clock
-- phy-handle: See ethernet.txt file in the same directory
- phy-mode: See ethernet.txt file in the same directory
Optional properties:
- mac-address: mac address to be assigned to the device. Can be overridden
by UEFI.
+- amd,speed-set: available network speeds
+ 0 - 1GbE and 10GbE
+ 1 - 2.5GbE and 10GbE
- dma-coherent: Present if dma operations are coherent
Example:
xgbe@e0700000 {
- compatible = "amd,xgbe-seattle-v1a";
+ compatible = "amd,xgbe-seattle-v1b";
reg = <0 0xe0700000 0 0x80000>,
- <0 0xe0780000 0 0x80000>;
+ <0 0xe0780000 0 0x80000>,
+ <0 0xe1240800 0 0x00400>,
+ <0 0xe1250000 0 0x00060>,
+ <0 0xe1250080 0 0x00004>;
interrupt-parent = <&gic>;
interrupts = <0 325 4>;
clocks = <&xgbe_dma_clk>, <&xgbe_ptp_clk>;
clock-names = "dma_clk", "ptp_clk";
- phy-handle = <&phy>;
phy-mode = "xgmii";
+ amd,speed-set = <0>;
mac-address = [ 02 a1 a2 a3 a4 a5 ];
};
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
index f9ffbfc..7382525 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
@@ -136,6 +136,8 @@ MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(XGBE_DRV_VERSION);
MODULE_DESCRIPTION(XGBE_DRV_DESC);
+static const struct of_device_id xgbe_of_match[];
+
static struct xgbe_channel *xgbe_alloc_rings(struct xgbe_prv_data *pdata)
{
struct xgbe_channel *channel_mem, *channel;
@@ -224,12 +226,14 @@ static int xgbe_probe(struct platform_device *pdev)
struct xgbe_hw_if *hw_if;
struct xgbe_desc_if *desc_if;
struct net_device *netdev;
+ const struct of_device_id *of_dev;
struct device *dev = &pdev->dev;
struct device_node *phy_node;
struct platform_device *phy_pdev;
struct resource *res;
const __be32 *property;
const u8 *mac_addr;
+ unsigned int mem_resource;
int ret;
DBGPR("--> xgbe_probe\n");
@@ -252,19 +256,34 @@ static int xgbe_probe(struct platform_device *pdev)
mutex_init(&pdata->xpcs_mutex);
spin_lock_init(&pdata->tstamp_lock);
- /* Obtain a reference the phy device */
- phy_node = of_parse_phandle(dev->of_node, "phy-handle", 0);
- if (!phy_node) {
- dev_err(dev, "unable to parse phy-handle\n");
- ret = -EINVAL;
+ of_dev = of_match_device(xgbe_of_match, dev);
+ if (!of_dev) {
+ ret = -ENODEV;
goto err_put;
}
+ pdata->drv_level = (enum xgbe_drv_level)of_dev->data;
+
+ if (pdata->drv_level == XGBE_DRV_LEVEL_V1A) {
+ /* Obtain a reference the phy device */
+ phy_node = of_parse_phandle(dev->of_node, "phy-handle", 0);
+ if (!phy_node) {
+ dev_err(dev, "unable to parse phy-handle\n");
+ ret = -EINVAL;
+ goto err_put;
+ }
- phy_pdev = of_find_device_by_node(phy_node);
- if (!phy_pdev) {
- dev_err(dev, "unable to locate phy device\n");
- ret = -EINVAL;
- goto err_io;
+ phy_pdev = of_find_device_by_node(phy_node);
+ if (!phy_pdev) {
+ dev_err(dev, "unable to locate phy device\n");
+ ret = -EINVAL;
+ goto err_io;
+ }
+
+ mem_resource = 0;
+ } else {
+ phy_node = of_node_get(dev->of_node);
+ phy_pdev = pdev;
+ mem_resource = 2;
}
/* Set and validate the number of descriptors for a ring */
@@ -321,7 +340,7 @@ static int xgbe_probe(struct platform_device *pdev)
DBGPR(" xpcs_regs = %p\n", pdata->xpcs_regs);
/* Obtain the mmio areas for the phy device */
- res = platform_get_resource(phy_pdev, IORESOURCE_MEM, 0);
+ res = platform_get_resource(phy_pdev, IORESOURCE_MEM, mem_resource++);
pdata->rxtx_regs = devm_ioremap_resource(dev, res);
if (IS_ERR(pdata->rxtx_regs)) {
dev_err(dev, "rxtx ioremap failed\n");
@@ -330,7 +349,7 @@ static int xgbe_probe(struct platform_device *pdev)
}
DBGPR(" rxtx_regs = %p\n", pdata->rxtx_regs);
- res = platform_get_resource(phy_pdev, IORESOURCE_MEM, 1);
+ res = platform_get_resource(phy_pdev, IORESOURCE_MEM, mem_resource++);
pdata->sir0_regs = devm_ioremap_nocache(dev, res->start,
resource_size(res));
if (IS_ERR(pdata->sir0_regs)) {
@@ -340,7 +359,7 @@ static int xgbe_probe(struct platform_device *pdev)
}
DBGPR(" sir0_regs = %p\n", pdata->sir0_regs);
- res = platform_get_resource(phy_pdev, IORESOURCE_MEM, 2);
+ res = platform_get_resource(phy_pdev, IORESOURCE_MEM, mem_resource++);
pdata->sir1_regs = devm_ioremap_nocache(dev, res->start,
resource_size(res));
if (IS_ERR(pdata->sir1_regs)) {
@@ -351,13 +370,13 @@ static int xgbe_probe(struct platform_device *pdev)
DBGPR(" sir1_regs = %p\n", pdata->sir1_regs);
/* Get the device speed set property */
- property = of_get_property(phy_node, XGBE_PHY_SPEEDSET_PROPERTY, NULL);
+ property = of_get_property(phy_node, XGBE_SPEEDSET_PROPERTY, NULL);
if (property)
pdata->speed_set = be32_to_cpu(*property);
switch (pdata->speed_set) {
- case XGBE_PHY_SPEEDSET_1000_10000:
- case XGBE_PHY_SPEEDSET_2500_10000:
+ case XGBE_SPEEDSET_1000_10000:
+ case XGBE_SPEEDSET_2500_10000:
break;
default:
dev_err(dev, "invalid amd,speed-set property\n");
@@ -604,7 +623,14 @@ static int xgbe_resume(struct device *dev)
#endif /* CONFIG_PM */
static const struct of_device_id xgbe_of_match[] = {
- { .compatible = "amd,xgbe-seattle-v1a", },
+ {
+ .compatible = "amd,xgbe-seattle-v1a",
+ .data = (void *)XGBE_DRV_LEVEL_V1A,
+ },
+ {
+ .compatible = "amd,xgbe-seattle-v1b",
+ .data = (void *)XGBE_DRV_LEVEL_V1B,
+ },
{},
};
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy.c
index 7aca21d..d072201 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-phy.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy.c
@@ -469,7 +469,7 @@ static void xgbe_phy_switch_mode(struct phy_device *phydev)
/* If we are in KR switch to KX, and vice-versa */
if (xgbe_phy_in_kr_mode(phydev)) {
- if (pdata->speed_set == XGBE_PHY_SPEEDSET_1000_10000)
+ if (pdata->speed_set == XGBE_SPEEDSET_1000_10000)
xgbe_phy_gmii_mode(phydev);
else
xgbe_phy_gmii_2500_mode(phydev);
@@ -805,10 +805,10 @@ static int xgbe_phy_config_init(struct phy_device *phydev)
phydev->supported |= SUPPORTED_10000baseKR_Full |
SUPPORTED_10000baseR_FEC;
switch (pdata->speed_set) {
- case XGBE_PHY_SPEEDSET_1000_10000:
+ case XGBE_SPEEDSET_1000_10000:
phydev->supported |= SUPPORTED_1000baseKX_Full;
break;
- case XGBE_PHY_SPEEDSET_2500_10000:
+ case XGBE_SPEEDSET_2500_10000:
phydev->supported |= SUPPORTED_2500baseX_Full;
break;
}
@@ -981,11 +981,11 @@ static int xgbe_phy_read_status(struct phy_device *phydev)
xgbe_phy_set_mode(phydev, XGBE_SPEED_MODE_KR);
} else {
switch (pdata->speed_set) {
- case XGBE_PHY_SPEEDSET_1000_10000:
+ case XGBE_SPEEDSET_1000_10000:
phydev->speed = SPEED_1000;
break;
- case XGBE_PHY_SPEEDSET_2500_10000:
+ case XGBE_SPEEDSET_2500_10000:
phydev->speed = SPEED_2500;
break;
}
@@ -998,11 +998,11 @@ static int xgbe_phy_read_status(struct phy_device *phydev)
phydev->speed = SPEED_10000;
} else {
switch (pdata->speed_set) {
- case XGBE_PHY_SPEEDSET_1000_10000:
+ case XGBE_SPEEDSET_1000_10000:
phydev->speed = SPEED_1000;
break;
- case XGBE_PHY_SPEEDSET_2500_10000:
+ case XGBE_SPEEDSET_2500_10000:
phydev->speed = SPEED_2500;
break;
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index 36f8717..2d057cf 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -174,9 +174,9 @@
#define XGBE_PTP_CLOCK "ptp_clk"
/* Device-tree property names/values */
-#define XGBE_PHY_SPEEDSET_PROPERTY "amd,speed-set"
-#define XGBE_PHY_SPEEDSET_1000_10000 0
-#define XGBE_PHY_SPEEDSET_2500_10000 1
+#define XGBE_SPEEDSET_PROPERTY "amd,speed-set"
+#define XGBE_SPEEDSET_1000_10000 0
+#define XGBE_SPEEDSET_2500_10000 1
/* Timestamp support - values based on 50MHz PTP clock
* 50MHz => 20 nsec
@@ -611,11 +611,18 @@ struct xgbe_hw_features {
unsigned int aux_snap_num; /* Number of Aux snapshot inputs */
};
+enum xgbe_drv_level {
+ XGBE_DRV_LEVEL_V1A = 0,
+ XGBE_DRV_LEVEL_V1B,
+};
+
struct xgbe_prv_data {
struct net_device *netdev;
struct platform_device *pdev;
struct device *dev;
+ enum xgbe_drv_level drv_level;
+
/* XGMAC/XPCS related mmio registers */
void __iomem *xgmac_regs; /* XGMAC CSRs */
void __iomem *xpcs_regs; /* XPCS MMD registers */
^ permalink raw reply related
* [PATCH net-next v1 0/3] amd-xgbe: AMD XGBE driver updates 2014-08-29
From: Tom Lendacky @ 2014-08-29 15:21 UTC (permalink / raw)
To: netdev; +Cc: davem
The amd-xgbe-phy phylib driver support is integral to the amd-xgbe
driver and isn't meant to be used by other devices. For this reason
it is being removed from the driver/net/phy directory and integrated
into the amd-xgbe driver.
The following series of patches accomplishes this as well as providing
some updates to parallel detection during auto-negotiation.
- Move phylib driver into network driver
- Update device tree bindings to move the phy device resources and
properties into the network device while maintaining backwards
compatibility
- Update parallel detection logic to support KR mode
This patch series is based on net-next.
---
Tom Lendacky (3):
amd-xgbe: Move phy driver support into the network driver
amd-xgbe: Move the phy device resources into network device
amd-xgbe: Enhance auto-negotiation parallel detection
.../devicetree/bindings/net/amd-xgbe-phy.txt | 23
Documentation/devicetree/bindings/net/amd-xgbe.txt | 18
drivers/net/ethernet/amd/Kconfig | 1
drivers/net/ethernet/amd/xgbe/Makefile | 2
drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 16
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 10
drivers/net/ethernet/amd/xgbe/xgbe-main.c | 165 ++
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 39 -
drivers/net/ethernet/amd/xgbe/xgbe-phy.c | 1145 ++++++++++++++++
drivers/net/ethernet/amd/xgbe/xgbe.h | 67 +
drivers/net/phy/Kconfig | 6
drivers/net/phy/Makefile | 1
drivers/net/phy/amd-xgbe-phy.c | 1457 --------------------
13 files changed, 1385 insertions(+), 1565 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/net/amd-xgbe-phy.txt
create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-phy.c
delete mode 100644 drivers/net/phy/amd-xgbe-phy.c
--
Tom Lendacky
^ permalink raw reply
* Re: [PATCH 1/5] net: mvberlin_eth: add an Ethernet driver for Marvell Berlin
From: Ezequiel Garcia @ 2014-08-29 14:51 UTC (permalink / raw)
To: Antoine Tenart
Cc: sebastian.hesselbarth-Re5JQEeQqe8AvxtiuMwx3w,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
zmxu-eYqpPyKDWXRBDgjK7y7TUQ, jszhang-eYqpPyKDWXRBDgjK7y7TUQ,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1409320263-10295-2-git-send-email-antoine.tenart-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Hi Antoine,
A quick look...
On 29 Aug 03:50 PM, Antoine Tenart wrote:
> This patch introduces the Marvell Berlin network unit driver, which uses
> the MVMDIO interface to communicate to the PHY. This is a fast Ethernet
> driver.
>
> This driver is highly based on the mv643xx_eth driver, and reuse some of
> its functions. But lots of differences are there:
> - They do not have the same registers.
> - The mvberlin_eth driver only supports fast Ethernet.
> - The rx/tx handling functions logic is different.
> - The mv643xx_eth driver supports TSO.
TSO is just a software feature which needs just some basic hardware features
to work. I guess there's no point in implementing it for a fast Ethernet
controller?
> - The mvberlin_eth driver uses a hash table to filter incoming packets.
>
> No packet is dropped during a ping flood (ping -f) and an iperf test
> shows:
> ------------------------------------------------------------
> Client connecting to 192.168.0.11, TCP port 5001
> TCP window size: 85.0 KByte (default)
> ------------------------------------------------------------
> [ 3] local 192.168.0.20 port 44183 connected with 192.168.0.11 port 5001
> [ ID] Interval Transfer Bandwidth
> [ 3] 0.0-10.0 sec 113 MBytes 94.8 Mbits/sec
>
> Signed-off-by: Antoine Tenart <antoine.tenart-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
> drivers/net/ethernet/marvell/Kconfig | 9 +
> drivers/net/ethernet/marvell/Makefile | 1 +
> drivers/net/ethernet/marvell/mvberlin_eth.c | 2081 +++++++++++++++++++++++++++
> 3 files changed, 2091 insertions(+)
> create mode 100644 drivers/net/ethernet/marvell/mvberlin_eth.c
>
> diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
> index 1b4fc7c639e6..a4f12257d099 100644
> --- a/drivers/net/ethernet/marvell/Kconfig
> +++ b/drivers/net/ethernet/marvell/Kconfig
> @@ -18,6 +18,15 @@ config NET_VENDOR_MARVELL
>
> if NET_VENDOR_MARVELL
>
> +config MVBERLIN_ETH
> + tristate "Marvell Berlin ethernet support"
> + depends on ARCH_BERLIN && INET
> + select PHYLIB
> + select MVMDIO
> + ---help---
> + This driver supports the ethernet interface of the Marvell
> + Berlin SoCs.
> +
> config MV643XX_ETH
> tristate "Marvell Discovery (643XX) and Orion ethernet support"
> depends on (MV64X60 || PPC32 || PLAT_ORION) && INET
> diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile
> index f6425bd2884b..a802dba2503e 100644
> --- a/drivers/net/ethernet/marvell/Makefile
> +++ b/drivers/net/ethernet/marvell/Makefile
> @@ -2,6 +2,7 @@
> # Makefile for the Marvell device drivers.
> #
>
> +obj-$(CONFIG_MVBERLIN_ETH) += mvberlin_eth.o
> obj-$(CONFIG_MVMDIO) += mvmdio.o
> obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
> obj-$(CONFIG_MVNETA) += mvneta.o
> diff --git a/drivers/net/ethernet/marvell/mvberlin_eth.c b/drivers/net/ethernet/marvell/mvberlin_eth.c
> new file mode 100644
> index 000000000000..5f1874b58017
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/mvberlin_eth.c
> @@ -0,0 +1,2081 @@
> +/*
> + * Copyright (C) 2014 Marvell Technology Group Ltd.
> + *
> + * Antoine Tenart <antoine.tenart-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> + * Jisheng Zhang <jszhang-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
> + *
> + * Based on the driver for Marvell Discovery (MV643XX) and Marvell Orion
> + * ethernet ports
> + * Copyright (C) 2002 Matthew Dharm <mdharm-K4l4QA/CiyFBDgjK7y7TUQ@public.gmane.org>
> + *
> + * Based on the 64360 driver from:
> + * Copyright (C) 2002 Rabeeh Khoury <rabeeh-YsvTK5mC77DRNpEZXGUFxA@public.gmane.org>
> + * Rabeeh Khoury <rabeeh-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
> + *
> + * Copyright (C) 2003 PMC-Sierra, Inc.,
> + * written by Manish Lachwani
> + *
> + * Copyright (C) 2003 Ralf Baechle <ralf-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org>
> + *
> + * Copyright (C) 2004-2006 MontaVista Software, Inc.
> + * Dale Farnsworth <dale-1viX+2+OPRFcxvNqPlePQg@public.gmane.org>
> + *
> + * Copyright (C) 2004 Steven J. Hill <sjhill1-lFk7bPDcGtkY5TsXZYaR1UEOCMrvLtNR@public.gmane.org>
> + * <sjhill-6kvYhFKpFNxhpcrUJlKPNtBPR1lH4CV8@public.gmane.org>
> + *
> + * Copyright (C) 2007-2008 Marvell Semiconductor
> + * Lennert Buytenhek <buytenh-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
> + *
> + * Copyright (C) 2013 Michael Stapelberg <michael-Ocq5V1Lzw0g3ibs+7tISlw@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/etherdevice.h>
> +#include <linux/ethtool.h>
> +#include <linux/in.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/ip.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mv643xx_eth.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_net.h>
> +#include <linux/of_mdio.h>
> +#include <linux/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/tcp.h>
> +#include <linux/types.h>
> +#include <linux/udp.h>
> +#include <linux/workqueue.h>
> +
> +static const char mvberlin_eth_driver_name[] = "mvberlin_eth";
> +static const char mvberlin_eth_driver_version[] = "1.0";
> +
> +/* Main per-port registers. These live at offset 0x0400 */
> +#define PORT_CONFIG 0x0000
> +#define PROMISCUOUS_MODE 0x00000001
I think that using BIT() makes the code more readable.
> +
> +/* SDMA configuration register default value */
> +#if defined(__BIG_ENDIAN)
> +#define PORT_SDMA_CONFIG_DEFAULT_VALUE \
> + (BURST_SIZE_8_64BIT | \
> + 0x3c | \
> + RX_FRAME_INTERRUPT)
> +#elif defined(__LITTLE_ENDIAN)
> +#define PORT_SDMA_CONFIG_DEFAULT_VALUE \
> + (BURST_SIZE_8_64BIT | \
> + BLM_RX_LE | \
> + BLM_TX_LE | \
> + 0x3c | \
> + RX_FRAME_INTERRUPT)
> +#else
> +#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined
> +#endif
> +
> +/* Misc definitions */
> +#define DEFAULT_RX_QUEUE_SIZE 128
> +#define DEFAULT_TX_QUEUE_SIZE 512
> +#define SKB_DMA_REALIGN ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES)
> +
> +/* RX/TX descriptors */
> +#if defined(__BIG_ENDIAN)
Maybe you can pack this inside the above ifdef and squash all the
endian-dependent stuff?
> +struct rx_desc {
> + u16 buf_size; /* Buffer size */
> + u16 byte_cnt; /* Descriptor buffer byte count */
> + u32 cmd_sts; /* Command/status field */
> + u32 next_desc_ptr; /* Next descriptor pointer */
> + u32 buf_ptr; /* Descriptor buffer pointer */
> +};
> +
> +struct tx_desc {
> + u16 byte_cnt; /* buffer byte count */
> + u16 l4i_chk; /* CPU provided TCP checksum */
> + u32 cmd_sts; /* Command/status field */
> + u32 next_desc_ptr; /* Pointer to next descriptor */
> + u32 buf_ptr; /* pointer to buffer for this descriptor*/
> +};
> +#elif defined(__LITTLE_ENDIAN)
> +struct rx_desc {
> + u32 cmd_sts; /* Descriptor command status */
> + u16 byte_cnt; /* Descriptor buffer byte count */
> + u16 buf_size; /* Buffer size */
> + u32 buf_ptr; /* Descriptor buffer pointer */
> + u32 next_desc_ptr; /* Next descriptor pointer */
> +};
> +
> +struct tx_desc {
> + u32 cmd_sts; /* Command/status field */
> + u16 l4i_chk; /* CPU provided TCP checksum */
> + u16 byte_cnt; /* buffer byte count */
> + u32 buf_ptr; /* pointer to buffer for this descriptor*/
> + u32 next_desc_ptr; /* Pointer to next descriptor */
> +};
> +#else
> +#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined
> +#endif
> +
> +/* RX & TX descriptor command */
> +#define BUFFER_OWNED_BY_DMA 0x80000000
> +
Ditto about BIT() usage.
> +static netdev_tx_t mvberlin_eth_xmit(struct sk_buff *skb,
> + struct net_device *dev)
> +{
> + struct mvberlin_eth_private *mp = netdev_priv(dev);
> + int length, queue;
> + struct tx_queue *txq;
> + struct netdev_queue *nq;
> +
> + queue = skb_get_queue_mapping(skb);
> + txq = mp->txq + queue;
> + nq = netdev_get_tx_queue(dev, queue);
> +
> + if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) {
> + netdev_printk(KERN_DEBUG, dev,
> + "failed to linearize skb with tiny unaligned fragment\n");
netdev_dbg?
> + return NETDEV_TX_BUSY;
> + }
> +
> + length = skb->len;
> +
> + if (!txq_submit_skb(txq, skb, dev)) {
> + txq->tx_bytes += length;
> + txq->tx_packets++;
> +
> + if (txq->tx_desc_count >= txq->tx_stop_threshold)
> + netif_tx_stop_queue(nq);
> + } else {
> + txq->tx_dropped++;
> + dev_kfree_skb_any(skb);
> + }
> +
> + return NETDEV_TX_OK;
> +}
> +
> +
> +static void handle_link_event(struct mvberlin_eth_private *mp)
> +{
> + struct net_device *dev = mp->dev;
> + u32 port_status;
> + int speed;
> + int duplex;
> + int fc;
> +
> + port_status = rdlp(mp, PORT_STATUS);
> + if (!(port_status & LINK_UP)) {
> + if (netif_carrier_ok(dev)) {
> + int i;
> +
> + netdev_info(dev, "link down\n");
> +
> + netif_carrier_off(dev);
> +
> + for (i = 0; i < mp->txq_count; i++) {
> + struct tx_queue *txq = mp->txq + i;
> +
> + txq_reclaim(txq, txq->tx_ring_size, 1);
> + txq_reset_hw_ptr(txq);
> + }
> + }
> + return;
> + }
> +
> + switch (port_status & PORT_SPEED_MASK) {
> + case PORT_SPEED_10:
> + speed = 10;
> + break;
> + case PORT_SPEED_100:
> + speed = 100;
> + break;
> + default:
> + speed = -1;
> + break;
> + }
> +
> + duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
> + fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
> +
> + netdev_info(dev, "link up, %d Mb/s, %s duplex, flow control %sabled\n",
> + speed, duplex ? "full" : "half", fc ? "en" : "dis");
Maybe you can use phy_print_status() instead of rolling your own?
> +
> + if (!netif_carrier_ok(dev))
> + netif_carrier_on(dev);
> +}
> +
> +static irqreturn_t mvberlin_eth_irq(int irq, void *dev_id)
> +{
> + struct net_device *dev = (struct net_device *)dev_id;
> + struct mvberlin_eth_private *mp = netdev_priv(dev);
> + u32 int_cause, txstatus;
> + int i;
> +
> + int_cause = rdlp(mp, INT_CAUSE) & mp->int_mask;
> +
> + if (int_cause == 0)
> + return IRQ_NONE;
> + wrlp(mp, INT_CAUSE, ~int_cause);
> +
> + if (int_cause & INT_RX) {
> + wrlp(mp, INT_MASK, mp->int_mask & ~INT_RX);
> + napi_schedule(&mp->napi);
> + }
> +
> + if (int_cause & INT_EXT)
> + handle_link_event(mp);
> +
> + txstatus = int_cause & INT_TX;
> + for (i = 0; i < mp->txq_count; ++i) {
> + if (txstatus & INT_TX_0 << i) {
> + txq_reclaim(mp->txq + i, 16, 0);
> + txq_maybe_wake(mp->txq + i);
> + }
> + }
> +
> + txstatus = ((int_cause & INT_TX_END) >> 6) &
> + ~((rdlp(mp, SDMA_COMMAND) >> 16) & 0x3);
> + for (i = 0; i < mp->txq_count; ++i) {
> + if (txstatus & 1 << i)
> + txq_kick(mp->txq + i);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +static void set_params(struct mvberlin_eth_private *mp,
> + struct mv643xx_eth_platform_data *pd)
> +{
> + struct net_device *dev = mp->dev;
> +
> + if (is_valid_ether_addr(pd->mac_addr))
> + memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN);
> + else
> + uc_addr_get(mp, dev->dev_addr);
> +
> + mp->rx_ring_size = DEFAULT_RX_QUEUE_SIZE;
> + if (pd->rx_queue_size)
> + mp->rx_ring_size = pd->rx_queue_size;
> + mp->rx_desc_sram_addr = pd->rx_sram_addr;
> + mp->rx_desc_sram_size = pd->rx_sram_size;
> +
> + mp->rxq_count = pd->rx_queue_count ? : 1;
> +
> + mp->tx_ring_size = DEFAULT_TX_QUEUE_SIZE;
> + if (pd->tx_queue_size)
> + mp->tx_ring_size = pd->tx_queue_size;
> +
> + mp->tx_desc_sram_addr = pd->tx_sram_addr;
> + mp->tx_desc_sram_size = pd->tx_sram_size;
> +
> + mp->txq_count = pd->tx_queue_count ? : 1;
> +}
> +
> +static const struct net_device_ops mvberlin_eth_netdev_ops = {
> + .ndo_open = mvberlin_eth_open,
> + .ndo_stop = mvberlin_eth_stop,
> + .ndo_start_xmit = mvberlin_eth_xmit,
> + .ndo_set_rx_mode = mvberlin_eth_set_multicast_list,
> + .ndo_set_mac_address = mvberlin_eth_set_mac_address,
> + .ndo_validate_addr = eth_validate_addr,
> + .ndo_do_ioctl = mvberlin_eth_ioctl,
> + .ndo_change_mtu = mvberlin_eth_change_mtu,
> + .ndo_tx_timeout = mvberlin_eth_tx_timeout,
> + .ndo_get_stats = mvberlin_eth_get_stats,
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> + .ndo_poll_controller = mvberlin_eth_netpoll,
> +#endif
> +};
> +
> +static int mvberlin_eth_probe(struct platform_device *pdev)
> +{
> + struct mv643xx_eth_platform_data *pd;
mv643xx_eth_platform_data? You really lost me here :) I'm having a hard
time figuring out who will set this platform data. I guess I'm overlooking
something?
> + struct mvberlin_eth_private *mp;
> + struct net_device *dev;
> + struct resource *res;
> + int ret;
> +
> + dev = alloc_etherdev_mq(sizeof(struct mvberlin_eth_private), 8);
> + if (!dev)
> + return -ENOMEM;
> +
> + pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
> + if (!pd)
> + return -ENOMEM;
> +
> + mp = netdev_priv(dev);
> + platform_set_drvdata(pdev, mp);
> + mp->dev = dev;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res)
> + return -ENOMEM;
> +
> + mp->shared = devm_kzalloc(&pdev->dev,
> + sizeof(struct mvberlin_eth_shared_private),
> + GFP_KERNEL);
> + if (!mp->shared)
> + return -ENOMEM;
> +
> + mp->shared->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(mp->shared->base))
> + return PTR_ERR(mp->shared->base);
> + mp->base = mp->shared->base + 0x400;
> +
> + mp->clk = devm_clk_get(&pdev->dev, NULL);
> + if (!IS_ERR(mp->clk)) {
> + clk_prepare_enable(mp->clk);
> + mp->t_clk = clk_get_rate(mp->clk);
> + }
> +
The binding doesn't declare the clock as optional, I'd say you should enforce
the requirement here.
> + set_params(mp, pd);
> + netif_set_real_num_tx_queues(dev, mp->txq_count);
> + netif_set_real_num_rx_queues(dev, mp->rxq_count);
> +
> + pd->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
> + if (!pd->phy_node) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + mp->phy = of_phy_connect(dev, pd->phy_node,
> + mvberlin_eth_adjust_link, 0,
> + PHY_INTERFACE_MODE_RGMII);
> + if (!mp->phy) {
> + ret = -EPROBE_DEFER;
> + goto out;
> + }
> +
> + dev->ethtool_ops = &mvberlin_eth_ethtool_ops;
> +
> + init_pscr(mp);
> +
> + init_hash_table(mp);
> + mvberlin_eth_program_unicast_filter(mp, NULL, dev->dev_addr);
> +
> + mib_counters_clear(mp);
> +
> + INIT_WORK(&mp->tx_timeout_task, tx_timeout_task);
> +
> + netif_napi_add(dev, &mp->napi, mvberlin_eth_poll, NAPI_POLL_WEIGHT);
> +
> + init_timer(&mp->rx_oom);
> + mp->rx_oom.data = (unsigned long)mp;
> + mp->rx_oom.function = oom_timer_wrapper;
> +
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + BUG_ON(!res);
Hm... BUG_ON!?!? Are you sure you want to kill the entire system?
There's another BUG_ON above, and since this is just network driver,
I think it can be relaxed.
> + dev->irq = res->start;
> +
> + dev->netdev_ops = &mvberlin_eth_netdev_ops;
> +
> + dev->watchdog_timeo = 2 * HZ;
> + dev->base_addr = 0;
> +
> + SET_NETDEV_DEV(dev, &pdev->dev);
> +
> + wrlp(mp, SDMA_CONFIG, PORT_SDMA_CONFIG_DEFAULT_VALUE);
> +
> + ret = register_netdev(dev);
> + if (ret)
> + goto out;
> +
> + netif_carrier_off(dev);
> +
> + return 0;
> +
> +out:
> + if (!IS_ERR(mp->clk))
> + clk_disable_unprepare(mp->clk);
> + free_netdev(dev);
> +
> + return ret;
> +}
> +
> +static int mvberlin_eth_remove(struct platform_device *pdev)
> +{
> + struct mvberlin_eth_private *mp = platform_get_drvdata(pdev);
> +
> + unregister_netdev(mp->dev);
> + if (mp->phy != NULL)
> + phy_disconnect(mp->phy);
> + cancel_work_sync(&mp->tx_timeout_task);
> +
> + if (!IS_ERR(mp->clk))
> + clk_disable_unprepare(mp->clk);
> +
Ditto for the optional clock.
--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [patch net-next RFC 10/12] openvswitch: add support for datapath hardware offload
From: Jamal Hadi Salim @ 2014-08-29 14:20 UTC (permalink / raw)
To: Thomas Graf, Alexei Starovoitov
Cc: Andy Gospodarek, Jiri Pirko, Roopa Prabhu, John Fastabend,
Scott Feldman, netdev, David Miller, Neil Horman, Andy Gospodarek,
dborkman, ogerlitz, Jesse Gross, Pravin Shelar, Andy Zhou, ben,
Stephen Hemminger, jeffrey.t.kirsher, vyasevic, Cong Wang,
john.r.fastabend, Eric Dumazet, Florian Fainelli, John Linville,
dev@openvswitch.org, jasowang
In-Reply-To: <20140826205419.GH15316@casper.infradead.org>
On 08/26/14 16:54, Thomas Graf wrote:
> On 08/26/14 at 01:13pm, Alexei Starovoitov wrote:
>> I think it's important distinction. In-kernel OVS is not OF.
>> It's a networking function that has hard-coded packet parser,
>> N-tuple match and programmable actions.
>> There were times when HW vendors were using OF check-box
>> to sell more chips, but at the end there is not a single HW
>> that is fully OF compliant. OF brand is still around, but
>> OF 2.0 is not tcam+action anymore.
>> Imo trying to standardize HW offload interface based on OF 1.x
>> principles is strange.
I actually have no issues with whatever classifier someone decides
to use. To each their poison. But I do take issue mandating the
specified classifer it as THE CLASSIFIER as in this case,
is where i start taking issue. I have a few things that i offload
to hardware with speacilized classifiers such that i object strongly
to the approach this driver has taken.
cheers,
jamal
^ permalink raw reply
* [ipsec-next v4 2/2] xfrm: configure policy hash table thresholds by netlink
From: Christophe Gouault @ 2014-08-29 14:16 UTC (permalink / raw)
To: Steffen Klassert; +Cc: David S. Miller, netdev, Christophe Gouault
In-Reply-To: <1409321765-2012-1-git-send-email-christophe.gouault@6wind.com>
Enable to specify local and remote prefix length thresholds for the
policy hash table via a netlink XFRM_MSG_NEWSPDINFO message.
prefix length thresholds are specified by XFRMA_SPD_IPV4_HTHRESH and
XFRMA_SPD_IPV6_HTHRESH optional attributes (struct xfrmu_spdhthresh).
example:
struct xfrmu_spdhthresh thresh4 = {
.lbits = 0;
.rbits = 24;
};
struct xfrmu_spdhthresh thresh6 = {
.lbits = 0;
.rbits = 56;
};
struct nlmsghdr *hdr;
struct nl_msg *msg;
msg = nlmsg_alloc();
hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, XFRMA_SPD_IPV4_HTHRESH, sizeof(__u32), NLM_F_REQUEST);
nla_put(msg, XFRMA_SPD_IPV4_HTHRESH, sizeof(thresh4), &thresh4);
nla_put(msg, XFRMA_SPD_IPV6_HTHRESH, sizeof(thresh6), &thresh6);
nla_send_auto(sk, msg);
The numbers are the policy selector minimum prefix lengths to put a
policy in the hash table.
- lbits is the local threshold (source address for out policies,
destination address for in and fwd policies).
- rbits is the remote threshold (destination address for out
policies, source address for in and fwd policies).
The default values are:
XFRMA_SPD_IPV4_HTHRESH: 32 32
XFRMA_SPD_IPV6_HTHRESH: 128 128
Dynamic re-building of the SPD is performed when the thresholds values
are changed.
The current thresholds can be read via a XFRM_MSG_GETSPDINFO request:
the kernel replies to XFRM_MSG_GETSPDINFO requests by an
XFRM_MSG_NEWSPDINFO message, with both attributes
XFRMA_SPD_IPV4_HTHRESH and XFRMA_SPD_IPV6_HTHRESH.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
---
include/net/netns/xfrm.h | 10 ++++++
include/net/xfrm.h | 1 +
include/uapi/linux/xfrm.h | 7 ++++
net/xfrm/xfrm_policy.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++
net/xfrm/xfrm_user.c | 80 +++++++++++++++++++++++++++++++++++++++++--
5 files changed, 182 insertions(+), 3 deletions(-)
diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h
index 41902a8..9da7982 100644
--- a/include/net/netns/xfrm.h
+++ b/include/net/netns/xfrm.h
@@ -19,6 +19,15 @@ struct xfrm_policy_hash {
u8 sbits6;
};
+struct xfrm_policy_hthresh {
+ struct work_struct work;
+ seqlock_t lock;
+ u8 lbits4;
+ u8 rbits4;
+ u8 lbits6;
+ u8 rbits6;
+};
+
struct netns_xfrm {
struct list_head state_all;
/*
@@ -45,6 +54,7 @@ struct netns_xfrm {
struct xfrm_policy_hash policy_bydst[XFRM_POLICY_MAX * 2];
unsigned int policy_count[XFRM_POLICY_MAX * 2];
struct work_struct policy_hash_work;
+ struct xfrm_policy_hthresh policy_hthresh;
struct sock *nlsk;
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 721e9c3..dc4865e 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -1591,6 +1591,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark,
struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir,
u32 id, int delete, int *err);
int xfrm_policy_flush(struct net *net, u8 type, bool task_valid);
+void xfrm_policy_hash_rebuild(struct net *net);
u32 xfrm_get_acqseq(void);
int verify_spi_info(u8 proto, u32 min, u32 max);
int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h
index 25e5dd9..02d5125 100644
--- a/include/uapi/linux/xfrm.h
+++ b/include/uapi/linux/xfrm.h
@@ -328,6 +328,8 @@ enum xfrm_spdattr_type_t {
XFRMA_SPD_UNSPEC,
XFRMA_SPD_INFO,
XFRMA_SPD_HINFO,
+ XFRMA_SPD_IPV4_HTHRESH,
+ XFRMA_SPD_IPV6_HTHRESH,
__XFRMA_SPD_MAX
#define XFRMA_SPD_MAX (__XFRMA_SPD_MAX - 1)
@@ -347,6 +349,11 @@ struct xfrmu_spdhinfo {
__u32 spdhmcnt;
};
+struct xfrmu_spdhthresh {
+ __u8 lbits;
+ __u8 rbits;
+};
+
struct xfrm_usersa_info {
struct xfrm_selector sel;
struct xfrm_id id;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index e6ff7b4..55bcb86 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -566,6 +566,86 @@ static void xfrm_hash_resize(struct work_struct *work)
mutex_unlock(&hash_resize_mutex);
}
+static void xfrm_hash_rebuild(struct work_struct *work)
+{
+ struct net *net = container_of(work, struct net,
+ xfrm.policy_hthresh.work);
+ unsigned int hmask;
+ struct xfrm_policy *pol;
+ struct xfrm_policy *policy;
+ struct hlist_head *chain;
+ struct hlist_head *odst;
+ struct hlist_node *newpos;
+ int i;
+ int dir;
+ unsigned seq;
+ u8 lbits4, rbits4, lbits6, rbits6;
+
+ mutex_lock(&hash_resize_mutex);
+
+ /* read selector prefixlen thresholds */
+ do {
+ seq = read_seqbegin(&net->xfrm.policy_hthresh.lock);
+
+ lbits4 = net->xfrm.policy_hthresh.lbits4;
+ rbits4 = net->xfrm.policy_hthresh.rbits4;
+ lbits6 = net->xfrm.policy_hthresh.lbits6;
+ rbits6 = net->xfrm.policy_hthresh.rbits6;
+ } while (read_seqretry(&net->xfrm.policy_hthresh.lock, seq));
+
+ write_lock_bh(&net->xfrm.xfrm_policy_lock);
+
+ /* reset the bydst and inexact table in all directions */
+ for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
+ INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]);
+ hmask = net->xfrm.policy_bydst[dir].hmask;
+ odst = net->xfrm.policy_bydst[dir].table;
+ for (i = hmask; i >= 0; i--)
+ INIT_HLIST_HEAD(odst + i);
+ if ((dir & XFRM_POLICY_MASK) == XFRM_POLICY_OUT) {
+ /* dir out => dst = remote, src = local */
+ net->xfrm.policy_bydst[dir].dbits4 = rbits4;
+ net->xfrm.policy_bydst[dir].sbits4 = lbits4;
+ net->xfrm.policy_bydst[dir].dbits6 = rbits6;
+ net->xfrm.policy_bydst[dir].sbits6 = lbits6;
+ } else {
+ /* dir in/fwd => dst = local, src = remote */
+ net->xfrm.policy_bydst[dir].dbits4 = lbits4;
+ net->xfrm.policy_bydst[dir].sbits4 = rbits4;
+ net->xfrm.policy_bydst[dir].dbits6 = lbits6;
+ net->xfrm.policy_bydst[dir].sbits6 = rbits6;
+ }
+ }
+
+ /* re-insert all policies by order of creation */
+ list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) {
+ newpos = NULL;
+ chain = policy_hash_bysel(net, &policy->selector,
+ policy->family,
+ xfrm_policy_id2dir(policy->index));
+ hlist_for_each_entry(pol, chain, bydst) {
+ if (policy->priority >= pol->priority)
+ newpos = &pol->bydst;
+ else
+ break;
+ }
+ if (newpos)
+ hlist_add_behind(&policy->bydst, newpos);
+ else
+ hlist_add_head(&policy->bydst, chain);
+ }
+
+ write_unlock_bh(&net->xfrm.xfrm_policy_lock);
+
+ mutex_unlock(&hash_resize_mutex);
+}
+
+void xfrm_policy_hash_rebuild(struct net *net)
+{
+ schedule_work(&net->xfrm.policy_hthresh.work);
+}
+EXPORT_SYMBOL(xfrm_policy_hash_rebuild);
+
/* Generate new index... KAME seems to generate them ordered by cost
* of an absolute inpredictability of ordering of rules. This will not pass. */
static u32 xfrm_gen_index(struct net *net, int dir, u32 index)
@@ -2872,9 +2952,16 @@ static int __net_init xfrm_policy_init(struct net *net)
htab->dbits6 = 128;
htab->sbits6 = 128;
}
+ net->xfrm.policy_hthresh.lbits4 = 32;
+ net->xfrm.policy_hthresh.rbits4 = 32;
+ net->xfrm.policy_hthresh.lbits6 = 128;
+ net->xfrm.policy_hthresh.rbits6 = 128;
+
+ seqlock_init(&net->xfrm.policy_hthresh.lock);
INIT_LIST_HEAD(&net->xfrm.policy_all);
INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize);
+ INIT_WORK(&net->xfrm.policy_hthresh.work, xfrm_hash_rebuild);
if (net_eq(net, &init_net))
register_netdevice_notifier(&xfrm_dev_notifier);
return 0;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index d4db6eb..eaf8a8f 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -964,7 +964,9 @@ static inline size_t xfrm_spdinfo_msgsize(void)
{
return NLMSG_ALIGN(4)
+ nla_total_size(sizeof(struct xfrmu_spdinfo))
- + nla_total_size(sizeof(struct xfrmu_spdhinfo));
+ + nla_total_size(sizeof(struct xfrmu_spdhinfo))
+ + nla_total_size(sizeof(struct xfrmu_spdhthresh))
+ + nla_total_size(sizeof(struct xfrmu_spdhthresh));
}
static int build_spdinfo(struct sk_buff *skb, struct net *net,
@@ -973,9 +975,11 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net,
struct xfrmk_spdinfo si;
struct xfrmu_spdinfo spc;
struct xfrmu_spdhinfo sph;
+ struct xfrmu_spdhthresh spt4, spt6;
struct nlmsghdr *nlh;
int err;
u32 *f;
+ unsigned lseq;
nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0);
if (nlh == NULL) /* shouldn't really happen ... */
@@ -993,9 +997,22 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net,
sph.spdhcnt = si.spdhcnt;
sph.spdhmcnt = si.spdhmcnt;
+ do {
+ lseq = read_seqbegin(&net->xfrm.policy_hthresh.lock);
+
+ spt4.lbits = net->xfrm.policy_hthresh.lbits4;
+ spt4.rbits = net->xfrm.policy_hthresh.rbits4;
+ spt6.lbits = net->xfrm.policy_hthresh.lbits6;
+ spt6.rbits = net->xfrm.policy_hthresh.rbits6;
+ } while (read_seqretry(&net->xfrm.policy_hthresh.lock, lseq));
+
err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc);
if (!err)
err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph);
+ if (!err)
+ err = nla_put(skb, XFRMA_SPD_IPV4_HTHRESH, sizeof(spt4), &spt4);
+ if (!err)
+ err = nla_put(skb, XFRMA_SPD_IPV6_HTHRESH, sizeof(spt6), &spt6);
if (err) {
nlmsg_cancel(skb, nlh);
return err;
@@ -1004,6 +1021,51 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net,
return nlmsg_end(skb, nlh);
}
+static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct nlattr **attrs)
+{
+ struct net *net = sock_net(skb->sk);
+ struct xfrmu_spdhthresh *thresh4 = NULL;
+ struct xfrmu_spdhthresh *thresh6 = NULL;
+
+ /* selector prefixlen thresholds to hash policies */
+ if (attrs[XFRMA_SPD_IPV4_HTHRESH]) {
+ struct nlattr *rta = attrs[XFRMA_SPD_IPV4_HTHRESH];
+
+ if (nla_len(rta) < sizeof(*thresh4))
+ return -EINVAL;
+ thresh4 = nla_data(rta);
+ if (thresh4->lbits > 32 || thresh4->rbits > 32)
+ return -EINVAL;
+ }
+ if (attrs[XFRMA_SPD_IPV6_HTHRESH]) {
+ struct nlattr *rta = attrs[XFRMA_SPD_IPV6_HTHRESH];
+
+ if (nla_len(rta) < sizeof(*thresh6))
+ return -EINVAL;
+ thresh6 = nla_data(rta);
+ if (thresh6->lbits > 128 || thresh6->rbits > 128)
+ return -EINVAL;
+ }
+
+ if (thresh4 || thresh6) {
+ write_seqlock(&net->xfrm.policy_hthresh.lock);
+ if (thresh4) {
+ net->xfrm.policy_hthresh.lbits4 = thresh4->lbits;
+ net->xfrm.policy_hthresh.rbits4 = thresh4->rbits;
+ }
+ if (thresh6) {
+ net->xfrm.policy_hthresh.lbits6 = thresh6->lbits;
+ net->xfrm.policy_hthresh.rbits6 = thresh6->rbits;
+ }
+ write_sequnlock(&net->xfrm.policy_hthresh.lock);
+
+ xfrm_policy_hash_rebuild(net);
+ }
+
+ return 0;
+}
+
static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
@@ -2274,6 +2336,7 @@ static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
[XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report),
[XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
[XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32),
+ [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
};
@@ -2308,10 +2371,17 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
[XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) },
};
+static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
+ [XFRMA_SPD_IPV4_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) },
+ [XFRMA_SPD_IPV6_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) },
+};
+
static const struct xfrm_link {
int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **);
int (*dump)(struct sk_buff *, struct netlink_callback *);
int (*done)(struct netlink_callback *);
+ const struct nla_policy *nla_pol;
+ int nla_max;
} xfrm_dispatch[XFRM_NR_MSGTYPES] = {
[XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa },
[XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa },
@@ -2335,6 +2405,9 @@ static const struct xfrm_link {
[XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae },
[XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate },
[XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo },
+ [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_set_spdinfo,
+ .nla_pol = xfrma_spd_policy,
+ .nla_max = XFRMA_SPD_MAX },
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo },
};
@@ -2371,8 +2444,9 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
}
}
- err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX,
- xfrma_policy);
+ err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs,
+ link->nla_max ? : XFRMA_MAX,
+ link->nla_pol ? : xfrma_policy);
if (err < 0)
return err;
--
1.9.1
^ permalink raw reply related
* [ipsec-next v4 1/2] xfrm: hash prefixed policies based on preflen thresholds
From: Christophe Gouault @ 2014-08-29 14:16 UTC (permalink / raw)
To: Steffen Klassert; +Cc: David S. Miller, netdev, Christophe Gouault
In-Reply-To: <1409321765-2012-1-git-send-email-christophe.gouault@6wind.com>
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
---
include/net/netns/xfrm.h | 4 +++
net/xfrm/xfrm_hash.h | 76 +++++++++++++++++++++++++++++++++++++++++-------
net/xfrm/xfrm_policy.c | 53 +++++++++++++++++++++++++++++----
3 files changed, 117 insertions(+), 16 deletions(-)
diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h
index 3492434..41902a8 100644
--- a/include/net/netns/xfrm.h
+++ b/include/net/netns/xfrm.h
@@ -13,6 +13,10 @@ struct ctl_table_header;
struct xfrm_policy_hash {
struct hlist_head *table;
unsigned int hmask;
+ u8 dbits4;
+ u8 sbits4;
+ u8 dbits6;
+ u8 sbits6;
};
struct netns_xfrm {
diff --git a/net/xfrm/xfrm_hash.h b/net/xfrm/xfrm_hash.h
index 0622d31..666c5ff 100644
--- a/net/xfrm/xfrm_hash.h
+++ b/net/xfrm/xfrm_hash.h
@@ -3,6 +3,7 @@
#include <linux/xfrm.h>
#include <linux/socket.h>
+#include <linux/jhash.h>
static inline unsigned int __xfrm4_addr_hash(const xfrm_address_t *addr)
{
@@ -28,6 +29,58 @@ static inline unsigned int __xfrm6_daddr_saddr_hash(const xfrm_address_t *daddr,
saddr->a6[2] ^ saddr->a6[3]);
}
+static inline u32 __bits2mask32(__u8 bits)
+{
+ u32 mask32 = 0xffffffff;
+
+ if (bits == 0)
+ mask32 = 0;
+ else if (bits < 32)
+ mask32 <<= (32 - bits);
+
+ return mask32;
+}
+
+static inline unsigned int __xfrm4_dpref_spref_hash(const xfrm_address_t *daddr,
+ const xfrm_address_t *saddr,
+ __u8 dbits,
+ __u8 sbits)
+{
+ return jhash_2words(ntohl(daddr->a4) & __bits2mask32(dbits),
+ ntohl(saddr->a4) & __bits2mask32(sbits),
+ 0);
+}
+
+static inline unsigned int __xfrm6_pref_hash(const xfrm_address_t *addr,
+ __u8 prefixlen)
+{
+ int pdw;
+ int pbi;
+ u32 initval = 0;
+
+ pdw = prefixlen >> 5; /* num of whole u32 in prefix */
+ pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
+
+ if (pbi) {
+ __be32 mask;
+
+ mask = htonl((0xffffffff) << (32 - pbi));
+
+ initval = (__force u32)(addr->a6[pdw] & mask);
+ }
+
+ return jhash2((__force u32 *)addr->a6, pdw, initval);
+}
+
+static inline unsigned int __xfrm6_dpref_spref_hash(const xfrm_address_t *daddr,
+ const xfrm_address_t *saddr,
+ __u8 dbits,
+ __u8 sbits)
+{
+ return __xfrm6_pref_hash(daddr, dbits) ^
+ __xfrm6_pref_hash(saddr, sbits);
+}
+
static inline unsigned int __xfrm_dst_hash(const xfrm_address_t *daddr,
const xfrm_address_t *saddr,
u32 reqid, unsigned short family,
@@ -84,7 +137,8 @@ static inline unsigned int __idx_hash(u32 index, unsigned int hmask)
}
static inline unsigned int __sel_hash(const struct xfrm_selector *sel,
- unsigned short family, unsigned int hmask)
+ unsigned short family, unsigned int hmask,
+ u8 dbits, u8 sbits)
{
const xfrm_address_t *daddr = &sel->daddr;
const xfrm_address_t *saddr = &sel->saddr;
@@ -92,19 +146,19 @@ static inline unsigned int __sel_hash(const struct xfrm_selector *sel,
switch (family) {
case AF_INET:
- if (sel->prefixlen_d != 32 ||
- sel->prefixlen_s != 32)
+ if (sel->prefixlen_d < dbits ||
+ sel->prefixlen_s < sbits)
return hmask + 1;
- h = __xfrm4_daddr_saddr_hash(daddr, saddr);
+ h = __xfrm4_dpref_spref_hash(daddr, saddr, dbits, sbits);
break;
case AF_INET6:
- if (sel->prefixlen_d != 128 ||
- sel->prefixlen_s != 128)
+ if (sel->prefixlen_d < dbits ||
+ sel->prefixlen_s < sbits)
return hmask + 1;
- h = __xfrm6_daddr_saddr_hash(daddr, saddr);
+ h = __xfrm6_dpref_spref_hash(daddr, saddr, dbits, sbits);
break;
}
h ^= (h >> 16);
@@ -113,17 +167,19 @@ static inline unsigned int __sel_hash(const struct xfrm_selector *sel,
static inline unsigned int __addr_hash(const xfrm_address_t *daddr,
const xfrm_address_t *saddr,
- unsigned short family, unsigned int hmask)
+ unsigned short family,
+ unsigned int hmask,
+ u8 dbits, u8 sbits)
{
unsigned int h = 0;
switch (family) {
case AF_INET:
- h = __xfrm4_daddr_saddr_hash(daddr, saddr);
+ h = __xfrm4_dpref_spref_hash(daddr, saddr, dbits, sbits);
break;
case AF_INET6:
- h = __xfrm6_daddr_saddr_hash(daddr, saddr);
+ h = __xfrm6_dpref_spref_hash(daddr, saddr, dbits, sbits);
break;
}
h ^= (h >> 16);
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index beeed60..e6ff7b4 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -344,12 +344,39 @@ static inline unsigned int idx_hash(struct net *net, u32 index)
return __idx_hash(index, net->xfrm.policy_idx_hmask);
}
+/* calculate policy hash thresholds */
+static void __get_hash_thresh(struct net *net,
+ unsigned short family, int dir,
+ u8 *dbits, u8 *sbits)
+{
+ switch (family) {
+ case AF_INET:
+ *dbits = net->xfrm.policy_bydst[dir].dbits4;
+ *sbits = net->xfrm.policy_bydst[dir].sbits4;
+ break;
+
+ case AF_INET6:
+ *dbits = net->xfrm.policy_bydst[dir].dbits6;
+ *sbits = net->xfrm.policy_bydst[dir].sbits6;
+ break;
+
+ default:
+ *dbits = 0;
+ *sbits = 0;
+ }
+}
+
static struct hlist_head *policy_hash_bysel(struct net *net,
const struct xfrm_selector *sel,
unsigned short family, int dir)
{
unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
- unsigned int hash = __sel_hash(sel, family, hmask);
+ unsigned int hash;
+ u8 dbits;
+ u8 sbits;
+
+ __get_hash_thresh(net, family, dir, &dbits, &sbits);
+ hash = __sel_hash(sel, family, hmask, dbits, sbits);
return (hash == hmask + 1 ?
&net->xfrm.policy_inexact[dir] :
@@ -362,25 +389,35 @@ static struct hlist_head *policy_hash_direct(struct net *net,
unsigned short family, int dir)
{
unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
- unsigned int hash = __addr_hash(daddr, saddr, family, hmask);
+ unsigned int hash;
+ u8 dbits;
+ u8 sbits;
+
+ __get_hash_thresh(net, family, dir, &dbits, &sbits);
+ hash = __addr_hash(daddr, saddr, family, hmask, dbits, sbits);
return net->xfrm.policy_bydst[dir].table + hash;
}
-static void xfrm_dst_hash_transfer(struct hlist_head *list,
+static void xfrm_dst_hash_transfer(struct net *net,
+ struct hlist_head *list,
struct hlist_head *ndsttable,
- unsigned int nhashmask)
+ unsigned int nhashmask,
+ int dir)
{
struct hlist_node *tmp, *entry0 = NULL;
struct xfrm_policy *pol;
unsigned int h0 = 0;
+ u8 dbits;
+ u8 sbits;
redo:
hlist_for_each_entry_safe(pol, tmp, list, bydst) {
unsigned int h;
+ __get_hash_thresh(net, pol->family, dir, &dbits, &sbits);
h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr,
- pol->family, nhashmask);
+ pol->family, nhashmask, dbits, sbits);
if (!entry0) {
hlist_del(&pol->bydst);
hlist_add_head(&pol->bydst, ndsttable+h);
@@ -434,7 +471,7 @@ static void xfrm_bydst_resize(struct net *net, int dir)
write_lock_bh(&net->xfrm.xfrm_policy_lock);
for (i = hmask; i >= 0; i--)
- xfrm_dst_hash_transfer(odst + i, ndst, nhashmask);
+ xfrm_dst_hash_transfer(net, odst + i, ndst, nhashmask, dir);
net->xfrm.policy_bydst[dir].table = ndst;
net->xfrm.policy_bydst[dir].hmask = nhashmask;
@@ -2830,6 +2867,10 @@ static int __net_init xfrm_policy_init(struct net *net)
if (!htab->table)
goto out_bydst;
htab->hmask = hmask;
+ htab->dbits4 = 32;
+ htab->sbits4 = 32;
+ htab->dbits6 = 128;
+ htab->sbits6 = 128;
}
INIT_LIST_HEAD(&net->xfrm.policy_all);
--
1.9.1
^ permalink raw reply related
* [ipsec-next v4 0/2] xfrm: scalability enhancements for policy database
From: Christophe Gouault @ 2014-08-29 14:16 UTC (permalink / raw)
To: Steffen Klassert; +Cc: David S. Miller, netdev
In-Reply-To: <20140829095431.GF6390@secunet.com>
This patchset enables to hash more policies than just non-prefixed
ones: hash policies whose prefix lengths are greater or equal to
configurable thresholds.
These thresholds are configured via netlink message
XFRM_MSG_NEWSPDINFO, attributes XFRMA_SPD_IPV4_HTHRESH and
XFRMA_SPD_IPV6_HTHRESH.
The related iproute2 patch for configuring the thresholds is available
on demand.
Best Regards,
Christophe
----
v2:
- change configuration API from proc to netlink
v3:
- initialize xfrm_policy_hthresh lock
- remove "rebuilding SPD hash table" log
- replace deprecated hlist_add_after by hlist_add_behind
- remove netlink reply to XFRM_MSG_NEWSPDINFO request
v4:
- remove unused variables in xfrm_set_spdinfo
---
include/net/netns/xfrm.h | 14 +++++++
include/net/xfrm.h | 1 +
include/uapi/linux/xfrm.h | 7 ++++
net/xfrm/xfrm_hash.h | 76 +++++++++++++++++++++++++++++++-----
net/xfrm/xfrm_policy.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
net/xfrm/xfrm_user.c | 80 ++++++++++++++++++++++++++++++++++++--
6 files changed, 299 insertions(+), 19 deletions(-)
^ permalink raw reply
* Business Proposal.
From: Edward Lampl @ 2014-08-29 12:04 UTC (permalink / raw)
Good Day,
My name is Edward Lampl, i have gone through your profile and found out
that you are capable of
handling this project, I work with an INTESTATE & HEIR ESTATE BENEFICIARY
TRACING company in united states.
I have an important business proposal that would be of mutual benefit to
us, all I need is your TRUST and CONFIDENCE. It is risk free as long as
you would follow every instruction.
If interested, contact me on: edward@admin.in.th
Regards,
Ed Lampl
^ permalink raw reply
* [PATCH 5/5] ARM: dts: berlin: enable the Ethernet port on the BG2Q DMP
From: Antoine Tenart @ 2014-08-29 13:51 UTC (permalink / raw)
To: sebastian.hesselbarth, thomas.petazzoni
Cc: Antoine Tenart, alexandre.belloni, zmxu, jszhang, netdev,
linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <1409320263-10295-1-git-send-email-antoine.tenart@free-electrons.com>
This patch enables the Ethernet port on the Marvell Berlin2Q DMP board.
The Ethernet PHY node is also added.
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
arch/arm/boot/dts/berlin2q-marvell-dmp.dts | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm/boot/dts/berlin2q-marvell-dmp.dts b/arch/arm/boot/dts/berlin2q-marvell-dmp.dts
index a357ce02a64e..1826534ffce3 100644
--- a/arch/arm/boot/dts/berlin2q-marvell-dmp.dts
+++ b/arch/arm/boot/dts/berlin2q-marvell-dmp.dts
@@ -45,3 +45,16 @@
&uart0 {
status = "okay";
};
+
+ð0 {
+ phy-handle = <ðphy0>;
+ status = "okay";
+};
+
+&mdio {
+ status = "okay";
+
+ ethphy0: ethernet-phy@0 {
+ reg = <0>;
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH 4/5] ARM: dts: berlin: add ethernet and mdio nodes
From: Antoine Tenart @ 2014-08-29 13:51 UTC (permalink / raw)
To: sebastian.hesselbarth, thomas.petazzoni
Cc: Antoine Tenart, alexandre.belloni, zmxu, jszhang, netdev,
linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <1409320263-10295-1-git-send-email-antoine.tenart@free-electrons.com>
This patch adds the mdio and the Ethernet nodes, enabling the network
unit on Berlin BG2Q SoCs.
The mac address is set to 00:00:00:00:00:00 so that the one set by the
bootloader is used. The PHY nodes are in the board device tree.
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
arch/arm/boot/dts/berlin2q.dtsi | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm/boot/dts/berlin2q.dtsi b/arch/arm/boot/dts/berlin2q.dtsi
index 400c40fceccc..bb84a3d3740f 100644
--- a/arch/arm/boot/dts/berlin2q.dtsi
+++ b/arch/arm/boot/dts/berlin2q.dtsi
@@ -114,6 +114,25 @@
#interrupt-cells = <3>;
};
+ eth0: ethernet@b90000 {
+ compatible = "marvell,berlin-eth";
+ reg = <0xb90000 0x10000>;
+ clocks = <&chip CLKID_GETH0>;
+ interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
+ /* set by bootloader */
+ local-mac-address = [00 00 00 00 00 00];
+ status = "disabled";
+ };
+
+ mdio: mdio-bus@b90010 {
+ compatible = "marvell,orion-mdio";
+ reg = <0xb90010 0x84>;
+ clocks = <&chip CLKID_GETH0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
cpu-ctrl@dd0000 {
compatible = "marvell,berlin-cpu-ctrl";
reg = <0xdd0000 0x10000>;
--
1.9.1
^ permalink raw reply related
* [PATCH 3/5] Documentation: devicetree: net: mention Marvell Berlin
From: Antoine Tenart @ 2014-08-29 13:51 UTC (permalink / raw)
To: sebastian.hesselbarth, thomas.petazzoni
Cc: Antoine Tenart, alexandre.belloni, zmxu, jszhang, netdev,
linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <1409320263-10295-1-git-send-email-antoine.tenart@free-electrons.com>
Marvell Berlin SoCs have a MDIO interface for their Ethernet
controllers. Mention Berlin in the SoC list.
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
Documentation/devicetree/bindings/net/marvell-orion-mdio.txt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
index 9417e54c26c0..4580c2256039 100644
--- a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
+++ b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
@@ -1,9 +1,9 @@
* Marvell MDIO Ethernet Controller interface
The Ethernet controllers of the Marvel Kirkwood, Dove, Orion5x,
-MV78xx0, Armada 370 and Armada XP have an identical unit that provides
-an interface with the MDIO bus. This driver handles this MDIO
-interface.
+MV78xx0, Armada 370, Armada XP and Berlin have an identical unit
+that provides an interface with the MDIO bus. This driver handles
+this MDIO interface.
Required properties:
- compatible: "marvell,orion-mdio"
--
1.9.1
^ permalink raw reply related
* [PATCH 2/5] Documentation: bindings: net: add the Marvell Berlin Ethernet controller
From: Antoine Tenart @ 2014-08-29 13:51 UTC (permalink / raw)
To: sebastian.hesselbarth, thomas.petazzoni
Cc: zmxu, jszhang, devicetree, netdev, Antoine Tenart, linux-kernel,
alexandre.belloni, linux-arm-kernel
In-Reply-To: <1409320263-10295-1-git-send-email-antoine.tenart@free-electrons.com>
This adds the binding documentation for the Marvell Berlin Ethernet
controller.
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
.../devicetree/bindings/net/marvell-berlin.txt | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/marvell-berlin.txt
diff --git a/Documentation/devicetree/bindings/net/marvell-berlin.txt b/Documentation/devicetree/bindings/net/marvell-berlin.txt
new file mode 100644
index 000000000000..0ae468aef13f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/marvell-berlin.txt
@@ -0,0 +1,23 @@
+* Marvell Berlin Ethernet Controller
+
+Required properties:
+- compatible: should be "marvell,berlin-eth".
+- reg: address and length of the register set for the device.
+- interrupts: interrupt for the device.
+- phy-handle: pointer to the Ethernet PHY.
+- clocks: pointer to the clock for the device.
+
+Optional properties:
+- local-mac-address: see ethernet.txt file in the same directory.
+
+
+Example:
+
+ eth0: ethernet@f7b90000 {
+ compatible = "marvell,berlin-eth";
+ reg = <0xf7b90000 0x10000>;
+ clocks = <&chip CLKID_GETH0>;
+ interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
+ phy-handle = <ðphy0>;
+ local-mac-address = [00 00 00 00 00 00];
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH 1/5] net: mvberlin_eth: add an Ethernet driver for Marvell Berlin
From: Antoine Tenart @ 2014-08-29 13:50 UTC (permalink / raw)
To: sebastian.hesselbarth, thomas.petazzoni
Cc: zmxu, jszhang, devicetree, netdev, Antoine Tenart, linux-kernel,
alexandre.belloni, linux-arm-kernel
In-Reply-To: <1409320263-10295-1-git-send-email-antoine.tenart@free-electrons.com>
This patch introduces the Marvell Berlin network unit driver, which uses
the MVMDIO interface to communicate to the PHY. This is a fast Ethernet
driver.
This driver is highly based on the mv643xx_eth driver, and reuse some of
its functions. But lots of differences are there:
- They do not have the same registers.
- The mvberlin_eth driver only supports fast Ethernet.
- The rx/tx handling functions logic is different.
- The mv643xx_eth driver supports TSO.
- The mvberlin_eth driver uses a hash table to filter incoming packets.
No packet is dropped during a ping flood (ping -f) and an iperf test
shows:
------------------------------------------------------------
Client connecting to 192.168.0.11, TCP port 5001
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.0.20 port 44183 connected with 192.168.0.11 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 113 MBytes 94.8 Mbits/sec
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
drivers/net/ethernet/marvell/Kconfig | 9 +
drivers/net/ethernet/marvell/Makefile | 1 +
drivers/net/ethernet/marvell/mvberlin_eth.c | 2081 +++++++++++++++++++++++++++
3 files changed, 2091 insertions(+)
create mode 100644 drivers/net/ethernet/marvell/mvberlin_eth.c
diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index 1b4fc7c639e6..a4f12257d099 100644
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -18,6 +18,15 @@ config NET_VENDOR_MARVELL
if NET_VENDOR_MARVELL
+config MVBERLIN_ETH
+ tristate "Marvell Berlin ethernet support"
+ depends on ARCH_BERLIN && INET
+ select PHYLIB
+ select MVMDIO
+ ---help---
+ This driver supports the ethernet interface of the Marvell
+ Berlin SoCs.
+
config MV643XX_ETH
tristate "Marvell Discovery (643XX) and Orion ethernet support"
depends on (MV64X60 || PPC32 || PLAT_ORION) && INET
diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile
index f6425bd2884b..a802dba2503e 100644
--- a/drivers/net/ethernet/marvell/Makefile
+++ b/drivers/net/ethernet/marvell/Makefile
@@ -2,6 +2,7 @@
# Makefile for the Marvell device drivers.
#
+obj-$(CONFIG_MVBERLIN_ETH) += mvberlin_eth.o
obj-$(CONFIG_MVMDIO) += mvmdio.o
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
obj-$(CONFIG_MVNETA) += mvneta.o
diff --git a/drivers/net/ethernet/marvell/mvberlin_eth.c b/drivers/net/ethernet/marvell/mvberlin_eth.c
new file mode 100644
index 000000000000..5f1874b58017
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvberlin_eth.c
@@ -0,0 +1,2081 @@
+/*
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Antoine Tenart <antoine.tenart@free-electrons.com>
+ * Jisheng Zhang <jszhang@marvell.com>
+ *
+ * Based on the driver for Marvell Discovery (MV643XX) and Marvell Orion
+ * ethernet ports
+ * Copyright (C) 2002 Matthew Dharm <mdharm@momenco.com>
+ *
+ * Based on the 64360 driver from:
+ * Copyright (C) 2002 Rabeeh Khoury <rabeeh@galileo.co.il>
+ * Rabeeh Khoury <rabeeh@marvell.com>
+ *
+ * Copyright (C) 2003 PMC-Sierra, Inc.,
+ * written by Manish Lachwani
+ *
+ * Copyright (C) 2003 Ralf Baechle <ralf@linux-mips.org>
+ *
+ * Copyright (C) 2004-2006 MontaVista Software, Inc.
+ * Dale Farnsworth <dale@farnsworth.org>
+ *
+ * Copyright (C) 2004 Steven J. Hill <sjhill1@rockwellcollins.com>
+ * <sjhill@realitydiluted.com>
+ *
+ * Copyright (C) 2007-2008 Marvell Semiconductor
+ * Lennert Buytenhek <buytenh@marvell.com>
+ *
+ * Copyright (C) 2013 Michael Stapelberg <michael@stapelberg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/in.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mv643xx_eth.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/udp.h>
+#include <linux/workqueue.h>
+
+static const char mvberlin_eth_driver_name[] = "mvberlin_eth";
+static const char mvberlin_eth_driver_version[] = "1.0";
+
+/* Main per-port registers. These live at offset 0x0400 */
+#define PORT_CONFIG 0x0000
+#define PROMISCUOUS_MODE 0x00000001
+#define BROADCAST_REJECT_MODE 0x00000002
+#define PORT_ENABLE 0x00000080
+#define HASH_SIZE_HALF_K 0x00001000
+#define HASH_FUNCTION_1 0x00002000
+#define HASH_PASS_MODE 0x00004000
+#define PORT_EXT_CONFIG 0x0008
+#define EXT_IGMP 0x00000001
+#define EXT_SPAN 0x00000002
+#define EXT_FC_AN_DISABLE 0x00000400
+#define EXT_FLP_DISABLE 0x00000800
+#define EXT_FC_ENABLE 0x00000c00
+#define EXT_MRU_ALL_MASK 0x0000c000
+#define EXT_MIB_CLEAR 0x00010000
+#define EXT_DSCP_EN 0x00200000
+#define EXT_MAC_RX_2BSTUFF 0x10000000
+#define HASH_TABLE 0x0028
+#define MAC_ADDR_LOW 0x0030
+#define MAC_ADDR_HIGH 0x0038
+#define SDMA_CONFIG 0x0040
+#define BURST_SIZE_8_64BIT 0x00003000
+#define BLM_RX_LE 0x00000040
+#define BLM_TX_LE 0x00000080
+#define SET_MII_SPEED_TO_10 0x00000000
+#define SET_MII_SPEED_TO_100 0x00000001
+#define SET_FULL_DUPLEX_MODE 0x00000002
+#define RX_FRAME_INTERRUPT 0x00000200
+#define PORT_STATUS 0x0018
+#define TX_IN_PROGRESS 0x00000080
+#define PORT_SPEED_MASK 0x00000001
+#define PORT_SPEED_100 0x00000001
+#define PORT_SPEED_10 0x00000000
+#define FLOW_CONTROL_ENABLED 0x00000004
+#define FULL_DUPLEX 0x00000002
+#define LINK_UP 0x00000008
+#define INT_CAUSE 0x0050
+#define INT_TX_0 0x00000004
+#define INT_TX 0x0000000c
+#define INT_TX_END 0x000000c0
+#define INT_TX_END_0 0x00000040
+#define INT_RX 0x000f0000
+#define INT_RX_0 0x00010000
+#define INT_EXT 0x10000000
+#define INT_EXT_LINK_PHY 0x00000010
+#define INT_EXT_TX 0x00000080
+#define INT_MASK 0x0058
+#define RXQ_CURRENT_DESC_PTR(q) (0x00a0 + ((q) << 2))
+#define RXQ_FIRST_DESC_PTR(q) (0x0080 + ((q) << 2))
+#define SDMA_COMMAND 0x0048
+#define RX_ENABLE 0x00000080
+#define RX_ABORT 0x00008000
+#define TX_STOP 0x00010000
+#define TX_START 0x00800000
+#define TXQ_CURRENT_DESC_PTR(q) (0x00e0 + ((1-q) << 2))
+#define ETH_EDSCP2P0L 0x0060
+#define ETH_EDSCP2P0H 0x0064
+#define ETH_EDSCP2P1L 0x0068
+#define ETH_EDSCP2P1H 0x006c
+
+#define MRU_1518 0x00000000
+#define MRU_1536 0x00004000
+#define MRU_2048 0x00008000
+#define MRU_64K 0x0000c000
+
+#define HASH_TABLE_ENTRY_VALID 0x00000001
+#define HASH_TABLE_ENTRY_SKIP 0x00000002
+#define HASH_TABLE_SIZE 0x4000
+
+/* Hash function macroes */
+#define NIBBLE_SWAPPING_16_BIT(x) \
+ (((x & 0xf) << 4) | \
+ ((x & 0xf0) >> 4) | \
+ ((x & 0xf00) << 4) | \
+ ((x & 0xf000) >> 4))
+
+#define NIBBLE_SWAPPING_32_BIT(x) \
+ (((x & 0xf) << 4) | \
+ ((x & 0xf0) >> 4) | \
+ ((x & 0xf00) << 4) | \
+ ((x & 0xf000) >> 4) | \
+ ((x & 0xf0000) << 4) | \
+ ((x & 0xf00000) >> 4) | \
+ ((x & 0xf000000) << 4) | \
+ ((x & 0xf0000000) >> 4))
+
+#define GT_NIBBLE(x) \
+ (((x & 0x1) << 3) + \
+ ((x & 0x2) << 1) + \
+ ((x & 0x4) >> 1) + \
+ ((x & 0x8) >> 3))
+
+/* Misc per-port registers */
+#define MIB_COUNTERS(p) (0x0500 + ((p) << 7))
+
+/* SDMA configuration register default value */
+#if defined(__BIG_ENDIAN)
+#define PORT_SDMA_CONFIG_DEFAULT_VALUE \
+ (BURST_SIZE_8_64BIT | \
+ 0x3c | \
+ RX_FRAME_INTERRUPT)
+#elif defined(__LITTLE_ENDIAN)
+#define PORT_SDMA_CONFIG_DEFAULT_VALUE \
+ (BURST_SIZE_8_64BIT | \
+ BLM_RX_LE | \
+ BLM_TX_LE | \
+ 0x3c | \
+ RX_FRAME_INTERRUPT)
+#else
+#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined
+#endif
+
+/* Misc definitions */
+#define DEFAULT_RX_QUEUE_SIZE 128
+#define DEFAULT_TX_QUEUE_SIZE 512
+#define SKB_DMA_REALIGN ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES)
+
+/* RX/TX descriptors */
+#if defined(__BIG_ENDIAN)
+struct rx_desc {
+ u16 buf_size; /* Buffer size */
+ u16 byte_cnt; /* Descriptor buffer byte count */
+ u32 cmd_sts; /* Command/status field */
+ u32 next_desc_ptr; /* Next descriptor pointer */
+ u32 buf_ptr; /* Descriptor buffer pointer */
+};
+
+struct tx_desc {
+ u16 byte_cnt; /* buffer byte count */
+ u16 l4i_chk; /* CPU provided TCP checksum */
+ u32 cmd_sts; /* Command/status field */
+ u32 next_desc_ptr; /* Pointer to next descriptor */
+ u32 buf_ptr; /* pointer to buffer for this descriptor*/
+};
+#elif defined(__LITTLE_ENDIAN)
+struct rx_desc {
+ u32 cmd_sts; /* Descriptor command status */
+ u16 byte_cnt; /* Descriptor buffer byte count */
+ u16 buf_size; /* Buffer size */
+ u32 buf_ptr; /* Descriptor buffer pointer */
+ u32 next_desc_ptr; /* Next descriptor pointer */
+};
+
+struct tx_desc {
+ u32 cmd_sts; /* Command/status field */
+ u16 l4i_chk; /* CPU provided TCP checksum */
+ u16 byte_cnt; /* buffer byte count */
+ u32 buf_ptr; /* pointer to buffer for this descriptor*/
+ u32 next_desc_ptr; /* Pointer to next descriptor */
+};
+#else
+#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined
+#endif
+
+/* RX & TX descriptor command */
+#define BUFFER_OWNED_BY_DMA 0x80000000
+
+/* RX & TX descriptor status */
+#define ERROR_SUMMARY 0x00008000
+
+/* RX descriptor status */
+#define RX_ENABLE_INTERRUPT 0x00800000
+#define RX_FIRST_DESC 0x00020000
+#define RX_LAST_DESC 0x00010000
+
+/* TX descriptor command */
+#define TX_ENABLE_INTERRUPT 0x00800000
+#define GEN_CRC 0x00400000
+#define TX_FIRST_DESC 0x00020000
+#define TX_LAST_DESC 0x00010000
+#define ZERO_PADDING 0x00040000
+
+/* global *******************************************************************/
+struct mvberlin_eth_shared_private {
+ /* Ethernet controller base address */
+ void __iomem *base;
+
+ /* Per-port MBUS window access register value */
+ u32 win_protect;
+
+ /* Hardware-specific parameters */
+ int extended_rx_coal_limit;
+ int tx_bw_control;
+ int tx_csum_limit;
+ struct clk *clk;
+};
+
+static int mvberlin_eth_open(struct net_device *dev);
+static int mvberlin_eth_stop(struct net_device *dev);
+
+/* per-port *****************************************************************/
+struct rx_queue {
+ int index;
+
+ int rx_ring_size;
+
+ int rx_desc_count;
+ int rx_curr_desc;
+ int rx_used_desc;
+
+ struct rx_desc *rx_desc_area;
+ dma_addr_t rx_desc_dma;
+ int rx_desc_area_size;
+ struct sk_buff **rx_skb;
+};
+
+struct tx_queue {
+ int index;
+
+ int tx_ring_size;
+
+ int tx_desc_count;
+ int tx_curr_desc;
+ int tx_used_desc;
+
+ int tx_stop_threshold;
+ int tx_wake_threshold;
+
+ struct tx_desc *tx_desc_area;
+ dma_addr_t tx_desc_dma;
+ int tx_desc_area_size;
+
+ struct sk_buff_head tx_skb;
+
+ unsigned long tx_packets;
+ unsigned long tx_bytes;
+ unsigned long tx_dropped;
+};
+
+struct mvberlin_eth_private {
+ struct mvberlin_eth_shared_private *shared;
+ void __iomem *base;
+ int port_num;
+
+ struct net_device *dev;
+
+ struct phy_device *phy;
+
+ struct work_struct tx_timeout_task;
+
+ struct napi_struct napi;
+ u32 int_mask;
+ u8 oom;
+ u8 work_rx_refill;
+
+ int skb_size;
+
+ /* RX state */
+ int rx_ring_size;
+ unsigned long rx_desc_sram_addr;
+ int rx_desc_sram_size;
+ int rxq_count;
+ struct timer_list rx_oom;
+ struct rx_queue rxq[8];
+
+ /* TX state */
+ int tx_ring_size;
+ unsigned long tx_desc_sram_addr;
+ int tx_desc_sram_size;
+ int txq_count;
+ struct tx_queue txq[8];
+
+ /* Hardware-specific parameters */
+ struct clk *clk;
+ unsigned int t_clk;
+ void *hash_tbl;
+ dma_addr_t hash_dma;
+};
+
+/* port register accessors **************************************************/
+static inline u32 rdl(struct mvberlin_eth_private *mp, int offset)
+{
+ return readl(mp->shared->base + offset);
+}
+
+static inline u32 rdlp(struct mvberlin_eth_private *mp, int offset)
+{
+ return readl(mp->base + offset);
+}
+
+static inline void wrl(struct mvberlin_eth_private *mp, int offset, u32 data)
+{
+ writel(data, mp->shared->base + offset);
+}
+
+static inline void wrlp(struct mvberlin_eth_private *mp, int offset, u32 data)
+{
+ writel(data, mp->base + offset);
+}
+
+/* rxq/txq helper functions *************************************************/
+static struct mvberlin_eth_private *rxq_to_mp(struct rx_queue *rxq)
+{
+ return container_of(rxq, struct mvberlin_eth_private, rxq[rxq->index]);
+}
+
+static struct mvberlin_eth_private *txq_to_mp(struct tx_queue *txq)
+{
+ return container_of(txq, struct mvberlin_eth_private, txq[txq->index]);
+}
+
+static void rxq_enable(struct rx_queue *rxq)
+{
+ struct mvberlin_eth_private *mp = rxq_to_mp(rxq);
+
+ wrlp(mp, SDMA_COMMAND, RX_ENABLE);
+}
+
+static void rxq_disable(struct rx_queue *rxq)
+{
+ struct mvberlin_eth_private *mp = rxq_to_mp(rxq);
+
+ wrlp(mp, SDMA_COMMAND, RX_ABORT);
+ while (rdlp(mp, SDMA_COMMAND) & RX_ABORT)
+ udelay(10);
+}
+
+static void txq_reset_hw_ptr(struct tx_queue *txq)
+{
+ struct mvberlin_eth_private *mp = txq_to_mp(txq);
+ u32 addr;
+
+ addr = (u32)txq->tx_desc_dma;
+ addr += txq->tx_curr_desc * sizeof(struct tx_desc);
+ wrlp(mp, TXQ_CURRENT_DESC_PTR(txq->index), addr);
+}
+
+static void txq_enable(struct tx_queue *txq)
+{
+ struct mvberlin_eth_private *mp = txq_to_mp(txq);
+
+ wrlp(mp, SDMA_COMMAND, TX_START);
+}
+
+static void txq_disable(struct tx_queue *txq)
+{
+ struct mvberlin_eth_private *mp = txq_to_mp(txq);
+
+ wrlp(mp, SDMA_COMMAND, TX_STOP);
+}
+
+static void txq_maybe_wake(struct tx_queue *txq)
+{
+ struct mvberlin_eth_private *mp = txq_to_mp(txq);
+ struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index);
+
+ if (netif_tx_queue_stopped(nq)) {
+ __netif_tx_lock(nq, smp_processor_id());
+ if (txq->tx_desc_count <= txq->tx_wake_threshold)
+ netif_tx_wake_queue(nq);
+ __netif_tx_unlock(nq);
+ }
+}
+
+static int rxq_process(struct rx_queue *rxq, int budget)
+{
+ struct mvberlin_eth_private *mp = rxq_to_mp(rxq);
+ struct net_device_stats *stats = &mp->dev->stats;
+ int rx;
+
+ rx = 0;
+ while (rx < budget && rxq->rx_desc_count) {
+ struct rx_desc *rx_desc;
+ unsigned int cmd_sts;
+ struct sk_buff *skb;
+ u16 byte_cnt;
+
+ rx_desc = &rxq->rx_desc_area[rxq->rx_curr_desc];
+
+ cmd_sts = rx_desc->cmd_sts;
+ if (cmd_sts & BUFFER_OWNED_BY_DMA)
+ break;
+ rmb();
+
+ skb = rxq->rx_skb[rxq->rx_curr_desc];
+ rxq->rx_skb[rxq->rx_curr_desc] = NULL;
+
+ rxq->rx_curr_desc++;
+ if (rxq->rx_curr_desc == rxq->rx_ring_size)
+ rxq->rx_curr_desc = 0;
+
+ dma_unmap_single(mp->dev->dev.parent, rx_desc->buf_ptr,
+ rx_desc->buf_size, DMA_FROM_DEVICE);
+ rxq->rx_desc_count--;
+ rx++;
+
+ mp->work_rx_refill |= 1 << rxq->index;
+
+ byte_cnt = rx_desc->byte_cnt;
+
+ /* Update statistics.
+ *
+ * Note that the descriptor byte count includes 2 dummy
+ * bytes automatically inserted by the hardware at the
+ * start of the packet (which we don't count), and a 4
+ * byte CRC at the end of the packet (which we do count).
+ */
+ stats->rx_packets++;
+ stats->rx_bytes += byte_cnt - 2;
+
+ /* In case we received a packet without first / last bits
+ * on, or the error summary bit is set, the packet needs
+ * to be dropped.
+ */
+ if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC | ERROR_SUMMARY))
+ != (RX_FIRST_DESC | RX_LAST_DESC))
+ goto err;
+
+ /* The -4 is for the CRC in the trailer of the
+ * received packet
+ */
+ skb_put(skb, byte_cnt - 2 - 4);
+
+ skb->protocol = eth_type_trans(skb, mp->dev);
+
+ napi_gro_receive(&mp->napi, skb);
+
+ continue;
+
+err:
+ stats->rx_dropped++;
+
+ if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
+ (RX_FIRST_DESC | RX_LAST_DESC)) {
+ if (net_ratelimit())
+ netdev_err(mp->dev,
+ "received packet spanning multiple descriptors\n");
+ }
+
+ if (cmd_sts & ERROR_SUMMARY)
+ stats->rx_errors++;
+
+ dev_kfree_skb(skb);
+ }
+
+ return rx;
+}
+
+static int rxq_refill(struct rx_queue *rxq, int budget)
+{
+ struct mvberlin_eth_private *mp = rxq_to_mp(rxq);
+ int refilled;
+
+ refilled = 0;
+ while (refilled < budget && rxq->rx_desc_count < rxq->rx_ring_size) {
+ struct sk_buff *skb;
+ int rx;
+ struct rx_desc *rx_desc;
+ int size;
+
+ skb = netdev_alloc_skb(mp->dev, mp->skb_size);
+
+ if (skb == NULL) {
+ mp->oom = 1;
+ goto oom;
+ }
+
+ if (SKB_DMA_REALIGN)
+ skb_reserve(skb, SKB_DMA_REALIGN);
+
+ refilled++;
+ rxq->rx_desc_count++;
+
+ rx = rxq->rx_used_desc++;
+ if (rxq->rx_used_desc == rxq->rx_ring_size)
+ rxq->rx_used_desc = 0;
+
+ rx_desc = rxq->rx_desc_area + rx;
+
+ size = skb_end_pointer(skb) - skb->data;
+ rx_desc->buf_ptr = dma_map_single(mp->dev->dev.parent,
+ skb->data, size,
+ DMA_FROM_DEVICE);
+ rx_desc->buf_size = size;
+ rxq->rx_skb[rx] = skb;
+ wmb();
+ rx_desc->cmd_sts = BUFFER_OWNED_BY_DMA | RX_ENABLE_INTERRUPT;
+ wmb();
+
+ /* The hardware automatically prepends 2 bytes of
+ * dummy data to each received packet, so that the
+ * IP header ends up 16-byte aligned.
+ */
+ skb_reserve(skb, 2);
+ }
+
+ if (refilled < budget)
+ mp->work_rx_refill &= ~(1 << rxq->index);
+
+oom:
+ return refilled;
+}
+
+/* tx ***********************************************************************/
+static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
+{
+ int frag;
+
+ for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
+ const skb_frag_t *fragp = &skb_shinfo(skb)->frags[frag];
+
+ if (skb_frag_size(fragp) <= 8 && fragp->page_offset & 7)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
+{
+ struct mvberlin_eth_private *mp = txq_to_mp(txq);
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+ int frag;
+
+ for (frag = 0; frag < nr_frags; frag++) {
+ skb_frag_t *this_frag;
+ int tx_index;
+ struct tx_desc *desc;
+ void *addr;
+
+ this_frag = &skb_shinfo(skb)->frags[frag];
+ addr = page_address(this_frag->page.p) + this_frag->page_offset;
+ tx_index = txq->tx_curr_desc++;
+ if (txq->tx_curr_desc == txq->tx_ring_size)
+ txq->tx_curr_desc = 0;
+ desc = &txq->tx_desc_area[tx_index];
+
+ /* The last fragment will generate an interrupt
+ * which will free the skb on TX completion.
+ */
+ if (frag == nr_frags - 1) {
+ desc->cmd_sts = BUFFER_OWNED_BY_DMA |
+ ZERO_PADDING | TX_LAST_DESC |
+ TX_ENABLE_INTERRUPT;
+ } else {
+ desc->cmd_sts = BUFFER_OWNED_BY_DMA;
+ }
+
+ desc->l4i_chk = 0;
+ desc->byte_cnt = skb_frag_size(this_frag);
+ desc->buf_ptr = dma_map_single(mp->dev->dev.parent, addr,
+ desc->byte_cnt, DMA_TO_DEVICE);
+ }
+}
+
+static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct mvberlin_eth_private *mp = txq_to_mp(txq);
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+ int tx_index;
+ struct tx_desc *desc;
+ u32 cmd_sts;
+ u16 l4i_chk;
+ int length;
+
+ cmd_sts = 0;
+ l4i_chk = 0;
+
+ if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) {
+ if (net_ratelimit())
+ netdev_err(dev, "tx queue full?!\n");
+ return -EBUSY;
+ }
+
+ cmd_sts |= TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA;
+
+ tx_index = txq->tx_curr_desc++;
+ if (txq->tx_curr_desc == txq->tx_ring_size)
+ txq->tx_curr_desc = 0;
+ desc = &txq->tx_desc_area[tx_index];
+
+ if (nr_frags) {
+ txq_submit_frag_skb(txq, skb);
+ length = skb_headlen(skb);
+ } else {
+ cmd_sts |= ZERO_PADDING | TX_LAST_DESC | TX_ENABLE_INTERRUPT;
+ length = skb->len;
+ }
+
+ desc->l4i_chk = l4i_chk;
+ desc->byte_cnt = length;
+ desc->buf_ptr = dma_map_single(mp->dev->dev.parent, skb->data,
+ length, DMA_TO_DEVICE);
+
+ __skb_queue_tail(&txq->tx_skb, skb);
+
+ skb_tx_timestamp(skb);
+
+ /* ensure all other descriptors are written before first cmd_sts */
+ wmb();
+ desc->cmd_sts = cmd_sts;
+
+ /* ensure all descriptors are written before poking hardware */
+ wmb();
+ txq_enable(txq);
+
+ txq->tx_desc_count += nr_frags + 1;
+
+ return 0;
+}
+
+static netdev_tx_t mvberlin_eth_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ int length, queue;
+ struct tx_queue *txq;
+ struct netdev_queue *nq;
+
+ queue = skb_get_queue_mapping(skb);
+ txq = mp->txq + queue;
+ nq = netdev_get_tx_queue(dev, queue);
+
+ if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) {
+ netdev_printk(KERN_DEBUG, dev,
+ "failed to linearize skb with tiny unaligned fragment\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ length = skb->len;
+
+ if (!txq_submit_skb(txq, skb, dev)) {
+ txq->tx_bytes += length;
+ txq->tx_packets++;
+
+ if (txq->tx_desc_count >= txq->tx_stop_threshold)
+ netif_tx_stop_queue(nq);
+ } else {
+ txq->tx_dropped++;
+ dev_kfree_skb_any(skb);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+/* tx napi ******************************************************************/
+static void txq_kick(struct tx_queue *txq)
+{
+ struct mvberlin_eth_private *mp = txq_to_mp(txq);
+ struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index);
+ u32 hw_desc_ptr;
+ u32 expected_ptr;
+
+ __netif_tx_lock(nq, smp_processor_id());
+
+ if (rdlp(mp, SDMA_COMMAND) & TX_START)
+ goto out;
+
+ hw_desc_ptr = rdlp(mp, TXQ_CURRENT_DESC_PTR(txq->index));
+ expected_ptr = (u32)txq->tx_desc_dma +
+ txq->tx_curr_desc * sizeof(struct tx_desc);
+
+ if (hw_desc_ptr != expected_ptr)
+ txq_enable(txq);
+
+out:
+ __netif_tx_unlock(nq);
+}
+
+static int txq_reclaim(struct tx_queue *txq, int budget, int force)
+{
+ struct mvberlin_eth_private *mp = txq_to_mp(txq);
+ int reclaimed;
+
+ reclaimed = 0;
+ while (reclaimed < budget && txq->tx_desc_count > 0) {
+ int tx_index;
+ struct tx_desc *desc;
+ u32 cmd_sts;
+ struct sk_buff *skb;
+
+ tx_index = txq->tx_used_desc;
+ desc = &txq->tx_desc_area[tx_index];
+ cmd_sts = desc->cmd_sts;
+
+ if (cmd_sts & BUFFER_OWNED_BY_DMA) {
+ if (!force)
+ break;
+ desc->cmd_sts = cmd_sts & ~BUFFER_OWNED_BY_DMA;
+ }
+
+ txq->tx_used_desc = tx_index + 1;
+ if (txq->tx_used_desc == txq->tx_ring_size)
+ txq->tx_used_desc = 0;
+
+ reclaimed++;
+ txq->tx_desc_count--;
+
+ skb = NULL;
+ if (cmd_sts & TX_LAST_DESC)
+ skb = __skb_dequeue(&txq->tx_skb);
+
+ if (cmd_sts & ERROR_SUMMARY) {
+ netdev_info(mp->dev, "tx error\n");
+ mp->dev->stats.tx_errors++;
+ }
+
+ dev_kfree_skb_irq(skb);
+ }
+
+ return reclaimed;
+}
+
+/* mii management interface *************************************************/
+static void mvberlin_eth_adjust_link(struct net_device *dev)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ u32 pscr = rdlp(mp, PORT_CONFIG);
+ u32 autoneg_disable = HASH_PASS_MODE;
+
+ if (mp->phy->autoneg == AUTONEG_ENABLE) {
+ /* enable auto negotiation */
+ pscr &= ~autoneg_disable;
+ goto out_write;
+ }
+
+ pscr |= autoneg_disable;
+
+ if (mp->phy->speed == SPEED_100)
+ pscr |= SET_MII_SPEED_TO_100;
+ else
+ pscr &= ~SET_MII_SPEED_TO_100;
+
+ if (mp->phy->duplex == DUPLEX_FULL)
+ pscr |= SET_FULL_DUPLEX_MODE;
+ else
+ pscr &= ~SET_FULL_DUPLEX_MODE;
+
+out_write:
+ wrlp(mp, PORT_CONFIG, pscr);
+}
+
+/* statistics ***************************************************************/
+static struct net_device_stats *mvberlin_eth_get_stats(struct net_device *dev)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long tx_packets = 0;
+ unsigned long tx_bytes = 0;
+ unsigned long tx_dropped = 0;
+ int i;
+
+ for (i = 0; i < mp->txq_count; i++) {
+ struct tx_queue *txq = mp->txq + i;
+
+ tx_packets += txq->tx_packets;
+ tx_bytes += txq->tx_bytes;
+ tx_dropped += txq->tx_dropped;
+ }
+
+ stats->tx_packets = tx_packets;
+ stats->tx_bytes = tx_bytes;
+ stats->tx_dropped = tx_dropped;
+
+ return stats;
+}
+
+static inline u32 mib_read(struct mvberlin_eth_private *mp, int offset)
+{
+ return rdl(mp, MIB_COUNTERS(mp->port_num) + offset);
+}
+
+/* ethtool ******************************************************************/
+struct mvberlin_eth_stats {
+ char stat_string[ETH_GSTRING_LEN];
+ int sizeof_stat;
+ int netdev_off;
+ int mp_off;
+};
+
+#define SSTAT(m) \
+ { #m, FIELD_SIZEOF(struct net_device_stats, m), \
+ offsetof(struct net_device, stats.m), -1 }
+
+static const struct mvberlin_eth_stats mvberlin_eth_stats[] = {
+ SSTAT(rx_packets),
+ SSTAT(tx_packets),
+ SSTAT(rx_bytes),
+ SSTAT(tx_bytes),
+ SSTAT(rx_errors),
+ SSTAT(tx_errors),
+ SSTAT(rx_dropped),
+ SSTAT(tx_dropped),
+};
+
+static int
+mvberlin_eth_get_settings_phy(struct mvberlin_eth_private *mp,
+ struct ethtool_cmd *cmd)
+{
+ int err;
+
+ err = phy_read_status(mp->phy);
+ if (err == 0)
+ err = phy_ethtool_gset(mp->phy, cmd);
+
+ return err;
+}
+
+static int
+mvberlin_eth_get_settings_phyless(struct mvberlin_eth_private *mp,
+ struct ethtool_cmd *cmd)
+{
+ u32 port_status;
+
+ port_status = rdlp(mp, PORT_STATUS);
+
+ cmd->supported = SUPPORTED_MII;
+ cmd->advertising = ADVERTISED_MII;
+
+ switch (port_status & PORT_SPEED_MASK) {
+ case PORT_SPEED_10:
+ ethtool_cmd_speed_set(cmd, SPEED_10);
+ break;
+ case PORT_SPEED_100:
+ ethtool_cmd_speed_set(cmd, SPEED_100);
+ break;
+ default:
+ cmd->speed = -1;
+ break;
+ }
+
+ cmd->duplex = (port_status & FULL_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
+ cmd->port = PORT_MII;
+ cmd->phy_address = 0;
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->autoneg = AUTONEG_DISABLE;
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+
+ return 0;
+}
+
+static void
+mvberlin_eth_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+
+ wol->supported = 0;
+ wol->wolopts = 0;
+ if (mp->phy)
+ phy_ethtool_get_wol(mp->phy, wol);
+}
+
+static int
+mvberlin_eth_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ int err;
+
+ if (mp->phy == NULL)
+ return -EOPNOTSUPP;
+
+ err = phy_ethtool_set_wol(mp->phy, wol);
+ /* Given that mvberlin works without the marvell-specific PHY driver,
+ * this debugging hint is useful to have.
+ */
+ if (err == -EOPNOTSUPP)
+ netdev_info(dev, "The PHY does not support set_wol, was CONFIG_MARVELL_PHY enabled?\n");
+ return err;
+}
+
+static int
+mvberlin_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+
+ if (mp->phy != NULL)
+ return mvberlin_eth_get_settings_phy(mp, cmd);
+
+ return mvberlin_eth_get_settings_phyless(mp, cmd);
+}
+
+static int
+mvberlin_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ int ret;
+
+ if (mp->phy == NULL)
+ return -EINVAL;
+
+ ret = phy_ethtool_sset(mp->phy, cmd);
+ if (!ret)
+ mvberlin_eth_adjust_link(dev);
+ return ret;
+}
+
+static void mvberlin_eth_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ strlcpy(drvinfo->driver, mvberlin_eth_driver_name,
+ sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, mvberlin_eth_driver_version,
+ sizeof(drvinfo->version));
+ strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
+ strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info));
+ drvinfo->n_stats = ARRAY_SIZE(mvberlin_eth_stats);
+}
+
+static int mvberlin_eth_nway_reset(struct net_device *dev)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+
+ if (mp->phy == NULL)
+ return -EINVAL;
+
+ return genphy_restart_aneg(mp->phy);
+}
+
+static void
+mvberlin_eth_get_ringparam(struct net_device *dev, struct ethtool_ringparam *er)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+
+ er->rx_max_pending = 4096;
+ er->tx_max_pending = 4096;
+
+ er->rx_pending = mp->rx_ring_size;
+ er->tx_pending = mp->tx_ring_size;
+}
+
+static int
+mvberlin_eth_set_ringparam(struct net_device *dev, struct ethtool_ringparam *er)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+
+ if (er->rx_mini_pending || er->rx_jumbo_pending)
+ return -EINVAL;
+
+ mp->rx_ring_size = er->rx_pending < 4096 ? er->rx_pending : 4096;
+ mp->tx_ring_size = er->tx_pending < 4096 ? er->tx_pending : 4096;
+ if (mp->tx_ring_size != er->tx_pending)
+ netdev_warn(dev, "TX queue size set to %u (requested %u)\n",
+ mp->tx_ring_size, er->tx_pending);
+
+ if (netif_running(dev)) {
+ mvberlin_eth_stop(dev);
+ if (mvberlin_eth_open(dev)) {
+ netdev_err(dev,
+ "fatal error on re-opening device after ring param change\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static void mvberlin_eth_get_strings(struct net_device *dev,
+ uint32_t stringset, uint8_t *data)
+{
+ int i;
+
+ if (stringset == ETH_SS_STATS) {
+ for (i = 0; i < ARRAY_SIZE(mvberlin_eth_stats); i++) {
+ memcpy(data + i * ETH_GSTRING_LEN,
+ mvberlin_eth_stats[i].stat_string,
+ ETH_GSTRING_LEN);
+ }
+ }
+}
+
+static void mvberlin_eth_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats,
+ uint64_t *data)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ int i;
+
+ mvberlin_eth_get_stats(dev);
+
+ for (i = 0; i < ARRAY_SIZE(mvberlin_eth_stats); i++) {
+ const struct mvberlin_eth_stats *stat;
+ void *p;
+
+ stat = mvberlin_eth_stats + i;
+
+ if (stat->netdev_off >= 0)
+ p = ((void *)mp->dev) + stat->netdev_off;
+ else
+ p = ((void *)mp) + stat->mp_off;
+
+ data[i] = (stat->sizeof_stat == 8) ?
+ *(uint64_t *)p : *(uint32_t *)p;
+ }
+}
+
+static int mvberlin_eth_get_sset_count(struct net_device *dev, int sset)
+{
+ if (sset == ETH_SS_STATS)
+ return ARRAY_SIZE(mvberlin_eth_stats);
+
+ return -EOPNOTSUPP;
+}
+
+const struct ethtool_ops mvberlin_eth_ethtool_ops = {
+ .get_settings = mvberlin_eth_get_settings,
+ .set_settings = mvberlin_eth_set_settings,
+ .get_drvinfo = mvberlin_eth_get_drvinfo,
+ .nway_reset = mvberlin_eth_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_ringparam = mvberlin_eth_get_ringparam,
+ .set_ringparam = mvberlin_eth_set_ringparam,
+ .get_strings = mvberlin_eth_get_strings,
+ .get_ethtool_stats = mvberlin_eth_get_ethtool_stats,
+ .get_sset_count = mvberlin_eth_get_sset_count,
+ .get_ts_info = ethtool_op_get_ts_info,
+ .get_wol = mvberlin_eth_get_wol,
+ .set_wol = mvberlin_eth_set_wol,
+};
+
+/* rx/tx queue initialisation ***********************************************/
+static int rxq_init(struct mvberlin_eth_private *mp, int index)
+{
+ struct rx_queue *rxq = mp->rxq + index;
+ struct rx_desc *rx_desc;
+ int size;
+ int i;
+
+ rxq->index = index;
+
+ rxq->rx_ring_size = mp->rx_ring_size;
+
+ rxq->rx_desc_count = 0;
+ rxq->rx_curr_desc = 0;
+ rxq->rx_used_desc = 0;
+
+ size = rxq->rx_ring_size * sizeof(struct rx_desc);
+
+ if (index == 0 && size <= mp->rx_desc_sram_size) {
+ rxq->rx_desc_area = ioremap(mp->rx_desc_sram_addr,
+ mp->rx_desc_sram_size);
+ rxq->rx_desc_dma = mp->rx_desc_sram_addr;
+ } else {
+ rxq->rx_desc_area = dma_alloc_coherent(mp->dev->dev.parent,
+ size, &rxq->rx_desc_dma,
+ GFP_KERNEL);
+ }
+
+ if (rxq->rx_desc_area == NULL) {
+ netdev_err(mp->dev,
+ "can't allocate rx ring (%d bytes)\n", size);
+ goto out;
+ }
+ memset(rxq->rx_desc_area, 0, size);
+
+ rxq->rx_desc_area_size = size;
+ rxq->rx_skb = kcalloc(rxq->rx_ring_size, sizeof(*rxq->rx_skb),
+ GFP_KERNEL);
+ if (rxq->rx_skb == NULL)
+ goto out_free;
+
+ rx_desc = rxq->rx_desc_area;
+ for (i = 0; i < rxq->rx_ring_size; i++) {
+ int nexti;
+
+ nexti = i + 1;
+ if (nexti == rxq->rx_ring_size)
+ nexti = 0;
+
+ rx_desc[i].next_desc_ptr = rxq->rx_desc_dma +
+ nexti * sizeof(struct rx_desc);
+ }
+
+ return 0;
+
+out_free:
+ if (index == 0 && size <= mp->rx_desc_sram_size)
+ iounmap(rxq->rx_desc_area);
+ else
+ dma_free_coherent(mp->dev->dev.parent, size,
+ rxq->rx_desc_area,
+ rxq->rx_desc_dma);
+
+out:
+ return -ENOMEM;
+}
+
+static void rxq_deinit(struct rx_queue *rxq)
+{
+ struct mvberlin_eth_private *mp = rxq_to_mp(rxq);
+ int i;
+
+ rxq_disable(rxq);
+
+ for (i = 0; i < rxq->rx_ring_size; i++) {
+ if (rxq->rx_skb[i]) {
+ dev_kfree_skb(rxq->rx_skb[i]);
+ rxq->rx_desc_count--;
+ }
+ }
+
+ if (rxq->rx_desc_count) {
+ netdev_err(mp->dev, "error freeing rx ring -- %d skbs stuck\n",
+ rxq->rx_desc_count);
+ }
+
+ if (rxq->index == 0 &&
+ rxq->rx_desc_area_size <= mp->rx_desc_sram_size)
+ iounmap(rxq->rx_desc_area);
+ else
+ dma_free_coherent(mp->dev->dev.parent, rxq->rx_desc_area_size,
+ rxq->rx_desc_area, rxq->rx_desc_dma);
+
+ kfree(rxq->rx_skb);
+}
+
+static int txq_init(struct mvberlin_eth_private *mp, int index)
+{
+ struct tx_queue *txq = mp->txq + index;
+ struct tx_desc *tx_desc;
+ int size;
+ int i;
+
+ txq->index = index;
+
+ txq->tx_ring_size = mp->tx_ring_size;
+
+ txq->tx_desc_count = 0;
+ txq->tx_curr_desc = 0;
+ txq->tx_used_desc = 0;
+
+ size = txq->tx_ring_size * sizeof(struct tx_desc);
+
+ if (index == 0 && size <= mp->tx_desc_sram_size) {
+ txq->tx_desc_area = ioremap(mp->tx_desc_sram_addr,
+ mp->tx_desc_sram_size);
+ txq->tx_desc_dma = mp->tx_desc_sram_addr;
+ } else {
+ txq->tx_desc_area = dma_alloc_coherent(mp->dev->dev.parent,
+ size, &txq->tx_desc_dma,
+ GFP_KERNEL);
+ }
+
+ if (txq->tx_desc_area == NULL) {
+ netdev_err(mp->dev,
+ "can't allocate tx ring (%d bytes)\n", size);
+ return -ENOMEM;
+ }
+ memset(txq->tx_desc_area, 0, size);
+
+ txq->tx_desc_area_size = size;
+
+ tx_desc = txq->tx_desc_area;
+ for (i = 0; i < txq->tx_ring_size; i++) {
+ struct tx_desc *txd = tx_desc + i;
+ int nexti;
+
+ nexti = i + 1;
+ if (nexti == txq->tx_ring_size)
+ nexti = 0;
+
+ txd->cmd_sts = 0;
+ txd->next_desc_ptr = txq->tx_desc_dma +
+ nexti * sizeof(struct tx_desc);
+ }
+
+ skb_queue_head_init(&txq->tx_skb);
+
+ return 0;
+}
+
+static void txq_deinit(struct tx_queue *txq)
+{
+ struct mvberlin_eth_private *mp = txq_to_mp(txq);
+
+ txq_disable(txq);
+ txq_reclaim(txq, txq->tx_ring_size, 1);
+
+ BUG_ON(txq->tx_used_desc != txq->tx_curr_desc);
+
+ if (txq->index == 0 &&
+ txq->tx_desc_area_size <= mp->tx_desc_sram_size)
+ iounmap(txq->tx_desc_area);
+ else
+ dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size,
+ txq->tx_desc_area, txq->tx_desc_dma);
+}
+
+static void handle_link_event(struct mvberlin_eth_private *mp)
+{
+ struct net_device *dev = mp->dev;
+ u32 port_status;
+ int speed;
+ int duplex;
+ int fc;
+
+ port_status = rdlp(mp, PORT_STATUS);
+ if (!(port_status & LINK_UP)) {
+ if (netif_carrier_ok(dev)) {
+ int i;
+
+ netdev_info(dev, "link down\n");
+
+ netif_carrier_off(dev);
+
+ for (i = 0; i < mp->txq_count; i++) {
+ struct tx_queue *txq = mp->txq + i;
+
+ txq_reclaim(txq, txq->tx_ring_size, 1);
+ txq_reset_hw_ptr(txq);
+ }
+ }
+ return;
+ }
+
+ switch (port_status & PORT_SPEED_MASK) {
+ case PORT_SPEED_10:
+ speed = 10;
+ break;
+ case PORT_SPEED_100:
+ speed = 100;
+ break;
+ default:
+ speed = -1;
+ break;
+ }
+
+ duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
+ fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
+
+ netdev_info(dev, "link up, %d Mb/s, %s duplex, flow control %sabled\n",
+ speed, duplex ? "full" : "half", fc ? "en" : "dis");
+
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+}
+
+static irqreturn_t mvberlin_eth_irq(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ u32 int_cause, txstatus;
+ int i;
+
+ int_cause = rdlp(mp, INT_CAUSE) & mp->int_mask;
+
+ if (int_cause == 0)
+ return IRQ_NONE;
+ wrlp(mp, INT_CAUSE, ~int_cause);
+
+ if (int_cause & INT_RX) {
+ wrlp(mp, INT_MASK, mp->int_mask & ~INT_RX);
+ napi_schedule(&mp->napi);
+ }
+
+ if (int_cause & INT_EXT)
+ handle_link_event(mp);
+
+ txstatus = int_cause & INT_TX;
+ for (i = 0; i < mp->txq_count; ++i) {
+ if (txstatus & INT_TX_0 << i) {
+ txq_reclaim(mp->txq + i, 16, 0);
+ txq_maybe_wake(mp->txq + i);
+ }
+ }
+
+ txstatus = ((int_cause & INT_TX_END) >> 6) &
+ ~((rdlp(mp, SDMA_COMMAND) >> 16) & 0x3);
+ for (i = 0; i < mp->txq_count; ++i) {
+ if (txstatus & 1 << i)
+ txq_kick(mp->txq + i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mvberlin_eth_poll(struct napi_struct *napi, int budget)
+{
+ struct mvberlin_eth_private *mp;
+ int i, work_done;
+
+ mp = container_of(napi, struct mvberlin_eth_private, napi);
+
+ if (unlikely(mp->oom)) {
+ mp->oom = 0;
+ del_timer(&mp->rx_oom);
+ }
+
+ work_done = 0;
+ for (i = mp->rxq_count - 1; work_done < budget && i >= 0; i--) {
+ struct rx_queue *rxq = mp->rxq + i;
+ int work_tbd = budget - work_done;
+
+ work_done += rxq_process(rxq, work_tbd);
+ wrlp(mp, INT_CAUSE, ~(INT_RX_0 << i));
+ if (likely(!mp->oom))
+ if (mp->work_rx_refill & 1 << i)
+ rxq_refill(rxq, work_tbd);
+ }
+
+ if (work_done < budget) {
+ if (mp->oom)
+ mod_timer(&mp->rx_oom, jiffies + (HZ / 10));
+ napi_complete(napi);
+ wrlp(mp, INT_MASK, mp->int_mask);
+ }
+
+ return work_done;
+}
+
+static inline void oom_timer_wrapper(unsigned long data)
+{
+ struct mvberlin_eth_private *mp = (void *)data;
+
+ napi_schedule(&mp->napi);
+}
+
+static inline unsigned int cal_mfl(int size)
+{
+ unsigned int pcxr;
+
+ if (size > 2048)
+ pcxr = MRU_64K;
+ else if (size > 1536)
+ pcxr = MRU_2048;
+ else if (size > 1518)
+ pcxr = MRU_1536;
+ else
+ pcxr = MRU_1518;
+
+ return pcxr;
+}
+
+static void port_start(struct mvberlin_eth_private *mp)
+{
+ u32 pscr, pcxr;
+ int i;
+
+ /* Perform PHY reset, if there is a PHY */
+ if (mp->phy != NULL) {
+ struct ethtool_cmd cmd;
+
+ mvberlin_eth_get_settings(mp->dev, &cmd);
+ phy_init_hw(mp->phy);
+ mvberlin_eth_set_settings(mp->dev, &cmd);
+ phy_start(mp->phy);
+ }
+
+ /* Configure basic link parameters */
+ pcxr = rdlp(mp, PORT_EXT_CONFIG);
+ pcxr &= ~EXT_MRU_ALL_MASK;
+ pcxr |= cal_mfl(mp->skb_size);
+ wrlp(mp, PORT_EXT_CONFIG, pcxr);
+
+ pscr = rdlp(mp, PORT_CONFIG);
+ pscr |= PORT_ENABLE;
+ wrlp(mp, PORT_CONFIG, pscr);
+
+ /* Configure TX path and queues */
+ for (i = 0; i < mp->txq_count; i++) {
+ struct tx_queue *txq = mp->txq + i;
+
+ txq_reset_hw_ptr(txq);
+ }
+
+ /* Enable the receive queues */
+ for (i = 0; i < mp->rxq_count; i++) {
+ struct rx_queue *rxq = mp->rxq + i;
+ u32 addr;
+
+ addr = (u32)rxq->rx_desc_dma;
+ addr += rxq->rx_curr_desc * sizeof(struct rx_desc);
+ wrlp(mp, RXQ_CURRENT_DESC_PTR(i), addr);
+ wrlp(mp, RXQ_FIRST_DESC_PTR(i), addr);
+
+ rxq_enable(rxq);
+ }
+}
+
+static void mvberlin_eth_recalc_skb_size(struct mvberlin_eth_private *mp)
+{
+ int skb_size;
+
+ /* Reserve 2+14 bytes for an ethernet header (the hardware
+ * automatically prepends 2 bytes of dummy data to each
+ * received packet), 16 bytes for up to four VLAN tags, and
+ * 4 bytes for the trailing FCS -- 36 bytes total.
+ */
+ skb_size = mp->dev->mtu + 36;
+
+ /* Make sure that the skb size is a multiple of 8 bytes, as
+ * the lower three bits of the receive descriptor's buffer
+ * size field are ignored by the hardware.
+ */
+ mp->skb_size = (skb_size + 7) & ~7;
+
+ /* If NET_SKB_PAD is smaller than a cache line,
+ * netdev_alloc_skb() will cause skb->data to be misaligned
+ * to a cache line boundary. If this is the case, include
+ * some extra space to allow re-aligning the data area.
+ */
+ mp->skb_size += SKB_DMA_REALIGN;
+}
+
+static int mvberlin_eth_open(struct net_device *dev)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ int err;
+ int i;
+
+ wrlp(mp, INT_CAUSE, 0);
+ wrlp(mp, INT_MASK, 0);
+
+ err = request_irq(dev->irq, mvberlin_eth_irq,
+ IRQF_SHARED, dev->name, dev);
+ if (err) {
+ netdev_err(dev, "can't assign irq\n");
+ return -EAGAIN;
+ }
+
+ mvberlin_eth_recalc_skb_size(mp);
+
+ napi_enable(&mp->napi);
+
+ mp->int_mask = INT_EXT;
+
+ for (i = 0; i < mp->rxq_count; i++) {
+ err = rxq_init(mp, i);
+ if (err) {
+ while (--i >= 0)
+ rxq_deinit(mp->rxq + i);
+ goto out;
+ }
+
+ rxq_refill(mp->rxq + i, INT_MAX);
+ mp->int_mask |= INT_RX_0 << i;
+ }
+
+ if (mp->oom) {
+ mp->rx_oom.expires = jiffies + (HZ / 10);
+ add_timer(&mp->rx_oom);
+ }
+
+ for (i = 0; i < mp->txq_count; i++) {
+ err = txq_init(mp, i);
+ if (err) {
+ while (--i >= 0)
+ txq_deinit(mp->txq + i);
+ goto out_free;
+ }
+ mp->int_mask |= INT_TX_0 << i;
+ mp->int_mask |= INT_TX_END_0 << i;
+ }
+
+ port_start(mp);
+
+ wrlp(mp, INT_MASK, mp->int_mask);
+
+ return 0;
+
+out_free:
+ for (i = 0; i < mp->rxq_count; i++)
+ rxq_deinit(mp->rxq + i);
+out:
+ free_irq(dev->irq, dev);
+
+ return err;
+}
+
+static void port_reset(struct mvberlin_eth_private *mp)
+{
+ unsigned int data;
+ int i;
+
+ for (i = 0; i < mp->rxq_count; i++)
+ rxq_disable(mp->rxq + i);
+ for (i = 0; i < mp->txq_count; i++)
+ txq_disable(mp->txq + i);
+
+ /* Reset the Enable bit in the Configuration Register */
+ data = rdlp(mp, PORT_CONFIG);
+ data &= ~(PORT_ENABLE | HASH_PASS_MODE);
+ wrlp(mp, PORT_CONFIG, data);
+}
+
+static int mvberlin_eth_stop(struct net_device *dev)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ int i;
+
+ wrlp(mp, INT_MASK, 0x00000000);
+ rdlp(mp, INT_MASK);
+
+ napi_disable(&mp->napi);
+
+ del_timer_sync(&mp->rx_oom);
+
+ netif_carrier_off(dev);
+ if (mp->phy)
+ phy_stop(mp->phy);
+ free_irq(dev->irq, dev);
+
+ port_reset(mp);
+ mvberlin_eth_get_stats(dev);
+
+ for (i = 0; i < mp->rxq_count; i++)
+ rxq_deinit(mp->rxq + i);
+ for (i = 0; i < mp->txq_count; i++)
+ txq_deinit(mp->txq + i);
+
+ return 0;
+}
+
+static int mvberlin_eth_ioctl(struct net_device *dev, struct ifreq *ifr,
+ int cmd)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ int ret;
+
+ if (mp->phy == NULL)
+ return -ENOTSUPP;
+
+ ret = phy_mii_ioctl(mp->phy, ifr, cmd);
+ if (!ret)
+ mvberlin_eth_adjust_link(dev);
+ return ret;
+}
+
+static int mvberlin_eth_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+
+ if (new_mtu < 64 || new_mtu > 9500)
+ return -EINVAL;
+
+ dev->mtu = new_mtu;
+ mvberlin_eth_recalc_skb_size(mp);
+
+ if (!netif_running(dev))
+ return 0;
+
+ /* Stop and then re-open the interface. This will allocate RX
+ * skbs of the new MTU.
+ * There is a possible danger that the open will not succeed,
+ * due to memory being full.
+ */
+ mvberlin_eth_stop(dev);
+ if (mvberlin_eth_open(dev)) {
+ netdev_err(dev,
+ "fatal error on re-opening device after MTU change\n");
+ }
+
+ return 0;
+}
+
+static void tx_timeout_task(struct work_struct *ugly)
+{
+ struct mvberlin_eth_private *mp;
+
+ mp = container_of(ugly, struct mvberlin_eth_private, tx_timeout_task);
+ if (netif_running(mp->dev)) {
+ netif_tx_stop_all_queues(mp->dev);
+ port_reset(mp);
+ port_start(mp);
+ netif_tx_wake_all_queues(mp->dev);
+ }
+}
+
+static void mvberlin_eth_tx_timeout(struct net_device *dev)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+
+ netdev_info(dev, "tx timeout\n");
+
+ schedule_work(&mp->tx_timeout_task);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void mvberlin_eth_netpoll(struct net_device *dev)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+
+ wrlp(mp, INT_MASK, 0x00000000);
+ rdlp(mp, INT_MASK);
+
+ mvberlin_eth_irq(dev->irq, dev);
+
+ wrlp(mp, INT_MASK, mp->int_mask);
+}
+#endif
+
+/* hash_table_function - Hash calculation function */
+static unsigned int hash_table_function(unsigned int mac_h, unsigned int mac_l)
+{
+ unsigned int hash_result;
+ unsigned int addr_h;
+ unsigned int addr_l;
+ unsigned int addr_0;
+ unsigned int addr_1;
+ unsigned int addr_2;
+ unsigned int addr_3;
+ unsigned int addr_h_swapped = 0;
+ unsigned int addr_l_swapped = 0;
+
+ addr_h = NIBBLE_SWAPPING_16_BIT(mac_h);
+ addr_l = NIBBLE_SWAPPING_32_BIT(mac_l);
+
+ addr_h_swapped = GT_NIBBLE(addr_h & 0xf) +
+ ((GT_NIBBLE((addr_h>>4) & 0xf)) << 4) +
+ ((GT_NIBBLE((addr_h>>8) & 0xf)) << 8) +
+ ((GT_NIBBLE((addr_h>>12) & 0xf)) << 12);
+
+ addr_l_swapped = GT_NIBBLE(addr_l & 0xf) +
+ ((GT_NIBBLE((addr_l>>4) & 0xf)) << 4) +
+ ((GT_NIBBLE((addr_l>>8) & 0xf)) << 8) +
+ ((GT_NIBBLE((addr_l>>12) & 0xf)) << 12) +
+ ((GT_NIBBLE((addr_l>>16) & 0xf)) << 16) +
+ ((GT_NIBBLE((addr_l>>20) & 0xf)) << 20) +
+ ((GT_NIBBLE((addr_l>>24) & 0xf)) << 24) +
+ ((GT_NIBBLE((addr_l>>28) & 0xf)) << 28);
+
+ addr_h = addr_h_swapped;
+ addr_l = addr_l_swapped;
+
+ /* hash mode 0 */
+ addr_0 = (addr_l >> 2) & 0x3f;
+ addr_1 = (addr_l & 0x3) | ((addr_l>>8) & 0x7f)<<2;
+ addr_2 = (addr_l >> 15) & 0x1ff;
+ addr_3 = ((addr_l >> 24) & 0xff) | ((addr_h & 0x1) << 8);
+
+ hash_result = (addr_0 << 9) | (addr_1 ^ addr_2 ^ addr_3);
+ hash_result = hash_result & 0x7ff; /* half-k */
+
+ return hash_result;
+}
+
+static void add_del_table_entry(void *ptr, unsigned char *addr,
+ int rd, int skip, int del)
+{
+ unsigned int addr_h, addr_l;
+ unsigned int addr_l_read, addr_h_read;
+ unsigned int mac_h, mac_l, *entry;
+ int i;
+
+ mac_h = (addr[0] << 8) | (addr[1]);
+ mac_l = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | (addr[5]);
+ entry = (unsigned int *)(ptr + (8 * hash_table_function(mac_h, mac_l)));
+
+ addr_l = HASH_TABLE_ENTRY_VALID | (rd<<2) |
+ (((mac_h>>8) & 0xf)<<3) | (((mac_h>>12) & 0xf) << 7) |
+ (((mac_h>>0) & 0xf)<<11) | (((mac_h>>4) & 0xf) << 15) |
+ (((mac_l>>24) & 0xf)<<19) | (((mac_l>>28) & 0xf) << 23) |
+ (((mac_l>>16) & 0xf)<<27) | ((((mac_l>>20) & 0x1) << 31));
+
+ addr_h = ((mac_l>>21) & 0x7) | (((mac_l>>8) & 0xf)<<3) |
+ (((mac_l>>12) & 0xf) << 7) | (((mac_l>>0) & 0xf) << 11) |
+ (((mac_l>>4) & 0xf) << 15);
+
+ if (skip)
+ addr_l |= HASH_TABLE_ENTRY_SKIP;
+
+ /* find a free place */
+ for (i = 0 ; i < 12 ; i++) {
+ addr_l_read = *(entry + (i*2));
+ if (!(addr_l_read & HASH_TABLE_ENTRY_VALID) ||
+ (addr_l_read & HASH_TABLE_ENTRY_SKIP)) {
+ entry = entry + (i*2);
+ break;
+ } else {
+ addr_h_read = *(entry + (i*2) + 1);
+ if (((addr_l_read>>3) & 0x1fffffff) ==
+ ((addr_l>>3) & 0x1fffffff) &&
+ (addr_h_read == addr_h)) {
+ entry = entry + (i*2);
+ break;
+ }
+ }
+ }
+
+ if (i == 12)
+ return;
+
+ /* update address entry */
+ if (del) {
+ *entry = 0;
+ *(entry + 1) = 0;
+ } else {
+ *entry = addr_l;
+ *(entry + 1) = addr_h;
+ }
+}
+
+static void init_hash_table(struct mvberlin_eth_private *mp)
+{
+ u32 epcr;
+
+ epcr = rdlp(mp, PORT_CONFIG);
+ epcr |= HASH_SIZE_HALF_K;
+ epcr &= ~HASH_FUNCTION_1;
+ /* reset HDM to 0: discard addresses not found in hash table */
+ epcr &= ~HASH_PASS_MODE;
+ wrlp(mp, PORT_CONFIG, epcr);
+ mp->hash_tbl = dma_alloc_coherent(mp->dev->dev.parent,
+ HASH_TABLE_SIZE,
+ &mp->hash_dma, GFP_KERNEL);
+ wrlp(mp, HASH_TABLE, mp->hash_dma);
+}
+
+static void mvberlin_eth_set_multicast_list(struct net_device *dev)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ u32 epcr;
+
+ epcr = rdlp(mp, PORT_CONFIG);
+ if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+ epcr |= PROMISCUOUS_MODE;
+ wrlp(mp, PORT_CONFIG, epcr);
+ } else {
+ struct netdev_hw_addr *ha;
+
+ epcr &= ~(PROMISCUOUS_MODE | BROADCAST_REJECT_MODE);
+ wrlp(mp, PORT_CONFIG, epcr);
+
+ memset(mp->hash_tbl, 0, HASH_TABLE_SIZE);
+ add_del_table_entry(mp->hash_tbl, dev->dev_addr, 1, 0, 0);
+ netdev_for_each_mc_addr(ha, dev)
+ add_del_table_entry(mp->hash_tbl, ha->addr, 1, 0, 0);
+ }
+}
+
+static void init_pscr(struct mvberlin_eth_private *mp)
+{
+ u32 pcxr;
+
+ wrlp(mp, PORT_CONFIG, 0);
+ pcxr = EXT_FC_AN_DISABLE | EXT_FLP_DISABLE | EXT_FC_ENABLE |
+ EXT_MAC_RX_2BSTUFF | EXT_IGMP | EXT_SPAN | EXT_DSCP_EN;
+
+ /* Only use HIGH TXQ when only one TXQ, so set all pkts are from HIGH */
+ if (mp->txq_count == 1)
+ pcxr |= (7 << 3);
+
+ wrlp(mp, PORT_EXT_CONFIG, pcxr);
+ wrlp(mp, ETH_EDSCP2P0L, 0xFFFFFFFE);
+ wrlp(mp, ETH_EDSCP2P1L, 0x0);
+ wrlp(mp, ETH_EDSCP2P0H, 0xFFFFFFFF);
+ wrlp(mp, ETH_EDSCP2P1H, 0x0);
+}
+
+static void mib_counters_clear(struct mvberlin_eth_private *mp)
+{
+ int i;
+ u32 data;
+
+ data = rdlp(mp, PORT_EXT_CONFIG);
+ data &= ~EXT_MIB_CLEAR; /* 0 to read-clear */
+ wrlp(mp, PORT_EXT_CONFIG, data);
+ for (i = 0; i < 0x60; i += 4)
+ mib_read(mp, i);
+ data |= EXT_MIB_CLEAR; /* 1 to read-no-effect */
+ wrlp(mp, PORT_EXT_CONFIG, data);
+}
+
+static void uc_addr_get(struct mvberlin_eth_private *mp, unsigned char *addr)
+{
+ unsigned int mac_h = rdlp(mp, MAC_ADDR_HIGH);
+ unsigned int mac_l = rdlp(mp, MAC_ADDR_LOW);
+
+ addr[0] = (mac_h >> 24) & 0xff;
+ addr[1] = (mac_h >> 16) & 0xff;
+ addr[2] = (mac_h >> 8) & 0xff;
+ addr[3] = mac_h & 0xff;
+ addr[4] = (mac_l >> 8) & 0xff;
+ addr[5] = mac_l & 0xff;
+}
+
+static void uc_addr_set(struct mvberlin_eth_private *mp, unsigned char *addr)
+{
+ wrlp(mp, MAC_ADDR_HIGH,
+ (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]);
+ wrlp(mp, MAC_ADDR_LOW, (addr[4] << 8) | addr[5]);
+}
+
+static void mvberlin_eth_program_unicast_filter(struct mvberlin_eth_private *mp,
+ unsigned char *old,
+ unsigned char *new)
+{
+ uc_addr_set(mp, new);
+
+ /* delete the old address from the filter table */
+ if (old)
+ add_del_table_entry(mp->hash_tbl, old, 1, 0, 1);
+
+ /* add the new address to filter table */
+ add_del_table_entry(mp->hash_tbl, new, 1, 0, 0);
+}
+
+static int mvberlin_eth_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct mvberlin_eth_private *mp = netdev_priv(dev);
+ struct sockaddr *sa = addr;
+ unsigned char old[ETH_ALEN];
+
+ if (!is_valid_ether_addr(sa->sa_data))
+ return -EINVAL;
+
+ memcpy(old, dev->dev_addr, ETH_ALEN);
+ dev->addr_assign_type &= ~NET_ADDR_RANDOM;
+ memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
+
+ netif_addr_lock_bh(dev);
+ mvberlin_eth_program_unicast_filter(mp, old, dev->dev_addr);
+ netif_addr_unlock_bh(dev);
+
+ return 0;
+}
+
+static void set_params(struct mvberlin_eth_private *mp,
+ struct mv643xx_eth_platform_data *pd)
+{
+ struct net_device *dev = mp->dev;
+
+ if (is_valid_ether_addr(pd->mac_addr))
+ memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN);
+ else
+ uc_addr_get(mp, dev->dev_addr);
+
+ mp->rx_ring_size = DEFAULT_RX_QUEUE_SIZE;
+ if (pd->rx_queue_size)
+ mp->rx_ring_size = pd->rx_queue_size;
+ mp->rx_desc_sram_addr = pd->rx_sram_addr;
+ mp->rx_desc_sram_size = pd->rx_sram_size;
+
+ mp->rxq_count = pd->rx_queue_count ? : 1;
+
+ mp->tx_ring_size = DEFAULT_TX_QUEUE_SIZE;
+ if (pd->tx_queue_size)
+ mp->tx_ring_size = pd->tx_queue_size;
+
+ mp->tx_desc_sram_addr = pd->tx_sram_addr;
+ mp->tx_desc_sram_size = pd->tx_sram_size;
+
+ mp->txq_count = pd->tx_queue_count ? : 1;
+}
+
+static const struct net_device_ops mvberlin_eth_netdev_ops = {
+ .ndo_open = mvberlin_eth_open,
+ .ndo_stop = mvberlin_eth_stop,
+ .ndo_start_xmit = mvberlin_eth_xmit,
+ .ndo_set_rx_mode = mvberlin_eth_set_multicast_list,
+ .ndo_set_mac_address = mvberlin_eth_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = mvberlin_eth_ioctl,
+ .ndo_change_mtu = mvberlin_eth_change_mtu,
+ .ndo_tx_timeout = mvberlin_eth_tx_timeout,
+ .ndo_get_stats = mvberlin_eth_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = mvberlin_eth_netpoll,
+#endif
+};
+
+static int mvberlin_eth_probe(struct platform_device *pdev)
+{
+ struct mv643xx_eth_platform_data *pd;
+ struct mvberlin_eth_private *mp;
+ struct net_device *dev;
+ struct resource *res;
+ int ret;
+
+ dev = alloc_etherdev_mq(sizeof(struct mvberlin_eth_private), 8);
+ if (!dev)
+ return -ENOMEM;
+
+ pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return -ENOMEM;
+
+ mp = netdev_priv(dev);
+ platform_set_drvdata(pdev, mp);
+ mp->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOMEM;
+
+ mp->shared = devm_kzalloc(&pdev->dev,
+ sizeof(struct mvberlin_eth_shared_private),
+ GFP_KERNEL);
+ if (!mp->shared)
+ return -ENOMEM;
+
+ mp->shared->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mp->shared->base))
+ return PTR_ERR(mp->shared->base);
+ mp->base = mp->shared->base + 0x400;
+
+ mp->clk = devm_clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(mp->clk)) {
+ clk_prepare_enable(mp->clk);
+ mp->t_clk = clk_get_rate(mp->clk);
+ }
+
+ set_params(mp, pd);
+ netif_set_real_num_tx_queues(dev, mp->txq_count);
+ netif_set_real_num_rx_queues(dev, mp->rxq_count);
+
+ pd->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
+ if (!pd->phy_node) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mp->phy = of_phy_connect(dev, pd->phy_node,
+ mvberlin_eth_adjust_link, 0,
+ PHY_INTERFACE_MODE_RGMII);
+ if (!mp->phy) {
+ ret = -EPROBE_DEFER;
+ goto out;
+ }
+
+ dev->ethtool_ops = &mvberlin_eth_ethtool_ops;
+
+ init_pscr(mp);
+
+ init_hash_table(mp);
+ mvberlin_eth_program_unicast_filter(mp, NULL, dev->dev_addr);
+
+ mib_counters_clear(mp);
+
+ INIT_WORK(&mp->tx_timeout_task, tx_timeout_task);
+
+ netif_napi_add(dev, &mp->napi, mvberlin_eth_poll, NAPI_POLL_WEIGHT);
+
+ init_timer(&mp->rx_oom);
+ mp->rx_oom.data = (unsigned long)mp;
+ mp->rx_oom.function = oom_timer_wrapper;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ BUG_ON(!res);
+ dev->irq = res->start;
+
+ dev->netdev_ops = &mvberlin_eth_netdev_ops;
+
+ dev->watchdog_timeo = 2 * HZ;
+ dev->base_addr = 0;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ wrlp(mp, SDMA_CONFIG, PORT_SDMA_CONFIG_DEFAULT_VALUE);
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto out;
+
+ netif_carrier_off(dev);
+
+ return 0;
+
+out:
+ if (!IS_ERR(mp->clk))
+ clk_disable_unprepare(mp->clk);
+ free_netdev(dev);
+
+ return ret;
+}
+
+static int mvberlin_eth_remove(struct platform_device *pdev)
+{
+ struct mvberlin_eth_private *mp = platform_get_drvdata(pdev);
+
+ unregister_netdev(mp->dev);
+ if (mp->phy != NULL)
+ phy_disconnect(mp->phy);
+ cancel_work_sync(&mp->tx_timeout_task);
+
+ if (!IS_ERR(mp->clk))
+ clk_disable_unprepare(mp->clk);
+
+ free_netdev(mp->dev);
+
+ return 0;
+}
+
+static void mvberlin_eth_shutdown(struct platform_device *pdev)
+{
+ struct mvberlin_eth_private *mp = platform_get_drvdata(pdev);
+
+ /* Mask all interrupts on ethernet port */
+ wrlp(mp, INT_MASK, 0);
+ rdlp(mp, INT_MASK);
+
+ if (netif_running(mp->dev))
+ port_reset(mp);
+}
+
+static const struct of_device_id mvberlin_eth_of_match[] = {
+ { .compatible = "marvell,berlin-eth" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mvberlin_eth_of_match);
+
+static struct platform_driver mvberlin_eth_driver = {
+ .probe = mvberlin_eth_probe,
+ .remove = mvberlin_eth_remove,
+ .shutdown = mvberlin_eth_shutdown,
+ .driver = {
+ .name = "mvberlin-ethernet",
+ .owner = THIS_MODULE,
+ .of_match_table = mvberlin_eth_of_match,
+ },
+};
+module_platform_driver(mvberlin_eth_driver);
+
+MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
+MODULE_DESCRIPTION("Ethernet driver for Marvell Berlin SoCs");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related
* [PATCH 0/5] ARM: Berlin: Ethernet support
From: Antoine Tenart @ 2014-08-29 13:50 UTC (permalink / raw)
To: sebastian.hesselbarth, thomas.petazzoni
Cc: Antoine Tenart, alexandre.belloni, zmxu, jszhang, netdev,
linux-arm-kernel, devicetree, linux-kernel
Hi all,
This series introduce the Marvell Berlin Ethernet driver, allowing to
handle the fast Ethernet port. This driver is based on the mv643xx_eth
driver and reuse some of its functions. While I wanted to make these
functions common to the two drivers at first, I finally do think this
is not a great idea:
- Registers are very different.
- The mv643xx_eth supports up to 1000Mbps interfaces and performance
issues may occur, whereas the mvberlin_eth is only for fast Ethernet.
- The mvberlin_eth driver uses a hash table to filter incoming packets.
- A few other differences.
I tried to stay close to the mv643xx_eth implementation, so that the
discussion is still open, but I still ended up with lots of tiny
differences that can be hard to manage for both cases. In the end I do
think having two separate drivers is a good choice.
That being said, I tested the Ethernet communication with ICMP pings,
netcat, tried to use some network related softwares (such as ssh). Here
is an iperf output:
------------------------------------------------------------
Client connecting to 192.168.0.11, TCP port 5001
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.0.20 port 44183 connected with 192.168.0.11 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 113 MBytes 94.8 Mbits/sec
Tests were done on a BG2Q DMP, and this series currently does not add
other device tree nodes than the ones for this board.
Thanks!
Antoine
Antoine Tenart (5):
net: mvberlin_eth: add an Ethernet driver for Marvell Berlin
Documentation: bindings: net: add the Marvell Berlin Ethernet
controller
Documentation: devicetree: net: mention Marvell Berlin
ARM: dts: berlin: add ethernet and mdio nodes
ARM: dts: berlin: enable the Ethernet port on the BG2Q DMP
.../devicetree/bindings/net/marvell-berlin.txt | 23 +
.../devicetree/bindings/net/marvell-orion-mdio.txt | 6 +-
arch/arm/boot/dts/berlin2q-marvell-dmp.dts | 13 +
arch/arm/boot/dts/berlin2q.dtsi | 19 +
drivers/net/ethernet/marvell/Kconfig | 9 +
drivers/net/ethernet/marvell/Makefile | 1 +
drivers/net/ethernet/marvell/mvberlin_eth.c | 2081 ++++++++++++++++++++
7 files changed, 2149 insertions(+), 3 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/marvell-berlin.txt
create mode 100644 drivers/net/ethernet/marvell/mvberlin_eth.c
--
1.9.1
^ permalink raw reply
* [PATCH] packet.7: fix include file
From: Sorin Dumitru @ 2014-08-29 13:20 UTC (permalink / raw)
To: linux-man-u79uwXL29TY76Z2rM5mHXA
Cc: mtk.manpages-Re5JQEeQqe8AvxtiuMwx3w,
netdev-u79uwXL29TY76Z2rM5mHXA, sorin-W1xJSxASJ7qhKNWrAYCRhA
It looks like most of the socket options from this man pages
are not defined in <netpacket/packet.h>. They are defined in
<linux/if_packet.h> so we should include that one.
Signed-off-by: Sorin Dumitru <sdumitru-+zzKsuq53OdBDgjK7y7TUQ@public.gmane.org>
---
man7/packet.7 | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/man7/packet.7 b/man7/packet.7
index 57cd3ca..7b6f20d 100644
--- a/man7/packet.7
+++ b/man7/packet.7
@@ -16,7 +16,7 @@ packet \- packet interface on device level
.nf
.B #include <sys/socket.h>
.br
-.B #include <netpacket/packet.h>
+.B #include <linux/if_packet.h>
.br
.B #include <net/ethernet.h> /* the L2 protocols */
.sp
@@ -535,18 +535,6 @@ is a new feature in Linux 2.2.
Earlier Linux versions supported only
.BR SOCK_PACKET .
.PP
-The include file
-.I <netpacket/packet.h>
-is present since glibc 2.1.
-Older systems need:
-.sp
-.in +4n
-.nf
-#include <asm/types.h>
-#include <linux/if_packet.h>
-#include <linux/if_ether.h> /* The L2 protocols */
-.fi
-.in
.SH NOTES
For portable programs it is suggested to use
.B AF_PACKET
--
2.0.1
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [net-next PATCH 1/1] drivers: net: cpsw: Add support for pause frames
From: Sergei Shtylyov @ 2014-08-29 12:53 UTC (permalink / raw)
To: Mugunthan V N, netdev; +Cc: davem
In-Reply-To: <1409302581-25290-1-git-send-email-mugunthanvnm@ti.com>
Hello.
On 8/29/2014 12:56 PM, Mugunthan V N wrote:
> CPSW supports both rx and tx pause frames for flow control.
> Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
> ---
> drivers/net/ethernet/ti/cpsw.c | 37 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 37 insertions(+)
> diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
> index 999fb72..81325d4 100644
> --- a/drivers/net/ethernet/ti/cpsw.c
> +++ b/drivers/net/ethernet/ti/cpsw.c
> @@ -397,6 +397,8 @@ struct cpsw_priv {
> struct cpdma_ctlr *dma;
> struct cpdma_chan *txch, *rxch;
> struct cpsw_ale *ale;
> + bool rx_pause;
> + bool tx_pause;
> /* snapshot of IRQ numbers */
> u32 irqs_table[4];
> u32 num_irqs;
[...]
> @@ -1784,6 +1795,30 @@ static int cpsw_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
> return -EOPNOTSUPP;
> }
>
> +static void cpsw_get_pauseparam(struct net_device *ndev,
> + struct ethtool_pauseparam *pause)
> +{
> + struct cpsw_priv *priv = netdev_priv(ndev);
> +
> + pause->autoneg = AUTONEG_DISABLE;
> + pause->rx_pause = (priv->rx_pause) ? true : false;
> + pause->tx_pause = (priv->tx_pause) ? true : false;
Aren't 'priv->[rt]x_queue' already *bool*? And the parens are not needed
anyway.
> +}
> +
> +static int cpsw_set_pauseparam(struct net_device *ndev,
> + struct ethtool_pauseparam *pause)
> +{
> + struct cpsw_priv *priv = netdev_priv(ndev);
> + bool link;
> +
> + priv->rx_pause = (pause->rx_pause) ? true : false;
> + priv->tx_pause = (pause->tx_pause) ? true : false;
Same here...
WBR, Sergei
^ permalink raw reply
* Re: [PATCH net-next] tcp: whitespace fixes
From: Jeff Kirsher @ 2014-08-29 12:27 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: David Miller, netdev
In-Reply-To: <20140829000646.0647e9c5@urahara>
On Fri, Aug 29, 2014 at 12:06 AM, Stephen Hemminger
<stephen@networkplumber.org> wrote:
> Fix places where there is space before tab, long lines, and
> awkward if(){.
>
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
>
>
> --- a/net/ipv4/tcp_bic.c 2014-05-05 23:44:19.000000000 -0700
> +++ b/net/ipv4/tcp_bic.c 2014-08-22 17:17:51.448069718 -0700
> @@ -50,7 +50,7 @@ MODULE_PARM_DESC(smooth_part, "log(B/(B*
> /* BIC TCP Parameters */
> struct bictcp {
> u32 cnt; /* increase cwnd by 1 after ACKs */
> - u32 last_max_cwnd; /* last maximum snd_cwnd */
> + u32 last_max_cwnd; /* last maximum snd_cwnd */
> u32 loss_cwnd; /* congestion window at last loss */
> u32 last_cwnd; /* the last snd_cwnd */
> u32 last_time; /* time when updated last_cwnd */
> @@ -103,7 +103,7 @@ static inline void bictcp_update(struct
>
> /* binary increase */
> if (cwnd < ca->last_max_cwnd) {
> - __u32 dist = (ca->last_max_cwnd - cwnd)
> + __u32 dist = (ca->last_max_cwnd - cwnd)
> / BICTCP_B;
>
> if (dist > max_increment)
> --- a/net/ipv4/tcp_cubic.c 2014-06-16 14:48:29.000000000 -0700
> +++ b/net/ipv4/tcp_cubic.c 2014-08-22 17:20:10.368073438 -0700
> @@ -82,7 +82,7 @@ MODULE_PARM_DESC(hystart_ack_delta, "spa
> /* BIC TCP Parameters */
> struct bictcp {
> u32 cnt; /* increase cwnd by 1 after ACKs */
> - u32 last_max_cwnd; /* last maximum snd_cwnd */
> + u32 last_max_cwnd; /* last maximum snd_cwnd */
> u32 loss_cwnd; /* congestion window at last loss */
> u32 last_cwnd; /* the last snd_cwnd */
> u32 last_time; /* time when updated last_cwnd */
> @@ -263,9 +263,9 @@ static inline void bictcp_update(struct
>
> /* c/rtt * (t-K)^3 */
> delta = (cube_rtt_scale * offs * offs * offs) >> (10+3*BICTCP_HZ);
> - if (t < ca->bic_K) /* below origin*/
> + if (t < ca->bic_K) /* below origin*/
> bic_target = ca->bic_origin_point - delta;
> - else /* above origin*/
> + else /* above origin*/
> bic_target = ca->bic_origin_point + delta;
>
> /* cubic function - calc bictcp_cnt*/
> @@ -291,7 +291,7 @@ static inline void bictcp_update(struct
> ca->tcp_cwnd++;
> }
>
> - if (ca->tcp_cwnd > cwnd){ /* if bic is slower than tcp */
> + if (ca->tcp_cwnd > cwnd) { /* if bic is slower than tcp */
> delta = ca->tcp_cwnd - cwnd;
> max_cnt = cwnd / delta;
> if (ca->cnt > max_cnt)
> @@ -452,7 +452,7 @@ static int __init cubictcp_register(void
> * based on SRTT of 100ms
> */
>
> - beta_scale = 8*(BICTCP_BETA_SCALE+beta)/ 3 / (BICTCP_BETA_SCALE - beta);
> + beta_scale = 8*(BICTCP_BETA_SCALE+beta) / 3 / (BICTCP_BETA_SCALE - beta);
Since you are fixing up the line, shouldn't it be:
beta_scale = 8 * (BICTCP_BETA_SCALE + beta) / 3 /
(BICTCP_BETA_SCALE - beta);
>
> cube_rtt_scale = (bic_scale * 10); /* 1024*c/rtt */
>
> --- a/net/ipv4/tcp_yeah.c 2014-05-05 23:44:19.000000000 -0700
> +++ b/net/ipv4/tcp_yeah.c 2014-08-22 17:15:47.788066407 -0700
> @@ -84,7 +84,7 @@ static void tcp_yeah_cong_avoid(struct s
> /* Scalable */
>
> tp->snd_cwnd_cnt += yeah->pkts_acked;
> - if (tp->snd_cwnd_cnt > min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT)){
> + if (tp->snd_cwnd_cnt > min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT)) {
> if (tp->snd_cwnd < tp->snd_cwnd_clamp)
> tp->snd_cwnd++;
> tp->snd_cwnd_cnt = 0;
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Cheers,
Jeff
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox