Netdev List
 help / color / mirror / Atom feed
* Re: am335x: no multicast reception over VLAN
From: Mugunthan V N @ 2016-03-30 16:52 UTC (permalink / raw)
  To: Yegor Yefremov
  Cc: Grygorii Strashko, netdev, linux-omap@vger.kernel.org, drivshin,
	ml
In-Reply-To: <CAGm1_kvXA4egrK8-oCedYjvPm7BhJdy_w-5ON24snYdKiN3K7g@mail.gmail.com>

On Wednesday 30 March 2016 02:05 PM, Yegor Yefremov wrote:
> On Wed, Mar 30, 2016 at 7:33 AM, Mugunthan V N <mugunthanvnm@ti.com> wrote:
>> On Tuesday 29 March 2016 06:14 PM, Grygorii Strashko wrote:
>>> On 03/29/2016 03:35 PM, Yegor Yefremov wrote:
>>>> On Tue, Mar 29, 2016 at 1:05 PM, Grygorii Strashko
>>>> <grygorii.strashko@ti.com> wrote:
>>>>> On 03/29/2016 08:21 AM, Yegor Yefremov wrote:
>>>>>> Hi Mugunthan,
>>>>>>
>>>>>> On Tue, Mar 29, 2016 at 6:00 AM, Mugunthan V N <mugunthanvnm@ti.com> wrote:
>>>>>>> Hi Yegor
>>>>>>>
>>>>>>> On Wednesday 16 March 2016 08:35 PM, Yegor Yefremov wrote:
>>>>>>>> I have an am335x based board using CPSW in Dual EMAC mode. Without
>>>>>>>> VLAN IDs I can receive and send multicast packets [1]. When I create
>>>>>>>> VLAN ID:
>>>>>>>>
>>>>>>>> ip link add link eth1 name eth1.100 type vlan id 100
>>>>>>>> ip addr add 192.168.100.2/24 brd 192.168.100.255 dev eth1.100
>>>>>>>> route add -net 224.0.0.0 netmask 224.0.0.0 eth1.100
>>>>>>>>
>>>>>>>> I can successfully send multicast packets, but not receive them. On
>>>>>>>> the other side of the Ethernet cable I've used Pandaboard. Pandaboard
>>>>>>>> could both receive and send multicast packets via VLAN.
>>>>>>>
>>>>>>> Are you trying multicast tx/rx on eth1 or eth1.100?
>>>>>>
>>>>>> I'm trying multicast tx/rx on eth1.100.
>>>>>>
>>>>>> eth1 has no problems.
>>>>>>
>>>>>
>>>>> it'd be nice if will be able to post here output fom commands:
>>>>> # switch-config -d [git://git.ti.com/switch-config/switch-config.git v4.1]
>>>>> # ifconfig -a
>>>>> # tcpdump -e -f -Q in -i eth0
>>>>> # tcpdump -e -f -Q in -i eth0.100
>>>>
>>>> Which kernel/branch do you want me to test?
>>>>
>>>> git://git.ti.com/ti-linux-kernel/ti-linux-kernel.git and ti-rt-linux-4.1.y?
>>>>
>>>> So far I was using vanilla kernel.
>>>
>>> Your branch (but better 4.5 kernels (or 4.4)).
>>> Just when you've done with configuration run cmds 1&2,
>>> and when you run your use-case - run cmds 2&3 on receiver side (grap ~5-10 packets).
>>> then stop test and run cmd 1 again.
>>>
>>> After all could you provide your console output here, pls.
>>>
>>>
>>
>> To use command 1, you need TI kernel [1] as it won't build with vanilla
>> kernel.
>>
>> [1]: git://git.ti.com/ti-linux-kernel/ti-linux-kernel.git ti-linux-4.1.y
> 
> # uname -a
> Linux buildroot 4.1.18 #1 SMP Wed Mar 30 09:48:37 CEST 2016 armv7l GNU/Linux
> 
> # switch-config -d
> cpsw hw version 1.12 (0)
> 0   : type: vlan , vid = 1, untag_force = 0x3, reg_mcast = 0x3,
> unreg_mcast = 0x1, member_list = 0x3
> 
> 1   : type: mcast, vid = 1, addr = ff:ff:ff:ff:ff:ff, mcast_state = f,
> no super, port_mask = 0x3
> 2   : type: ucast, vid = 1, addr = 74:6a:8f:00:16:12, ucast_type =
> persistant, port_num = 0x0, Secure
> 3   : type: vlan , vid = 0, untag_force = 0x7, reg_mcast = 0x0,
> unreg_mcast = 0x1, member_list = 0x7
> 4   : type: mcast, vid = 1, addr = 01:00:5e:00:00:01, mcast_state = f,
> no super, port_mask = 0x3
> 5   : type: vlan , vid = 2, untag_force = 0x5, reg_mcast = 0x5,
> unreg_mcast = 0x1, member_list = 0x5
> 6   : type: mcast, vid = 2, addr = ff:ff:ff:ff:ff:ff, mcast_state = f,
> no super, port_mask = 0x5
> 7   : type: ucast, vid = 2, addr = 74:6a:8f:00:16:13, ucast_type =
> persistant, port_num = 0x0, Secure
> 8   : type: mcast, vid = 2, addr = 01:00:5e:00:00:01, mcast_state = f,
> no super, port_mask = 0x5
> 9   : type: vlan , vid = 100, untag_force = 0x0, reg_mcast = 0x5,
> unreg_mcast = 0x1, member_list = 0x5
> 10  : type: ucast, vid = 100, addr = 74:6a:8f:00:16:13, ucast_type =
> persistant, port_num = 0x0
> 11  : type: mcast, vid = 100, addr = ff:ff:ff:ff:ff:ff, mcast_state =
> f, no super, port_mask = 0x5
> 12  : type: mcast, vid = 2, addr = 01:80:c2:00:00:21, mcast_state = f,
> no super, port_mask = 0x5
> 13  : type: mcast, vid = 2, addr = 01:00:5e:03:1d:47, mcast_state = f,
> no super, port_mask = 0x5

I don't see multicast entry added to ALE table for 01:00:5e:03:1d:47,
can you run the receive command in background/another terminal and get
the dump when receiving on eth1 and eth1.100 as well and don't enable
tcpdump as it will put the switch in promiscuous mode.

> 
> # ifconfig -a
> can0      Link encap:UNSPEC  HWaddr
> 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
>           NOARP  MTU:16  Metric:1
>           RX packets:0 errors:0 dropped:0 overruns:0 frame:0
>           TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
>           collisions:0 txqueuelen:10
>           RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
>           Interrupt:165
> 
> eth0      Link encap:Ethernet  HWaddr 74:6A:8F:00:16:12
>           inet addr:192.168.254.254  Bcast:0.0.0.0  Mask:255.255.255.0
>           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
>           RX packets:0 errors:0 dropped:0 overruns:0 frame:0
>           TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
>           collisions:0 txqueuelen:1000
>           RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
>           Interrupt:175
> 
> eth1      Link encap:Ethernet  HWaddr 74:6A:8F:00:16:13
>           inet addr:192.168.1.233  Bcast:0.0.0.0  Mask:255.255.255.0
>           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
>           RX packets:245 errors:0 dropped:0 overruns:0 frame:0
>           TX packets:123 errors:0 dropped:0 overruns:0 carrier:0
>           collisions:0 txqueuelen:1000
>           RX bytes:40995 (40.0 KiB)  TX bytes:14086 (13.7 KiB)
> 
> eth1.100  Link encap:Ethernet  HWaddr 74:6A:8F:00:16:13
>           inet addr:192.168.100.2  Bcast:192.168.100.255  Mask:255.255.255.0
>           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
>           RX packets:65 errors:0 dropped:0 overruns:0 frame:0
>           TX packets:51 errors:0 dropped:0 overruns:0 carrier:0
>           collisions:0 txqueuelen:0
>           RX bytes:8369 (8.1 KiB)  TX bytes:6340 (6.1 KiB)
> 
> lo        Link encap:Local Loopback
>           inet addr:127.0.0.1  Mask:255.0.0.0
>           UP LOOPBACK RUNNING  MTU:65536  Metric:1
>           RX packets:4 errors:0 dropped:0 overruns:0 frame:0
>           TX packets:4 errors:0 dropped:0 overruns:0 carrier:0
>           collisions:0 txqueuelen:0
>           RX bytes:344 (344.0 B)  TX bytes:344 (344.0 B)
> 
> # tcpdump -e -f -Q in -i eth1
> tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
> listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
> 00:47:40.240731 06:15:4d:85:61:1e (oui Unknown) > 01:00:5e:03:1d:47
> (oui Unknown), ethertype 802.1Q (0x8100), length 65: vlan 100, p 0,
> ethertype IPv4, 192.168.100.1.59870 > 224.3.29.71.10000: UDP, length
> 19
> 00:47:45.259285 06:15:4d:85:61:1e (oui Unknown) > 74:6a:8f:00:16:13
> (oui Unknown), ethertype 802.1Q (0x8100), length 60: vlan 100, p 0,
> ethertype ARP, Reply 192.168.100.1 is-at 06:15:4d:85:61:1e (oui
> Unknown), length 42
> 
> # tcpdump -e -f -Q in -i eth1.100
> tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
> listening on eth1.100, link-type EN10MB (Ethernet), capture size 262144 bytes
> 00:48:52.477500 06:15:4d:85:61:1e (oui Unknown) > 01:00:5e:03:1d:47
> (oui Unknown), ethertype IPv4 (0x0800), length 61: 192.168.100.1.54382
>> 224.3.29.71.10000: UDP, length 19
> 00:48:57.486437 06:15:4d:85:61:1e (oui Unknown) > 74:6a:8f:00:16:13
> (oui Unknown), ethertype ARP (0x0806), length 56: Reply 192.168.100.1
> is-at 06:15:4d:85:61:1e (oui Unknown), length 42

You had received these packets as tcpdump will enable promiscuous mode
so that you receive all the packets from the wire.

> 
> What does "no super" mean and where can I see the "--fwd-state <value
> 0-3>" value/description?

Multicast supervisory packets are designated by the super bit in the
table entry. Supervisory packets are not dropped due to rate limiting,
OUI, or VLAN processing.

You can get the documentation in TRM under title "Multicast Forward
State (MCAST_FWD_STATE)" for forward state description.

Regards
Mugunthan V N

^ permalink raw reply

* Re: [PATCH v3 0/8] arm64: rockchip: Initial GeekBox enablement
From: Dinh Nguyen @ 2016-03-30 16:44 UTC (permalink / raw)
  To: Giuseppe CAVALLARO
  Cc: Tomeu Vizoso, Andreas Färber, Fabrice GASNIER,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Heiko Stübner, netdev-u79uwXL29TY76Z2rM5mHXA,
	open list:ARM/Rockchip SoC..., LAKML, Gabriel Fernandez,
	Alexandre TORGUE, Frank Schäfer, LKML
In-Reply-To: <56E801EB.6030107-qxv4g6HH51o@public.gmane.org>

On Tue, Mar 15, 2016 at 7:36 AM, Giuseppe CAVALLARO
<peppe.cavallaro-qxv4g6HH51o@public.gmane.org> wrote:
> Hello Tomeu
>
> On 3/15/2016 8:23 AM, Tomeu Vizoso wrote:
>>
>> Thanks.
>>
>> Btw, I have rebased on top of 4.5 this morning and I have noticed that
>> 88f8b1bb41c6 ("stmmac: Fix 'eth0: No PHY found' regression") got in
>> there, so I guess we have now a bunch of boards with broken network on
>> that release:(
>
>
>
> This is the status on my side: I am testing on an HW that has the
> Enhanced descriptors and all works fine.
>
> On this HW, if I force the driver to use the normal descriptor
> layout, I meet problems but using both net.git and net-next.
> So I suspect I cannot ply with this HW forcing the normal descriptors.
> But! That is helping me to check if, on net-next, the stmmac is
> actually  programming fine the normal desc case.
> I have just found another fix so I kindly ask you to apply the temp
> patch  attached and let me know.
> In details, I have noticed that the OWN bit was not set in the right
> TDES0.
>
> I also ask you to give me a log of the kernel where the stmmac was
> running fine. I would like to see which configuration it is selected
> at runtime by the driver on your box.
> From your previous logs (where the stmmac failed), it seems that
> the  problem is on normal desc but, to be honest, this is the first
> case I see a 3.50a with HW capability register and w/o Enhanced
> descriptors.
>

Are you still working on a fix for:

[    1.196110] libphy: PHY stmmac-0:ffffffff not found
[    1.200972] eth0: Could not attach to PHY
[    1.204991] stmmac_open: Cannot attach to PHY (error: -19)

I see the error still there as of linux-next 20160330.

Dinh
--
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

* [PATCH 4/4] net: w5100: support SPI interface mode
From: Akinobu Mita @ 2016-03-30 16:38 UTC (permalink / raw)
  To: netdev; +Cc: Akinobu Mita, Mike Sinkovsky, David S. Miller
In-Reply-To: <1459355920-14623-1-git-send-email-akinobu.mita@gmail.com>

This adds new w5100-spi driver which shares the bus interface
independent code with existing w5100 driver.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Mike Sinkovsky <msink@permonline.ru>
Cc: David S. Miller <davem@davemloft.net>
---
 drivers/net/ethernet/wiznet/Kconfig     |  14 ++++
 drivers/net/ethernet/wiznet/Makefile    |   1 +
 drivers/net/ethernet/wiznet/w5100-spi.c | 135 ++++++++++++++++++++++++++++++++
 3 files changed, 150 insertions(+)
 create mode 100644 drivers/net/ethernet/wiznet/w5100-spi.c

diff --git a/drivers/net/ethernet/wiznet/Kconfig b/drivers/net/ethernet/wiznet/Kconfig
index f98b91d..d1ab353 100644
--- a/drivers/net/ethernet/wiznet/Kconfig
+++ b/drivers/net/ethernet/wiznet/Kconfig
@@ -69,4 +69,18 @@ config WIZNET_BUS_ANY
 	  Performance may decrease compared to explicitly selected bus mode.
 endchoice
 
+config WIZNET_W5100_SPI
+	tristate "WIZnet W5100 Ethernet support for SPI mode"
+	depends on WIZNET_BUS_ANY
+	depends on SPI
+	---help---
+	  In SPI mode host system accesses registers using SPI protocol
+	  (mode 0) on the SPI bus.
+
+	  Performance decreases compared to other bus interface mode.
+	  In W5100 SPI mode, burst READ/WRITE processing are not provided.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called w5100-spi.
+
 endif # NET_VENDOR_WIZNET
diff --git a/drivers/net/ethernet/wiznet/Makefile b/drivers/net/ethernet/wiznet/Makefile
index c614535..1e05e1a 100644
--- a/drivers/net/ethernet/wiznet/Makefile
+++ b/drivers/net/ethernet/wiznet/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_WIZNET_W5100) += w5100.o
+obj-$(CONFIG_WIZNET_W5100_SPI) += w5100-spi.o
 obj-$(CONFIG_WIZNET_W5300) += w5300.o
diff --git a/drivers/net/ethernet/wiznet/w5100-spi.c b/drivers/net/ethernet/wiznet/w5100-spi.c
new file mode 100644
index 0000000..cf2b845
--- /dev/null
+++ b/drivers/net/ethernet/wiznet/w5100-spi.c
@@ -0,0 +1,135 @@
+/*
+ * Ethernet driver for the WIZnet W5100 chip.
+ *
+ * Copyright (C) 2016 Akinobu Mita <akinobu.mita@gmail.com>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/spi/spi.h>
+
+#include "w5100.h"
+
+#define W5100_SPI_WRITE_OPCODE 0xf0
+#define W5100_SPI_READ_OPCODE 0x0f
+
+static int w5100_spi_read(struct net_device *ndev, u16 addr)
+{
+	struct spi_device *spi = to_spi_device(ndev->dev.parent);
+	u8 cmd[3] = { W5100_SPI_READ_OPCODE, addr >> 8, addr & 0xff };
+	u8 data;
+	int ret;
+
+	ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, 1);
+
+	return ret ? ret : data;
+}
+
+static int w5100_spi_write(struct net_device *ndev, u16 addr, u8 data)
+{
+	struct spi_device *spi = to_spi_device(ndev->dev.parent);
+	u8 cmd[4] = { W5100_SPI_WRITE_OPCODE, addr >> 8, addr & 0xff, data};
+
+	return spi_write(spi, cmd, sizeof(cmd));
+}
+
+static int w5100_spi_read16(struct net_device *ndev, u16 addr)
+{
+	u16 data;
+	int ret;
+
+	ret = w5100_spi_read(ndev, addr);
+	if (ret < 0)
+		return ret;
+	data = ret << 8;
+	ret = w5100_spi_read(ndev, addr + 1);
+
+	return ret < 0 ? ret : data | ret;
+}
+
+static int w5100_spi_write16(struct net_device *ndev, u16 addr, u16 data)
+{
+	int ret;
+
+	ret = w5100_spi_write(ndev, addr, data >> 8);
+	if (ret)
+		return ret;
+
+	return w5100_spi_write(ndev, addr + 1, data & 0xff);
+}
+
+static int w5100_spi_readbulk(struct net_device *ndev, u16 addr, u8 *buf,
+			      int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		int ret = w5100_spi_read(ndev, addr + i);
+
+		if (ret < 0)
+			return ret;
+		buf[i] = ret;
+	}
+
+	return 0;
+}
+
+static int w5100_spi_writebulk(struct net_device *ndev, u16 addr, const u8 *buf,
+			       int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		int ret = w5100_spi_write(ndev, addr + i, buf[i]);
+
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static const struct w5100_ops w5100_spi_ops = {
+	.may_sleep = true,
+	.read = w5100_spi_read,
+	.write = w5100_spi_write,
+	.read16 = w5100_spi_read16,
+	.write16 = w5100_spi_write16,
+	.readbulk = w5100_spi_readbulk,
+	.writebulk = w5100_spi_writebulk,
+};
+
+static int w5100_spi_probe(struct spi_device *spi)
+{
+	return w5100_probe(&spi->dev, &w5100_spi_ops, NULL, spi->irq, -EINVAL);
+}
+
+static int w5100_spi_remove(struct spi_device *spi)
+{
+	return w5100_remove(&spi->dev);
+}
+
+static const struct spi_device_id w5100_spi_ids[] = {
+	{ "w5100", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(spi, w5100_spi_ids);
+
+static struct spi_driver w5100_spi_driver = {
+	.driver		= {
+		.name	= "w5100",
+		.pm	= &w5100_pm_ops,
+	},
+	.probe		= w5100_spi_probe,
+	.remove		= w5100_spi_remove,
+	.id_table	= w5100_spi_ids,
+};
+module_spi_driver(w5100_spi_driver);
+
+MODULE_DESCRIPTION("WIZnet W5100 Ethernet driver for SPI mode");
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
+MODULE_LICENSE("GPL");
-- 
2.5.0

^ permalink raw reply related

* [PATCH 3/4] net: w5100: enable to support sleepable register access interface
From: Akinobu Mita @ 2016-03-30 16:38 UTC (permalink / raw)
  To: netdev; +Cc: Akinobu Mita, Mike Sinkovsky, David S. Miller
In-Reply-To: <1459355920-14623-1-git-send-email-akinobu.mita@gmail.com>

SPI transfer routines are callable only from contexts that can sleep.

This adds ability to tell the core driver that the interface mode
cannot access w5100 register on atomic contexts.  In this case,
workqueue and threaded irq are required.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Mike Sinkovsky <msink@permonline.ru>
Cc: David S. Miller <davem@davemloft.net>
---
 drivers/net/ethernet/wiznet/w5100.c | 184 ++++++++++++++++++++++++++++--------
 drivers/net/ethernet/wiznet/w5100.h |   1 +
 2 files changed, 148 insertions(+), 37 deletions(-)

diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c
index 7290096..a2a0c59 100644
--- a/drivers/net/ethernet/wiznet/w5100.c
+++ b/drivers/net/ethernet/wiznet/w5100.c
@@ -102,6 +102,13 @@ struct w5100_priv {
 	bool promisc;
 	u32 msg_enable;
 
+	struct workqueue_struct *xfer_wq;
+	struct work_struct rx_work;
+	struct sk_buff_head tx_queue;
+	struct work_struct tx_work;
+	struct work_struct setrx_work;
+	struct work_struct restart_work;
+
 	struct w5100_mmio_priv mmio_priv;
 };
 
@@ -608,7 +615,7 @@ static void w5100_get_regs(struct net_device *ndev,
 	w5100_readbulk(priv, W5100_S0_REGS, buf, W5100_S0_REGS_LEN);
 }
 
-static void w5100_tx_timeout(struct net_device *ndev)
+static void w5100_restart(struct net_device *ndev)
 {
 	struct w5100_priv *priv = netdev_priv(ndev);
 
@@ -620,12 +627,28 @@ static void w5100_tx_timeout(struct net_device *ndev)
 	netif_wake_queue(ndev);
 }
 
-static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
+static void w5100_restart_work(struct work_struct *work)
+{
+	struct w5100_priv *priv = container_of(work, struct w5100_priv,
+					       restart_work);
+
+	w5100_restart(priv->ndev);
+}
+
+static void w5100_tx_timeout(struct net_device *ndev)
 {
 	struct w5100_priv *priv = netdev_priv(ndev);
-	u16 offset;
 
-	netif_stop_queue(ndev);
+	if (priv->ops->may_sleep)
+		schedule_work(&priv->restart_work);
+	else
+		w5100_restart(ndev);
+}
+
+static void w5100_tx_skb(struct net_device *ndev, struct sk_buff *skb)
+{
+	struct w5100_priv *priv = netdev_priv(ndev);
+	u16 offset;
 
 	offset = w5100_read16(priv, W5100_S0_TX_WR);
 	w5100_writebuf(priv, offset, skb->data, skb->len);
@@ -635,47 +658,94 @@ static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
 	dev_kfree_skb(skb);
 
 	w5100_command(priv, S0_CR_SEND);
+}
+
+static void w5100_tx_work(struct work_struct *work)
+{
+	struct w5100_priv *priv = container_of(work, struct w5100_priv,
+					       tx_work);
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&priv->tx_queue)))
+		w5100_tx_skb(priv->ndev, skb);
+}
+
+static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct w5100_priv *priv = netdev_priv(ndev);
+
+	netif_stop_queue(ndev);
+
+	if (priv->ops->may_sleep) {
+		skb_queue_tail(&priv->tx_queue, skb);
+		queue_work(priv->xfer_wq, &priv->tx_work);
+	} else {
+		w5100_tx_skb(ndev, skb);
+	}
 
 	return NETDEV_TX_OK;
 }
 
-static int w5100_napi_poll(struct napi_struct *napi, int budget)
+static struct sk_buff *w5100_rx_skb(struct net_device *ndev)
 {
-	struct w5100_priv *priv = container_of(napi, struct w5100_priv, napi);
-	struct net_device *ndev = priv->ndev;
+	struct w5100_priv *priv = netdev_priv(ndev);
 	struct sk_buff *skb;
-	int rx_count;
 	u16 rx_len;
 	u16 offset;
 	u8 header[2];
+	u16 rx_buf_len = w5100_read16(priv, W5100_S0_RX_RSR);
 
-	for (rx_count = 0; rx_count < budget; rx_count++) {
-		u16 rx_buf_len = w5100_read16(priv, W5100_S0_RX_RSR);
-		if (rx_buf_len == 0)
-			break;
+	if (rx_buf_len == 0)
+		return NULL;
 
-		offset = w5100_read16(priv, W5100_S0_RX_RD);
-		w5100_readbuf(priv, offset, header, 2);
-		rx_len = get_unaligned_be16(header) - 2;
-
-		skb = netdev_alloc_skb_ip_align(ndev, rx_len);
-		if (unlikely(!skb)) {
-			w5100_write16(priv, W5100_S0_RX_RD,
-					    offset + rx_buf_len);
-			w5100_command(priv, S0_CR_RECV);
-			ndev->stats.rx_dropped++;
-			return -ENOMEM;
-		}
+	offset = w5100_read16(priv, W5100_S0_RX_RD);
+	w5100_readbuf(priv, offset, header, 2);
+	rx_len = get_unaligned_be16(header) - 2;
 
-		skb_put(skb, rx_len);
-		w5100_readbuf(priv, offset + 2, skb->data, rx_len);
-		w5100_write16(priv, W5100_S0_RX_RD, offset + 2 + rx_len);
+	skb = netdev_alloc_skb_ip_align(ndev, rx_len);
+	if (unlikely(!skb)) {
+		w5100_write16(priv, W5100_S0_RX_RD, offset + rx_buf_len);
 		w5100_command(priv, S0_CR_RECV);
-		skb->protocol = eth_type_trans(skb, ndev);
+		ndev->stats.rx_dropped++;
+		return NULL;
+	}
+
+	skb_put(skb, rx_len);
+	w5100_readbuf(priv, offset + 2, skb->data, rx_len);
+	w5100_write16(priv, W5100_S0_RX_RD, offset + 2 + rx_len);
+	w5100_command(priv, S0_CR_RECV);
+	skb->protocol = eth_type_trans(skb, ndev);
+
+	ndev->stats.rx_packets++;
+	ndev->stats.rx_bytes += rx_len;
+
+	return skb;
+}
+
+static void w5100_rx_work(struct work_struct *work)
+{
+	struct w5100_priv *priv = container_of(work, struct w5100_priv,
+					       rx_work);
+	struct sk_buff *skb;
+
+	while ((skb = w5100_rx_skb(priv->ndev)))
+		netif_rx_ni(skb);
+
+	w5100_write(priv, W5100_IMR, IR_S0);
+}
+
+static int w5100_napi_poll(struct napi_struct *napi, int budget)
+{
+	struct w5100_priv *priv = container_of(napi, struct w5100_priv, napi);
+	int rx_count;
+
+	for (rx_count = 0; rx_count < budget; rx_count++) {
+		struct sk_buff *skb = w5100_rx_skb(priv->ndev);
 
-		netif_receive_skb(skb);
-		ndev->stats.rx_packets++;
-		ndev->stats.rx_bytes += rx_len;
+		if (skb)
+			netif_receive_skb(skb);
+		else
+			break;
 	}
 
 	if (rx_count < budget) {
@@ -702,10 +772,12 @@ static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
 	}
 
 	if (ir & S0_IR_RECV) {
-		if (napi_schedule_prep(&priv->napi)) {
-			w5100_write(priv, W5100_IMR, 0);
+		w5100_write(priv, W5100_IMR, 0);
+
+		if (priv->ops->may_sleep)
+			queue_work(priv->xfer_wq, &priv->rx_work);
+		else if (napi_schedule_prep(&priv->napi))
 			__napi_schedule(&priv->napi);
-		}
 	}
 
 	return IRQ_HANDLED;
@@ -729,6 +801,14 @@ static irqreturn_t w5100_detect_link(int irq, void *ndev_instance)
 	return IRQ_HANDLED;
 }
 
+static void w5100_setrx_work(struct work_struct *work)
+{
+	struct w5100_priv *priv = container_of(work, struct w5100_priv,
+					       setrx_work);
+
+	w5100_hw_start(priv);
+}
+
 static void w5100_set_rx_mode(struct net_device *ndev)
 {
 	struct w5100_priv *priv = netdev_priv(ndev);
@@ -736,7 +816,11 @@ static void w5100_set_rx_mode(struct net_device *ndev)
 
 	if (priv->promisc != set_promisc) {
 		priv->promisc = set_promisc;
-		w5100_hw_start(priv);
+
+		if (priv->ops->may_sleep)
+			schedule_work(&priv->setrx_work);
+		else
+			w5100_hw_start(priv);
 	}
 }
 
@@ -860,6 +944,18 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops, u8 *mac_addr,
 	if (err < 0)
 		goto err_register;
 
+	priv->xfer_wq = create_workqueue(netdev_name(ndev));
+	if (!priv->xfer_wq) {
+		err = -ENOMEM;
+		goto err_wq;
+	}
+
+	skb_queue_head_init(&priv->tx_queue);
+	INIT_WORK(&priv->rx_work, w5100_rx_work);
+	INIT_WORK(&priv->tx_work, w5100_tx_work);
+	INIT_WORK(&priv->setrx_work, w5100_setrx_work);
+	INIT_WORK(&priv->restart_work, w5100_restart_work);
+
 	if (mac_addr)
 		memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
 	else
@@ -877,8 +973,14 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops, u8 *mac_addr,
 		goto err_hw;
 	}
 
-	err = request_irq(priv->irq, w5100_interrupt, IRQF_TRIGGER_LOW,
-			  netdev_name(ndev), ndev);
+	if (ops->may_sleep) {
+		err = request_threaded_irq(priv->irq, NULL, w5100_interrupt,
+					   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					   netdev_name(ndev), ndev);
+	} else {
+		err = request_irq(priv->irq, w5100_interrupt,
+				  IRQF_TRIGGER_LOW, netdev_name(ndev), ndev);
+	}
 	if (err)
 		goto err_hw;
 
@@ -903,6 +1005,8 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops, u8 *mac_addr,
 err_gpio:
 	free_irq(priv->irq, ndev);
 err_hw:
+	destroy_workqueue(priv->xfer_wq);
+err_wq:
 	unregister_netdev(ndev);
 err_register:
 	free_netdev(ndev);
@@ -920,6 +1024,12 @@ int w5100_remove(struct device *dev)
 	if (gpio_is_valid(priv->link_gpio))
 		free_irq(priv->link_irq, ndev);
 
+	flush_work(&priv->setrx_work);
+	flush_work(&priv->restart_work);
+	flush_workqueue(priv->xfer_wq);
+	skb_queue_purge(&priv->tx_queue);
+	destroy_workqueue(priv->xfer_wq);
+
 	unregister_netdev(ndev);
 	free_netdev(ndev);
 	return 0;
diff --git a/drivers/net/ethernet/wiznet/w5100.h b/drivers/net/ethernet/wiznet/w5100.h
index 368862d..292fbd5 100644
--- a/drivers/net/ethernet/wiznet/w5100.h
+++ b/drivers/net/ethernet/wiznet/w5100.h
@@ -8,6 +8,7 @@
  */
 
 struct w5100_ops {
+	bool may_sleep;
 	int (*read)(struct net_device *ndev, u16 addr);
 	int (*write)(struct net_device *ndev, u16 addr, u8 data);
 	int (*read16)(struct net_device *ndev, u16 addr);
-- 
2.5.0

^ permalink raw reply related

* [PATCH 2/4] net: w5100: add ability to support other bus interface
From: Akinobu Mita @ 2016-03-30 16:38 UTC (permalink / raw)
  To: netdev; +Cc: Akinobu Mita, Mike Sinkovsky, David S. Miller
In-Reply-To: <1459355920-14623-1-git-send-email-akinobu.mita@gmail.com>

The w5100 driver currently only supports direct and indirect bus
interface mode which use MMIO space for accessing w5100 registers.

In order to support SPI interface mode which is supported by W5100 chip,
this makes the bus interface abstraction layer more generic so that
separated w5100-spi driver can use w5100 driver as core module.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Mike Sinkovsky <msink@permonline.ru>
Cc: David S. Miller <davem@davemloft.net>
---
 drivers/net/ethernet/wiznet/w5100.c | 588 ++++++++++++++++++++++++------------
 drivers/net/ethernet/wiznet/w5100.h |  26 ++
 2 files changed, 415 insertions(+), 199 deletions(-)
 create mode 100644 drivers/net/ethernet/wiznet/w5100.h

diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c
index f4b7200..7290096 100644
--- a/drivers/net/ethernet/wiznet/w5100.c
+++ b/drivers/net/ethernet/wiznet/w5100.c
@@ -27,6 +27,8 @@
 #include <linux/irq.h>
 #include <linux/gpio.h>
 
+#include "w5100.h"
+
 #define DRV_NAME	"w5100"
 #define DRV_VERSION	"2012-04-04"
 
@@ -76,25 +78,21 @@ MODULE_LICENSE("GPL");
 #define W5100_S0_REGS_LEN	0x0040
 
 #define W5100_TX_MEM_START	0x4000
-#define W5100_TX_MEM_END	0x5fff
-#define W5100_TX_MEM_MASK	0x1fff
+#define W5100_TX_MEM_SIZE	0x2000
 #define W5100_RX_MEM_START	0x6000
-#define W5100_RX_MEM_END	0x7fff
-#define W5100_RX_MEM_MASK	0x1fff
+#define W5100_RX_MEM_SIZE	0x2000
 
 /*
  * Device driver private data structure
  */
-struct w5100_priv {
+
+struct w5100_mmio_priv {
 	void __iomem *base;
 	spinlock_t reg_lock;
-	bool indirect;
-	u8   (*read)(struct w5100_priv *priv, u16 addr);
-	void (*write)(struct w5100_priv *priv, u16 addr, u8 data);
-	u16  (*read16)(struct w5100_priv *priv, u16 addr);
-	void (*write16)(struct w5100_priv *priv, u16 addr, u16 data);
-	void (*readbuf)(struct w5100_priv *priv, u16 addr, u8 *buf, int len);
-	void (*writebuf)(struct w5100_priv *priv, u16 addr, u8 *buf, int len);
+};
+
+struct w5100_priv {
+	const struct w5100_ops *ops;
 	int irq;
 	int link_irq;
 	int link_gpio;
@@ -103,6 +101,8 @@ struct w5100_priv {
 	struct net_device *ndev;
 	bool promisc;
 	u32 msg_enable;
+
+	struct w5100_mmio_priv mmio_priv;
 };
 
 /************************************************************************
@@ -111,72 +111,117 @@ struct w5100_priv {
  *
  ***********************************************************************/
 
+static inline struct w5100_mmio_priv *to_w5100_mmio_priv(struct net_device *dev)
+{
+	struct w5100_priv *priv = netdev_priv(dev);
+
+	return &priv->mmio_priv;
+}
+
+static inline void __iomem *w5100_mmio(struct net_device *ndev)
+{
+	struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
+
+	return mmio_priv->base;
+}
+
 /*
  * In direct address mode host system can directly access W5100 registers
  * after mapping to Memory-Mapped I/O space.
  *
  * 0x8000 bytes are required for memory space.
  */
-static inline u8 w5100_read_direct(struct w5100_priv *priv, u16 addr)
+static inline int w5100_read_direct(struct net_device *ndev, u16 addr)
 {
-	return ioread8(priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+	return ioread8(w5100_mmio(ndev) + (addr << CONFIG_WIZNET_BUS_SHIFT));
 }
 
-static inline void __w5100_write_direct(struct w5100_priv *priv, u16 addr,
-					u8 data)
+static inline int __w5100_write_direct(struct net_device *ndev, u16 addr,
+				       u8 data)
 {
-	iowrite8(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+	iowrite8(data, w5100_mmio(ndev) + (addr << CONFIG_WIZNET_BUS_SHIFT));
+
+	return 0;
 }
 
-static inline void w5100_write_direct(struct w5100_priv *priv,
-				      u16 addr, u8 data)
+static inline int w5100_write_direct(struct net_device *ndev, u16 addr, u8 data)
 {
-	__w5100_write_direct(priv, addr, data);
+	__w5100_write_direct(ndev, addr, data);
 	mmiowb();
+
+	return 0;
 }
 
-static u16 w5100_read16_direct(struct w5100_priv *priv, u16 addr)
+static int w5100_read16_direct(struct net_device *ndev, u16 addr)
 {
 	u16 data;
-	data  = w5100_read_direct(priv, addr) << 8;
-	data |= w5100_read_direct(priv, addr + 1);
+	data  = w5100_read_direct(ndev, addr) << 8;
+	data |= w5100_read_direct(ndev, addr + 1);
 	return data;
 }
 
-static void w5100_write16_direct(struct w5100_priv *priv, u16 addr, u16 data)
+static int w5100_write16_direct(struct net_device *ndev, u16 addr, u16 data)
 {
-	__w5100_write_direct(priv, addr, data >> 8);
-	__w5100_write_direct(priv, addr + 1, data);
+	__w5100_write_direct(ndev, addr, data >> 8);
+	__w5100_write_direct(ndev, addr + 1, data);
 	mmiowb();
+
+	return 0;
 }
 
-static void w5100_readbuf_direct(struct w5100_priv *priv,
-				 u16 offset, u8 *buf, int len)
+static int w5100_readbulk_direct(struct net_device *ndev, u16 addr, u8 *buf,
+				 int len)
 {
-	u16 addr = W5100_RX_MEM_START + (offset & W5100_RX_MEM_MASK);
 	int i;
 
-	for (i = 0; i < len; i++, addr++) {
-		if (unlikely(addr > W5100_RX_MEM_END))
-			addr = W5100_RX_MEM_START;
-		*buf++ = w5100_read_direct(priv, addr);
-	}
+	for (i = 0; i < len; i++, addr++)
+		*buf++ = w5100_read_direct(ndev, addr);
+
+	return 0;
 }
 
-static void w5100_writebuf_direct(struct w5100_priv *priv,
-				  u16 offset, u8 *buf, int len)
+static int w5100_writebulk_direct(struct net_device *ndev, u16 addr,
+				  const u8 *buf, int len)
 {
-	u16 addr = W5100_TX_MEM_START + (offset & W5100_TX_MEM_MASK);
 	int i;
 
-	for (i = 0; i < len; i++, addr++) {
-		if (unlikely(addr > W5100_TX_MEM_END))
-			addr = W5100_TX_MEM_START;
-		__w5100_write_direct(priv, addr, *buf++);
-	}
+	for (i = 0; i < len; i++, addr++)
+		__w5100_write_direct(ndev, addr, *buf++);
+
 	mmiowb();
+
+	return 0;
 }
 
+static int w5100_mmio_init(struct net_device *ndev)
+{
+	struct platform_device *pdev = to_platform_device(ndev->dev.parent);
+	struct w5100_priv *priv = netdev_priv(ndev);
+	struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
+	struct resource *mem;
+
+	spin_lock_init(&mmio_priv->reg_lock);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mmio_priv->base = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(mmio_priv->base))
+		return PTR_ERR(mmio_priv->base);
+
+	netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, priv->irq);
+
+	return 0;
+}
+
+static const struct w5100_ops w5100_mmio_direct_ops = {
+	.read = w5100_read_direct,
+	.write = w5100_write_direct,
+	.read16 = w5100_read16_direct,
+	.write16 = w5100_write16_direct,
+	.readbulk = w5100_readbulk_direct,
+	.writebulk = w5100_writebulk_direct,
+	.init = w5100_mmio_init,
+};
+
 /*
  * In indirect address mode host system indirectly accesses registers by
  * using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data
@@ -188,121 +233,276 @@ static void w5100_writebuf_direct(struct w5100_priv *priv,
 #define W5100_IDM_AR		0x01   /* Indirect Mode Address Register */
 #define W5100_IDM_DR		0x03   /* Indirect Mode Data Register */
 
-static u8 w5100_read_indirect(struct w5100_priv *priv, u16 addr)
+static int w5100_read_indirect(struct net_device *ndev, u16 addr)
 {
+	struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
 	unsigned long flags;
 	u8 data;
 
-	spin_lock_irqsave(&priv->reg_lock, flags);
-	w5100_write16_direct(priv, W5100_IDM_AR, addr);
-	data = w5100_read_direct(priv, W5100_IDM_DR);
-	spin_unlock_irqrestore(&priv->reg_lock, flags);
+	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+	data = w5100_read_direct(ndev, W5100_IDM_DR);
+	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
 
 	return data;
 }
 
-static void w5100_write_indirect(struct w5100_priv *priv, u16 addr, u8 data)
+static int w5100_write_indirect(struct net_device *ndev, u16 addr, u8 data)
 {
+	struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
 	unsigned long flags;
 
-	spin_lock_irqsave(&priv->reg_lock, flags);
-	w5100_write16_direct(priv, W5100_IDM_AR, addr);
-	w5100_write_direct(priv, W5100_IDM_DR, data);
-	spin_unlock_irqrestore(&priv->reg_lock, flags);
+	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+	w5100_write_direct(ndev, W5100_IDM_DR, data);
+	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
+
+	return 0;
 }
 
-static u16 w5100_read16_indirect(struct w5100_priv *priv, u16 addr)
+static int w5100_read16_indirect(struct net_device *ndev, u16 addr)
 {
+	struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
 	unsigned long flags;
 	u16 data;
 
-	spin_lock_irqsave(&priv->reg_lock, flags);
-	w5100_write16_direct(priv, W5100_IDM_AR, addr);
-	data  = w5100_read_direct(priv, W5100_IDM_DR) << 8;
-	data |= w5100_read_direct(priv, W5100_IDM_DR);
-	spin_unlock_irqrestore(&priv->reg_lock, flags);
+	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+	data  = w5100_read_direct(ndev, W5100_IDM_DR) << 8;
+	data |= w5100_read_direct(ndev, W5100_IDM_DR);
+	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
 
 	return data;
 }
 
-static void w5100_write16_indirect(struct w5100_priv *priv, u16 addr, u16 data)
+static int w5100_write16_indirect(struct net_device *ndev, u16 addr, u16 data)
 {
+	struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
 	unsigned long flags;
 
-	spin_lock_irqsave(&priv->reg_lock, flags);
-	w5100_write16_direct(priv, W5100_IDM_AR, addr);
-	__w5100_write_direct(priv, W5100_IDM_DR, data >> 8);
-	w5100_write_direct(priv, W5100_IDM_DR, data);
-	spin_unlock_irqrestore(&priv->reg_lock, flags);
+	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+	__w5100_write_direct(ndev, W5100_IDM_DR, data >> 8);
+	w5100_write_direct(ndev, W5100_IDM_DR, data);
+	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
+
+	return 0;
 }
 
-static void w5100_readbuf_indirect(struct w5100_priv *priv,
-				   u16 offset, u8 *buf, int len)
+static int w5100_readbulk_indirect(struct net_device *ndev, u16 addr, u8 *buf,
+				   int len)
 {
-	u16 addr = W5100_RX_MEM_START + (offset & W5100_RX_MEM_MASK);
+	struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
 	unsigned long flags;
 	int i;
 
-	spin_lock_irqsave(&priv->reg_lock, flags);
-	w5100_write16_direct(priv, W5100_IDM_AR, addr);
+	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+
+	for (i = 0; i < len; i++)
+		*buf++ = w5100_read_direct(ndev, W5100_IDM_DR);
 
-	for (i = 0; i < len; i++, addr++) {
-		if (unlikely(addr > W5100_RX_MEM_END)) {
-			addr = W5100_RX_MEM_START;
-			w5100_write16_direct(priv, W5100_IDM_AR, addr);
-		}
-		*buf++ = w5100_read_direct(priv, W5100_IDM_DR);
-	}
 	mmiowb();
-	spin_unlock_irqrestore(&priv->reg_lock, flags);
+	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
+
+	return 0;
 }
 
-static void w5100_writebuf_indirect(struct w5100_priv *priv,
-				    u16 offset, u8 *buf, int len)
+static int w5100_writebulk_indirect(struct net_device *ndev, u16 addr,
+				    const u8 *buf, int len)
 {
-	u16 addr = W5100_TX_MEM_START + (offset & W5100_TX_MEM_MASK);
+	struct w5100_mmio_priv *mmio_priv = to_w5100_mmio_priv(ndev);
 	unsigned long flags;
 	int i;
 
-	spin_lock_irqsave(&priv->reg_lock, flags);
-	w5100_write16_direct(priv, W5100_IDM_AR, addr);
+	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
+	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
+
+	for (i = 0; i < len; i++)
+		__w5100_write_direct(ndev, W5100_IDM_DR, *buf++);
 
-	for (i = 0; i < len; i++, addr++) {
-		if (unlikely(addr > W5100_TX_MEM_END)) {
-			addr = W5100_TX_MEM_START;
-			w5100_write16_direct(priv, W5100_IDM_AR, addr);
-		}
-		__w5100_write_direct(priv, W5100_IDM_DR, *buf++);
-	}
 	mmiowb();
-	spin_unlock_irqrestore(&priv->reg_lock, flags);
+	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
+
+	return 0;
 }
 
+static int w5100_reset_indirect(struct net_device *ndev)
+{
+	w5100_write_direct(ndev, W5100_MR, MR_RST);
+	mdelay(5);
+	w5100_write_direct(ndev, W5100_MR, MR_PB | MR_AI | MR_IND);
+
+	return 0;
+}
+
+static const struct w5100_ops w5100_mmio_indirect_ops = {
+	.read = w5100_read_indirect,
+	.write = w5100_write_indirect,
+	.read16 = w5100_read16_indirect,
+	.write16 = w5100_write16_indirect,
+	.readbulk = w5100_readbulk_indirect,
+	.writebulk = w5100_writebulk_indirect,
+	.init = w5100_mmio_init,
+	.reset = w5100_reset_indirect,
+};
+
 #if defined(CONFIG_WIZNET_BUS_DIRECT)
-#define w5100_read	w5100_read_direct
-#define w5100_write	w5100_write_direct
-#define w5100_read16	w5100_read16_direct
-#define w5100_write16	w5100_write16_direct
-#define w5100_readbuf	w5100_readbuf_direct
-#define w5100_writebuf	w5100_writebuf_direct
+
+static int w5100_read(struct w5100_priv *priv, u16 addr)
+{
+	return w5100_read_direct(priv->ndev, addr);
+}
+
+static int w5100_write(struct w5100_priv *priv, u16 addr, u8 data)
+{
+	return w5100_write_direct(priv->ndev, addr, data);
+}
+
+static int w5100_read16(struct w5100_priv *priv, u16 addr)
+{
+	return w5100_read16_direct(priv->ndev, addr);
+}
+
+static int w5100_write16(struct w5100_priv *priv, u16 addr, u16 data)
+{
+	return w5100_write16_direct(priv->ndev, addr, data);
+}
+
+static int w5100_readbulk(struct w5100_priv *priv, u16 addr, u8 *buf, int len)
+{
+	return w5100_readbulk_direct(priv->ndev, addr, buf, len);
+}
+
+static int w5100_writebulk(struct w5100_priv *priv, u16 addr, const u8 *buf,
+			   int len)
+{
+	return w5100_writebulk_direct(priv->ndev, addr, buf, len);
+}
 
 #elif defined(CONFIG_WIZNET_BUS_INDIRECT)
-#define w5100_read	w5100_read_indirect
-#define w5100_write	w5100_write_indirect
-#define w5100_read16	w5100_read16_indirect
-#define w5100_write16	w5100_write16_indirect
-#define w5100_readbuf	w5100_readbuf_indirect
-#define w5100_writebuf	w5100_writebuf_indirect
+
+static int w5100_read(struct w5100_priv *priv, u16 addr)
+{
+	return w5100_read_indirect(priv->ndev, addr);
+}
+
+static int w5100_write(struct w5100_priv *priv, u16 addr, u8 data)
+{
+	return w5100_write_indirect(priv->ndev, addr, data);
+}
+
+static int w5100_read16(struct w5100_priv *priv, u16 addr)
+{
+	return w5100_read16_indirect(priv->ndev, addr);
+}
+
+static int w5100_write16(struct w5100_priv *priv, u16 addr, u16 data)
+{
+	return w5100_write16_indirect(priv->ndev, addr, data);
+}
+
+static int w5100_readbulk(struct w5100_priv *priv, u16 addr, u8 *buf, int len)
+{
+	return w5100_readbulk_indirect(priv->ndev, addr, buf, len);
+}
+
+static int w5100_writebulk(struct w5100_priv *priv, u16 addr, const u8 *buf,
+			   int len)
+{
+	return w5100_writebulk_indirect(priv->ndev, addr, buf, len);
+}
 
 #else /* CONFIG_WIZNET_BUS_ANY */
-#define w5100_read	priv->read
-#define w5100_write	priv->write
-#define w5100_read16	priv->read16
-#define w5100_write16	priv->write16
-#define w5100_readbuf	priv->readbuf
-#define w5100_writebuf	priv->writebuf
+
+static int w5100_read(struct w5100_priv *priv, u16 addr)
+{
+	return priv->ops->read(priv->ndev, addr);
+}
+
+static int w5100_write(struct w5100_priv *priv, u16 addr, u8 data)
+{
+	return priv->ops->write(priv->ndev, addr, data);
+}
+
+static int w5100_read16(struct w5100_priv *priv, u16 addr)
+{
+	return priv->ops->read16(priv->ndev, addr);
+}
+
+static int w5100_write16(struct w5100_priv *priv, u16 addr, u16 data)
+{
+	return priv->ops->write16(priv->ndev, addr, data);
+}
+
+static int w5100_readbulk(struct w5100_priv *priv, u16 addr, u8 *buf, int len)
+{
+	return priv->ops->readbulk(priv->ndev, addr, buf, len);
+}
+
+static int w5100_writebulk(struct w5100_priv *priv, u16 addr, const u8 *buf,
+			   int len)
+{
+	return priv->ops->writebulk(priv->ndev, addr, buf, len);
+}
+
 #endif
 
+static int w5100_readbuf(struct w5100_priv *priv, u16 offset, u8 *buf, int len)
+{
+	u16 addr;
+	int remain = 0;
+	int ret;
+
+	offset %= W5100_RX_MEM_SIZE;
+	addr = W5100_RX_MEM_START + offset;
+
+	if (offset + len > W5100_RX_MEM_SIZE) {
+		remain = (offset + len) % W5100_RX_MEM_SIZE;
+		len = W5100_RX_MEM_SIZE - offset;
+	}
+
+	ret = w5100_readbulk(priv, addr, buf, len);
+	if (ret || !remain)
+		return ret;
+
+	return w5100_readbulk(priv, W5100_RX_MEM_START, buf + len, remain);
+}
+
+static int w5100_writebuf(struct w5100_priv *priv, u16 offset, const u8 *buf,
+			  int len)
+{
+	u16 addr;
+	int ret;
+	int remain = 0;
+
+	offset %= W5100_TX_MEM_SIZE;
+	addr = W5100_TX_MEM_START + offset;
+
+	if (offset + len > W5100_TX_MEM_SIZE) {
+		remain = (offset + len) % W5100_TX_MEM_SIZE;
+		len = W5100_TX_MEM_SIZE - offset;
+	}
+
+	ret = w5100_writebulk(priv, addr, buf, len);
+	if (ret || !remain)
+		return ret;
+
+	return w5100_writebulk(priv, W5100_TX_MEM_START, buf + len, remain);
+}
+
+static int w5100_reset(struct w5100_priv *priv)
+{
+	if (priv->ops->reset)
+		return priv->ops->reset(priv->ndev);
+
+	w5100_write(priv, W5100_MR, MR_RST);
+	mdelay(5);
+	w5100_write(priv, W5100_MR, MR_PB);
+
+	return 0;
+}
+
 static int w5100_command(struct w5100_priv *priv, u16 cmd)
 {
 	unsigned long timeout = jiffies + msecs_to_jiffies(100);
@@ -321,19 +521,14 @@ static int w5100_command(struct w5100_priv *priv, u16 cmd)
 static void w5100_write_macaddr(struct w5100_priv *priv)
 {
 	struct net_device *ndev = priv->ndev;
-	int i;
 
-	for (i = 0; i < ETH_ALEN; i++)
-		w5100_write(priv, W5100_SHAR + i, ndev->dev_addr[i]);
+	w5100_writebulk(priv, W5100_SHAR, ndev->dev_addr, ETH_ALEN);
 }
 
 static void w5100_hw_reset(struct w5100_priv *priv)
 {
-	w5100_write_direct(priv, W5100_MR, MR_RST);
-	mdelay(5);
-	w5100_write_direct(priv, W5100_MR, priv->indirect ?
-				  MR_PB | MR_AI | MR_IND :
-				  MR_PB);
+	w5100_reset(priv);
+
 	w5100_write(priv, W5100_IMR, 0);
 	w5100_write_macaddr(priv);
 
@@ -403,17 +598,14 @@ static int w5100_get_regs_len(struct net_device *ndev)
 }
 
 static void w5100_get_regs(struct net_device *ndev,
-			   struct ethtool_regs *regs, void *_buf)
+			   struct ethtool_regs *regs, void *buf)
 {
 	struct w5100_priv *priv = netdev_priv(ndev);
-	u8 *buf = _buf;
-	u16 i;
 
 	regs->version = 1;
-	for (i = 0; i < W5100_COMMON_REGS_LEN; i++)
-		*buf++ = w5100_read(priv, W5100_COMMON_REGS + i);
-	for (i = 0; i < W5100_S0_REGS_LEN; i++)
-		*buf++ = w5100_read(priv, W5100_S0_REGS + i);
+	w5100_readbulk(priv, W5100_COMMON_REGS, buf, W5100_COMMON_REGS_LEN);
+	buf += W5100_COMMON_REGS_LEN;
+	w5100_readbulk(priv, W5100_S0_REGS, buf, W5100_S0_REGS_LEN);
 }
 
 static void w5100_tx_timeout(struct net_device *ndev)
@@ -606,79 +798,38 @@ static const struct net_device_ops w5100_netdev_ops = {
 	.ndo_change_mtu		= eth_change_mtu,
 };
 
-static int w5100_hw_probe(struct platform_device *pdev)
+static int w5100_mmio_probe(struct platform_device *pdev)
 {
 	struct wiznet_platform_data *data = dev_get_platdata(&pdev->dev);
-	struct net_device *ndev = platform_get_drvdata(pdev);
-	struct w5100_priv *priv = netdev_priv(ndev);
-	const char *name = netdev_name(ndev);
+	u8 *mac_addr = NULL;
 	struct resource *mem;
-	int mem_size;
+	const struct w5100_ops *ops;
 	int irq;
-	int ret;
 
-	if (data && is_valid_ether_addr(data->mac_addr)) {
-		memcpy(ndev->dev_addr, data->mac_addr, ETH_ALEN);
-	} else {
-		eth_hw_addr_random(ndev);
-	}
+	if (data && is_valid_ether_addr(data->mac_addr))
+		mac_addr = data->mac_addr;
 
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	priv->base = devm_ioremap_resource(&pdev->dev, mem);
-	if (IS_ERR(priv->base))
-		return PTR_ERR(priv->base);
-
-	mem_size = resource_size(mem);
-
-	spin_lock_init(&priv->reg_lock);
-	priv->indirect = mem_size < W5100_BUS_DIRECT_SIZE;
-	if (priv->indirect) {
-		priv->read     = w5100_read_indirect;
-		priv->write    = w5100_write_indirect;
-		priv->read16   = w5100_read16_indirect;
-		priv->write16  = w5100_write16_indirect;
-		priv->readbuf  = w5100_readbuf_indirect;
-		priv->writebuf = w5100_writebuf_indirect;
-	} else {
-		priv->read     = w5100_read_direct;
-		priv->write    = w5100_write_direct;
-		priv->read16   = w5100_read16_direct;
-		priv->write16  = w5100_write16_direct;
-		priv->readbuf  = w5100_readbuf_direct;
-		priv->writebuf = w5100_writebuf_direct;
-	}
-
-	w5100_hw_reset(priv);
-	if (w5100_read16(priv, W5100_RTR) != RTR_DEFAULT)
-		return -ENODEV;
+	if (resource_size(mem) < W5100_BUS_DIRECT_SIZE)
+		ops = &w5100_mmio_indirect_ops;
+	else
+		ops = &w5100_mmio_direct_ops;
 
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0)
 		return irq;
-	ret = request_irq(irq, w5100_interrupt,
-			  IRQ_TYPE_LEVEL_LOW, name, ndev);
-	if (ret < 0)
-		return ret;
-	priv->irq = irq;
 
-	priv->link_gpio = data ? data->link_gpio : -EINVAL;
-	if (gpio_is_valid(priv->link_gpio)) {
-		char *link_name = devm_kzalloc(&pdev->dev, 16, GFP_KERNEL);
-		if (!link_name)
-			return -ENOMEM;
-		snprintf(link_name, 16, "%s-link", name);
-		priv->link_irq = gpio_to_irq(priv->link_gpio);
-		if (request_any_context_irq(priv->link_irq, w5100_detect_link,
-				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-				link_name, priv->ndev) < 0)
-			priv->link_gpio = -EINVAL;
-	}
+	return w5100_probe(&pdev->dev, ops, mac_addr, irq,
+			   data ? data->link_gpio : -EINVAL);
+}
 
-	netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, irq);
-	return 0;
+static int w5100_mmio_remove(struct platform_device *pdev)
+{
+	return w5100_remove(&pdev->dev);
 }
 
-static int w5100_probe(struct platform_device *pdev)
+int w5100_probe(struct device *dev, const struct w5100_ops *ops, u8 *mac_addr,
+		int irq, int link_gpio)
 {
 	struct w5100_priv *priv;
 	struct net_device *ndev;
@@ -687,10 +838,13 @@ static int w5100_probe(struct platform_device *pdev)
 	ndev = alloc_etherdev(sizeof(*priv));
 	if (!ndev)
 		return -ENOMEM;
-	SET_NETDEV_DEV(ndev, &pdev->dev);
-	platform_set_drvdata(pdev, ndev);
+	SET_NETDEV_DEV(ndev, dev);
+	dev_set_drvdata(dev, ndev);
 	priv = netdev_priv(ndev);
 	priv->ndev = ndev;
+	priv->ops = ops;
+	priv->irq = irq;
+	priv->link_gpio = link_gpio;
 
 	ndev->netdev_ops = &w5100_netdev_ops;
 	ndev->ethtool_ops = &w5100_ethtool_ops;
@@ -706,22 +860,59 @@ static int w5100_probe(struct platform_device *pdev)
 	if (err < 0)
 		goto err_register;
 
-	err = w5100_hw_probe(pdev);
-	if (err < 0)
-		goto err_hw_probe;
+	if (mac_addr)
+		memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
+	else
+		eth_hw_addr_random(ndev);
+
+	if (priv->ops->init) {
+		err = priv->ops->init(priv->ndev);
+		if (err)
+			goto err_hw;
+	}
+
+	w5100_hw_reset(priv);
+	if (w5100_read16(priv, W5100_RTR) != RTR_DEFAULT) {
+		err = -ENODEV;
+		goto err_hw;
+	}
+
+	err = request_irq(priv->irq, w5100_interrupt, IRQF_TRIGGER_LOW,
+			  netdev_name(ndev), ndev);
+	if (err)
+		goto err_hw;
+
+	if (gpio_is_valid(priv->link_gpio)) {
+		char *link_name = devm_kzalloc(dev, 16, GFP_KERNEL);
+
+		if (!link_name) {
+			err = -ENOMEM;
+			goto err_gpio;
+		}
+		snprintf(link_name, 16, "%s-link", netdev_name(ndev));
+		priv->link_irq = gpio_to_irq(priv->link_gpio);
+		if (request_any_context_irq(priv->link_irq, w5100_detect_link,
+					    IRQF_TRIGGER_RISING |
+					    IRQF_TRIGGER_FALLING,
+					    link_name, priv->ndev) < 0)
+			priv->link_gpio = -EINVAL;
+	}
 
 	return 0;
 
-err_hw_probe:
+err_gpio:
+	free_irq(priv->irq, ndev);
+err_hw:
 	unregister_netdev(ndev);
 err_register:
 	free_netdev(ndev);
 	return err;
 }
+EXPORT_SYMBOL_GPL(w5100_probe);
 
-static int w5100_remove(struct platform_device *pdev)
+int w5100_remove(struct device *dev)
 {
-	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct net_device *ndev = dev_get_drvdata(dev);
 	struct w5100_priv *priv = netdev_priv(ndev);
 
 	w5100_hw_reset(priv);
@@ -733,12 +924,12 @@ static int w5100_remove(struct platform_device *pdev)
 	free_netdev(ndev);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(w5100_remove);
 
 #ifdef CONFIG_PM_SLEEP
 static int w5100_suspend(struct device *dev)
 {
-	struct platform_device *pdev = to_platform_device(dev);
-	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct net_device *ndev = dev_get_drvdata(dev);
 	struct w5100_priv *priv = netdev_priv(ndev);
 
 	if (netif_running(ndev)) {
@@ -752,8 +943,7 @@ static int w5100_suspend(struct device *dev)
 
 static int w5100_resume(struct device *dev)
 {
-	struct platform_device *pdev = to_platform_device(dev);
-	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct net_device *ndev = dev_get_drvdata(dev);
 	struct w5100_priv *priv = netdev_priv(ndev);
 
 	if (netif_running(ndev)) {
@@ -769,15 +959,15 @@ static int w5100_resume(struct device *dev)
 }
 #endif /* CONFIG_PM_SLEEP */
 
-static SIMPLE_DEV_PM_OPS(w5100_pm_ops, w5100_suspend, w5100_resume);
+SIMPLE_DEV_PM_OPS(w5100_pm_ops, w5100_suspend, w5100_resume);
+EXPORT_SYMBOL_GPL(w5100_pm_ops);
 
-static struct platform_driver w5100_driver = {
+static struct platform_driver w5100_mmio_driver = {
 	.driver		= {
 		.name	= DRV_NAME,
 		.pm	= &w5100_pm_ops,
 	},
-	.probe		= w5100_probe,
-	.remove		= w5100_remove,
+	.probe		= w5100_mmio_probe,
+	.remove		= w5100_mmio_remove,
 };
-
-module_platform_driver(w5100_driver);
+module_platform_driver(w5100_mmio_driver);
diff --git a/drivers/net/ethernet/wiznet/w5100.h b/drivers/net/ethernet/wiznet/w5100.h
new file mode 100644
index 0000000..368862d
--- /dev/null
+++ b/drivers/net/ethernet/wiznet/w5100.h
@@ -0,0 +1,26 @@
+/*
+ * Ethernet driver for the WIZnet W5100 chip.
+ *
+ * Copyright (C) 2006-2008 WIZnet Co.,Ltd.
+ * Copyright (C) 2012 Mike Sinkovsky <msink@permonline.ru>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+struct w5100_ops {
+	int (*read)(struct net_device *ndev, u16 addr);
+	int (*write)(struct net_device *ndev, u16 addr, u8 data);
+	int (*read16)(struct net_device *ndev, u16 addr);
+	int (*write16)(struct net_device *ndev, u16 addr, u16 data);
+	int (*readbulk)(struct net_device *ndev, u16 addr, u8 *buf, int len);
+	int (*writebulk)(struct net_device *ndev, u16 addr, const u8 *buf,
+			 int len);
+	int (*reset)(struct net_device *ndev);
+	int (*init)(struct net_device *ndev);
+};
+
+int w5100_probe(struct device *dev, const struct w5100_ops *ops, u8 *mac_addr,
+		int irq, int link_gpio);
+int w5100_remove(struct device *dev);
+
+extern const struct dev_pm_ops w5100_pm_ops;
-- 
2.5.0

^ permalink raw reply related

* [PATCH 1/4] net: w5100: move mmiowb into register access callbacks
From: Akinobu Mita @ 2016-03-30 16:38 UTC (permalink / raw)
  To: netdev; +Cc: Akinobu Mita, Mike Sinkovsky, David S. Miller

Instead of sprinkle mmiowb over the driver code, move it into primary
register write callbacks. (w5100_write, w5100_write16, w5100_writebuf)

This is a preparation for supporting SPI interface which doesn't use
MMIO for accessing w5100 registers.

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Mike Sinkovsky <msink@permonline.ru>
Cc: David S. Miller <davem@davemloft.net>
---
 drivers/net/ethernet/wiznet/w5100.c | 44 +++++++++++++------------------------
 1 file changed, 15 insertions(+), 29 deletions(-)

diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c
index 8b282d0..f4b7200 100644
--- a/drivers/net/ethernet/wiznet/w5100.c
+++ b/drivers/net/ethernet/wiznet/w5100.c
@@ -122,10 +122,17 @@ static inline u8 w5100_read_direct(struct w5100_priv *priv, u16 addr)
 	return ioread8(priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
 }
 
+static inline void __w5100_write_direct(struct w5100_priv *priv, u16 addr,
+					u8 data)
+{
+	iowrite8(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+}
+
 static inline void w5100_write_direct(struct w5100_priv *priv,
 				      u16 addr, u8 data)
 {
-	iowrite8(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+	__w5100_write_direct(priv, addr, data);
+	mmiowb();
 }
 
 static u16 w5100_read16_direct(struct w5100_priv *priv, u16 addr)
@@ -138,8 +145,9 @@ static u16 w5100_read16_direct(struct w5100_priv *priv, u16 addr)
 
 static void w5100_write16_direct(struct w5100_priv *priv, u16 addr, u16 data)
 {
-	w5100_write_direct(priv, addr, data >> 8);
-	w5100_write_direct(priv, addr + 1, data);
+	__w5100_write_direct(priv, addr, data >> 8);
+	__w5100_write_direct(priv, addr + 1, data);
+	mmiowb();
 }
 
 static void w5100_readbuf_direct(struct w5100_priv *priv,
@@ -164,8 +172,9 @@ static void w5100_writebuf_direct(struct w5100_priv *priv,
 	for (i = 0; i < len; i++, addr++) {
 		if (unlikely(addr > W5100_TX_MEM_END))
 			addr = W5100_TX_MEM_START;
-		w5100_write_direct(priv, addr, *buf++);
+		__w5100_write_direct(priv, addr, *buf++);
 	}
+	mmiowb();
 }
 
 /*
@@ -186,7 +195,6 @@ static u8 w5100_read_indirect(struct w5100_priv *priv, u16 addr)
 
 	spin_lock_irqsave(&priv->reg_lock, flags);
 	w5100_write16_direct(priv, W5100_IDM_AR, addr);
-	mmiowb();
 	data = w5100_read_direct(priv, W5100_IDM_DR);
 	spin_unlock_irqrestore(&priv->reg_lock, flags);
 
@@ -199,9 +207,7 @@ static void w5100_write_indirect(struct w5100_priv *priv, u16 addr, u8 data)
 
 	spin_lock_irqsave(&priv->reg_lock, flags);
 	w5100_write16_direct(priv, W5100_IDM_AR, addr);
-	mmiowb();
 	w5100_write_direct(priv, W5100_IDM_DR, data);
-	mmiowb();
 	spin_unlock_irqrestore(&priv->reg_lock, flags);
 }
 
@@ -212,7 +218,6 @@ static u16 w5100_read16_indirect(struct w5100_priv *priv, u16 addr)
 
 	spin_lock_irqsave(&priv->reg_lock, flags);
 	w5100_write16_direct(priv, W5100_IDM_AR, addr);
-	mmiowb();
 	data  = w5100_read_direct(priv, W5100_IDM_DR) << 8;
 	data |= w5100_read_direct(priv, W5100_IDM_DR);
 	spin_unlock_irqrestore(&priv->reg_lock, flags);
@@ -226,10 +231,8 @@ static void w5100_write16_indirect(struct w5100_priv *priv, u16 addr, u16 data)
 
 	spin_lock_irqsave(&priv->reg_lock, flags);
 	w5100_write16_direct(priv, W5100_IDM_AR, addr);
-	mmiowb();
-	w5100_write_direct(priv, W5100_IDM_DR, data >> 8);
+	__w5100_write_direct(priv, W5100_IDM_DR, data >> 8);
 	w5100_write_direct(priv, W5100_IDM_DR, data);
-	mmiowb();
 	spin_unlock_irqrestore(&priv->reg_lock, flags);
 }
 
@@ -242,13 +245,11 @@ static void w5100_readbuf_indirect(struct w5100_priv *priv,
 
 	spin_lock_irqsave(&priv->reg_lock, flags);
 	w5100_write16_direct(priv, W5100_IDM_AR, addr);
-	mmiowb();
 
 	for (i = 0; i < len; i++, addr++) {
 		if (unlikely(addr > W5100_RX_MEM_END)) {
 			addr = W5100_RX_MEM_START;
 			w5100_write16_direct(priv, W5100_IDM_AR, addr);
-			mmiowb();
 		}
 		*buf++ = w5100_read_direct(priv, W5100_IDM_DR);
 	}
@@ -265,15 +266,13 @@ static void w5100_writebuf_indirect(struct w5100_priv *priv,
 
 	spin_lock_irqsave(&priv->reg_lock, flags);
 	w5100_write16_direct(priv, W5100_IDM_AR, addr);
-	mmiowb();
 
 	for (i = 0; i < len; i++, addr++) {
 		if (unlikely(addr > W5100_TX_MEM_END)) {
 			addr = W5100_TX_MEM_START;
 			w5100_write16_direct(priv, W5100_IDM_AR, addr);
-			mmiowb();
 		}
-		w5100_write_direct(priv, W5100_IDM_DR, *buf++);
+		__w5100_write_direct(priv, W5100_IDM_DR, *buf++);
 	}
 	mmiowb();
 	spin_unlock_irqrestore(&priv->reg_lock, flags);
@@ -309,7 +308,6 @@ static int w5100_command(struct w5100_priv *priv, u16 cmd)
 	unsigned long timeout = jiffies + msecs_to_jiffies(100);
 
 	w5100_write(priv, W5100_S0_CR, cmd);
-	mmiowb();
 
 	while (w5100_read(priv, W5100_S0_CR) != 0) {
 		if (time_after(jiffies, timeout))
@@ -327,18 +325,15 @@ static void w5100_write_macaddr(struct w5100_priv *priv)
 
 	for (i = 0; i < ETH_ALEN; i++)
 		w5100_write(priv, W5100_SHAR + i, ndev->dev_addr[i]);
-	mmiowb();
 }
 
 static void w5100_hw_reset(struct w5100_priv *priv)
 {
 	w5100_write_direct(priv, W5100_MR, MR_RST);
-	mmiowb();
 	mdelay(5);
 	w5100_write_direct(priv, W5100_MR, priv->indirect ?
 				  MR_PB | MR_AI | MR_IND :
 				  MR_PB);
-	mmiowb();
 	w5100_write(priv, W5100_IMR, 0);
 	w5100_write_macaddr(priv);
 
@@ -347,23 +342,19 @@ static void w5100_hw_reset(struct w5100_priv *priv)
 	 */
 	w5100_write(priv, W5100_RMSR, 0x03);
 	w5100_write(priv, W5100_TMSR, 0x03);
-	mmiowb();
 }
 
 static void w5100_hw_start(struct w5100_priv *priv)
 {
 	w5100_write(priv, W5100_S0_MR, priv->promisc ?
 			  S0_MR_MACRAW : S0_MR_MACRAW_MF);
-	mmiowb();
 	w5100_command(priv, S0_CR_OPEN);
 	w5100_write(priv, W5100_IMR, IR_S0);
-	mmiowb();
 }
 
 static void w5100_hw_close(struct w5100_priv *priv)
 {
 	w5100_write(priv, W5100_IMR, 0);
-	mmiowb();
 	w5100_command(priv, S0_CR_CLOSE);
 }
 
@@ -447,7 +438,6 @@ static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
 	offset = w5100_read16(priv, W5100_S0_TX_WR);
 	w5100_writebuf(priv, offset, skb->data, skb->len);
 	w5100_write16(priv, W5100_S0_TX_WR, offset + skb->len);
-	mmiowb();
 	ndev->stats.tx_bytes += skb->len;
 	ndev->stats.tx_packets++;
 	dev_kfree_skb(skb);
@@ -488,7 +478,6 @@ static int w5100_napi_poll(struct napi_struct *napi, int budget)
 		skb_put(skb, rx_len);
 		w5100_readbuf(priv, offset + 2, skb->data, rx_len);
 		w5100_write16(priv, W5100_S0_RX_RD, offset + 2 + rx_len);
-		mmiowb();
 		w5100_command(priv, S0_CR_RECV);
 		skb->protocol = eth_type_trans(skb, ndev);
 
@@ -500,7 +489,6 @@ static int w5100_napi_poll(struct napi_struct *napi, int budget)
 	if (rx_count < budget) {
 		napi_complete(napi);
 		w5100_write(priv, W5100_IMR, IR_S0);
-		mmiowb();
 	}
 
 	return rx_count;
@@ -515,7 +503,6 @@ static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
 	if (!ir)
 		return IRQ_NONE;
 	w5100_write(priv, W5100_S0_IR, ir);
-	mmiowb();
 
 	if (ir & S0_IR_SENDOK) {
 		netif_dbg(priv, tx_done, ndev, "tx done\n");
@@ -525,7 +512,6 @@ static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
 	if (ir & S0_IR_RECV) {
 		if (napi_schedule_prep(&priv->napi)) {
 			w5100_write(priv, W5100_IMR, 0);
-			mmiowb();
 			__napi_schedule(&priv->napi);
 		}
 	}
-- 
2.5.0

^ permalink raw reply related

* Re: Question on rhashtable in worst-case scenario.
From: David Miller @ 2016-03-30 16:38 UTC (permalink / raw)
  To: johannes; +Cc: greearb, linux-kernel, herbert, linux-wireless, netdev, tgraf
In-Reply-To: <1459329252.2055.1.camel@sipsolutions.net>

From: Johannes Berg <johannes@sipsolutions.net>
Date: Wed, 30 Mar 2016 11:14:12 +0200

> On Tue, 2016-03-29 at 09:16 -0700, Ben Greear wrote:
>> Looks like rhashtable has too much policy in it to properly deal with
>> cases where there are too many hash collisions, so I am going to work
>> on reverting it's use in mac80211.
> 
> I'm not really all that happy with that approach - can't we fix the
> rhashtable? It's a pretty rare corner case that many keys really are
> identical and no kind of hash algorithm, but it seems much better to
> still deal with it than to remove the rhashtable usage and go back to
> hand-rolling something.

Yeah reverting seems like a really idiotic way to deal with the issue.

^ permalink raw reply

* Re: [PATCH net-next v3 1/2] net: dsa: mv88e6xxx: Introduce _mv88e6xxx_phy_page_{read,write}
From: Patrick Uiterwijk @ 2016-03-30 15:21 UTC (permalink / raw)
  To: Guenter Roeck, davem, Vivien Didelot, Andrew Lunn
  Cc: netdev, Dennis Gilmore, Peter Robinson, Patrick Uiterwijk
In-Reply-To: <1459301981-26535-1-git-send-email-patrick@puiterwijk.org>

Hi,

Could this patch and the patch 2/2 v3 "net: dsa: mv88e6xxx: Clear the
PDOWN bit on setup" be put
on net, since this fixes a bug with the clearfog, which was introduced in v4.5?

Thanks,
Patrick

On Wed, Mar 30, 2016 at 1:39 AM, Patrick Uiterwijk
<patrick@puiterwijk.org> wrote:
> Add versions of the phy_page_read and _write functions to
> be used in a context where the SMI mutex is held.
>
> Tested-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
> Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
> Signed-off-by: Patrick Uiterwijk <patrick@puiterwijk.org>
> ---

^ permalink raw reply

* Re: [PATCH] net: mvneta: explicitly disable BM on 64bit platform
From: Gregory CLEMENT @ 2016-03-30 15:11 UTC (permalink / raw)
  To: Jisheng Zhang
  Cc: davem, mw, thomas.petazzoni, netdev, linux-kernel,
	linux-arm-kernel
In-Reply-To: <1459344310-626-1-git-send-email-jszhang@marvell.com>

Hi Jisheng,
 
 On mer., mars 30 2016, Jisheng Zhang <jszhang@marvell.com> wrote:

> The mvneta BM can't work on 64bit platform, as the BM hardware expects
> buf virtual address to be placed in the first four bytes of mapped
> buffer, but obviously the virtual address on 64bit platform can't be
> stored in 4 bytes. So we have to explicitly disable BM on 64bit
> platform.

Actually mvneta is used on Armada 3700 which is a 64bits platform.
Is it true that the driver needs some change to use BM in 64 bits, but
we don't have to disable it.

Here is the 64 bits part of the patch we have currently on the hardware
prototype. We have more things which are really related to the way the
mvneta is connected to the Armada 3700 SoC. This code was not ready for
mainline but I prefer share it now instead of having the HWBM blindly
disable for 64 bits platform:

--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -55,7 +55,7 @@ config MVNETA_BM_ENABLE
 
 config MVNETA
 	tristate "Marvell Armada 370/38x/XP network interface support"
-	depends on PLAT_ORION
+	depends on ARCH_MVEBU || COMPILE_TEST
 	select MVMDIO
 	select FIXED_PHY
 	---help---
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 577f7ca7deba..6929ad112b64 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -260,7 +260,7 @@
 
 #define MVNETA_VLAN_TAG_LEN             4
 
-#define MVNETA_CPU_D_CACHE_LINE_SIZE    32
+#define MVNETA_CPU_D_CACHE_LINE_SIZE    cache_line_size()
 #define MVNETA_TX_CSUM_DEF_SIZE		1600
 #define MVNETA_TX_CSUM_MAX_SIZE		9800
 #define MVNETA_ACC_MODE_EXT1		1
@@ -297,6 +297,12 @@
 /* descriptor aligned size */
 #define MVNETA_DESC_ALIGNED_SIZE	32
 
+/* Number of bytes to be taken into account by HW when putting incoming data
+ * to the buffers. It is needed in case NET_SKB_PAD exceeds maximum packet
+ * offset supported in MVNETA_RXQ_CONFIG_REG(q) registers.
+ */
+#define MVNETA_RX_PKT_OFFSET_CORRECTION		64
+
 #define MVNETA_RX_PKT_SIZE(mtu) \
 	ALIGN((mtu) + MVNETA_MH_SIZE + MVNETA_VLAN_TAG_LEN + \
 	      ETH_HLEN + ETH_FCS_LEN,			     \
@@ -417,6 +423,10 @@ struct mvneta_port {
 	u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
 
 	u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
+#ifdef CONFIG_64BIT
+	u64 data_high;
+#endif
+	u16 rx_offset_correction;
 };
 
 /* The mvneta_tx_desc and mvneta_rx_desc structures describe the
@@ -961,7 +971,9 @@ static int mvneta_bm_port_init(struct platform_device *pdev,
 			       struct mvneta_port *pp)
 {
 	struct device_node *dn = pdev->dev.of_node;
-	u32 long_pool_id, short_pool_id, wsize;
+	u32 long_pool_id, short_pool_id;
+#ifndef CONFIG_64BIT
+	u32 wsize;
 	u8 target, attr;
 	int err;
 
@@ -985,7 +997,7 @@ static int mvneta_bm_port_init(struct platform_device *pdev,
 		netdev_info(pp->dev, "missing long pool id\n");
 		return -EINVAL;
 	}
-
+#endif
 	/* Create port's long pool depending on mtu */
 	pp->pool_long = mvneta_bm_pool_use(pp->bm_priv, long_pool_id,
 					   MVNETA_BM_LONG, pp->id,
@@ -1790,6 +1802,10 @@ static int mvneta_rx_refill(struct mvneta_port *pp,
 	if (!data)
 		return -ENOMEM;
 
+#ifdef CONFIG_64BIT
+	if (unlikely(pp->data_high != ((u64)data & 0xffffffff00000000)))
+		return -ENOMEM;
+#endif
 	phys_addr = dma_map_single(pp->dev->dev.parent, data,
 				   MVNETA_RX_BUF_SIZE(pp->pkt_size),
 				   DMA_FROM_DEVICE);
@@ -1798,7 +1814,8 @@ static int mvneta_rx_refill(struct mvneta_port *pp,
 		return -ENOMEM;
 	}
 
-	mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)data);
+	phys_addr += pp->rx_offset_correction;
+	mvneta_rx_desc_fill(rx_desc, phys_addr, (uintptr_t)data);
 	return 0;
 }
 
@@ -1860,8 +1877,16 @@ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp,
 
 	for (i = 0; i < rxq->size; i++) {
 		struct mvneta_rx_desc *rx_desc = rxq->descs + i;
-		void *data = (void *)rx_desc->buf_cookie;
-
+		void *data = (u8 *)(uintptr_t)rx_desc->buf_cookie;
+#ifdef CONFIG_64BIT
+		/* In Neta HW only 32 bits data is supported, so in
+		 * order to obtain whole 64 bits address from RX
+		 * descriptor, we store the upper 32 bits when
+		 * allocating buffer, and put it back when using
+		 * buffer cookie for accessing packet in memory.
+		 */
+		data = (u8 *)(pp->data_high | (u64)data);
+#endif
 		dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr,
 				 MVNETA_RX_BUF_SIZE(pp->pkt_size), DMA_FROM_DEVICE);
 		mvneta_frag_free(pp->frag_size, data);
@@ -1898,7 +1923,17 @@ static int mvneta_rx_swbm(struct mvneta_port *pp, int rx_todo,
 		rx_done++;
 		rx_status = rx_desc->status;
 		rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE);
+#ifdef CONFIG_64BIT
+		/* In Neta HW only 32 bits data is supported, so in
+		 * order to obtain whole 64 bits address from RX
+		 * descriptor, we store the upper 32 bits when
+		 * allocating buffer, and put it back when using
+		 * buffer cookie for accessing packet in memory.
+		 */
+		data = (u8 *)(pp->data_high | (u64)rx_desc->buf_cookie);
+#else
 		data = (unsigned char *)rx_desc->buf_cookie;
+#endif
 		phys_addr = rx_desc->buf_phys_addr;
 
 		if (!mvneta_rxq_desc_is_first_last(rx_status) ||
@@ -2019,7 +2054,17 @@ static int mvneta_rx_hwbm(struct mvneta_port *pp, int rx_todo,
 		rx_done++;
 		rx_status = rx_desc->status;
 		rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE);
-		data = (unsigned char *)rx_desc->buf_cookie;
+#ifdef CONFIG_64BIT
+		/* In Neta HW only 32 bits data is supported, so in
+		 * order to obtain whole 64 bits address from RX
+		 * descriptor, we store the upper 32 bits when
+		 * allocating buffer, and put it back when using
+		 * buffer cookie for accessing packet in memory.
+		 */
+		data = (u8 *)(pp->data_high | (u64)rx_desc->buf_cookie);
+#else
+		data = (u8 *)rx_desc->buf_cookie;
+#endif
 		phys_addr = rx_desc->buf_phys_addr;
 		pool_id = MVNETA_RX_GET_BM_POOL_ID(rx_desc);
 		bm_pool = &pp->bm_priv->bm_pools[pool_id];
@@ -2774,7 +2819,7 @@ static int mvneta_rxq_init(struct mvneta_port *pp,
 	mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), rxq->size);
 
 	/* Set Offset */
-	mvneta_rxq_offset_set(pp, rxq, NET_SKB_PAD);
+	mvneta_rxq_offset_set(pp, rxq, NET_SKB_PAD - pp->rx_offset_correction);
 
 	/* Set coalescing pkts and time */
 	mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal);
@@ -2935,6 +2980,22 @@ static void mvneta_cleanup_rxqs(struct mvneta_port *pp)
 static int mvneta_setup_rxqs(struct mvneta_port *pp)
 {
 	int queue;
+#ifdef CONFIG_64BIT
+	void *data_tmp;
+
+	/* In Neta HW only 32 bits data is supported, so in order to
+	 * obtain whole 64 bits address from RX descriptor, we store
+	 * the upper 32 bits when allocating buffer, and put it back
+	 * when using buffer cookie for accessing packet in memory.
+	 * Frags should be allocated from single 'memory' region,
+	 * hence common upper address half should be sufficient.
+	 */
+	data_tmp = mvneta_frag_alloc(pp->frag_size);
+	if (data_tmp) {
+		pp->data_high = (u64)data_tmp & 0xffffffff00000000;
+		mvneta_frag_free(pp->frag_size, data_tmp);
+	}
+#endif
 
 	for (queue = 0; queue < rxq_number; queue++) {
 		int err = mvneta_rxq_init(pp, &pp->rxqs[queue]);
@@ -3672,25 +3733,24 @@ static void mvneta_ethtool_update_stats(struct mvneta_port *pp)
 	const struct mvneta_statistic *s;
 	void __iomem *base = pp->base;
 	u32 high, low, val;
-	u64 val64;
 	int i;
 
 	for (i = 0, s = mvneta_statistics;
 	     s < mvneta_statistics + ARRAY_SIZE(mvneta_statistics);
 	     s++, i++) {
+		val = 0;
 		switch (s->type) {
 		case T_REG_32:
 			val = readl_relaxed(base + s->offset);
-			pp->ethtool_stats[i] += val;
 			break;
 		case T_REG_64:
 			/* Docs say to read low 32-bit then high */
 			low = readl_relaxed(base + s->offset);
 			high = readl_relaxed(base + s->offset + 4);
-			val64 = (u64)high << 32 | low;
-			pp->ethtool_stats[i] += val64;
+			val = (u64)high << 32 | low;
 			break;
 		}
+		pp->ethtool_stats[i] += val;
 	}
 }
 
@@ -4034,6 +4094,13 @@ static int mvneta_probe(struct platform_device *pdev)
 
 	pp->rxq_def = rxq_def;
 
+	/* Set RX packet offset correction for platforms, whose
+	 * NET_SKB_PAD, exceeds 64B. It should be 64B for 64-bit
+	 * platforms and 0B for 32-bit ones.
+	 */
+	pp->rx_offset_correction =
+		max(0, NET_SKB_PAD - MVNETA_RX_PKT_OFFSET_CORRECTION);
+
 	pp->indir[0] = rxq_def;
 
 	pp->clk = devm_clk_get(&pdev->dev, "core");
--

>
> Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
> ---
>  drivers/net/ethernet/marvell/Kconfig | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
> index b5c6d42..53d6572 100644
> --- a/drivers/net/ethernet/marvell/Kconfig
> +++ b/drivers/net/ethernet/marvell/Kconfig
> @@ -42,7 +42,7 @@ config MVMDIO
>  
>  config MVNETA_BM_ENABLE
>  	tristate "Marvell Armada 38x/XP network interface BM support"
> -	depends on MVNETA
> +	depends on MVNETA && !64BIT
>  	---help---
>  	  This driver supports auxiliary block of the network
>  	  interface units in the Marvell ARMADA XP and ARMADA 38x SoC
> -- 
> 2.8.0.rc3
>

-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply related

* Re: [PATCH net-next v3 2/2] net: dsa: mv88e6xxx: Clear the PDOWN bit on setup
From: Vivien Didelot @ 2016-03-30 14:10 UTC (permalink / raw)
  To: Patrick Uiterwijk, linux, davem, andrew
  Cc: netdev, dennis, pbrobinson, Patrick Uiterwijk, Kevin Smith,
	Lucile Quirion
In-Reply-To: <1459301981-26535-2-git-send-email-patrick@puiterwijk.org>

Patrick Uiterwijk <patrick@puiterwijk.org> writes:

> Some of the vendor-specific bootloaders set up this part
> of the initialization for us, so this was never added.
> However, since upstream bootloaders don't initialize the
> chip specifically, they leave the fiber MII's PDOWN flag
> set, which means that the CPU port doesn't connect.
>
> This patch checks whether this flag has been clear prior
> by something else, and if not make us clear it.
>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
> Signed-off-by: Patrick Uiterwijk <patrick@puiterwijk.org>

Tested-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>

This fixes the SerDes link on my single 6352 x86 platform.

Thanks!
Vivien

^ permalink raw reply

* Re: Question on rhashtable in worst-case scenario.
From: Herbert Xu @ 2016-03-30 14:09 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Ben Greear, Linux Kernel Mailing List,
	linux-wireless@vger.kernel.org, netdev, Thomas Graf
In-Reply-To: <1459346588.2055.6.camel@sipsolutions.net>

On Wed, Mar 30, 2016 at 04:03:08PM +0200, Johannes Berg wrote:
> 
> But we really don't want that either - in the normal case where you
> don't create all these virtual interfaces for testing, you have a
> certain number of peers that can vary a lot (zero to hundreds, in
> theory thousands) where you *don't* have the same key, so we still want
> to have the rehashing if the chains get longer in that case.

insecure_elasticity only disables rehashing without growing, it
does not inhibit table expansion which is driven by the number of
objects in the whole table.

> It's really just the degenerate case that Ben is creating locally
> that's causing a problem, afaict, though it's a bit disconcerting that
> rhashtable in general can cause strange failures at delete time.

The failure is simple, rhashtable will rehash the table if a given
chain becomes too long.  This simply doesn't work if you hash many
objects with the same key since the chain will never get shorter
even after a rehash (or expansion).

Therefore if your hashtable has to do this then you have to disable
the rehash logic using the insecure_elasticity flag.

Alternatively you can construct your own linked list for objects
with the same key outside of rhashtable and hash the list instead.

Cheers,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: Question on rhashtable in worst-case scenario.
From: Johannes Berg @ 2016-03-30 14:03 UTC (permalink / raw)
  To: Herbert Xu
  Cc: Ben Greear, Linux Kernel Mailing List,
	linux-wireless@vger.kernel.org, netdev, Thomas Graf
In-Reply-To: <20160330135521.GA19423@gondor.apana.org.au>

On Wed, 2016-03-30 at 21:55 +0800, Herbert Xu wrote:

> Well to start with you should assess whether you really want to
> hash multiple objects with the same key.  In particular, can an
> adversary generate a large number of such objects?

No, the only reason this happens is local - if you take the single
hardware and connect it to the same AP many times. This is what Ben is
doing - he's creating virtual interfaces on top of the same physical
hardware, and then connection all of these to the same AP, mostly for
testing the AP.

> If your conclusion is that yes you really want to do this, then
> we have the parameter insecure_elasticity that you can use to
> disable the rehashing based on chain length.

But we really don't want that either - in the normal case where you
don't create all these virtual interfaces for testing, you have a
certain number of peers that can vary a lot (zero to hundreds, in
theory thousands) where you *don't* have the same key, so we still want
to have the rehashing if the chains get longer in that case.

It's really just the degenerate case that Ben is creating locally
that's causing a problem, afaict, though it's a bit disconcerting that
rhashtable in general can cause strange failures at delete time.

johannes

^ permalink raw reply

* Re: [RFC] net: mvneta: 64bit platform support
From: Arnd Bergmann @ 2016-03-30 13:55 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jisheng Zhang, thomas.petazzoni, davem, netdev, gregory.clement,
	mw
In-Reply-To: <20160330214535.4139ebe5@xhacker>

On Wednesday 30 March 2016 21:45:35 Jisheng Zhang wrote:
> oh, missing some code:
> #ifdef CONFIG_64BIT
> data = ((u64)rx_desc->reserved5 << 32) | rx_desc->buf_cookie;
> #else
> data = (void*)rx_desc->buf_cookie;
> #endif
> 
> 

Please use the upper_32_bits() and lower_32_bits() macros to split
up the pointer into two halves, and use the 64-bit version of the
code above unconditionally, that should both work on all architectures
without introducing any overhead.

	Arnd

^ permalink raw reply

* Re: Question on rhashtable in worst-case scenario.
From: Herbert Xu @ 2016-03-30 13:55 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Ben Greear, Linux Kernel Mailing List,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, netdev,
	Thomas Graf
In-Reply-To: <1459329252.2055.1.camel-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>

On Wed, Mar 30, 2016 at 11:14:12AM +0200, Johannes Berg wrote:
> On Tue, 2016-03-29 at 09:16 -0700, Ben Greear wrote:
> > Looks like rhashtable has too much policy in it to properly deal with
> > cases where there are too many hash collisions, so I am going to work
> > on reverting it's use in mac80211.
> 
> I'm not really all that happy with that approach - can't we fix the
> rhashtable? It's a pretty rare corner case that many keys really are
> identical and no kind of hash algorithm, but it seems much better to
> still deal with it than to remove the rhashtable usage and go back to
> hand-rolling something.

Well to start with you should assess whether you really want to
hash multiple objects with the same key.  In particular, can an
adversary generate a large number of such objects?

If your conclusion is that yes you really want to do this, then
we have the parameter insecure_elasticity that you can use to
disable the rehashing based on chain length.

Cheers,
-- 
Email: Herbert Xu <herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" 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: [RFC] net: mvneta: 64bit platform support
From: Jisheng Zhang @ 2016-03-30 13:45 UTC (permalink / raw)
  To: thomas.petazzoni, davem, netdev, linux-arm-kernel,
	gregory.clement, mw
In-Reply-To: <20160330213700.41876e09@xhacker>

On Wed, 30 Mar 2016 21:37:00 +0800 Jisheng Zhang wrote:

> Hi all,
> 
> Obviously, current mvneta driver can't work on 64bit platforms. For one thing
> the BM feature should be explicitly disabled, I just sent out one patch for
> this purpose. 
> 
> What's more, the buf_cookie in mvneta_rx_desc need to be carefully considered.
> The driver use the buf_cookie(u32 type) to store the buffer virtual address,
> obviously it can't store the virtual address on 64bit platforms. I have two
> solutions:
> 
> solution A: let one reserved type in current mvneta_rx_desc, e.g reserved5
> to store the high 32bit virt address, and hack code as the following:
> #ifdef CONFIG_64BIT
> rx_desc->reserved5 = high32(data);
> #endif

oh, missing some code:
#ifdef CONFIG_64BIT
data = ((u64)rx_desc->reserved5 << 32) | rx_desc->buf_cookie;
#else
data = (void*)rx_desc->buf_cookie;
#endif

> 
> solution B: add one member void **buf_virt_ptrs in mvneta_rx_queue, and point
> all buf_cookie usage to the according buf_virt_ptrs[i]
> 
> 
> Is there any elegant solutions?
> 
> Thanks,
> Jisheng
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH RFC] net: decrease the length of backlog queue immediately after it's detached from sk
From: Eric Dumazet @ 2016-03-30 13:47 UTC (permalink / raw)
  To: Yang Yingliang; +Cc: netdev, davem
In-Reply-To: <56FB6AA7.1080004@huawei.com>

On Wed, 2016-03-30 at 13:56 +0800, Yang Yingliang wrote:

> Sorry, I made a mistake. I am very sure my kernel has these two patches.
> And I can get some dropping of the packets in 10Gb eth.
> 
> # netstat -s | grep -i backlog
>      TCPBacklogDrop: 4135
> # netstat -s | grep -i backlog
>      TCPBacklogDrop: 4167

Sender will retransmit and the receiver backlog will lilely be emptied
before the packets arrive again.

Are you sure these are TCP drops ?

Which 10Gb NIC is it ? (ethtool -i eth0)

What is the max size of sendmsg() chunks are generated by your apps ?

Are they forcing small SO_RCVBUF or SO_SNDBUF ?

What percentage of drops do you have ?

Here (at Google), we have less than one backlog drop per billion
packets, on host facing the public Internet.

If a TCP sender sends a burst of tiny packets because it is misbehaving,
you absolutely will drop packets, especially if applications use
sendmsg() with very big lengths and big SO_SNDBUF.

Trying to not drop these hostile packets as you did is simply opening
your host to DOS attacks.

Eventually, we should even drop earlier in TCP stack (before taking
socket lock).

^ permalink raw reply

* [RFC] net: mvneta: 64bit platform support
From: Jisheng Zhang @ 2016-03-30 13:37 UTC (permalink / raw)
  To: thomas.petazzoni, davem, netdev, linux-arm-kernel,
	gregory.clement, mw

Hi all,

Obviously, current mvneta driver can't work on 64bit platforms. For one thing
the BM feature should be explicitly disabled, I just sent out one patch for
this purpose. 

What's more, the buf_cookie in mvneta_rx_desc need to be carefully considered.
The driver use the buf_cookie(u32 type) to store the buffer virtual address,
obviously it can't store the virtual address on 64bit platforms. I have two
solutions:

solution A: let one reserved type in current mvneta_rx_desc, e.g reserved5
to store the high 32bit virt address, and hack code as the following:
#ifdef CONFIG_64BIT
rx_desc->reserved5 = high32(data);
#endif

solution B: add one member void **buf_virt_ptrs in mvneta_rx_queue, and point
all buf_cookie usage to the according buf_virt_ptrs[i]


Is there any elegant solutions?

Thanks,
Jisheng

^ permalink raw reply

* [PATCH] net: mvneta: explicitly disable BM on 64bit platform
From: Jisheng Zhang @ 2016-03-30 13:25 UTC (permalink / raw)
  To: davem, gregory.clement, mw, thomas.petazzoni
  Cc: netdev, linux-kernel, linux-arm-kernel, Jisheng Zhang

The mvneta BM can't work on 64bit platform, as the BM hardware expects
buf virtual address to be placed in the first four bytes of mapped
buffer, but obviously the virtual address on 64bit platform can't be
stored in 4 bytes. So we have to explicitly disable BM on 64bit
platform.

Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
---
 drivers/net/ethernet/marvell/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index b5c6d42..53d6572 100644
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -42,7 +42,7 @@ config MVMDIO
 
 config MVNETA_BM_ENABLE
 	tristate "Marvell Armada 38x/XP network interface BM support"
-	depends on MVNETA
+	depends on MVNETA && !64BIT
 	---help---
 	  This driver supports auxiliary block of the network
 	  interface units in the Marvell ARMADA XP and ARMADA 38x SoC
-- 
2.8.0.rc3

^ permalink raw reply related

* Re: [PATCH RFC net-next] net: core: Pass XPS select queue decision to skb_tx_hash
From: Saeed Mahameed @ 2016-03-30 13:23 UTC (permalink / raw)
  To: John Fastabend, Saeed Mahameed, netdev
  Cc: Eric Dumazet, Tom Herbert, Jiri Pirko, David S. Miller,
	John Fastabend
In-Reply-To: <56FB1B64.40301@gmail.com>



On 3/30/2016 3:18 AM, John Fastabend wrote:
> I would prefer to not have another strange quirk users have to 
> remember in order to do tx classification. So with this change 
> depending on the driver the queue selection precedence changes. 
This change doesn't depend on the driver it affects all drivers that 
implement the select queue ndo and use the default fallback 
"pick_tx_queue" which this patch came to fix, or any driver that doesn't 
implement the ndo (the fallback is the default in this case).
> In short I agree with the problem statement but think we can find a 
> better solution. One idea that comes to mind is we can have a tc 
> action to force the queue selection? Now that we have the egress tc 
> hook it would probably be fairly cheap to implement and if users want 
> this behavior they can ask for it explicitly. If your thinking about 
> tc stuff we could fix the tooling to set this action when ever dcb is 
> turned on or hardware rate limiting is enabled, etc. And even if we 
> wanted we could have the driver add the rule in the cases where 
> firmware protocols are configuring the QOS/etc. 
Why would you ask for a bug fix explicitly ? IMHO this how I expect the 
pick _tx_queue routine to behave, why would I disable XPS in order for 
select queue to choose according TC QoS ?
as this patch suggests we can benefit from both without any additional 
tooling !

>>   	if (skb_vlan_tag_present(skb))
>>   		up = skb_vlan_tag_get(skb) >> VLAN_PRIO_SHIFT;
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index cb0d5d0..ad81ffe 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -3130,16 +3130,16 @@ static inline int netif_set_xps_queue(struct net_device *dev,
>>   #endif
>>   
>>   u16 __skb_tx_hash(const struct net_device *dev, struct sk_buff *skb,
>> -		  unsigned int num_tx_queues);
>> +		  unsigned int num_tx_queues, int txq_hint);
>>   
> [...]
>
> And all this seems like it would only ever be called by drivers select
> queue routines which I really wish we could kill off one of these days
> instead of add to. Now if the signal is something higher in the stack
> and not the driver I think it is OK.
I agree, drivers shouldn't call this function, the only reason drivers 
call this function is to bypass get_xps_queue
and after this patch i don't think driver will need to call it, since it 
will be called even if XPS is configured.

^ permalink raw reply

* [PATCH] wlcore: spi: add wl18xx support
From: Eyal Reizer @ 2016-03-30 13:06 UTC (permalink / raw)
  To: kvalo, linux-wireless, netdev, linux-kernel; +Cc: Eyal Reizer

Add support for using with both wl12xx and wl18xx.

- all wilink family needs special init command for entering wspi mode.
  extra clock cycles should be sent after the spi init command while the
  cs pin is high.
- switch to controling the cs pin from the spi driver for achieveing the
  above.
- the selected cs gpio is read from the spi device-tree node using the
  cs-gpios field and setup as a gpio.
- See the example below for specifying the cs gpio using the cs-gpios entry

&spi0	{
 	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&spi0_pins>;
	cs-gpios = <&gpio0 5 0>;
	#address-cells = <1>;
	#size-cells = <0>;
 	wlcore: wlcore@0 {
		compatible = "ti,wl1835";
		vwlan-supply = <&wlan_en_reg>;
		spi-max-frequency = <48000000>;
		reg = <0>;      /* chip select 0 on spi0, ie spi0.0 */
		interrupt-parent = <&gpio0>;
		interrupts = <27 IRQ_TYPE_EDGE_RISING>;
 	};
};

Signed-off-by: Eyal Reizer <eyalr@ti.com>
---
 drivers/net/wireless/ti/wlcore/spi.c |  176 ++++++++++++++++++++++++++++++----
 1 file changed, 157 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index 96d9c9d..6c5a369 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -32,6 +32,7 @@
 #include <linux/platform_device.h>
 #include <linux/of_irq.h>
 #include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
 
 #include "wlcore.h"
 #include "wl12xx_80211.h"
@@ -70,16 +71,30 @@
 #define WSPI_MAX_CHUNK_SIZE    4092
 
 /*
- * only support SPI for 12xx - this code should be reworked when 18xx
- * support is introduced
+ * wl18xx driver aggregation buffer size is (13 * PAGE_SIZE) compared to
+ * (4 * PAGE_SIZE) for wl12xx, so use the larger buffer needed for wl18xx
  */
-#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
+#define SPI_AGGR_BUFFER_SIZE (13 * PAGE_SIZE)
 
 /* Maximum number of SPI write chunks */
 #define WSPI_MAX_NUM_OF_CHUNKS \
 	((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
 
 
+struct wilink_familiy_data {
+	char name[8];
+};
+
+const struct wilink_familiy_data *wilink_data;
+
+static const struct wilink_familiy_data wl18xx_data = {
+	.name = "wl18xx",
+};
+
+static const struct wilink_familiy_data wl12xx_data = {
+	.name = "wl12xx",
+};
+
 struct wl12xx_spi_glue {
 	struct device *dev;
 	struct platform_device *core;
@@ -120,6 +135,8 @@ static void wl12xx_spi_init(struct device *child)
 	struct spi_transfer t;
 	struct spi_message m;
 	u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
 
 	if (!cmd) {
 		dev_err(child->parent,
@@ -127,6 +144,15 @@ static void wl12xx_spi_init(struct device *child)
 		return;
 	}
 
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return;
+	}
+
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
+
 	memset(&t, 0, sizeof(t));
 	spi_message_init(&m);
 
@@ -163,6 +189,26 @@ static void wl12xx_spi_init(struct device *child)
 	spi_message_add_tail(&t, &m);
 
 	spi_sync(to_spi_device(glue->dev), &m);
+
+	/* Send extra clocks with CS high. this is required by the wilink
+	 * family in order for successfully enter WSPI mode
+	 */
+	gpio_direction_output(master->cs_gpios[0], 1);
+
+	memset(&m, 0, sizeof(m));
+	spi_message_init(&m);
+
+	cmd[0] = 0xff;
+	cmd[1] = 0xff;
+	cmd[2] = 0xff;
+	cmd[3] = 0xff;
+	swab32s((u32 *)cmd);
+
+	t.tx_buf = cmd;
+	t.len = 4;
+	spi_message_add_tail(&t, &m);
+	spi_sync(to_spi_device(glue->dev), &m);
+
 	kfree(cmd);
 }
 
@@ -213,6 +259,16 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
 	u32 *busy_buf;
 	u32 *cmd;
 	u32 chunk_len;
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
+
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
 
 	while (len > 0) {
 		chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
@@ -267,25 +323,44 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
 		len -= chunk_len;
 	}
 
+	/* Drive CS line high */
+	gpio_direction_output(master->cs_gpios[0], 1);
 	return 0;
 }
 
-static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
-					     void *buf, size_t len, bool fixed)
+static int __wl12xx_spi_raw_write(struct device *child, int addr,
+				  void *buf, size_t len, bool fixed)
 {
 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
-	/* SPI write buffers - 2 for each chunk */
-	struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
+	struct spi_transfer *t;
 	struct spi_message m;
 	u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
 	u32 *cmd;
 	u32 chunk_len;
 	int i;
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
+
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+
+	/* SPI write buffers - 2 for each chunk */
+	t = kzalloc(sizeof(*t) * 2 * WSPI_MAX_NUM_OF_CHUNKS, GFP_KERNEL);
+	if (!t) {
+		dev_err(child->parent,
+			"could not allocate spi write buffer\n");
+		return -ENOMEM;
+	}
+
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
 
 	WARN_ON(len > SPI_AGGR_BUFFER_SIZE);
 
 	spi_message_init(&m);
-	memset(t, 0, sizeof(t));
 
 	cmd = &commands[0];
 	i = 0;
@@ -318,9 +393,29 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
 
 	spi_sync(to_spi_device(glue->dev), &m);
 
+	/* Drive CS line high */
+	gpio_direction_output(master->cs_gpios[0], 1);
+
+	kfree(t);
 	return 0;
 }
 
+static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
+					     void *buf, size_t len, bool fixed)
+{
+	int ret;
+
+	/* The ELP wakeup write may fail the first time due to internal
+	 * hardware latency. It is safer to send the wakeup command twice to
+	 * avoid unexpected failures.
+	 */
+	if (addr == HW_ACCESS_ELP_CTRL_REG)
+		ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
+	ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
+
+	return ret;
+}
+
 /**
  * wl12xx_spi_set_power - power on/off the wl12xx unit
  * @child: wl12xx device handle.
@@ -349,17 +444,38 @@ static int wl12xx_spi_set_power(struct device *child, bool enable)
 	return ret;
 }
 
+/**
+ * wl12xx_spi_set_block_size
+ *
+ * This function is not needed for spi mode, but need to be present.
+ * Without it defined the wlcore fallback to use the wrong packet
+ * allignment on tx.
+ */
+static void wl12xx_spi_set_block_size(struct device *child,
+				      unsigned int blksz)
+{
+}
+
 static struct wl1271_if_operations spi_ops = {
 	.read		= wl12xx_spi_raw_read,
 	.write		= wl12xx_spi_raw_write,
 	.reset		= wl12xx_spi_reset,
 	.init		= wl12xx_spi_init,
 	.power		= wl12xx_spi_set_power,
-	.set_block_size = NULL,
+	.set_block_size = wl12xx_spi_set_block_size,
 };
 
 static const struct of_device_id wlcore_spi_of_match_table[] = {
-	{ .compatible = "ti,wl1271" },
+	{ .compatible = "ti,wl1271", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1273", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1281", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1283", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1801", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1805", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1807", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1831", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1835", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1837", .data = &wl18xx_data},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table);
@@ -375,18 +491,24 @@ static int wlcore_probe_of(struct spi_device *spi, struct wl12xx_spi_glue *glue,
 			   struct wlcore_platdev_data *pdev_data)
 {
 	struct device_node *dt_node = spi->dev.of_node;
-	int ret;
+	const struct of_device_id *of_id;
+
+	of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
+	if (!of_id)
+		return -ENODEV;
+
+	wilink_data = of_id->data;
+	dev_info(&spi->dev, "selected chip familiy is %s\n",
+		 wilink_data->name);
 
 	if (of_find_property(dt_node, "clock-xtal", NULL))
 		pdev_data->ref_clock_xtal = true;
 
-	ret = of_property_read_u32(dt_node, "ref-clock-frequency",
-				   &pdev_data->ref_clock_freq);
-	if (IS_ERR_VALUE(ret)) {
-		dev_err(glue->dev,
-			"can't get reference clock frequency (%d)\n", ret);
-		return ret;
-	}
+	/* optional clock frequency params */
+	of_property_read_u32(dt_node, "ref-clock-frequency",
+			     &pdev_data->ref_clock_freq);
+	of_property_read_u32(dt_node, "tcxo-clock-frequency",
+			     &pdev_data->tcxo_clock_freq);
 
 	return 0;
 }
@@ -397,6 +519,7 @@ static int wl1271_probe(struct spi_device *spi)
 	struct wlcore_platdev_data pdev_data;
 	struct resource res[1];
 	int ret;
+	struct spi_master *master = spi->master;
 
 	memset(&pdev_data, 0x00, sizeof(pdev_data));
 
@@ -410,6 +533,12 @@ static int wl1271_probe(struct spi_device *spi)
 
 	glue->dev = &spi->dev;
 
+	if (!master->cs_gpios) {
+		dev_err(glue->dev,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+
 	spi_set_drvdata(spi, glue);
 
 	/* This is the only SPI value that we need to set here, the rest
@@ -431,15 +560,21 @@ static int wl1271_probe(struct spi_device *spi)
 		return ret;
 	}
 
+	if (gpio_request(master->cs_gpios[0], "spi1-cs0"))
+		return -EINVAL;
+
 	ret = spi_setup(spi);
 	if (ret < 0) {
 		dev_err(glue->dev, "spi_setup failed\n");
+		gpio_free(master->cs_gpios[0]);
 		return ret;
 	}
 
-	glue->core = platform_device_alloc("wl12xx", PLATFORM_DEVID_AUTO);
+	glue->core = platform_device_alloc(wilink_data->name,
+					   PLATFORM_DEVID_AUTO);
 	if (!glue->core) {
 		dev_err(glue->dev, "can't allocate platform_device\n");
+		gpio_free(master->cs_gpios[0]);
 		return -ENOMEM;
 	}
 
@@ -474,14 +609,17 @@ static int wl1271_probe(struct spi_device *spi)
 
 out_dev_put:
 	platform_device_put(glue->core);
+	gpio_free(master->cs_gpios[0]);
 	return ret;
 }
 
 static int wl1271_remove(struct spi_device *spi)
 {
 	struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
+	struct spi_master *master = spi->master;
 
 	platform_device_unregister(glue->core);
+	gpio_free(master->cs_gpios[0]);
 
 	return 0;
 }
-- 
1.7.9.5

^ permalink raw reply related

* Re: [PATCH] wlcore: spi: add wl18xx support
From: Emmanuel Grumbach @ 2016-03-30 13:02 UTC (permalink / raw)
  To: Eyal Reizer
  Cc: Kalle Valo, linux-wireless,
	netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Eyal Reizer
In-Reply-To: <1459342694-24461-1-git-send-email-eyalr-l0cyMroinI0@public.gmane.org>

On Wed, Mar 30, 2016 at 3:58 PM, Eyal Reizer <eyalreizer-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>
> From: Eyal <eyalr@eyalr-VirtualBox.(none)>

Are you trying to hide or something? :)

>
> Add support for using with both wl12xx and wl18xx.
>
> - all wilink family needs special init command for entering wspi mode.
>   extra clock cycles should be sent after the spi init command while the
>   cs pin is high.
> - switch to controling the cs pin from the spi driver for achieveing the
>   above.
> - the selected cs gpio is read from the spi device-tree node using the
>   cs-gpios field and setup as a gpio.
> - See the example below for specifying the cs gpio using the cs-gpios entry
>
> &spi0   {
>         status = "okay";
>         pinctrl-names = "default";
>         pinctrl-0 = <&spi0_pins>;
>         cs-gpios = <&gpio0 5 0>;
>         #address-cells = <1>;
>         #size-cells = <0>;
>         wlcore: wlcore@0 {
>                 compatible = "ti,wl1835";
>                 vwlan-supply = <&wlan_en_reg>;
>                 spi-max-frequency = <48000000>;
>                 reg = <0>;      /* chip select 0 on spi0, ie spi0.0 */
>                 interrupt-parent = <&gpio0>;
>                 interrupts = <27 IRQ_TYPE_EDGE_RISING>;
>         };
> };
>
> Signed-off-by: Eyal Reizer <eyalr-l0cyMroinI0@public.gmane.org>
> ---
>  drivers/net/wireless/ti/wlcore/spi.c |  176 ++++++++++++++++++++++++++++++----
>  1 file changed, 157 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
> index 96d9c9d..6c5a369 100644
> --- a/drivers/net/wireless/ti/wlcore/spi.c
> +++ b/drivers/net/wireless/ti/wlcore/spi.c
> @@ -32,6 +32,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/of_irq.h>
>  #include <linux/regulator/consumer.h>
> +#include <linux/gpio.h>
>
>  #include "wlcore.h"
>  #include "wl12xx_80211.h"
> @@ -70,16 +71,30 @@
>  #define WSPI_MAX_CHUNK_SIZE    4092
>
>  /*
> - * only support SPI for 12xx - this code should be reworked when 18xx
> - * support is introduced
> + * wl18xx driver aggregation buffer size is (13 * PAGE_SIZE) compared to
> + * (4 * PAGE_SIZE) for wl12xx, so use the larger buffer needed for wl18xx
>   */
> -#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
> +#define SPI_AGGR_BUFFER_SIZE (13 * PAGE_SIZE)
>
>  /* Maximum number of SPI write chunks */
>  #define WSPI_MAX_NUM_OF_CHUNKS \
>         ((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
>
>
> +struct wilink_familiy_data {
> +       char name[8];
> +};
> +
> +const struct wilink_familiy_data *wilink_data;
> +
> +static const struct wilink_familiy_data wl18xx_data = {
> +       .name = "wl18xx",
> +};
> +
> +static const struct wilink_familiy_data wl12xx_data = {
> +       .name = "wl12xx",
> +};
> +
>  struct wl12xx_spi_glue {
>         struct device *dev;
>         struct platform_device *core;
> @@ -120,6 +135,8 @@ static void wl12xx_spi_init(struct device *child)
>         struct spi_transfer t;
>         struct spi_message m;
>         u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
> +       struct spi_device *spi = (struct spi_device *)glue->dev;
> +       struct spi_master *master = spi->master;
>
>         if (!cmd) {
>                 dev_err(child->parent,
> @@ -127,6 +144,15 @@ static void wl12xx_spi_init(struct device *child)
>                 return;
>         }
>
> +       if (!master->cs_gpios) {
> +               dev_err(child->parent,
> +                       "spi chip select pin missing in platform data!\n");
> +               return;
> +       }
> +
> +       /* Drive CS line low */
> +       gpio_direction_output(master->cs_gpios[0], 0);
> +
>         memset(&t, 0, sizeof(t));
>         spi_message_init(&m);
>
> @@ -163,6 +189,26 @@ static void wl12xx_spi_init(struct device *child)
>         spi_message_add_tail(&t, &m);
>
>         spi_sync(to_spi_device(glue->dev), &m);
> +
> +       /* Send extra clocks with CS high. this is required by the wilink
> +        * family in order for successfully enter WSPI mode
> +        */
> +       gpio_direction_output(master->cs_gpios[0], 1);
> +
> +       memset(&m, 0, sizeof(m));
> +       spi_message_init(&m);
> +
> +       cmd[0] = 0xff;
> +       cmd[1] = 0xff;
> +       cmd[2] = 0xff;
> +       cmd[3] = 0xff;
> +       swab32s((u32 *)cmd);
> +
> +       t.tx_buf = cmd;
> +       t.len = 4;
> +       spi_message_add_tail(&t, &m);
> +       spi_sync(to_spi_device(glue->dev), &m);
> +
>         kfree(cmd);
>  }
>
> @@ -213,6 +259,16 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
>         u32 *busy_buf;
>         u32 *cmd;
>         u32 chunk_len;
> +       struct spi_device *spi = (struct spi_device *)glue->dev;
> +       struct spi_master *master = spi->master;
> +
> +       if (!master->cs_gpios) {
> +               dev_err(child->parent,
> +                       "spi chip select pin missing in platform data!\n");
> +               return -EINVAL;
> +       }
> +       /* Drive CS line low */
> +       gpio_direction_output(master->cs_gpios[0], 0);
>
>         while (len > 0) {
>                 chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
> @@ -267,25 +323,44 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
>                 len -= chunk_len;
>         }
>
> +       /* Drive CS line high */
> +       gpio_direction_output(master->cs_gpios[0], 1);
>         return 0;
>  }
>
> -static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
> -                                            void *buf, size_t len, bool fixed)
> +static int __wl12xx_spi_raw_write(struct device *child, int addr,
> +                                 void *buf, size_t len, bool fixed)
>  {
>         struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
> -       /* SPI write buffers - 2 for each chunk */
> -       struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
> +       struct spi_transfer *t;
>         struct spi_message m;
>         u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
>         u32 *cmd;
>         u32 chunk_len;
>         int i;
> +       struct spi_device *spi = (struct spi_device *)glue->dev;
> +       struct spi_master *master = spi->master;
> +
> +       if (!master->cs_gpios) {
> +               dev_err(child->parent,
> +                       "spi chip select pin missing in platform data!\n");
> +               return -EINVAL;
> +       }
> +
> +       /* SPI write buffers - 2 for each chunk */
> +       t = kzalloc(sizeof(*t) * 2 * WSPI_MAX_NUM_OF_CHUNKS, GFP_KERNEL);
> +       if (!t) {
> +               dev_err(child->parent,
> +                       "could not allocate spi write buffer\n");
> +               return -ENOMEM;
> +       }
> +
> +       /* Drive CS line low */
> +       gpio_direction_output(master->cs_gpios[0], 0);
>
>         WARN_ON(len > SPI_AGGR_BUFFER_SIZE);
>
>         spi_message_init(&m);
> -       memset(t, 0, sizeof(t));
>
>         cmd = &commands[0];
>         i = 0;
> @@ -318,9 +393,29 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
>
>         spi_sync(to_spi_device(glue->dev), &m);
>
> +       /* Drive CS line high */
> +       gpio_direction_output(master->cs_gpios[0], 1);
> +
> +       kfree(t);
>         return 0;
>  }
>
> +static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
> +                                            void *buf, size_t len, bool fixed)
> +{
> +       int ret;
> +
> +       /* The ELP wakeup write may fail the first time due to internal
> +        * hardware latency. It is safer to send the wakeup command twice to
> +        * avoid unexpected failures.
> +        */
> +       if (addr == HW_ACCESS_ELP_CTRL_REG)
> +               ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
> +       ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
> +
> +       return ret;
> +}
> +
>  /**
>   * wl12xx_spi_set_power - power on/off the wl12xx unit
>   * @child: wl12xx device handle.
> @@ -349,17 +444,38 @@ static int wl12xx_spi_set_power(struct device *child, bool enable)
>         return ret;
>  }
>
> +/**
> + * wl12xx_spi_set_block_size
> + *
> + * This function is not needed for spi mode, but need to be present.
> + * Without it defined the wlcore fallback to use the wrong packet
> + * allignment on tx.
> + */
> +static void wl12xx_spi_set_block_size(struct device *child,
> +                                     unsigned int blksz)
> +{
> +}
> +
>  static struct wl1271_if_operations spi_ops = {
>         .read           = wl12xx_spi_raw_read,
>         .write          = wl12xx_spi_raw_write,
>         .reset          = wl12xx_spi_reset,
>         .init           = wl12xx_spi_init,
>         .power          = wl12xx_spi_set_power,
> -       .set_block_size = NULL,
> +       .set_block_size = wl12xx_spi_set_block_size,
>  };
>
>  static const struct of_device_id wlcore_spi_of_match_table[] = {
> -       { .compatible = "ti,wl1271" },
> +       { .compatible = "ti,wl1271", .data = &wl12xx_data},
> +       { .compatible = "ti,wl1273", .data = &wl12xx_data},
> +       { .compatible = "ti,wl1281", .data = &wl12xx_data},
> +       { .compatible = "ti,wl1283", .data = &wl12xx_data},
> +       { .compatible = "ti,wl1801", .data = &wl18xx_data},
> +       { .compatible = "ti,wl1805", .data = &wl18xx_data},
> +       { .compatible = "ti,wl1807", .data = &wl18xx_data},
> +       { .compatible = "ti,wl1831", .data = &wl18xx_data},
> +       { .compatible = "ti,wl1835", .data = &wl18xx_data},
> +       { .compatible = "ti,wl1837", .data = &wl18xx_data},
>         { }
>  };
>  MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table);
> @@ -375,18 +491,24 @@ static int wlcore_probe_of(struct spi_device *spi, struct wl12xx_spi_glue *glue,
>                            struct wlcore_platdev_data *pdev_data)
>  {
>         struct device_node *dt_node = spi->dev.of_node;
> -       int ret;
> +       const struct of_device_id *of_id;
> +
> +       of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
> +       if (!of_id)
> +               return -ENODEV;
> +
> +       wilink_data = of_id->data;
> +       dev_info(&spi->dev, "selected chip familiy is %s\n",
> +                wilink_data->name);
>
>         if (of_find_property(dt_node, "clock-xtal", NULL))
>                 pdev_data->ref_clock_xtal = true;
>
> -       ret = of_property_read_u32(dt_node, "ref-clock-frequency",
> -                                  &pdev_data->ref_clock_freq);
> -       if (IS_ERR_VALUE(ret)) {
> -               dev_err(glue->dev,
> -                       "can't get reference clock frequency (%d)\n", ret);
> -               return ret;
> -       }
> +       /* optional clock frequency params */
> +       of_property_read_u32(dt_node, "ref-clock-frequency",
> +                            &pdev_data->ref_clock_freq);
> +       of_property_read_u32(dt_node, "tcxo-clock-frequency",
> +                            &pdev_data->tcxo_clock_freq);
>
>         return 0;
>  }
> @@ -397,6 +519,7 @@ static int wl1271_probe(struct spi_device *spi)
>         struct wlcore_platdev_data pdev_data;
>         struct resource res[1];
>         int ret;
> +       struct spi_master *master = spi->master;
>
>         memset(&pdev_data, 0x00, sizeof(pdev_data));
>
> @@ -410,6 +533,12 @@ static int wl1271_probe(struct spi_device *spi)
>
>         glue->dev = &spi->dev;
>
> +       if (!master->cs_gpios) {
> +               dev_err(glue->dev,
> +                       "spi chip select pin missing in platform data!\n");
> +               return -EINVAL;
> +       }
> +
>         spi_set_drvdata(spi, glue);
>
>         /* This is the only SPI value that we need to set here, the rest
> @@ -431,15 +560,21 @@ static int wl1271_probe(struct spi_device *spi)
>                 return ret;
>         }
>
> +       if (gpio_request(master->cs_gpios[0], "spi1-cs0"))
> +               return -EINVAL;
> +
>         ret = spi_setup(spi);
>         if (ret < 0) {
>                 dev_err(glue->dev, "spi_setup failed\n");
> +               gpio_free(master->cs_gpios[0]);
>                 return ret;
>         }
>
> -       glue->core = platform_device_alloc("wl12xx", PLATFORM_DEVID_AUTO);
> +       glue->core = platform_device_alloc(wilink_data->name,
> +                                          PLATFORM_DEVID_AUTO);
>         if (!glue->core) {
>                 dev_err(glue->dev, "can't allocate platform_device\n");
> +               gpio_free(master->cs_gpios[0]);
>                 return -ENOMEM;
>         }
>
> @@ -474,14 +609,17 @@ static int wl1271_probe(struct spi_device *spi)
>
>  out_dev_put:
>         platform_device_put(glue->core);
> +       gpio_free(master->cs_gpios[0]);
>         return ret;
>  }
>
>  static int wl1271_remove(struct spi_device *spi)
>  {
>         struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
> +       struct spi_master *master = spi->master;
>
>         platform_device_unregister(glue->core);
> +       gpio_free(master->cs_gpios[0]);
>
>         return 0;
>  }
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] wlcore: spi: add wl18xx support
From: Eyal Reizer @ 2016-03-30 12:58 UTC (permalink / raw)
  To: kvalo, linux-wireless, netdev, linux-kernel; +Cc: Eyal Reizer

From: Eyal <eyalr@eyalr-VirtualBox.(none)>

Add support for using with both wl12xx and wl18xx.

- all wilink family needs special init command for entering wspi mode.
  extra clock cycles should be sent after the spi init command while the
  cs pin is high.
- switch to controling the cs pin from the spi driver for achieveing the
  above.
- the selected cs gpio is read from the spi device-tree node using the
  cs-gpios field and setup as a gpio.
- See the example below for specifying the cs gpio using the cs-gpios entry

&spi0	{
 	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&spi0_pins>;
	cs-gpios = <&gpio0 5 0>;
	#address-cells = <1>;
	#size-cells = <0>;
 	wlcore: wlcore@0 {
		compatible = "ti,wl1835";
		vwlan-supply = <&wlan_en_reg>;
		spi-max-frequency = <48000000>;
		reg = <0>;      /* chip select 0 on spi0, ie spi0.0 */
		interrupt-parent = <&gpio0>;
		interrupts = <27 IRQ_TYPE_EDGE_RISING>;
 	};
};

Signed-off-by: Eyal Reizer <eyalr@ti.com>
---
 drivers/net/wireless/ti/wlcore/spi.c |  176 ++++++++++++++++++++++++++++++----
 1 file changed, 157 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index 96d9c9d..6c5a369 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -32,6 +32,7 @@
 #include <linux/platform_device.h>
 #include <linux/of_irq.h>
 #include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
 
 #include "wlcore.h"
 #include "wl12xx_80211.h"
@@ -70,16 +71,30 @@
 #define WSPI_MAX_CHUNK_SIZE    4092
 
 /*
- * only support SPI for 12xx - this code should be reworked when 18xx
- * support is introduced
+ * wl18xx driver aggregation buffer size is (13 * PAGE_SIZE) compared to
+ * (4 * PAGE_SIZE) for wl12xx, so use the larger buffer needed for wl18xx
  */
-#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
+#define SPI_AGGR_BUFFER_SIZE (13 * PAGE_SIZE)
 
 /* Maximum number of SPI write chunks */
 #define WSPI_MAX_NUM_OF_CHUNKS \
 	((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
 
 
+struct wilink_familiy_data {
+	char name[8];
+};
+
+const struct wilink_familiy_data *wilink_data;
+
+static const struct wilink_familiy_data wl18xx_data = {
+	.name = "wl18xx",
+};
+
+static const struct wilink_familiy_data wl12xx_data = {
+	.name = "wl12xx",
+};
+
 struct wl12xx_spi_glue {
 	struct device *dev;
 	struct platform_device *core;
@@ -120,6 +135,8 @@ static void wl12xx_spi_init(struct device *child)
 	struct spi_transfer t;
 	struct spi_message m;
 	u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
 
 	if (!cmd) {
 		dev_err(child->parent,
@@ -127,6 +144,15 @@ static void wl12xx_spi_init(struct device *child)
 		return;
 	}
 
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return;
+	}
+
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
+
 	memset(&t, 0, sizeof(t));
 	spi_message_init(&m);
 
@@ -163,6 +189,26 @@ static void wl12xx_spi_init(struct device *child)
 	spi_message_add_tail(&t, &m);
 
 	spi_sync(to_spi_device(glue->dev), &m);
+
+	/* Send extra clocks with CS high. this is required by the wilink
+	 * family in order for successfully enter WSPI mode
+	 */
+	gpio_direction_output(master->cs_gpios[0], 1);
+
+	memset(&m, 0, sizeof(m));
+	spi_message_init(&m);
+
+	cmd[0] = 0xff;
+	cmd[1] = 0xff;
+	cmd[2] = 0xff;
+	cmd[3] = 0xff;
+	swab32s((u32 *)cmd);
+
+	t.tx_buf = cmd;
+	t.len = 4;
+	spi_message_add_tail(&t, &m);
+	spi_sync(to_spi_device(glue->dev), &m);
+
 	kfree(cmd);
 }
 
@@ -213,6 +259,16 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
 	u32 *busy_buf;
 	u32 *cmd;
 	u32 chunk_len;
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
+
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
 
 	while (len > 0) {
 		chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
@@ -267,25 +323,44 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
 		len -= chunk_len;
 	}
 
+	/* Drive CS line high */
+	gpio_direction_output(master->cs_gpios[0], 1);
 	return 0;
 }
 
-static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
-					     void *buf, size_t len, bool fixed)
+static int __wl12xx_spi_raw_write(struct device *child, int addr,
+				  void *buf, size_t len, bool fixed)
 {
 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
-	/* SPI write buffers - 2 for each chunk */
-	struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
+	struct spi_transfer *t;
 	struct spi_message m;
 	u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
 	u32 *cmd;
 	u32 chunk_len;
 	int i;
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
+
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+
+	/* SPI write buffers - 2 for each chunk */
+	t = kzalloc(sizeof(*t) * 2 * WSPI_MAX_NUM_OF_CHUNKS, GFP_KERNEL);
+	if (!t) {
+		dev_err(child->parent,
+			"could not allocate spi write buffer\n");
+		return -ENOMEM;
+	}
+
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
 
 	WARN_ON(len > SPI_AGGR_BUFFER_SIZE);
 
 	spi_message_init(&m);
-	memset(t, 0, sizeof(t));
 
 	cmd = &commands[0];
 	i = 0;
@@ -318,9 +393,29 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
 
 	spi_sync(to_spi_device(glue->dev), &m);
 
+	/* Drive CS line high */
+	gpio_direction_output(master->cs_gpios[0], 1);
+
+	kfree(t);
 	return 0;
 }
 
+static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
+					     void *buf, size_t len, bool fixed)
+{
+	int ret;
+
+	/* The ELP wakeup write may fail the first time due to internal
+	 * hardware latency. It is safer to send the wakeup command twice to
+	 * avoid unexpected failures.
+	 */
+	if (addr == HW_ACCESS_ELP_CTRL_REG)
+		ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
+	ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
+
+	return ret;
+}
+
 /**
  * wl12xx_spi_set_power - power on/off the wl12xx unit
  * @child: wl12xx device handle.
@@ -349,17 +444,38 @@ static int wl12xx_spi_set_power(struct device *child, bool enable)
 	return ret;
 }
 
+/**
+ * wl12xx_spi_set_block_size
+ *
+ * This function is not needed for spi mode, but need to be present.
+ * Without it defined the wlcore fallback to use the wrong packet
+ * allignment on tx.
+ */
+static void wl12xx_spi_set_block_size(struct device *child,
+				      unsigned int blksz)
+{
+}
+
 static struct wl1271_if_operations spi_ops = {
 	.read		= wl12xx_spi_raw_read,
 	.write		= wl12xx_spi_raw_write,
 	.reset		= wl12xx_spi_reset,
 	.init		= wl12xx_spi_init,
 	.power		= wl12xx_spi_set_power,
-	.set_block_size = NULL,
+	.set_block_size = wl12xx_spi_set_block_size,
 };
 
 static const struct of_device_id wlcore_spi_of_match_table[] = {
-	{ .compatible = "ti,wl1271" },
+	{ .compatible = "ti,wl1271", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1273", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1281", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1283", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1801", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1805", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1807", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1831", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1835", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1837", .data = &wl18xx_data},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table);
@@ -375,18 +491,24 @@ static int wlcore_probe_of(struct spi_device *spi, struct wl12xx_spi_glue *glue,
 			   struct wlcore_platdev_data *pdev_data)
 {
 	struct device_node *dt_node = spi->dev.of_node;
-	int ret;
+	const struct of_device_id *of_id;
+
+	of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
+	if (!of_id)
+		return -ENODEV;
+
+	wilink_data = of_id->data;
+	dev_info(&spi->dev, "selected chip familiy is %s\n",
+		 wilink_data->name);
 
 	if (of_find_property(dt_node, "clock-xtal", NULL))
 		pdev_data->ref_clock_xtal = true;
 
-	ret = of_property_read_u32(dt_node, "ref-clock-frequency",
-				   &pdev_data->ref_clock_freq);
-	if (IS_ERR_VALUE(ret)) {
-		dev_err(glue->dev,
-			"can't get reference clock frequency (%d)\n", ret);
-		return ret;
-	}
+	/* optional clock frequency params */
+	of_property_read_u32(dt_node, "ref-clock-frequency",
+			     &pdev_data->ref_clock_freq);
+	of_property_read_u32(dt_node, "tcxo-clock-frequency",
+			     &pdev_data->tcxo_clock_freq);
 
 	return 0;
 }
@@ -397,6 +519,7 @@ static int wl1271_probe(struct spi_device *spi)
 	struct wlcore_platdev_data pdev_data;
 	struct resource res[1];
 	int ret;
+	struct spi_master *master = spi->master;
 
 	memset(&pdev_data, 0x00, sizeof(pdev_data));
 
@@ -410,6 +533,12 @@ static int wl1271_probe(struct spi_device *spi)
 
 	glue->dev = &spi->dev;
 
+	if (!master->cs_gpios) {
+		dev_err(glue->dev,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+
 	spi_set_drvdata(spi, glue);
 
 	/* This is the only SPI value that we need to set here, the rest
@@ -431,15 +560,21 @@ static int wl1271_probe(struct spi_device *spi)
 		return ret;
 	}
 
+	if (gpio_request(master->cs_gpios[0], "spi1-cs0"))
+		return -EINVAL;
+
 	ret = spi_setup(spi);
 	if (ret < 0) {
 		dev_err(glue->dev, "spi_setup failed\n");
+		gpio_free(master->cs_gpios[0]);
 		return ret;
 	}
 
-	glue->core = platform_device_alloc("wl12xx", PLATFORM_DEVID_AUTO);
+	glue->core = platform_device_alloc(wilink_data->name,
+					   PLATFORM_DEVID_AUTO);
 	if (!glue->core) {
 		dev_err(glue->dev, "can't allocate platform_device\n");
+		gpio_free(master->cs_gpios[0]);
 		return -ENOMEM;
 	}
 
@@ -474,14 +609,17 @@ static int wl1271_probe(struct spi_device *spi)
 
 out_dev_put:
 	platform_device_put(glue->core);
+	gpio_free(master->cs_gpios[0]);
 	return ret;
 }
 
 static int wl1271_remove(struct spi_device *spi)
 {
 	struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
+	struct spi_master *master = spi->master;
 
 	platform_device_unregister(glue->core);
+	gpio_free(master->cs_gpios[0]);
 
 	return 0;
 }
-- 
1.7.9.5

^ permalink raw reply related

* Re: [PATCH RFC] net: decrease the length of backlog queue immediately after it's detached from sk
From: Sergei Shtylyov @ 2016-03-30 12:56 UTC (permalink / raw)
  To: Yang Yingliang, netdev; +Cc: davem, eric.dumazet
In-Reply-To: <1459315001-3448-1-git-send-email-yangyingliang@huawei.com>

Hello.

On 3/30/2016 8:16 AM, Yang Yingliang wrote:

> When task A hold the sk owned in tcp_sendmsg, if lots of packets
> arrive and the packets will be added to backlog queue. The packets
> will be handled in release_sock called from tcp_sendmsg. When the
> sk_backlog is removed from sk, the length will not decrease until
> all the packets in backlog queue are handled. This may leads to the
> new packets be dropped because the lenth is too big. So set the
> lenth to 0 immediately after it's detached from sk.

    Length?

> Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
[...]

MBR, Sergei

^ permalink raw reply

* Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC
From: kbuild test robot @ 2016-03-30 12:53 UTC (permalink / raw)
  To: Zhao Qiang
  Cc: kbuild-all, davem, akpm, gregkh, oss, xiaobo.xie, linux-kernel,
	netdev, linuxppc-dev, Zhao Qiang
In-Reply-To: <1459327830-19829-5-git-send-email-qiang.zhao@nxp.com>

[-- Attachment #1: Type: text/plain, Size: 5695 bytes --]

Hi Zhao,

[auto build test ERROR on net/master]
[also build test ERROR on v4.6-rc1 next-20160330]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Zhao-Qiang/fsl-qe-add-rx_sync-and-tx_sync-for-TDM-mode/20160330-170411
config: i386-allmodconfig (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All error/warnings (new ones prefixed by >>):

   In file included from include/soc/fsl/qe/ucc_slow.h:21:0,
                    from drivers/tty/serial/ucc_uart.c:34:
>> include/soc/fsl/qe/qe.h:24:21: fatal error: asm/cpm.h: No such file or directory
   compilation terminated.
--
>> drivers/net/ethernet/freescale/gianfar_ptp.c:75:0: warning: "FS" redefined
    #define FS                    (1<<28) /* FIPER start indication */
    ^
   In file included from arch/x86/include/uapi/asm/ptrace.h:5:0,
                    from arch/x86/include/asm/ptrace.h:6,
                    from arch/x86/include/asm/alternative.h:8,
                    from arch/x86/include/asm/bitops.h:16,
                    from include/linux/bitops.h:36,
                    from include/linux/kernel.h:10,
                    from include/linux/list.h:8,
                    from include/linux/kobject.h:20,
                    from include/linux/device.h:17,
                    from drivers/net/ethernet/freescale/gianfar_ptp.c:23:
   arch/x86/include/uapi/asm/ptrace-abi.h:15:0: note: this is the location of the previous definition
    #define FS 9
    ^
--
   In file included from drivers/soc/fsl/qe/qe_ic.c:31:0:
   include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_low_ipic':
>> include/soc/fsl/qe/qe_ic.h:86:21: error: 'NO_IRQ' undeclared (first use in this function)
     if (cascade_irq != NO_IRQ)
                        ^
   include/soc/fsl/qe/qe_ic.h:86:21: note: each undeclared identifier is reported only once for each function it appears in
   include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_high_ipic':
   include/soc/fsl/qe/qe_ic.h:95:21: error: 'NO_IRQ' undeclared (first use in this function)
     if (cascade_irq != NO_IRQ)
                        ^
   include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_low_mpic':
   include/soc/fsl/qe/qe_ic.h:105:21: error: 'NO_IRQ' undeclared (first use in this function)
     if (cascade_irq != NO_IRQ)
                        ^
   include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_high_mpic':
   include/soc/fsl/qe/qe_ic.h:117:21: error: 'NO_IRQ' undeclared (first use in this function)
     if (cascade_irq != NO_IRQ)
                        ^
   include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_muxed_mpic':
   include/soc/fsl/qe/qe_ic.h:130:21: error: 'NO_IRQ' undeclared (first use in this function)
     if (cascade_irq == NO_IRQ)
                        ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_read':
>> drivers/soc/fsl/qe/qe_ic.c:180:9: error: implicit declaration of function 'in_be32' [-Werror=implicit-function-declaration]
     return in_be32(base + (reg >> 2));
            ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_write':
>> drivers/soc/fsl/qe/qe_ic.c:186:2: error: implicit declaration of function 'out_be32' [-Werror=implicit-function-declaration]
     out_be32(base + (reg >> 2), value);
     ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_get_low_irq':
>> drivers/soc/fsl/qe/qe_ic.c:299:10: error: 'NO_IRQ' undeclared (first use in this function)
      return NO_IRQ;
             ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_get_high_irq':
   drivers/soc/fsl/qe/qe_ic.c:315:10: error: 'NO_IRQ' undeclared (first use in this function)
      return NO_IRQ;
             ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_init':
   drivers/soc/fsl/qe/qe_ic.c:350:25: error: 'NO_IRQ' undeclared (first use in this function)
     if (qe_ic->virq_low == NO_IRQ) {
                            ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_set_highest_priority':
>> drivers/soc/fsl/qe/qe_ic.c:392:21: error: implicit declaration of function 'virq_to_hw' [-Werror=implicit-function-declaration]
     unsigned int src = virq_to_hw(virq);
                        ^
   cc1: some warnings being treated as errors

vim +24 include/soc/fsl/qe/qe.h

98658538 include/asm-powerpc/qe.h      Li Yang         2006-10-03  18  
1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang      2015-11-30  19  #include <linux/compiler.h>
1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang      2015-11-30  20  #include <linux/genalloc.h>
5e41486c include/asm-powerpc/qe.h      Anton Vorontsov 2008-05-23  21  #include <linux/spinlock.h>
1b9e8904 arch/powerpc/include/asm/qe.h Anton Vorontsov 2008-12-03  22  #include <linux/errno.h>
1b9e8904 arch/powerpc/include/asm/qe.h Anton Vorontsov 2008-12-03  23  #include <linux/err.h>
5093bb96 include/asm-powerpc/qe.h      Anton Vorontsov 2008-05-23 @24  #include <asm/cpm.h>
7aa1aa6e include/soc/fsl/qe/qe.h       Zhao Qiang      2015-11-30  25  #include <soc/fsl/qe/immap_qe.h>
1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang      2015-11-30  26  #include <linux/of.h>
1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang      2015-11-30  27  #include <linux/of_address.h>

:::::: The code at line 24 was first introduced by commit
:::::: 5093bb965a163fe288c3e5db0275165f86c895c2 powerpc/QE: switch to the cpm_muram implementation

:::::: TO: Anton Vorontsov <avorontsov@ru.mvista.com>
:::::: CC: Kumar Gala <galak@kernel.crashing.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 54451 bytes --]

^ permalink raw reply

* Re: bpf: net/core/filter.c:2115 suspicious rcu_dereference_protected() usage!
From: Daniel Borkmann @ 2016-03-30 12:38 UTC (permalink / raw)
  To: Michal Kubecek
  Cc: Sasha Levin, Jiri Slaby, David S. Miller, ast,
	netdev@vger.kernel.org, LKML
In-Reply-To: <20160330122418.GD15048@unicorn.suse.cz>

On 03/30/2016 02:24 PM, Michal Kubecek wrote:
> On Wed, Mar 30, 2016 at 01:33:44PM +0200, Daniel Borkmann wrote:
>> On 03/30/2016 11:42 AM, Michal Kubecek wrote:
>>>
>>> I'm just not sure checking if we hold the right lock depending on caller
>>> is worth the extra complexity. After all, what is really needed is to
>>> hold _some_ lock guaranteeing sk_attach_prog() and sk_detach_filter()
>>> are safe so that just changing the condition in both to
>>>
>>>    sock_owned_by_user(sk) || lockdep_rtnl_is_held()
>>
>> It would certainly silence it, but would be less accurate in terms of lock
>> proving as opposed to the diff above. E.g. rntl could be held elsewhere,
>> while someone attaches a socket filter w/o having locked the socket (currently
>> not the case, but it would kind of defeat the purpose of rcu_dereference_protected()
>> here). Was thinking about using a extra socket flag to indicate it's
>> externally managed, but it's not really worth wasting sk's flags bit
>> space just for this corner case.
>
> Originally my reasoning was that to actually hide a locking issue from
> lockdep, this would have to happen every time we get down into the
> function which is unlikely. But thinking about it again, this code path
> is not so frequent and the fuzzers tend to do strange things so that it
> could really happen.

In this case actually nothing too fancy, just seems that filters on tap devices
might not be really used by anyone (issue is already couple of years old).

> Sasha/Jiri, could you test the patch with your testcases? I received it
> corrupted (strange leading whitespaces) so I better add cleaned up
> version below.

Tested this yesterday night on my machine with PROVE_RCU + PROVE_RCU_REPEATEDLY
enabled, and it can easily be triggered with a simple ioctl(tun_fd,
TUN{ATTACH,DETACH}FILTER, ...) on a tap device, and the patch now silences
it. Sorry for the white space damage (should have just attached it), I'd send
it later today.

Thanks,
Daniel

^ permalink raw reply


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