Re: [PATCH v3 09/24] wfx: add hwio.c/hwio.h

2020-12-23 Thread Greg Kroah-Hartman
On Wed, Dec 23, 2020 at 09:01:33AM +0100, Jérôme Pouiller wrote:
> On Tuesday 22 December 2020 16:27:01 CET Greg Kroah-Hartman wrote:
> > On Tue, Dec 22, 2020 at 05:10:11PM +0200, Kalle Valo wrote:
> > > Jerome Pouiller  writes:
> > >
> > > > +/*
> > > > + * Internal helpers.
> > > > + *
> > > > + * About CONFIG_VMAP_STACK:
> > > > + * When CONFIG_VMAP_STACK is enabled, it is not possible to run DMA on 
> > > > stack
> > > > + * allocated data. Functions below that work with registers (aka 
> > > > functions
> > > > + * ending with "32") automatically reallocate buffers with kmalloc. 
> > > > However,
> > > > + * functions that work with arbitrary length buffers let's caller to 
> > > > handle
> > > > + * memory location. In doubt, enable CONFIG_DEBUG_SG to detect badly 
> > > > located
> > > > + * buffer.
> > > > + */
> > >
> > > This sounds very hacky to me, I have understood that you should never
> > > use stack with DMA.
> > 
> > You should never do that because some platforms do not support it, so no
> > driver should ever try to do that as they do not know what platform they
> > are running on.
> 
> Just to be curious, why these platforms don't support DMA in a stack
> allocated area?

Hardware is odd :(

> If the memory is contiguous (= not vmalloced), correctly
> aligned and in the first 4GB of physical memory, it should be sufficient,
> shouldn't?

Nope, sorry, this just does not work at all on many platforms.

thanks,

greg k-h
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v3 09/24] wfx: add hwio.c/hwio.h

2020-12-23 Thread Jérôme Pouiller
On Tuesday 22 December 2020 16:27:01 CET Greg Kroah-Hartman wrote:
> On Tue, Dec 22, 2020 at 05:10:11PM +0200, Kalle Valo wrote:
> > Jerome Pouiller  writes:
> >
> > > +/*
> > > + * Internal helpers.
> > > + *
> > > + * About CONFIG_VMAP_STACK:
> > > + * When CONFIG_VMAP_STACK is enabled, it is not possible to run DMA on 
> > > stack
> > > + * allocated data. Functions below that work with registers (aka 
> > > functions
> > > + * ending with "32") automatically reallocate buffers with kmalloc. 
> > > However,
> > > + * functions that work with arbitrary length buffers let's caller to 
> > > handle
> > > + * memory location. In doubt, enable CONFIG_DEBUG_SG to detect badly 
> > > located
> > > + * buffer.
> > > + */
> >
> > This sounds very hacky to me, I have understood that you should never
> > use stack with DMA.
> 
> You should never do that because some platforms do not support it, so no
> driver should ever try to do that as they do not know what platform they
> are running on.

Just to be curious, why these platforms don't support DMA in a stack
allocated area? If the memory is contiguous (= not vmalloced), correctly
aligned and in the first 4GB of physical memory, it should be sufficient,
shouldn't? 

-- 
Jérôme Pouiller


___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH 1/2] media: cedrus: Remove checking for required controls

2020-12-23 Thread Jernej Skrabec
According to v4l2 request api specifications, it's allowed to skip
control if its content isn't changed for performance reasons. Cedrus
driver predates that, so it has implemented mechanism to check if all
required controls are included in one request.

Conform to specifications with removing that mechanism.

Note that this mechanism with static required flag isn't very good
anyway because need for control is usually signaled in other controls.

Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver")
Signed-off-by: Jernej Skrabec 
---
 drivers/staging/media/sunxi/cedrus/cedrus.c | 49 -
 drivers/staging/media/sunxi/cedrus/cedrus.h |  1 -
 2 files changed, 50 deletions(-)

diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c 
b/drivers/staging/media/sunxi/cedrus/cedrus.c
index ddad5d274ee8..7bd9291c8d5f 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
@@ -34,56 +34,48 @@ static const struct cedrus_control cedrus_controls[] = {
.id = V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS,
},
.codec  = CEDRUS_CODEC_MPEG2,
-   .required   = true,
},
{
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION,
},
.codec  = CEDRUS_CODEC_MPEG2,
-   .required   = false,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS,
},
.codec  = CEDRUS_CODEC_H264,
-   .required   = true,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_SLICE_PARAMS,
},
.codec  = CEDRUS_CODEC_H264,
-   .required   = true,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_SPS,
},
.codec  = CEDRUS_CODEC_H264,
-   .required   = true,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_PPS,
},
.codec  = CEDRUS_CODEC_H264,
-   .required   = true,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX,
},
.codec  = CEDRUS_CODEC_H264,
-   .required   = false,
},
{
.cfg = {
.id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS,
},
.codec  = CEDRUS_CODEC_H264,
-   .required   = false,
},
{
.cfg = {
@@ -92,7 +84,6 @@ static const struct cedrus_control cedrus_controls[] = {
.def= V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED,
},
.codec  = CEDRUS_CODEC_H264,
-   .required   = false,
},
{
.cfg = {
@@ -101,7 +92,6 @@ static const struct cedrus_control cedrus_controls[] = {
.def= V4L2_STATELESS_H264_START_CODE_NONE,
},
.codec  = CEDRUS_CODEC_H264,
-   .required   = false,
},
/*
 * We only expose supported profiles information,
@@ -120,28 +110,24 @@ static const struct cedrus_control cedrus_controls[] = {
BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED),
},
.codec  = CEDRUS_CODEC_H264,
-   .required   = false,
},
{
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_SPS,
},
.codec  = CEDRUS_CODEC_H265,
-   .required   = true,
},
{
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_PPS,
},
.codec  = CEDRUS_CODEC_H265,
-   .required   = true,
},
{
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS,
},
.codec  = CEDRUS_CODEC_H265,
-   .required   = true,
},
{
.cfg = {
@@ -150,7 +136,6 @@ static const struct cedrus_control cedrus_controls[] = {
.def= V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
},
.codec  = CEDRUS_CODEC_H265,
-   .required   = false,
},
{
.cfg = {
@@ -159,14 +144,12 @@ static const struct cedrus_control cedrus_controls[] = {
.def= V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
},
 

[PATCH 0/2] media: cedrus: a couple of fixes

2020-12-23 Thread Jernej Skrabec
This short series fixes two issues:
1. conformance to v4l2 request api specifications
2. regression in h264 video decoding introduced during h264 api rework

Please take a look.

Best regards,
Jernej

Jernej Skrabec (2):
  media: cedrus: Remove checking for required controls
  media: cedrus: Fix H264 decoding

 drivers/staging/media/sunxi/cedrus/cedrus.c   | 49 ---
 drivers/staging/media/sunxi/cedrus/cedrus.h   |  1 -
 .../staging/media/sunxi/cedrus/cedrus_h264.c  |  2 +-
 3 files changed, 1 insertion(+), 51 deletions(-)

-- 
2.29.2

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH 2/2] media: cedrus: Fix H264 decoding

2020-12-23 Thread Jernej Skrabec
During H264 API overhaul subtle bug was introduced Cedrus driver.
Progressive references have both, top and bottom reference flags set.
Cedrus reference list expects only bottom reference flag and only when
interlaced frames are decoded. However, due to a bug in Cedrus check,
exclusivity is not tested and that flag is set also for progressive
references. That causes "jumpy" background with many videos.

Fix that by checking that only bottom reference flag is set in control
and nothing else.

Tested-by: Andre Heider 
Fixes: cfc8c3ed533e ("media: cedrus: h264: Properly configure reference field")
Signed-off-by: Jernej Skrabec 
---
 drivers/staging/media/sunxi/cedrus/cedrus_h264.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c 
b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
index 781c84a9b1b7..de7442d4834d 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
@@ -203,7 +203,7 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx,
position = cedrus_buf->codec.h264.position;
 
sram_array[i] |= position << 1;
-   if (ref_list[i].fields & V4L2_H264_BOTTOM_FIELD_REF)
+   if (ref_list[i].fields == V4L2_H264_BOTTOM_FIELD_REF)
sram_array[i] |= BIT(0);
}
 
-- 
2.29.2

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v3 05/24] wfx: add main.c/main.h

2020-12-23 Thread Jérôme Pouiller
On Tuesday 22 December 2020 16:44:05 CET Kalle Valo wrote:
> Jerome Pouiller  writes:
> 
> > +/* NOTE: wfx_send_pds() destroy buf */
> > +int wfx_send_pds(struct wfx_dev *wdev, u8 *buf, size_t len)
> > +{
> > + int ret;
> > + int start, brace_level, i;
> > +
> > + start = 0;
> > + brace_level = 0;
> > + if (buf[0] != '{') {
> > + dev_err(wdev->dev, "valid PDS start with '{'. Did you forget to
> > compress it?\n");
> > + return -EINVAL;
> > + }
> > + for (i = 1; i < len - 1; i++) {
> > + if (buf[i] == '{')
> > + brace_level++;
> > + if (buf[i] == '}')
> > + brace_level--;
> > + if (buf[i] == '}' && !brace_level) {
> > + i++;
> > + if (i - start + 1 > WFX_PDS_MAX_SIZE)
> > + return -EFBIG;
> > + buf[start] = '{';
> > + buf[i] = 0;
> > + dev_dbg(wdev->dev, "send PDS '%s}'\n", buf + start);
> > + buf[i] = '}';
> > + ret = hif_configuration(wdev, buf + start,
> > + i - start + 1);
> > + if (ret > 0) {
> > + dev_err(wdev->dev, "PDS bytes %d to %d: invalid data (unsupported
> > options?)\n",
> > + start, i);
> > + return -EINVAL;
> > + }
> > + if (ret == -ETIMEDOUT) {
> > + dev_err(wdev->dev, "PDS bytes %d to %d: chip didn't reply (corrupted
> > file?)\n",
> > + start, i);
> > + return ret;
> > + }
> > + if (ret) {
> > + dev_err(wdev->dev, "PDS bytes %d to %d: chip returned an unknown
> > error\n",
> > + start, i);
> > + return -EIO;
> > + }
> > + buf[i] = ',';
> > + start = i;
> > + }
> > + }
> > + return 0;
> > +}
> 
> What does this function do? Looks very strange.

I am going to add this comment:

The device need data about the antenna configuration. This information in
provided by PDS (Platform Data Set, this is the wording used in WF200
documentation) files. For hardware integrators, the full process to create
PDS files is described here:
  https://github.com/SiliconLabs/wfx-firmware/blob/master/PDS/README.md

So this function aims to send PDS to the device. However, the PDS file is
often bigger than Rx buffers of the chip, so it has to be sent in multiple
parts.

In add, the PDS data cannot be split anywhere. The PDS files contains tree
structures. Braces are used to enter/leave a level of the tree (in a JSON
fashion). PDS files can only been split between root nodes.

-- 
Jérôme Pouiller



___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH -next] staging: vc04_services: use DEFINE_MUTEX (and mutex_init() had been too late)

2020-12-23 Thread Zheng Yongjun
Signed-off-by: Zheng Yongjun 
---
 .../vc04_services/interface/vchiq_arm/vchiq_connected.c| 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git 
a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c 
b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c
index 79b75efa6868..864253866155 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c
@@ -12,13 +12,12 @@ static   intg_connected;
 static   intg_num_deferred_callbacks;
 static   VCHIQ_CONNECTED_CALLBACK_T g_deferred_callback[MAX_CALLBACKS];
 static   intg_once_init;
-static   struct mutex   g_connected_mutex;
+static   DEFINE_MUTEX(g_connected_mutex);
 
 /* Function to initialize our lock */
 static void connected_init(void)
 {
if (!g_once_init) {
-   mutex_init(&g_connected_mutex);
g_once_init = 1;
}
 }
-- 
2.22.0

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH -next] staging: most: use DEFINE_MUTEX (and mutex_init() had been too late)

2020-12-23 Thread Zheng Yongjun
Signed-off-by: Zheng Yongjun 
---
 drivers/staging/most/net/net.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c
index b6fecb06a0e6..f125bb6da406 100644
--- a/drivers/staging/most/net/net.c
+++ b/drivers/staging/most/net/net.c
@@ -68,7 +68,7 @@ struct net_dev_context {
 };
 
 static struct list_head net_devices = LIST_HEAD_INIT(net_devices);
-static struct mutex probe_disc_mt; /* ch->linked = true, most_nd_open */
+static DEFINE_MUTEX(probe_disc_mt); /* ch->linked = true, most_nd_open */
 static DEFINE_SPINLOCK(list_lock); /* list_head, ch->linked = false, dev_hold 
*/
 static struct most_component comp;
 
@@ -520,7 +520,6 @@ static int __init most_net_init(void)
 {
int err;
 
-   mutex_init(&probe_disc_mt);
err = most_register_component(&comp);
if (err)
return err;
-- 
2.22.0

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH -next] staging: vc04_services: use DEFINE_MUTEX (and mutex_init() had been too late)

2020-12-23 Thread Greg KH
On Wed, Dec 23, 2020 at 10:11:29PM +0800, Zheng Yongjun wrote:
> Signed-off-by: Zheng Yongjun 
> ---
>  .../vc04_services/interface/vchiq_arm/vchiq_connected.c| 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)

I can not take patches without any changelog text :(
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH -next] staging: most: use DEFINE_MUTEX (and mutex_init() had been too late)

2020-12-23 Thread Greg KH
On Wed, Dec 23, 2020 at 10:11:38PM +0800, Zheng Yongjun wrote:
> Signed-off-by: Zheng Yongjun 
> ---
>  drivers/staging/most/net/net.c | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)

Again, no changelog text?

And why are you not cc:ing the developers and maintainers of the code
you are sending patches out for?  Please always use
scripts/get_maintainer.pl.

greg k-h
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH v4 04/24] wfx: add wfx.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/wfx.h | 166 ++
 1 file changed, 166 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/wfx.h

diff --git a/drivers/net/wireless/silabs/wfx/wfx.h 
b/drivers/net/wireless/silabs/wfx/wfx.h
new file mode 100644
index ..ba18bbfacd2b
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/wfx.h
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Common private data for Silicon Labs WFx chips.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ * Copyright (c) 2006, Michael Wu 
+ * Copyright 2004-2006 Jean-Baptiste Note , et al.
+ */
+#ifndef WFX_H
+#define WFX_H
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "bh.h"
+#include "data_tx.h"
+#include "main.h"
+#include "queue.h"
+#include "hif_tx.h"
+
+#define USEC_PER_TXOP 32 /* see struct ieee80211_tx_queue_params */
+#define USEC_PER_TU 1024
+
+struct hwbus_ops;
+
+struct wfx_dev {
+   struct wfx_platform_data pdata;
+   struct device   *dev;
+   struct ieee80211_hw *hw;
+   struct ieee80211_vif*vif[2];
+   struct mac_address  addresses[2];
+   const struct hwbus_ops  *hwbus_ops;
+   void*hwbus_priv;
+
+   u8  keyset;
+   struct completion   firmware_ready;
+   struct hif_ind_startup  hw_caps;
+   struct wfx_hif  hif;
+   struct delayed_work cooling_timeout_work;
+   boolpoll_irq;
+   boolchip_frozen;
+   struct mutexconf_mutex;
+
+   struct wfx_hif_cmd  hif_cmd;
+   struct sk_buff_head tx_pending;
+   wait_queue_head_t   tx_dequeue;
+   atomic_ttx_lock;
+
+   atomic_tpacket_id;
+   u32 key_map;
+
+   struct hif_rx_stats rx_stats;
+   struct mutexrx_stats_lock;
+   struct hif_tx_power_loop_info tx_power_loop_info;
+   struct mutextx_power_loop_info_lock;
+   int force_ps_timeout;
+};
+
+struct wfx_vif {
+   struct wfx_dev  *wdev;
+   struct ieee80211_vif*vif;
+   struct ieee80211_channel *channel;
+   int id;
+
+   u32 link_id_map;
+
+   boolafter_dtim_tx_allowed;
+   booljoin_in_progress;
+
+   struct delayed_work beacon_loss_work;
+
+   struct wfx_queuetx_queue[4];
+   struct tx_policy_cache  tx_policy_cache;
+   struct work_struct  tx_policy_upload_work;
+
+   struct work_struct  update_tim_work;
+
+   unsigned long   uapsd_mask;
+
+   /* avoid some operations in parallel with scan */
+   struct mutexscan_lock;
+   struct work_struct  scan_work;
+   struct completion   scan_complete;
+   boolscan_abort;
+   struct ieee80211_scan_request *scan_req;
+
+   struct completion   set_pm_mode_complete;
+};
+
+static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id)
+{
+   if (vif_id >= ARRAY_SIZE(wdev->vif)) {
+   dev_dbg(wdev->dev, "requesting non-existent vif: %d\n", vif_id);
+   return NULL;
+   }
+   vif_id = array_index_nospec(vif_id, ARRAY_SIZE(wdev->vif));
+   if (!wdev->vif[vif_id]) {
+   dev_dbg(wdev->dev, "requesting non-allocated vif: %d\n",
+   vif_id);
+   return NULL;
+   }
+   return (struct wfx_vif *) wdev->vif[vif_id]->drv_priv;
+}
+
+static inline struct wfx_vif *wvif_iterate(struct wfx_dev *wdev,
+  struct wfx_vif *cur)
+{
+   int i;
+   int mark = 0;
+   struct wfx_vif *tmp;
+
+   if (!cur)
+   mark = 1;
+   for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) {
+   tmp = wdev_to_wvif(wdev, i);
+   if (mark && tmp)
+   return tmp;
+   if (tmp == cur)
+   mark = 1;
+   }
+   return NULL;
+}
+
+static inline int wvif_count(struct wfx_dev *wdev)
+{
+   int i;
+   int ret = 0;
+   struct wfx_vif *wvif;
+
+   for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) {
+   wvif = wdev_to_wvif(wdev, i);
+   if (wvif)
+   ret++;
+   }
+   return ret;
+}
+
+static inline void memreverse(u8 *src, u8 length)
+{
+   u8 *lo = src;
+   u8 *hi = src + length - 1;
+   u8 swap;
+
+   while (lo < hi) {
+   swap = *lo;
+   *lo++ = *hi;
+   *hi-- = swap;
+   }
+}
+
+static inline int memzcmp(void *src, unsigned int size)
+{
+   u8 *buf = src;
+
+   if (!size)
+   return 0;
+   if (*b

[PATCH v4 07/24] wfx: add bus_spi.c

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/bus_spi.c | 271 ++
 1 file changed, 271 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/bus_spi.c

diff --git a/drivers/net/wireless/silabs/wfx/bus_spi.c 
b/drivers/net/wireless/silabs/wfx/bus_spi.c
new file mode 100644
index ..56375004c920
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/bus_spi.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SPI interface.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2011, Sagrad Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "bus.h"
+#include "wfx.h"
+#include "hwio.h"
+#include "main.h"
+#include "bh.h"
+
+#define SET_WRITE 0x7FFF/* usage: and operation */
+#define SET_READ 0x8000 /* usage: or operation */
+
+#define WFX_RESET_INVERTED 1
+
+static const struct wfx_platform_data wfx_spi_pdata = {
+   .file_fw = "wfm_wf200",
+   .file_pds = "wf200.pds",
+   .use_rising_clk = true,
+};
+
+struct wfx_spi_priv {
+   struct spi_device *func;
+   struct wfx_dev *core;
+   struct gpio_desc *gpio_reset;
+   bool need_swab;
+};
+
+/* WFx chip read data 16bits at time and place them directly into (little
+ * endian) CPU register. So, chip expect byte order like "B1 B0 B3 B2" (while
+ * LE is "B0 B1 B2 B3" and BE is "B3 B2 B1 B0")
+ *
+ * A little endian host with bits_per_word == 16 should do the right job
+ * natively. The code below to support big endian host and commonly used SPI
+ * 8bits.
+ */
+static int wfx_spi_copy_from_io(void *priv, unsigned int addr,
+   void *dst, size_t count)
+{
+   struct wfx_spi_priv *bus = priv;
+   u16 regaddr = (addr << 12) | (count / 2) | SET_READ;
+   struct spi_message  m;
+   struct spi_transfer t_addr = {
+   .tx_buf = ®addr,
+   .len= sizeof(regaddr),
+   };
+   struct spi_transfer t_msg = {
+   .rx_buf = dst,
+   .len= count,
+   };
+   u16 *dst16 = dst;
+   int ret, i;
+
+   WARN(count % 2, "buffer size must be a multiple of 2");
+
+   cpu_to_le16s(®addr);
+   if (bus->need_swab)
+   swab16s(®addr);
+
+   spi_message_init(&m);
+   spi_message_add_tail(&t_addr, &m);
+   spi_message_add_tail(&t_msg, &m);
+   ret = spi_sync(bus->func, &m);
+
+   if (bus->need_swab && addr == WFX_REG_CONFIG)
+   for (i = 0; i < count / 2; i++)
+   swab16s(&dst16[i]);
+   return ret;
+}
+
+static int wfx_spi_copy_to_io(void *priv, unsigned int addr,
+ const void *src, size_t count)
+{
+   struct wfx_spi_priv *bus = priv;
+   u16 regaddr = (addr << 12) | (count / 2);
+   /* FIXME: use a bounce buffer */
+   u16 *src16 = (void *)src;
+   int ret, i;
+   struct spi_message  m;
+   struct spi_transfer t_addr = {
+   .tx_buf = ®addr,
+   .len= sizeof(regaddr),
+   };
+   struct spi_transfer t_msg = {
+   .tx_buf = src,
+   .len= count,
+   };
+
+   WARN(count % 2, "buffer size must be a multiple of 2");
+   WARN(regaddr & SET_READ, "bad addr or size overflow");
+
+   cpu_to_le16s(®addr);
+
+   /* Register address and CONFIG content always use 16bit big endian
+* ("BADC" order)
+*/
+   if (bus->need_swab)
+   swab16s(®addr);
+   if (bus->need_swab && addr == WFX_REG_CONFIG)
+   for (i = 0; i < count / 2; i++)
+   swab16s(&src16[i]);
+
+   spi_message_init(&m);
+   spi_message_add_tail(&t_addr, &m);
+   spi_message_add_tail(&t_msg, &m);
+   ret = spi_sync(bus->func, &m);
+
+   if (bus->need_swab && addr == WFX_REG_CONFIG)
+   for (i = 0; i < count / 2; i++)
+   swab16s(&src16[i]);
+   return ret;
+}
+
+static void wfx_spi_lock(void *priv)
+{
+}
+
+static void wfx_spi_unlock(void *priv)
+{
+}
+
+static irqreturn_t wfx_spi_irq_handler(int irq, void *priv)
+{
+   struct wfx_spi_priv *bus = priv;
+
+   wfx_bh_request_rx(bus->core);
+   return IRQ_HANDLED;
+}
+
+static int wfx_spi_irq_subscribe(void *priv)
+{
+   struct wfx_spi_priv *bus = priv;
+   u32 flags;
+
+   flags = irq_get_trigger_type(bus->func->irq);
+   if (!flags)
+   flags = IRQF_TRIGGER_HIGH;
+   flags |= IRQF_ONESHOT;
+   return devm_request_threaded_irq(&bus->func->dev, bus->func->irq, NULL,
+wfx_spi_irq_handler, IRQF_ONESHOT,
+"wfx", bus);
+}
+
+static int wfx_spi_irq_unsubscribe(void *priv)
+{
+   struct

[PATCH v4 02/24] dt-bindings: introduce silabs,wfx.yaml

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 .../bindings/net/wireless/silabs,wfx.yaml | 133 ++
 1 file changed, 133 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/net/wireless/silabs,wfx.yaml

diff --git a/Documentation/devicetree/bindings/net/wireless/silabs,wfx.yaml 
b/Documentation/devicetree/bindings/net/wireless/silabs,wfx.yaml
new file mode 100644
index ..487d46c5fdc0
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/wireless/silabs,wfx.yaml
@@ -0,0 +1,133 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (c) 2020, Silicon Laboratories, Inc.
+%YAML 1.2
+---
+
+$id: http://devicetree.org/schemas/net/wireless/silabs,wfx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Silicon Labs WFxxx devicetree bindings
+
+maintainers:
+  - Jérôme Pouiller 
+
+description: >
+  Support for the Wifi chip WFxxx from Silicon Labs. Currently, the only device
+  from the WFxxx series is the WF200 described here:
+ https://www.silabs.com/documents/public/data-sheets/wf200-datasheet.pdf
+  
+  The WF200 can be connected via SPI or via SDIO.
+  
+  For SDIO:
+  
+Declaring the WFxxx chip in device tree is mandatory (usually, the VID/PID 
is
+sufficient for the SDIO devices).
+  
+It is recommended to declare a mmc-pwrseq on SDIO host above WFx. Without
+it, you may encounter issues during reboot. The mmc-pwrseq should be
+compatible with mmc-pwrseq-simple. Please consult
+Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt for more
+information.
+  
+  For SPI:
+  
+In add of the properties below, please consult
+Documentation/devicetree/bindings/spi/spi-controller.yaml for optional SPI
+related properties.
+
+properties:
+  compatible:
+const: silabs,wf200
+
+  reg:
+description:
+  When used on SDIO bus,  must be set to 1. When used on SPI bus, it 
is
+  the chip select address of the device as defined in the SPI devices
+  bindings.
+maxItems: 1
+
+  spi-max-frequency: true
+
+  interrupts:
+description: The interrupt line. Triggers IRQ_TYPE_LEVEL_HIGH and
+  IRQ_TYPE_EDGE_RISING are both supported by the chip and the driver. When
+  SPI is used, this property is required. When SDIO is used, the "in-band"
+  interrupt provided by the SDIO bus is used unless an interrupt is defined
+  in the Device Tree.
+maxItems: 1
+
+  reset-gpios:
+description: (SPI only) Phandle of gpio that will be used to reset chip
+  during probe. Without this property, you may encounter issues with warm
+  boot. (For legacy purpose, the gpio in inverted when compatible ==
+  "silabs,wfx-spi")
+
+  For SDIO, the reset gpio should declared using a mmc-pwrseq.
+maxItems: 1
+
+  wakeup-gpios:
+description: Phandle of gpio that will be used to wake-up chip. Without 
this
+  property, driver will disable most of power saving features.
+maxItems: 1
+
+  silabs,antenna-config-file:
+$ref: /schemas/types.yaml#/definitions/string
+description: Use an alternative file for antenna configuration (aka
+  "Platform Data Set" in Silabs jargon). Default is 'wf200.pds'.
+
+  local-mac-address: true
+
+  mac-address: true
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+
+examples:
+  - |
+#include 
+#include 
+
+spi0 {
+#address-cells = <1>;
+#size-cells = <0>;
+
+wifi@0 {
+compatible = "silabs,wf200";
+pinctrl-names = "default";
+pinctrl-0 = <&wfx_irq &wfx_gpios>;
+reg = <0>;
+interrupts-extended = <&gpio 16 IRQ_TYPE_EDGE_RISING>;
+wakeup-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>;
+reset-gpios = <&gpio 13 GPIO_ACTIVE_LOW>;
+spi-max-frequency = <4200>;
+};
+};
+
+  - |
+#include 
+#include 
+
+wfx_pwrseq: wfx_pwrseq {
+compatible = "mmc-pwrseq-simple";
+pinctrl-names = "default";
+pinctrl-0 = <&wfx_reset>;
+reset-gpios = <&gpio 13 GPIO_ACTIVE_LOW>;
+};
+
+mmc0 {
+mmc-pwrseq = <&wfx_pwrseq>;
+#address-cells = <1>;
+#size-cells = <0>;
+
+wifi@1 {
+compatible = "silabs,wf200";
+pinctrl-names = "default";
+pinctrl-0 = <&wfx_wakeup>;
+reg = <1>;
+wakeup-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>;
+};
+};
+...
-- 
2.29.2

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH v4 01/24] mmc: sdio: add SDIO IDs for Silabs WF200 chip

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Add Silabs SDIO ID to sdio_ids.h.

Note that the values used by Silabs are uncommon. A driver cannot fully
rely on the SDIO PnP. It should also check if the device is declared in
the DT.

Signed-off-by: Jérôme Pouiller 
---
 include/linux/mmc/sdio_ids.h | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
index 12036619346c..734918fbf164 100644
--- a/include/linux/mmc/sdio_ids.h
+++ b/include/linux/mmc/sdio_ids.h
@@ -25,6 +25,13 @@
  * Vendors and devices.  Sort key: vendor first, device next.
  */
 
+/*
+ * Silabs does not use a reliable vendor ID. To avoid conflicts, the driver
+ * won't probe the device if it is not also declared in the DT.
+ */
+#define SDIO_VENDOR_ID_SILABS  0x
+#define SDIO_DEVICE_ID_SILABS_WF2000x1000
+
 #define SDIO_VENDOR_ID_STE 0x0020
 #define SDIO_DEVICE_ID_STE_CW1200  0x2280
 
-- 
2.29.2

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH v4 16/24] wfx: add data_rx.c/data_rx.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/data_rx.c | 94 +++
 drivers/net/wireless/silabs/wfx/data_rx.h | 18 +
 2 files changed, 112 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/data_rx.c
 create mode 100644 drivers/net/wireless/silabs/wfx/data_rx.h

diff --git a/drivers/net/wireless/silabs/wfx/data_rx.c 
b/drivers/net/wireless/silabs/wfx/data_rx.c
new file mode 100644
index ..e6d9d8746d4d
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/data_rx.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Datapath implementation.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+
+#include "data_rx.h"
+#include "wfx.h"
+#include "bh.h"
+#include "sta.h"
+
+static void wfx_rx_handle_ba(struct wfx_vif *wvif, struct ieee80211_mgmt *mgmt)
+{
+   int params, tid;
+
+   if (wfx_api_older_than(wvif->wdev, 3, 6))
+   return;
+
+   switch (mgmt->u.action.u.addba_req.action_code) {
+   case WLAN_ACTION_ADDBA_REQ:
+   params = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+   tid = (params & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+   ieee80211_start_rx_ba_session_offl(wvif->vif, mgmt->sa, tid);
+   break;
+   case WLAN_ACTION_DELBA:
+   params = le16_to_cpu(mgmt->u.action.u.delba.params);
+   tid = (params &  IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
+   ieee80211_stop_rx_ba_session_offl(wvif->vif, mgmt->sa, tid);
+   break;
+   }
+}
+
+void wfx_rx_cb(struct wfx_vif *wvif,
+  const struct hif_ind_rx *arg, struct sk_buff *skb)
+{
+   struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
+   struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
+   struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+
+   memset(hdr, 0, sizeof(*hdr));
+
+   if (arg->status == HIF_STATUS_RX_FAIL_MIC)
+   hdr->flag |= RX_FLAG_MMIC_ERROR | RX_FLAG_IV_STRIPPED;
+   else if (arg->status)
+   goto drop;
+
+   if (skb->len < sizeof(struct ieee80211_pspoll)) {
+   dev_warn(wvif->wdev->dev, "malformed SDU received\n");
+   goto drop;
+   }
+
+   hdr->band = NL80211_BAND_2GHZ;
+   hdr->freq = ieee80211_channel_to_frequency(arg->channel_number,
+  hdr->band);
+
+   if (arg->rxed_rate >= 14) {
+   hdr->encoding = RX_ENC_HT;
+   hdr->rate_idx = arg->rxed_rate - 14;
+   } else if (arg->rxed_rate >= 4) {
+   hdr->rate_idx = arg->rxed_rate - 2;
+   } else {
+   hdr->rate_idx = arg->rxed_rate;
+   }
+
+   if (!arg->rcpi_rssi) {
+   hdr->flag |= RX_FLAG_NO_SIGNAL_VAL;
+   dev_info(wvif->wdev->dev, "received frame without RSSI data\n");
+   }
+   hdr->signal = arg->rcpi_rssi / 2 - 110;
+   hdr->antenna = 0;
+
+   if (arg->encryp)
+   hdr->flag |= RX_FLAG_DECRYPTED;
+
+   /* Block ack negotiation is offloaded by the firmware. However,
+* re-ordering must be done by the mac80211.
+*/
+   if (ieee80211_is_action(frame->frame_control) &&
+   mgmt->u.action.category == WLAN_CATEGORY_BACK &&
+   skb->len > IEEE80211_MIN_ACTION_SIZE) {
+   wfx_rx_handle_ba(wvif, mgmt);
+   goto drop;
+   }
+
+   ieee80211_rx_irqsafe(wvif->wdev->hw, skb);
+   return;
+
+drop:
+   dev_kfree_skb(skb);
+}
diff --git a/drivers/net/wireless/silabs/wfx/data_rx.h 
b/drivers/net/wireless/silabs/wfx/data_rx.h
new file mode 100644
index ..a320cd858273
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/data_rx.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Datapath implementation.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#ifndef WFX_DATA_RX_H
+#define WFX_DATA_RX_H
+
+struct wfx_vif;
+struct sk_buff;
+struct hif_ind_rx;
+
+void wfx_rx_cb(struct wfx_vif *wvif,
+  const struct hif_ind_rx *arg, struct sk_buff *skb);
+
+#endif
-- 
2.29.2

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH v4 22/24] wfx: add traces.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/traces.h | 501 +++
 1 file changed, 501 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/traces.h

diff --git a/drivers/net/wireless/silabs/wfx/traces.h 
b/drivers/net/wireless/silabs/wfx/traces.h
new file mode 100644
index ..e90dc73c4b01
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/traces.h
@@ -0,0 +1,501 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Tracepoints definitions.
+ *
+ * Copyright (c) 2018-2020, Silicon Laboratories, Inc.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM wfx
+
+#if !defined(_WFX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _WFX_TRACE_H
+
+#include 
+#include 
+
+#include "bus.h"
+#include "hif_api_cmd.h"
+#include "hif_api_mib.h"
+
+/* The hell below need some explanations. For each symbolic number, we need to
+ * define it with TRACE_DEFINE_ENUM() and in a list for __print_symbolic.
+ *
+ *   1. Define a new macro that call TRACE_DEFINE_ENUM():
+ *
+ *  #define xxx_name(sym) TRACE_DEFINE_ENUM(sym);
+ *
+ *   2. Define list of all symbols:
+ *
+ *  #define list_names \
+ * ... \
+ * xxx_name(XXX)   \
+ * ...
+ *
+ *   3. Instantiate that list_names:
+ *
+ *  list_names
+ *
+ *   4. Redefine xxx_name() as an entry of array for __print_symbolic()
+ *
+ *  #undef xxx_name
+ *  #define xxx_name(msg) { msg, #msg },
+ *
+ *   5. list_name can now nearly be used with __print_symbolic() but,
+ *  __print_symbolic() dislike last comma of list. So we define a new list
+ *  with a dummy element:
+ *
+ *  #define list_for_print_symbolic list_names { -1, NULL }
+ */
+
+#define _hif_msg_list   \
+   hif_cnf_name(ADD_KEY)   \
+   hif_cnf_name(BEACON_TRANSMIT)   \
+   hif_cnf_name(EDCA_QUEUE_PARAMS) \
+   hif_cnf_name(JOIN)  \
+   hif_cnf_name(MAP_LINK)  \
+   hif_cnf_name(READ_MIB)  \
+   hif_cnf_name(REMOVE_KEY)\
+   hif_cnf_name(RESET) \
+   hif_cnf_name(SET_BSS_PARAMS)\
+   hif_cnf_name(SET_PM_MODE)   \
+   hif_cnf_name(START) \
+   hif_cnf_name(START_SCAN)\
+   hif_cnf_name(STOP_SCAN) \
+   hif_cnf_name(TX)\
+   hif_cnf_name(MULTI_TRANSMIT)\
+   hif_cnf_name(UPDATE_IE) \
+   hif_cnf_name(WRITE_MIB) \
+   hif_cnf_name(CONFIGURATION) \
+   hif_cnf_name(CONTROL_GPIO)  \
+   hif_cnf_name(PREVENT_ROLLBACK)  \
+   hif_cnf_name(SET_SL_MAC_KEY)\
+   hif_cnf_name(SL_CONFIGURE)  \
+   hif_cnf_name(SL_EXCHANGE_PUB_KEYS)  \
+   hif_cnf_name(SHUT_DOWN) \
+   hif_ind_name(EVENT) \
+   hif_ind_name(JOIN_COMPLETE) \
+   hif_ind_name(RX)\
+   hif_ind_name(SCAN_CMPL) \
+   hif_ind_name(SET_PM_MODE_CMPL)  \
+   hif_ind_name(SUSPEND_RESUME_TX) \
+   hif_ind_name(SL_EXCHANGE_PUB_KEYS)  \
+   hif_ind_name(ERROR) \
+   hif_ind_name(EXCEPTION) \
+   hif_ind_name(GENERIC)   \
+   hif_ind_name(WAKEUP)\
+   hif_ind_name(STARTUP)
+
+#define hif_msg_list_enum _hif_msg_list
+
+#undef hif_cnf_name
+#undef hif_ind_name
+#define hif_cnf_name(msg) TRACE_DEFINE_ENUM(HIF_CNF_ID_##msg);
+#define hif_ind_name(msg) TRACE_DEFINE_ENUM(HIF_IND_ID_##msg);
+hif_msg_list_enum
+#undef hif_cnf_name
+#undef hif_ind_name
+#define hif_cnf_name(msg) { HIF_CNF_ID_##msg, #msg },
+#define hif_ind_name(msg) { HIF_IND_ID_##msg, #msg },
+#define hif_msg_list hif_msg_list_enum { -1, NULL }
+
+#define _hif_mib_list\
+   hif_mib_name(ARP_IP_ADDRESSES_TABLE) \
+   hif_mib_name(ARP_KEEP_ALIVE_PERIOD)  \
+   hif_mib_name(BEACON_FILTER_ENABLE)   \
+   hif_mib_name(BEACON_FILTER_TABLE)\
+   hif_mib_name(BEACON_STATS)   \
+   hif_mib_name(BEACON_WAKEUP_PERIOD)   \
+   hif_mib_name(BLOCK_ACK_POLICY)   \
+   hif_mib_name(CCA_CONFIG) \
+   hif_mib_name(CONFIG_DATA_FILTER) \
+   hif_mib_name(COUNTERS_TABLE) \
+   hif_mib_name(CURRENT_TX_POWER_LEVEL) \
+   hif_mib_name(DOT11_MAC_ADDRESS)  \
+   hif_mib_name(DOT11_MAX_RECEIVE_LIFETIME) \
+   hif_mib_name(DOT11_MAX_TRANSMIT_MSDU_LIFETIME) \
+   hif_mib_name(DOT11_RTS_THRESHOLD)\
+   hif_mib_name(DOT11_WEP_DEFAULT_KEY_ID)   \
+   hif_mib_name(ETHERTYPE_DATAFRAME_CONDITION)  \
+   hif_mib_name(EXTENDED_COUNTERS_TABLE)\
+   hif_mib_name(GL_BLOCK

[PATCH v4 09/24] wfx: add hwio.c/hwio.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/hwio.c | 340 +
 drivers/net/wireless/silabs/wfx/hwio.h |  79 ++
 2 files changed, 419 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/hwio.c
 create mode 100644 drivers/net/wireless/silabs/wfx/hwio.h

diff --git a/drivers/net/wireless/silabs/wfx/hwio.c 
b/drivers/net/wireless/silabs/wfx/hwio.c
new file mode 100644
index ..393bcb1e2f4e
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/hwio.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Low-level I/O functions.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+#include 
+
+#include "hwio.h"
+#include "wfx.h"
+#include "bus.h"
+#include "traces.h"
+
+static int read32(struct wfx_dev *wdev, int reg, u32 *val)
+{
+   int ret;
+   __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL);
+
+   *val = ~0; /* Never return undefined value */
+   if (!tmp)
+   return -ENOMEM;
+   ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, reg, tmp,
+   sizeof(u32));
+   if (ret >= 0)
+   *val = le32_to_cpu(*tmp);
+   kfree(tmp);
+   if (ret)
+   dev_err(wdev->dev, "%s: bus communication error: %d\n",
+   __func__, ret);
+   return ret;
+}
+
+static int write32(struct wfx_dev *wdev, int reg, u32 val)
+{
+   int ret;
+   __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL);
+
+   if (!tmp)
+   return -ENOMEM;
+   *tmp = cpu_to_le32(val);
+   ret = wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, reg, tmp,
+ sizeof(u32));
+   kfree(tmp);
+   if (ret)
+   dev_err(wdev->dev, "%s: bus communication error: %d\n",
+   __func__, ret);
+   return ret;
+}
+
+static int read32_locked(struct wfx_dev *wdev, int reg, u32 *val)
+{
+   int ret;
+
+   wdev->hwbus_ops->lock(wdev->hwbus_priv);
+   ret = read32(wdev, reg, val);
+   _trace_io_read32(reg, *val);
+   wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+   return ret;
+}
+
+static int write32_locked(struct wfx_dev *wdev, int reg, u32 val)
+{
+   int ret;
+
+   wdev->hwbus_ops->lock(wdev->hwbus_priv);
+   ret = write32(wdev, reg, val);
+   _trace_io_write32(reg, val);
+   wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+   return ret;
+}
+
+static int write32_bits_locked(struct wfx_dev *wdev, int reg, u32 mask, u32 
val)
+{
+   int ret;
+   u32 val_r, val_w;
+
+   WARN_ON(~mask & val);
+   val &= mask;
+   wdev->hwbus_ops->lock(wdev->hwbus_priv);
+   ret = read32(wdev, reg, &val_r);
+   _trace_io_read32(reg, val_r);
+   if (ret < 0)
+   goto err;
+   val_w = (val_r & ~mask) | val;
+   if (val_w != val_r) {
+   ret = write32(wdev, reg, val_w);
+   _trace_io_write32(reg, val_w);
+   }
+err:
+   wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+   return ret;
+}
+
+static int indirect_read(struct wfx_dev *wdev, int reg, u32 addr,
+void *buf, size_t len)
+{
+   int ret;
+   int i;
+   u32 cfg;
+   u32 prefetch;
+
+   WARN_ON(len >= 0x2000);
+   WARN_ON(reg != WFX_REG_AHB_DPORT && reg != WFX_REG_SRAM_DPORT);
+
+   if (reg == WFX_REG_AHB_DPORT)
+   prefetch = CFG_PREFETCH_AHB;
+   else if (reg == WFX_REG_SRAM_DPORT)
+   prefetch = CFG_PREFETCH_SRAM;
+   else
+   return -ENODEV;
+
+   ret = write32(wdev, WFX_REG_BASE_ADDR, addr);
+   if (ret < 0)
+   goto err;
+
+   ret = read32(wdev, WFX_REG_CONFIG, &cfg);
+   if (ret < 0)
+   goto err;
+
+   ret = write32(wdev, WFX_REG_CONFIG, cfg | prefetch);
+   if (ret < 0)
+   goto err;
+
+   for (i = 0; i < 20; i++) {
+   ret = read32(wdev, WFX_REG_CONFIG, &cfg);
+   if (ret < 0)
+   goto err;
+   if (!(cfg & prefetch))
+   break;
+   usleep_range(200, 250);
+   }
+   if (i == 20) {
+   ret = -ETIMEDOUT;
+   goto err;
+   }
+
+   ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, reg, buf, len);
+
+err:
+   if (ret < 0)
+   memset(buf, 0xFF, len); /* Never return undefined value */
+   return ret;
+}
+
+static int indirect_write(struct wfx_dev *wdev, int reg, u32 addr,
+ const void *buf, size_t len)
+{
+   int ret;
+
+   WARN_ON(len >= 0x2000);
+   WARN_ON(reg != WFX_REG_AHB_DPORT && reg != WFX_REG_SRAM_DPORT);
+   ret = write32(wdev, WFX_REG_BASE_ADDR, addr);
+   if (ret < 0)
+   return ret;
+
+   return wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv,

[PATCH v4 12/24] wfx: add hif_api_*.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/hif_api_cmd.h | 555 ++
 .../net/wireless/silabs/wfx/hif_api_general.h | 262 +
 drivers/net/wireless/silabs/wfx/hif_api_mib.h | 343 +++
 3 files changed, 1160 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/hif_api_cmd.h
 create mode 100644 drivers/net/wireless/silabs/wfx/hif_api_general.h
 create mode 100644 drivers/net/wireless/silabs/wfx/hif_api_mib.h

diff --git a/drivers/net/wireless/silabs/wfx/hif_api_cmd.h 
b/drivers/net/wireless/silabs/wfx/hif_api_cmd.h
new file mode 100644
index ..fcd4fe8b4672
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/hif_api_cmd.h
@@ -0,0 +1,555 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/*
+ * WFx hardware interface definitions
+ *
+ * Copyright (c) 2018-2020, Silicon Laboratories Inc.
+ */
+
+#ifndef WFX_HIF_API_CMD_H
+#define WFX_HIF_API_CMD_H
+
+#include 
+
+#include "hif_api_general.h"
+
+enum hif_requests_ids {
+   HIF_REQ_ID_RESET= 0x0a,
+   HIF_REQ_ID_READ_MIB = 0x05,
+   HIF_REQ_ID_WRITE_MIB= 0x06,
+   HIF_REQ_ID_START_SCAN   = 0x07,
+   HIF_REQ_ID_STOP_SCAN= 0x08,
+   HIF_REQ_ID_TX   = 0x04,
+   HIF_REQ_ID_JOIN = 0x0b,
+   HIF_REQ_ID_SET_PM_MODE  = 0x10,
+   HIF_REQ_ID_SET_BSS_PARAMS   = 0x11,
+   HIF_REQ_ID_ADD_KEY  = 0x0c,
+   HIF_REQ_ID_REMOVE_KEY   = 0x0d,
+   HIF_REQ_ID_EDCA_QUEUE_PARAMS= 0x13,
+   HIF_REQ_ID_START= 0x17,
+   HIF_REQ_ID_BEACON_TRANSMIT  = 0x18,
+   HIF_REQ_ID_UPDATE_IE= 0x1b,
+   HIF_REQ_ID_MAP_LINK = 0x1c,
+};
+
+enum hif_confirmations_ids {
+   HIF_CNF_ID_RESET= 0x0a,
+   HIF_CNF_ID_READ_MIB = 0x05,
+   HIF_CNF_ID_WRITE_MIB= 0x06,
+   HIF_CNF_ID_START_SCAN   = 0x07,
+   HIF_CNF_ID_STOP_SCAN= 0x08,
+   HIF_CNF_ID_TX   = 0x04,
+   HIF_CNF_ID_MULTI_TRANSMIT   = 0x1e,
+   HIF_CNF_ID_JOIN = 0x0b,
+   HIF_CNF_ID_SET_PM_MODE  = 0x10,
+   HIF_CNF_ID_SET_BSS_PARAMS   = 0x11,
+   HIF_CNF_ID_ADD_KEY  = 0x0c,
+   HIF_CNF_ID_REMOVE_KEY   = 0x0d,
+   HIF_CNF_ID_EDCA_QUEUE_PARAMS= 0x13,
+   HIF_CNF_ID_START= 0x17,
+   HIF_CNF_ID_BEACON_TRANSMIT  = 0x18,
+   HIF_CNF_ID_UPDATE_IE= 0x1b,
+   HIF_CNF_ID_MAP_LINK = 0x1c,
+};
+
+enum hif_indications_ids {
+   HIF_IND_ID_RX   = 0x84,
+   HIF_IND_ID_SCAN_CMPL= 0x86,
+   HIF_IND_ID_JOIN_COMPLETE= 0x8f,
+   HIF_IND_ID_SET_PM_MODE_CMPL = 0x89,
+   HIF_IND_ID_SUSPEND_RESUME_TX= 0x8c,
+   HIF_IND_ID_EVENT= 0x85
+};
+
+struct hif_req_reset {
+   u8 reset_stat:1;
+   u8 reset_all_int:1;
+   u8 reserved1:6;
+   u8 reserved2[3];
+} __packed;
+
+struct hif_cnf_reset {
+   __le32 status;
+} __packed;
+
+struct hif_req_read_mib {
+   __le16 mib_id;
+   __le16 reserved;
+} __packed;
+
+struct hif_cnf_read_mib {
+   __le32 status;
+   __le16 mib_id;
+   __le16 length;
+   u8 mib_data[];
+} __packed;
+
+struct hif_req_write_mib {
+   __le16 mib_id;
+   __le16 length;
+   u8 mib_data[];
+} __packed;
+
+struct hif_cnf_write_mib {
+   __le32 status;
+} __packed;
+
+struct hif_req_update_ie {
+   u8 beacon:1;
+   u8 probe_resp:1;
+   u8 probe_req:1;
+   u8 reserved1:5;
+   u8 reserved2;
+   __le16 num_ies;
+   struct element ie[];
+} __packed;
+
+struct hif_cnf_update_ie {
+   __le32 status;
+} __packed;
+
+struct hif_ssid_def {
+   __le32 ssid_length;
+   u8 ssid[IEEE80211_MAX_SSID_LEN];
+} __packed;
+
+#define HIF_API_MAX_NB_SSIDS   2
+#define HIF_API_MAX_NB_CHANNELS   14
+
+struct hif_req_start_scan_alt {
+   u8 band;
+   u8 maintain_current_bss:1;
+   u8 periodic:1;
+   u8 reserved1:6;
+   u8 disallow_ps:1;
+   u8 reserved2:1;
+   u8 short_preamble:1;
+   u8 reserved3:5;
+   u8 max_transmit_rate;
+   __le16 periodic_interval;
+   u8 reserved4;
+   s8 periodic_rssi_thr;
+   u8 num_of_probe_requests;
+   u8 probe_delay;
+   u8 num_of_ssids;
+   u8 num_of_channels;
+   __le32 min_channel_time;
+   __le32 max_channel_time;
+   __le32 tx_power_level; /* signed value */
+   struct hif_ssid_def ssid_def[HIF_API_MAX_NB_SSIDS];
+   u8 channel_list[];
+} __packed;
+
+struct hif_cnf_start_scan {
+   __le32 status;
+} __packed;
+
+struct hif_cnf_stop_scan {
+   __le32 status;
+} __packe

[PATCH v4 14/24] wfx: add key.c/key.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/key.c | 241 ++
 drivers/net/wireless/silabs/wfx/key.h |  20 +++
 2 files changed, 261 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/key.c
 create mode 100644 drivers/net/wireless/silabs/wfx/key.h

diff --git a/drivers/net/wireless/silabs/wfx/key.c 
b/drivers/net/wireless/silabs/wfx/key.c
new file mode 100644
index ..65134a174683
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/key.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Key management related functions.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+
+#include "key.h"
+#include "wfx.h"
+#include "hif_tx_mib.h"
+
+static int wfx_alloc_key(struct wfx_dev *wdev)
+{
+   int idx;
+
+   idx = ffs(~wdev->key_map) - 1;
+   if (idx < 0 || idx >= MAX_KEY_ENTRIES)
+   return -1;
+
+   wdev->key_map |= BIT(idx);
+   return idx;
+}
+
+static void wfx_free_key(struct wfx_dev *wdev, int idx)
+{
+   WARN(!(wdev->key_map & BIT(idx)), "inconsistent key allocation");
+   wdev->key_map &= ~BIT(idx);
+}
+
+static u8 fill_wep_pair(struct hif_wep_pairwise_key *msg,
+   struct ieee80211_key_conf *key, u8 *peer_addr)
+{
+   WARN(key->keylen > sizeof(msg->key_data), "inconsistent data");
+   msg->key_length = key->keylen;
+   memcpy(msg->key_data, key->key, key->keylen);
+   ether_addr_copy(msg->peer_address, peer_addr);
+   return HIF_KEY_TYPE_WEP_PAIRWISE;
+}
+
+static u8 fill_wep_group(struct hif_wep_group_key *msg,
+struct ieee80211_key_conf *key)
+{
+   WARN(key->keylen > sizeof(msg->key_data), "inconsistent data");
+   msg->key_id = key->keyidx;
+   msg->key_length = key->keylen;
+   memcpy(msg->key_data, key->key, key->keylen);
+   return HIF_KEY_TYPE_WEP_DEFAULT;
+}
+
+static u8 fill_tkip_pair(struct hif_tkip_pairwise_key *msg,
+struct ieee80211_key_conf *key, u8 *peer_addr)
+{
+   u8 *keybuf = key->key;
+
+   WARN(key->keylen != sizeof(msg->tkip_key_data)
+   + sizeof(msg->tx_mic_key)
+   + sizeof(msg->rx_mic_key), "inconsistent data");
+   memcpy(msg->tkip_key_data, keybuf, sizeof(msg->tkip_key_data));
+   keybuf += sizeof(msg->tkip_key_data);
+   memcpy(msg->tx_mic_key, keybuf, sizeof(msg->tx_mic_key));
+   keybuf += sizeof(msg->tx_mic_key);
+   memcpy(msg->rx_mic_key, keybuf, sizeof(msg->rx_mic_key));
+   ether_addr_copy(msg->peer_address, peer_addr);
+   return HIF_KEY_TYPE_TKIP_PAIRWISE;
+}
+
+static u8 fill_tkip_group(struct hif_tkip_group_key *msg,
+ struct ieee80211_key_conf *key,
+ struct ieee80211_key_seq *seq,
+ enum nl80211_iftype iftype)
+{
+   u8 *keybuf = key->key;
+
+   WARN(key->keylen != sizeof(msg->tkip_key_data)
+   + 2 * sizeof(msg->rx_mic_key), "inconsistent data");
+   msg->key_id = key->keyidx;
+   memcpy(msg->rx_sequence_counter,
+  &seq->tkip.iv16, sizeof(seq->tkip.iv16));
+   memcpy(msg->rx_sequence_counter + sizeof(u16),
+  &seq->tkip.iv32, sizeof(seq->tkip.iv32));
+   memcpy(msg->tkip_key_data, keybuf, sizeof(msg->tkip_key_data));
+   keybuf += sizeof(msg->tkip_key_data);
+   if (iftype == NL80211_IFTYPE_AP)
+   /* Use Tx MIC Key */
+   memcpy(msg->rx_mic_key, keybuf + 0, sizeof(msg->rx_mic_key));
+   else
+   /* Use Rx MIC Key */
+   memcpy(msg->rx_mic_key, keybuf + 8, sizeof(msg->rx_mic_key));
+   return HIF_KEY_TYPE_TKIP_GROUP;
+}
+
+static u8 fill_ccmp_pair(struct hif_aes_pairwise_key *msg,
+struct ieee80211_key_conf *key, u8 *peer_addr)
+{
+   WARN(key->keylen != sizeof(msg->aes_key_data), "inconsistent data");
+   ether_addr_copy(msg->peer_address, peer_addr);
+   memcpy(msg->aes_key_data, key->key, key->keylen);
+   return HIF_KEY_TYPE_AES_PAIRWISE;
+}
+
+static u8 fill_ccmp_group(struct hif_aes_group_key *msg,
+ struct ieee80211_key_conf *key,
+ struct ieee80211_key_seq *seq)
+{
+   WARN(key->keylen != sizeof(msg->aes_key_data), "inconsistent data");
+   memcpy(msg->aes_key_data, key->key, key->keylen);
+   memcpy(msg->rx_sequence_counter, seq->ccmp.pn, sizeof(seq->ccmp.pn));
+   memreverse(msg->rx_sequence_counter, sizeof(seq->ccmp.pn));
+   msg->key_id = key->keyidx;
+   return HIF_KEY_TYPE_AES_GROUP;
+}
+
+static u8 fill_sms4_pair(struct hif_wapi_pairwise_key *msg,
+struct ieee80211_key_conf *key, u8 *peer_addr)
+{
+   u8 *keybuf = key->key;
+
+   WARN(key->keylen != sizeof(msg->wapi_key_data)
+

[PATCH v4 21/24] wfx: add debug.c/debug.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/debug.c | 361 
 drivers/net/wireless/silabs/wfx/debug.h |  19 ++
 2 files changed, 380 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/debug.c
 create mode 100644 drivers/net/wireless/silabs/wfx/debug.h

diff --git a/drivers/net/wireless/silabs/wfx/debug.c 
b/drivers/net/wireless/silabs/wfx/debug.c
new file mode 100644
index ..3f7101213df3
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/debug.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Debugfs interface.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+#include 
+
+#include "debug.h"
+#include "wfx.h"
+#include "sta.h"
+#include "main.h"
+#include "hif_tx.h"
+#include "hif_tx_mib.h"
+
+#define CREATE_TRACE_POINTS
+#include "traces.h"
+
+static const struct trace_print_flags hif_msg_print_map[] = {
+   hif_msg_list,
+};
+
+static const struct trace_print_flags hif_mib_print_map[] = {
+   hif_mib_list,
+};
+
+static const struct trace_print_flags wfx_reg_print_map[] = {
+   wfx_reg_list,
+};
+
+static const char *get_symbol(unsigned long val,
+ const struct trace_print_flags *symbol_array)
+{
+   int i;
+
+   for (i = 0; symbol_array[i].mask != -1; i++) {
+   if (val == symbol_array[i].mask)
+   return symbol_array[i].name;
+   }
+
+   return "unknown";
+}
+
+const char *get_hif_name(unsigned long id)
+{
+   return get_symbol(id, hif_msg_print_map);
+}
+
+const char *get_mib_name(unsigned long id)
+{
+   return get_symbol(id, hif_mib_print_map);
+}
+
+const char *get_reg_name(unsigned long id)
+{
+   return get_symbol(id, wfx_reg_print_map);
+}
+
+static int wfx_counters_show(struct seq_file *seq, void *v)
+{
+   int ret, i;
+   struct wfx_dev *wdev = seq->private;
+   struct hif_mib_extended_count_table counters[3];
+
+   for (i = 0; i < ARRAY_SIZE(counters); i++) {
+   ret = hif_get_counters_table(wdev, i, counters + i);
+   if (ret < 0)
+   return ret;
+   if (ret > 0)
+   return -EIO;
+   }
+
+   seq_printf(seq, "%-24s %12s %12s %12s\n",
+  "", "global", "iface 0", "iface 1");
+
+#define PUT_COUNTER(name) \
+   seq_printf(seq, "%-24s %12d %12d %12d\n", #name, \
+  le32_to_cpu(counters[2].count_##name), \
+  le32_to_cpu(counters[0].count_##name), \
+  le32_to_cpu(counters[1].count_##name))
+
+   PUT_COUNTER(tx_packets);
+   PUT_COUNTER(tx_multicast_frames);
+   PUT_COUNTER(tx_frames_success);
+   PUT_COUNTER(tx_frame_failures);
+   PUT_COUNTER(tx_frames_retried);
+   PUT_COUNTER(tx_frames_multi_retried);
+
+   PUT_COUNTER(rts_success);
+   PUT_COUNTER(rts_failures);
+   PUT_COUNTER(ack_failures);
+
+   PUT_COUNTER(rx_packets);
+   PUT_COUNTER(rx_frames_success);
+   PUT_COUNTER(rx_packet_errors);
+   PUT_COUNTER(plcp_errors);
+   PUT_COUNTER(fcs_errors);
+   PUT_COUNTER(rx_decryption_failures);
+   PUT_COUNTER(rx_mic_failures);
+   PUT_COUNTER(rx_no_key_failures);
+   PUT_COUNTER(rx_frame_duplicates);
+   PUT_COUNTER(rx_multicast_frames);
+   PUT_COUNTER(rx_cmacicv_errors);
+   PUT_COUNTER(rx_cmac_replays);
+   PUT_COUNTER(rx_mgmt_ccmp_replays);
+
+   PUT_COUNTER(rx_beacon);
+   PUT_COUNTER(miss_beacon);
+
+#undef PUT_COUNTER
+
+   for (i = 0; i < ARRAY_SIZE(counters[0].reserved); i++)
+   seq_printf(seq, "reserved[%02d]%12s %12d %12d %12d\n", i, "",
+  le32_to_cpu(counters[2].reserved[i]),
+  le32_to_cpu(counters[0].reserved[i]),
+  le32_to_cpu(counters[1].reserved[i]));
+
+   return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(wfx_counters);
+
+static const char * const channel_names[] = {
+   [0] = "1M",
+   [1] = "2M",
+   [2] = "5.5M",
+   [3] = "11M",
+   /* Entries 4 and 5 does not exist */
+   [6] = "6M",
+   [7] = "9M",
+   [8] = "12M",
+   [9] = "18M",
+   [10] = "24M",
+   [11] = "36M",
+   [12] = "48M",
+   [13] = "54M",
+   [14] = "MCS0",
+   [15] = "MCS1",
+   [16] = "MCS2",
+   [17] = "MCS3",
+   [18] = "MCS4",
+   [19] = "MCS5",
+   [20] = "MCS6",
+   [21] = "MCS7",
+};
+
+static int wfx_rx_stats_show(struct seq_file *seq, void *v)
+{
+   struct wfx_dev *wdev = seq->private;
+   struct hif_rx_stats *st = &wdev->rx_stats;
+   int i;
+
+   mutex_lock(&wdev->rx_stats_lock);
+   seq_printf(seq, "Timestamp: %dus\n", st->date);
+   seq_printf(seq, "Low power clock: frequency %uHz, external %s\n",
+  le32_to_cpu(st->pwr_clk_freq),
+  

[PATCH v4 20/24] wfx: add scan.c/scan.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/scan.c | 131 +
 drivers/net/wireless/silabs/wfx/scan.h |  22 +
 2 files changed, 153 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/scan.c
 create mode 100644 drivers/net/wireless/silabs/wfx/scan.h

diff --git a/drivers/net/wireless/silabs/wfx/scan.c 
b/drivers/net/wireless/silabs/wfx/scan.c
new file mode 100644
index ..3d5f13c89bac
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/scan.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Scan related functions.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+
+#include "scan.h"
+#include "wfx.h"
+#include "sta.h"
+#include "hif_tx_mib.h"
+
+static void __ieee80211_scan_completed_compat(struct ieee80211_hw *hw,
+ bool aborted)
+{
+   struct cfg80211_scan_info info = {
+   .aborted = aborted,
+   };
+
+   ieee80211_scan_completed(hw, &info);
+}
+
+static int update_probe_tmpl(struct wfx_vif *wvif,
+struct cfg80211_scan_request *req)
+{
+   struct sk_buff *skb;
+
+   skb = ieee80211_probereq_get(wvif->wdev->hw, wvif->vif->addr,
+NULL, 0, req->ie_len);
+   if (!skb)
+   return -ENOMEM;
+
+   skb_put_data(skb, req->ie, req->ie_len);
+   hif_set_template_frame(wvif, skb, HIF_TMPLT_PRBREQ, 0);
+   dev_kfree_skb(skb);
+   return 0;
+}
+
+static int send_scan_req(struct wfx_vif *wvif,
+struct cfg80211_scan_request *req, int start_idx)
+{
+   int i, ret, timeout;
+   struct ieee80211_channel *ch_start, *ch_cur;
+
+   for (i = start_idx; i < req->n_channels; i++) {
+   ch_start = req->channels[start_idx];
+   ch_cur = req->channels[i];
+   WARN(ch_cur->band != NL80211_BAND_2GHZ, "band not supported");
+   if (ch_cur->max_power != ch_start->max_power)
+   break;
+   if ((ch_cur->flags ^ ch_start->flags) & IEEE80211_CHAN_NO_IR)
+   break;
+   }
+   wfx_tx_lock_flush(wvif->wdev);
+   wvif->scan_abort = false;
+   reinit_completion(&wvif->scan_complete);
+   ret = hif_scan(wvif, req, start_idx, i - start_idx, &timeout);
+   if (ret) {
+   wfx_tx_unlock(wvif->wdev);
+   return -EIO;
+   }
+   ret = wait_for_completion_timeout(&wvif->scan_complete, timeout);
+   if (req->channels[start_idx]->max_power != wvif->vif->bss_conf.txpower)
+   hif_set_output_power(wvif, wvif->vif->bss_conf.txpower);
+   wfx_tx_unlock(wvif->wdev);
+   if (!ret) {
+   dev_notice(wvif->wdev->dev, "scan timeout\n");
+   hif_stop_scan(wvif);
+   return -ETIMEDOUT;
+   }
+   if (wvif->scan_abort) {
+   dev_notice(wvif->wdev->dev, "scan abort\n");
+   return -ECONNABORTED;
+   }
+   return i - start_idx;
+}
+
+/* It is not really necessary to run scan request asynchronously. However,
+ * there is a bug in "iw scan" when ieee80211_scan_completed() is called before
+ * wfx_hw_scan() return
+ */
+void wfx_hw_scan_work(struct work_struct *work)
+{
+   struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan_work);
+   struct ieee80211_scan_request *hw_req = wvif->scan_req;
+   int chan_cur, ret;
+
+   mutex_lock(&wvif->wdev->conf_mutex);
+   mutex_lock(&wvif->scan_lock);
+   if (wvif->join_in_progress) {
+   dev_info(wvif->wdev->dev, "%s: abort in-progress REQ_JOIN",
+__func__);
+   wfx_reset(wvif);
+   }
+   update_probe_tmpl(wvif, &hw_req->req);
+   chan_cur = 0;
+   do {
+   ret = send_scan_req(wvif, &hw_req->req, chan_cur);
+   if (ret > 0)
+   chan_cur += ret;
+   } while (ret > 0 && chan_cur < hw_req->req.n_channels);
+   mutex_unlock(&wvif->scan_lock);
+   mutex_unlock(&wvif->wdev->conf_mutex);
+   __ieee80211_scan_completed_compat(wvif->wdev->hw, ret < 0);
+}
+
+int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+   struct ieee80211_scan_request *hw_req)
+{
+   struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
+
+   WARN_ON(hw_req->req.n_channels > HIF_API_MAX_NB_CHANNELS);
+   wvif->scan_req = hw_req;
+   schedule_work(&wvif->scan_work);
+   return 0;
+}
+
+void wfx_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+   struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
+
+   wvif->scan_abort = true;
+   hif_stop_scan(wvif);
+}
+
+void wfx_scan_complete(struct wfx_vif *wvif)
+{
+   complete(&wvif->scan_complete);
+}
diff --git a/drivers/net/wireless/silabs/wfx/scan.h

[PATCH v4 18/24] wfx: add data_tx.c/data_tx.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/data_tx.c | 590 ++
 drivers/net/wireless/silabs/wfx/data_tx.h |  67 +++
 2 files changed, 657 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/data_tx.c
 create mode 100644 drivers/net/wireless/silabs/wfx/data_tx.h

diff --git a/drivers/net/wireless/silabs/wfx/data_tx.c 
b/drivers/net/wireless/silabs/wfx/data_tx.c
new file mode 100644
index ..5f3931fb859d
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/data_tx.c
@@ -0,0 +1,590 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Datapath implementation.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+
+#include "data_tx.h"
+#include "wfx.h"
+#include "bh.h"
+#include "sta.h"
+#include "queue.h"
+#include "debug.h"
+#include "traces.h"
+#include "hif_tx_mib.h"
+
+static int wfx_get_hw_rate(struct wfx_dev *wdev,
+  const struct ieee80211_tx_rate *rate)
+{
+   struct ieee80211_supported_band *band;
+
+   if (rate->idx < 0)
+   return -1;
+   if (rate->flags & IEEE80211_TX_RC_MCS) {
+   if (rate->idx > 7) {
+   WARN(1, "wrong rate->idx value: %d", rate->idx);
+   return -1;
+   }
+   return rate->idx + 14;
+   }
+   /* WFx only support 2GHz, else band information should be retrieved
+* from ieee80211_tx_info
+*/
+   band = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ];
+   if (rate->idx >= band->n_bitrates) {
+   WARN(1, "wrong rate->idx value: %d", rate->idx);
+   return -1;
+   }
+   return band->bitrates[rate->idx].hw_value;
+}
+
+/* TX policy cache implementation */
+
+static void wfx_tx_policy_build(struct wfx_vif *wvif, struct tx_policy *policy,
+   struct ieee80211_tx_rate *rates)
+{
+   struct wfx_dev *wdev = wvif->wdev;
+   int i, rateid;
+   u8 count;
+
+   WARN(rates[0].idx < 0, "invalid rate policy");
+   memset(policy, 0, sizeof(*policy));
+   for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
+   if (rates[i].idx < 0)
+   break;
+   WARN_ON(rates[i].count > 15);
+   rateid = wfx_get_hw_rate(wdev, &rates[i]);
+   /* Pack two values in each byte of policy->rates */
+   count = rates[i].count;
+   if (rateid % 2)
+   count <<= 4;
+   policy->rates[rateid / 2] |= count;
+   }
+}
+
+static bool tx_policy_is_equal(const struct tx_policy *a,
+  const struct tx_policy *b)
+{
+   return !memcmp(a->rates, b->rates, sizeof(a->rates));
+}
+
+static int wfx_tx_policy_find(struct tx_policy_cache *cache,
+ struct tx_policy *wanted)
+{
+   struct tx_policy *it;
+
+   list_for_each_entry(it, &cache->used, link)
+   if (tx_policy_is_equal(wanted, it))
+   return it - cache->cache;
+   list_for_each_entry(it, &cache->free, link)
+   if (tx_policy_is_equal(wanted, it))
+   return it - cache->cache;
+   return -1;
+}
+
+static void wfx_tx_policy_use(struct tx_policy_cache *cache,
+ struct tx_policy *entry)
+{
+   ++entry->usage_count;
+   list_move(&entry->link, &cache->used);
+}
+
+static int wfx_tx_policy_release(struct tx_policy_cache *cache,
+struct tx_policy *entry)
+{
+   int ret = --entry->usage_count;
+
+   if (!ret)
+   list_move(&entry->link, &cache->free);
+   return ret;
+}
+
+static int wfx_tx_policy_get(struct wfx_vif *wvif,
+struct ieee80211_tx_rate *rates, bool *renew)
+{
+   int idx;
+   struct tx_policy_cache *cache = &wvif->tx_policy_cache;
+   struct tx_policy wanted;
+
+   wfx_tx_policy_build(wvif, &wanted, rates);
+
+   spin_lock_bh(&cache->lock);
+   if (list_empty(&cache->free)) {
+   WARN(1, "unable to get a valid Tx policy");
+   spin_unlock_bh(&cache->lock);
+   return HIF_TX_RETRY_POLICY_INVALID;
+   }
+   idx = wfx_tx_policy_find(cache, &wanted);
+   if (idx >= 0) {
+   *renew = false;
+   } else {
+   struct tx_policy *entry;
+   *renew = true;
+   /* If policy is not found create a new one
+* using the oldest entry in "free" list
+*/
+   entry = list_entry(cache->free.prev, struct tx_policy, link);
+   memcpy(entry->rates, wanted.rates, sizeof(entry->rates));
+   entry->uploaded = false;
+   entry->usage_count = 0;
+   idx = entry - cache->cache;
+   }
+   wfx_tx_policy_use(cache, &cache->ca

[PATCH v4 00/24] wfx: get out from the staging area

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

I think the wfx driver is now mature enough to be accepted in the
drivers/net/wireless directory.

The firmware associated with this driver is available here[1]. It is not yet
available in linux-firmware, but I am working on the PR.

[1]: https://github.com/SiliconLabs/wfx-firmware

As requested by Kalle[2], I send one file per patch. At the end, all the
patches (or at least the patches 3 to 24) will be squashed (therefore, I
didn't bother to write real commit messages).

[2]: https://lore.kernel.org/lkml/87ft6p2n0h@codeaurora.org/

Here is a diagram of the global architecture that may help to understand
the code:

,.
|mac80211|
`'
,+---+---.
|sta |   |   |
|scan|   |   |
|main|   |   |
++  data_tx  |   |
|key |   |  data_rx  |
| hif_tx_mib |   queue   |   |
|   hif_tx   |   |   |
|   hif_rx   |   |   |
|  hif_api_* |   |   |
++---+---+.
|  bh|  fwio  |
+++
| hwio|
+-+
|   bus_sdio  |
|   bus_spi   |
`-'
,-.
|  spi / sdio |
`-'

Roughly, I have sent the files from the bottom to the top.


Below the differences with the files from drivers/staging/wfx/:

v4:
  - Rebase on last staging tree
  - Add 'additionalProperties: false' to the DT specification (I have made
that change blindly because I am able to reproduce the Rob's error)
(Rob)
  - Replace C++ comments with Ansi C comments (Kalle)
  - Check that existing Ansi C comments comply with net/ "compact" style
  - Drop one obsolete comment
  - Remove comments after '#endif' in header files
  - Remove macro redefinitions in hif_api_general.h (Kalle)
  - Replace compiletime_assert() with BUILD_BUG_ON_MSG() (Kalle)
  - Rename ieee80211_is_action_back() (Kalle)
  - Add a comment explaining how the PDS isent to the device (Kalle)
  - Add a comment about case where CONFIG_MMC==m in the Makefile (Kalle)
  - Fix irrelevant comment about CONFIG_VMAP_STACK (Kalle)
  - Talk about the unreliable SDIO Vendor ID in the Kconfig help (Kalle)
  - Mention the firmware status in the cover letter (Kalle)
  - Fix misaligned function arguments in key.c

v3:
  - dt-bindings: Rename config-file property (Rob)
  - dt-bindings: No additional properties are allowed (spi-max-frequency is
already listed) (Rob)
  - dt-bindings: Remove references for mac-address properties (Rob)
  - Rebase on staging/staging-next

v2:
  - dt-bindings: Improve device description and add link to the datasheet
  (Rob)
  - dt-bindings: Add blank lines between each DT property (Rob)
  - dt-bindings: Explicitly mention mac-address and local-mac-address and
  add references to ethernet-controller.yaml (Rob)
  - dt-bindings: "config-file" is not for development/debug (Rob)
  - dt-bindings: Remove description of "spi-max-frequency" (Rob)
  - dt-bindings: Use "folded scalar" syntax instead of escaping the colons
  - bus_sdio.c: A compatible node in the DT is now mandatory to probe the
  device. Also change documentation of dt-bindings accordingly (Pali,
  Ulf)
  - bus_sdio.c: Move SDIO IDs to sdio_ids.h (Pali)
  - bh.c: Import patch "staging: wfx: fix test on return value of
  gpiod_get_value()" (Nathan)
  - data_tx.c: Import patch "staging: wfx: fix use of uninitialized
  pointer"
  - sta.c: Import patch "staging: wfx: make a const array static, makes
  object smaller" (Colin)

v1:
  - Drop the function name in the warning message (Kalle)
  - Replace goto by return in wfx_send_pdata_pds() (Kalle, Dan)
  - Improve error label in wfx_send_pdata_pds() (Kalle)


Jérôme Pouiller (24):
  mmc: sdio: add SDIO IDs for Silabs WF200 chip
  dt-bindings: introduce silabs,wfx.yaml
  wfx: add Makefile/Kconfig
  wfx: add wfx.h
  wfx: add main.c/main.h
  wfx: add bus.h
  wfx: add bus_spi.c
  wfx: add bus_sdio.c
  wfx: add hwio.c/hwio.h
  wfx: add fwio.c/fwio.h
  wfx: add bh.c/bh.h
  wfx: add hif_api_*.h
  wfx: add hif_tx*.c/hif_tx*.h
  wfx: add key.c/key.h
  wfx: add hif_rx.c/hif_rx.h
  wfx: add data_rx.c/data_rx.h
  wfx: add queue.c/queue.h
  wfx: add data_tx.c/data_tx.h
  wfx: add sta.c/sta.h
  wfx: add scan.c/scan.h
  wfx: add debug.c/debug.h
  wfx: add traces.h
  wfx: remove from the staging area
  wfx: get out from the staging area

 .../bindings/net/wireless/sil

[PATCH v4 10/24] wfx: add fwio.c/fwio.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/fwio.c | 405 +
 drivers/net/wireless/silabs/wfx/fwio.h |  15 +
 2 files changed, 420 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/fwio.c
 create mode 100644 drivers/net/wireless/silabs/wfx/fwio.h

diff --git a/drivers/net/wireless/silabs/wfx/fwio.c 
b/drivers/net/wireless/silabs/wfx/fwio.c
new file mode 100644
index ..3c8b0b787f42
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/fwio.c
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Firmware loading.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+#include 
+#include 
+
+#include "fwio.h"
+#include "wfx.h"
+#include "hwio.h"
+
+/* Addresses below are in SRAM area */
+#define WFX_DNLD_FIFO 0x09004000
+#define DNLD_BLOCK_SIZE   0x0400
+#define DNLD_FIFO_SIZE0x8000 /* (32 * DNLD_BLOCK_SIZE) */
+/* Download Control Area (DCA) */
+#define WFX_DCA_IMAGE_SIZE0x0900C000
+#define WFX_DCA_PUT   0x0900C004
+#define WFX_DCA_GET   0x0900C008
+#define WFX_DCA_HOST_STATUS   0x0900C00C
+#define HOST_READY0x87654321
+#define HOST_INFO_READ0xA753BD99
+#define HOST_UPLOAD_PENDING   0xABCDDCBA
+#define HOST_UPLOAD_COMPLETE  0xD4C64A99
+#define HOST_OK_TO_JUMP   0x174FC882
+#define WFX_DCA_NCP_STATUS0x0900C010
+#define NCP_NOT_READY 0x12345678
+#define NCP_READY 0x87654321
+#define NCP_INFO_READY0xBD53EF99
+#define NCP_DOWNLOAD_PENDING  0xABCDDCBA
+#define NCP_DOWNLOAD_COMPLETE 0xCAFEFECA
+#define NCP_AUTH_OK   0xD4C64A99
+#define NCP_AUTH_FAIL 0x174FC882
+#define NCP_PUB_KEY_RDY   0x7AB41D19
+#define WFX_DCA_FW_SIGNATURE  0x0900C014
+#define FW_SIGNATURE_SIZE 0x40
+#define WFX_DCA_FW_HASH   0x0900C054
+#define FW_HASH_SIZE  0x08
+#define WFX_DCA_FW_VERSION0x0900C05C
+#define FW_VERSION_SIZE   0x04
+#define WFX_DCA_RESERVED  0x0900C060
+#define DCA_RESERVED_SIZE 0x20
+#define WFX_STATUS_INFO   0x0900C080
+#define WFX_BOOTLOADER_LABEL  0x0900C084
+#define BOOTLOADER_LABEL_SIZE 0x3C
+#define WFX_PTE_INFO  0x0900C0C0
+#define PTE_INFO_KEYSET_IDX   0x0D
+#define PTE_INFO_SIZE 0x10
+#define WFX_ERR_INFO  0x0900C0D0
+#define ERR_INVALID_SEC_TYPE  0x05
+#define ERR_SIG_VERIF_FAILED  0x0F
+#define ERR_AES_CTRL_KEY  0x10
+#define ERR_ECC_PUB_KEY   0x11
+#define ERR_MAC_KEY   0x18
+
+#define DCA_TIMEOUT  50 /* milliseconds */
+#define WAKEUP_TIMEOUT 200 /* milliseconds */
+
+static const char * const fwio_errors[] = {
+   [ERR_INVALID_SEC_TYPE] = "Invalid section type or wrong encryption",
+   [ERR_SIG_VERIF_FAILED] = "Signature verification failed",
+   [ERR_AES_CTRL_KEY] = "AES control key not initialized",
+   [ERR_ECC_PUB_KEY] = "ECC public key not initialized",
+   [ERR_MAC_KEY] = "MAC key not initialized",
+};
+
+/* request_firmware() allocate data using vmalloc(). It is not compatible with
+ * underlying hardware that use DMA. Function below detect this case and
+ * allocate a bounce buffer if necessary.
+ *
+ * Notice that, in doubt, you can enable CONFIG_DEBUG_SG to ask kernel to
+ * detect this problem at runtime  (else, kernel silently fail).
+ *
+ * NOTE: it may also be possible to use 'pages' from struct firmware and avoid
+ * bounce buffer
+ */
+static int sram_write_dma_safe(struct wfx_dev *wdev, u32 addr, const u8 *buf,
+  size_t len)
+{
+   int ret;
+   const u8 *tmp;
+
+   if (!virt_addr_valid(buf)) {
+   tmp = kmemdup(buf, len, GFP_KERNEL);
+   if (!tmp)
+   return -ENOMEM;
+   } else {
+   tmp = buf;
+   }
+   ret = sram_buf_write(wdev, addr, tmp, len);
+   if (tmp != buf)
+   kfree(tmp);
+   return ret;
+}
+
+static int get_firmware(struct wfx_dev *wdev, u32 keyset_chip,
+   const struct firmware **fw, int *file_offset)
+{
+   int keyset_file;
+   char filename[256];
+   const char *data;
+   int ret;
+
+   snprintf(filename, sizeof(filename), "%s_%02X.sec",
+wdev->pdata.file_fw, keyset_chip);
+   ret = firmware_request_nowarn(fw, filename, wdev->dev);
+   if (ret) {
+   dev_info(wdev->dev, "can't load %s, falling back to %s.sec\n",
+filename, wdev->pdata.file_fw);
+   snprintf(filename, sizeof(filename), "%s.sec",
+wdev->pdata.file_fw);
+   ret = request_firmware(fw, filename

[PATCH v4 17/24] wfx: add queue.c/queue.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/queue.c | 307 
 drivers/net/wireless/silabs/wfx/queue.h |  45 
 2 files changed, 352 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/queue.c
 create mode 100644 drivers/net/wireless/silabs/wfx/queue.h

diff --git a/drivers/net/wireless/silabs/wfx/queue.c 
b/drivers/net/wireless/silabs/wfx/queue.c
new file mode 100644
index ..4dc161f5ff71
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/queue.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * O(1) TX queue with built-in allocator.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+
+#include "queue.h"
+#include "wfx.h"
+#include "sta.h"
+#include "data_tx.h"
+#include "traces.h"
+
+void wfx_tx_lock(struct wfx_dev *wdev)
+{
+   atomic_inc(&wdev->tx_lock);
+}
+
+void wfx_tx_unlock(struct wfx_dev *wdev)
+{
+   int tx_lock = atomic_dec_return(&wdev->tx_lock);
+
+   WARN(tx_lock < 0, "inconsistent tx_lock value");
+   if (!tx_lock)
+   wfx_bh_request_tx(wdev);
+}
+
+void wfx_tx_flush(struct wfx_dev *wdev)
+{
+   int ret;
+
+   /* Do not wait for any reply if chip is frozen */
+   if (wdev->chip_frozen)
+   return;
+
+   wfx_tx_lock(wdev);
+   mutex_lock(&wdev->hif_cmd.lock);
+   ret = wait_event_timeout(wdev->hif.tx_buffers_empty,
+!wdev->hif.tx_buffers_used,
+msecs_to_jiffies(3000));
+   if (!ret) {
+   dev_warn(wdev->dev, "cannot flush tx buffers (%d still busy)\n",
+wdev->hif.tx_buffers_used);
+   wfx_pending_dump_old_frames(wdev, 3000);
+   /* FIXME: drop pending frames here */
+   wdev->chip_frozen = true;
+   }
+   mutex_unlock(&wdev->hif_cmd.lock);
+   wfx_tx_unlock(wdev);
+}
+
+void wfx_tx_lock_flush(struct wfx_dev *wdev)
+{
+   wfx_tx_lock(wdev);
+   wfx_tx_flush(wdev);
+}
+
+void wfx_tx_queues_init(struct wfx_vif *wvif)
+{
+   /* The device is in charge to respect the details of the QoS parameters.
+* The driver just ensure that it roughtly respect the priorities to
+* avoid any shortage.
+*/
+   const int priorities[IEEE80211_NUM_ACS] = { 1, 2, 64, 128 };
+   int i;
+
+   for (i = 0; i < IEEE80211_NUM_ACS; ++i) {
+   skb_queue_head_init(&wvif->tx_queue[i].normal);
+   skb_queue_head_init(&wvif->tx_queue[i].cab);
+   wvif->tx_queue[i].priority = priorities[i];
+   }
+}
+
+void wfx_tx_queues_check_empty(struct wfx_vif *wvif)
+{
+   int i;
+
+   for (i = 0; i < IEEE80211_NUM_ACS; ++i) {
+   WARN_ON(atomic_read(&wvif->tx_queue[i].pending_frames));
+   WARN_ON(!skb_queue_empty_lockless(&wvif->tx_queue[i].normal));
+   WARN_ON(!skb_queue_empty_lockless(&wvif->tx_queue[i].cab));
+   }
+}
+
+bool wfx_tx_queue_empty(struct wfx_vif *wvif, struct wfx_queue *queue)
+{
+   return skb_queue_empty(&queue->normal) && skb_queue_empty(&queue->cab);
+}
+
+static void __wfx_tx_queue_drop(struct wfx_vif *wvif,
+   struct sk_buff_head *skb_queue,
+   struct sk_buff_head *dropped)
+{
+   struct sk_buff *skb, *tmp;
+
+   spin_lock_bh(&skb_queue->lock);
+   skb_queue_walk_safe(skb_queue, skb, tmp) {
+   __skb_unlink(skb, skb_queue);
+   skb_queue_head(dropped, skb);
+   }
+   spin_unlock_bh(&skb_queue->lock);
+}
+
+void wfx_tx_queue_drop(struct wfx_vif *wvif, struct wfx_queue *queue,
+  struct sk_buff_head *dropped)
+{
+   __wfx_tx_queue_drop(wvif, &queue->cab, dropped);
+   __wfx_tx_queue_drop(wvif, &queue->normal, dropped);
+   wake_up(&wvif->wdev->tx_dequeue);
+}
+
+void wfx_tx_queues_put(struct wfx_vif *wvif, struct sk_buff *skb)
+{
+   struct wfx_queue *queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
+   struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+
+   if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
+   skb_queue_tail(&queue->cab, skb);
+   else
+   skb_queue_tail(&queue->normal, skb);
+}
+
+void wfx_pending_drop(struct wfx_dev *wdev, struct sk_buff_head *dropped)
+{
+   struct wfx_queue *queue;
+   struct wfx_vif *wvif;
+   struct hif_msg *hif;
+   struct sk_buff *skb;
+
+   WARN(!wdev->chip_frozen, "%s should only be used to recover a frozen 
device",
+__func__);
+   while ((skb = skb_dequeue(&wdev->tx_pending)) != NULL) {
+   hif = (struct hif_msg *)skb->data;
+   wvif = wdev_to_wvif(wdev, hif->interface);
+   if (wvif) {
+   queue = &wvif->tx_queue[skb_get_queue_mapping(skb)];
+

[PATCH v4 15/24] wfx: add hif_rx.c/hif_rx.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/hif_rx.c | 416 +++
 drivers/net/wireless/silabs/wfx/hif_rx.h |  18 +
 2 files changed, 434 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/hif_rx.c
 create mode 100644 drivers/net/wireless/silabs/wfx/hif_rx.h

diff --git a/drivers/net/wireless/silabs/wfx/hif_rx.c 
b/drivers/net/wireless/silabs/wfx/hif_rx.c
new file mode 100644
index ..2a9c09c5ee1f
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/hif_rx.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Implementation of chip-to-host event (aka indications) of WFxxx Split Mac
+ * (WSM) API.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+
+#include "hif_rx.h"
+#include "wfx.h"
+#include "scan.h"
+#include "bh.h"
+#include "sta.h"
+#include "data_rx.h"
+#include "hif_api_cmd.h"
+
+static int hif_generic_confirm(struct wfx_dev *wdev,
+  const struct hif_msg *hif, const void *buf)
+{
+   /* All confirm messages start with status */
+   int status = le32_to_cpup((__le32 *)buf);
+   int cmd = hif->id;
+   int len = le16_to_cpu(hif->len) - 4; /* drop header */
+
+   WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error");
+
+   if (!wdev->hif_cmd.buf_send) {
+   dev_warn(wdev->dev, "unexpected confirmation: 0x%.2x\n", cmd);
+   return -EINVAL;
+   }
+
+   if (cmd != wdev->hif_cmd.buf_send->id) {
+   dev_warn(wdev->dev,
+"chip response mismatch request: 0x%.2x vs 0x%.2x\n",
+cmd, wdev->hif_cmd.buf_send->id);
+   return -EINVAL;
+   }
+
+   if (wdev->hif_cmd.buf_recv) {
+   if (wdev->hif_cmd.len_recv >= len && len > 0)
+   memcpy(wdev->hif_cmd.buf_recv, buf, len);
+   else
+   status = -EIO;
+   }
+   wdev->hif_cmd.ret = status;
+
+   complete(&wdev->hif_cmd.done);
+   return status;
+}
+
+static int hif_tx_confirm(struct wfx_dev *wdev,
+ const struct hif_msg *hif, const void *buf)
+{
+   const struct hif_cnf_tx *body = buf;
+
+   wfx_tx_confirm_cb(wdev, body);
+   return 0;
+}
+
+static int hif_multi_tx_confirm(struct wfx_dev *wdev,
+   const struct hif_msg *hif, const void *buf)
+{
+   const struct hif_cnf_multi_transmit *body = buf;
+   int i;
+
+   WARN(body->num_tx_confs <= 0, "corrupted message");
+   for (i = 0; i < body->num_tx_confs; i++)
+   wfx_tx_confirm_cb(wdev, &body->tx_conf_payload[i]);
+   return 0;
+}
+
+static int hif_startup_indication(struct wfx_dev *wdev,
+ const struct hif_msg *hif, const void *buf)
+{
+   const struct hif_ind_startup *body = buf;
+
+   if (body->status || body->firmware_type > 4) {
+   dev_err(wdev->dev, "received invalid startup indication");
+   return -EINVAL;
+   }
+   memcpy(&wdev->hw_caps, body, sizeof(struct hif_ind_startup));
+   le16_to_cpus((__le16 *)&wdev->hw_caps.hardware_id);
+   le16_to_cpus((__le16 *)&wdev->hw_caps.num_inp_ch_bufs);
+   le16_to_cpus((__le16 *)&wdev->hw_caps.size_inp_ch_buf);
+   le32_to_cpus((__le32 *)&wdev->hw_caps.supported_rate_mask);
+
+   complete(&wdev->firmware_ready);
+   return 0;
+}
+
+static int hif_wakeup_indication(struct wfx_dev *wdev,
+const struct hif_msg *hif, const void *buf)
+{
+   if (!wdev->pdata.gpio_wakeup ||
+   gpiod_get_value(wdev->pdata.gpio_wakeup) == 0) {
+   dev_warn(wdev->dev, "unexpected wake-up indication\n");
+   return -EIO;
+   }
+   return 0;
+}
+
+static int hif_receive_indication(struct wfx_dev *wdev,
+ const struct hif_msg *hif,
+ const void *buf, struct sk_buff *skb)
+{
+   struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
+   const struct hif_ind_rx *body = buf;
+
+   if (!wvif) {
+   dev_warn(wdev->dev, "%s: ignore rx data for non-existent vif 
%d\n",
+__func__, hif->interface);
+   return -EIO;
+   }
+   skb_pull(skb, sizeof(struct hif_msg) + sizeof(struct hif_ind_rx));
+   wfx_rx_cb(wvif, body, skb);
+
+   return 0;
+}
+
+static int hif_event_indication(struct wfx_dev *wdev,
+   const struct hif_msg *hif, const void *buf)
+{
+   struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
+   const struct hif_ind_event *body = buf;
+   int type = le32_to_cpu(body->event_id);
+
+   if (!wvif) {
+   dev_warn(wdev->dev, "%s: received event for non-existent 
vif\n", __func__);
+   return -EIO;

[PATCH v4 24/24] wfx: get out from the staging area

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

The wfx driver is now mature enough to leave the staging area.

Signed-off-by: Jérôme Pouiller 
---
 MAINTAINERS  |  3 ++-
 drivers/net/wireless/Kconfig |  1 +
 drivers/net/wireless/Makefile|  1 +
 drivers/net/wireless/silabs/Kconfig  | 18 ++
 drivers/net/wireless/silabs/Makefile |  3 +++
 drivers/staging/Kconfig  |  2 --
 drivers/staging/Makefile |  1 -
 drivers/staging/wfx/TODO |  6 --
 8 files changed, 25 insertions(+), 10 deletions(-)
 create mode 100644 drivers/net/wireless/silabs/Kconfig
 create mode 100644 drivers/net/wireless/silabs/Makefile
 delete mode 100644 drivers/staging/wfx/TODO

diff --git a/MAINTAINERS b/MAINTAINERS
index 9d7784a5cb88..35810219bad0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16219,7 +16219,8 @@ F:  drivers/platform/x86/touchscreen_dmi.c
 SILICON LABS WIRELESS DRIVERS (for WFxxx series)
 M: Jérôme Pouiller 
 S: Supported
-F: drivers/staging/wfx/
+F: Documentation/devicetree/bindings/net/wireless/silabs,wfx.yaml
+F: drivers/net/wireless/silabs/wfx/
 
 SILICON MOTION SM712 FRAME BUFFER DRIVER
 M: Sudip Mukherjee 
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..e78ff7af6517 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -31,6 +31,7 @@ source "drivers/net/wireless/microchip/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
+source "drivers/net/wireless/silabs/Kconfig"
 source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..76885e5f0ea7 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
+obj-$(CONFIG_WLAN_VENDOR_SILABS) += silabs/
 obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
diff --git a/drivers/net/wireless/silabs/Kconfig 
b/drivers/net/wireless/silabs/Kconfig
new file mode 100644
index ..6262a799bf36
--- /dev/null
+++ b/drivers/net/wireless/silabs/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config WLAN_VENDOR_SILABS
+   bool "Silicon Laboratories devices"
+   default y
+   help
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_SILABS
+
+source "drivers/net/wireless/silabs/wfx/Kconfig"
+
+endif # WLAN_VENDOR_SILABS
diff --git a/drivers/net/wireless/silabs/Makefile 
b/drivers/net/wireless/silabs/Makefile
new file mode 100644
index ..c2263ee21006
--- /dev/null
+++ b/drivers/net/wireless/silabs/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_WFX)  += wfx/
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index b22f73d7bfc4..b07de39b9f0a 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -110,8 +110,6 @@ source "drivers/staging/qlge/Kconfig"
 
 source "drivers/staging/wimax/Kconfig"
 
-source "drivers/staging/wfx/Kconfig"
-
 source "drivers/staging/hikey9xx/Kconfig"
 
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 2245059e69c7..c6a992d1edd5 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -45,5 +45,4 @@ obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
 obj-$(CONFIG_KPC2000)  += kpc2000/
 obj-$(CONFIG_QLGE) += qlge/
 obj-$(CONFIG_WIMAX)+= wimax/
-obj-$(CONFIG_WFX)  += wfx/
 obj-y  += hikey9xx/
diff --git a/drivers/staging/wfx/TODO b/drivers/staging/wfx/TODO
deleted file mode 100644
index 1b4bc2af94b6..
--- a/drivers/staging/wfx/TODO
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a list of things that need to be done to get this driver out of the
-staging directory.
-
-  - As suggested by Felix, rate control could be improved following this idea:
-https://lore.kernel.org/lkml/3099559.gv3Q75KnN1@pc-42/
-
-- 
2.29.2

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH v4 06/24] wfx: add bus.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/bus.h | 38 +++
 1 file changed, 38 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/bus.h

diff --git a/drivers/net/wireless/silabs/wfx/bus.h 
b/drivers/net/wireless/silabs/wfx/bus.h
new file mode 100644
index ..ca04b3da6204
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/bus.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Common bus abstraction layer.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#ifndef WFX_BUS_H
+#define WFX_BUS_H
+
+#include 
+#include 
+
+#define WFX_REG_CONFIG0x0
+#define WFX_REG_CONTROL   0x1
+#define WFX_REG_IN_OUT_QUEUE  0x2
+#define WFX_REG_AHB_DPORT 0x3
+#define WFX_REG_BASE_ADDR 0x4
+#define WFX_REG_SRAM_DPORT0x5
+#define WFX_REG_SET_GEN_R_W   0x6
+#define WFX_REG_FRAME_OUT 0x7
+
+struct hwbus_ops {
+   int (*copy_from_io)(void *bus_priv, unsigned int addr,
+   void *dst, size_t count);
+   int (*copy_to_io)(void *bus_priv, unsigned int addr,
+ const void *src, size_t count);
+   int (*irq_subscribe)(void *bus_priv);
+   int (*irq_unsubscribe)(void *bus_priv);
+   void (*lock)(void *bus_priv);
+   void (*unlock)(void *bus_priv);
+   size_t (*align_size)(void *bus_priv, size_t size);
+};
+
+extern struct sdio_driver wfx_sdio_driver;
+extern struct spi_driver wfx_spi_driver;
+
+#endif
-- 
2.29.2

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH v4 11/24] wfx: add bh.c/bh.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/bh.c | 332 +++
 drivers/net/wireless/silabs/wfx/bh.h |  33 +++
 2 files changed, 365 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/bh.c
 create mode 100644 drivers/net/wireless/silabs/wfx/bh.h

diff --git a/drivers/net/wireless/silabs/wfx/bh.c 
b/drivers/net/wireless/silabs/wfx/bh.c
new file mode 100644
index ..80de528ce974
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/bh.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Interrupt bottom half (BH).
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+
+#include "bh.h"
+#include "wfx.h"
+#include "hwio.h"
+#include "traces.h"
+#include "hif_rx.h"
+#include "hif_api_cmd.h"
+
+static void device_wakeup(struct wfx_dev *wdev)
+{
+   int max_retry = 3;
+
+   if (!wdev->pdata.gpio_wakeup)
+   return;
+   if (gpiod_get_value_cansleep(wdev->pdata.gpio_wakeup) > 0)
+   return;
+
+   if (wfx_api_older_than(wdev, 1, 4)) {
+   gpiod_set_value_cansleep(wdev->pdata.gpio_wakeup, 1);
+   if (!completion_done(&wdev->hif.ctrl_ready))
+   usleep_range(2000, 2500);
+   return;
+   }
+   for (;;) {
+   gpiod_set_value_cansleep(wdev->pdata.gpio_wakeup, 1);
+   /* completion.h does not provide any function to wait
+* completion without consume it (a kind of
+* wait_for_completion_done_timeout()). So we have to emulate
+* it.
+*/
+   if (wait_for_completion_timeout(&wdev->hif.ctrl_ready,
+   msecs_to_jiffies(2))) {
+   complete(&wdev->hif.ctrl_ready);
+   return;
+   } else if (max_retry-- > 0) {
+   /* Older firmwares have a race in sleep/wake-up process.
+* Redo the process is sufficient to unfreeze the
+* chip.
+*/
+   dev_err(wdev->dev, "timeout while wake up chip\n");
+   gpiod_set_value_cansleep(wdev->pdata.gpio_wakeup, 0);
+   usleep_range(2000, 2500);
+   } else {
+   dev_err(wdev->dev, "max wake-up retries reached\n");
+   return;
+   }
+   }
+}
+
+static void device_release(struct wfx_dev *wdev)
+{
+   if (!wdev->pdata.gpio_wakeup)
+   return;
+
+   gpiod_set_value_cansleep(wdev->pdata.gpio_wakeup, 0);
+}
+
+static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf)
+{
+   struct sk_buff *skb;
+   struct hif_msg *hif;
+   size_t alloc_len;
+   size_t computed_len;
+   int release_count;
+   int piggyback = 0;
+
+   WARN(read_len > round_down(0xFFF, 2) * sizeof(u16),
+"%s: request exceed WFx capability", __func__);
+
+   /* Add 2 to take into account piggyback size */
+   alloc_len = wdev->hwbus_ops->align_size(wdev->hwbus_priv, read_len + 2);
+   skb = dev_alloc_skb(alloc_len);
+   if (!skb)
+   return -ENOMEM;
+
+   if (wfx_data_read(wdev, skb->data, alloc_len))
+   goto err;
+
+   piggyback = le16_to_cpup((__le16 *)(skb->data + alloc_len - 2));
+   _trace_piggyback(piggyback, false);
+
+   hif = (struct hif_msg *)skb->data;
+   WARN(hif->encrypted & 0x3, "encryption is unsupported");
+   if (WARN(read_len < sizeof(struct hif_msg), "corrupted read"))
+   goto err;
+   computed_len = le16_to_cpu(hif->len);
+   computed_len = round_up(computed_len, 2);
+   if (computed_len != read_len) {
+   dev_err(wdev->dev, "inconsistent message length: %zu != %zu\n",
+   computed_len, read_len);
+   print_hex_dump(KERN_INFO, "hif: ", DUMP_PREFIX_OFFSET, 16, 1,
+  hif, read_len, true);
+   goto err;
+   }
+
+   if (!(hif->id & HIF_ID_IS_INDICATION)) {
+   (*is_cnf)++;
+   if (hif->id == HIF_CNF_ID_MULTI_TRANSMIT)
+   release_count = ((struct hif_cnf_multi_transmit 
*)hif->body)->num_tx_confs;
+   else
+   release_count = 1;
+   WARN(wdev->hif.tx_buffers_used < release_count, "corrupted 
buffer counter");
+   wdev->hif.tx_buffers_used -= release_count;
+   }
+   _trace_hif_recv(hif, wdev->hif.tx_buffers_used);
+
+   if (hif->id != HIF_IND_ID_EXCEPTION && hif->id != HIF_IND_ID_ERROR) {
+   if (hif->seqnum != wdev->hif.rx_seqnum)
+   dev_warn(wdev->dev, "wrong message sequence: %d != 
%d\n",
+hif->seqnum, wdev->hif.

[PATCH v4 05/24] wfx: add main.c/main.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/main.c | 503 +
 drivers/net/wireless/silabs/wfx/main.h |  43 +++
 2 files changed, 546 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/main.c
 create mode 100644 drivers/net/wireless/silabs/wfx/main.h

diff --git a/drivers/net/wireless/silabs/wfx/main.c 
b/drivers/net/wireless/silabs/wfx/main.c
new file mode 100644
index ..16c4806fc932
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/main.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Device probe and register.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ * Copyright (c) 2008, Johannes Berg 
+ * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (c) 2007-2009, Christian Lamparter 
+ * Copyright (c) 2006, Michael Wu 
+ * Copyright (c) 2004-2006 Jean-Baptiste Note , et al.
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "main.h"
+#include "wfx.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "bus.h"
+#include "bh.h"
+#include "sta.h"
+#include "key.h"
+#include "scan.h"
+#include "debug.h"
+#include "data_tx.h"
+#include "hif_tx_mib.h"
+#include "hif_api_cmd.h"
+
+#define WFX_PDS_MAX_SIZE 1500
+
+MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WFx");
+MODULE_AUTHOR("Jérôme Pouiller ");
+MODULE_LICENSE("GPL");
+
+#define RATETAB_ENT(_rate, _rateid, _flags) { \
+   .bitrate  = (_rate),   \
+   .hw_value = (_rateid), \
+   .flags= (_flags),  \
+}
+
+static struct ieee80211_rate wfx_rates[] = {
+   RATETAB_ENT(10,  0,  0),
+   RATETAB_ENT(20,  1,  IEEE80211_RATE_SHORT_PREAMBLE),
+   RATETAB_ENT(55,  2,  IEEE80211_RATE_SHORT_PREAMBLE),
+   RATETAB_ENT(110, 3,  IEEE80211_RATE_SHORT_PREAMBLE),
+   RATETAB_ENT(60,  6,  0),
+   RATETAB_ENT(90,  7,  0),
+   RATETAB_ENT(120, 8,  0),
+   RATETAB_ENT(180, 9,  0),
+   RATETAB_ENT(240, 10, 0),
+   RATETAB_ENT(360, 11, 0),
+   RATETAB_ENT(480, 12, 0),
+   RATETAB_ENT(540, 13, 0),
+};
+
+#define CHAN2G(_channel, _freq, _flags) { \
+   .band = NL80211_BAND_2GHZ, \
+   .center_freq = (_freq),\
+   .hw_value = (_channel),\
+   .flags = (_flags), \
+   .max_antenna_gain = 0, \
+   .max_power = 30,   \
+}
+
+static struct ieee80211_channel wfx_2ghz_chantable[] = {
+   CHAN2G(1,  2412, 0),
+   CHAN2G(2,  2417, 0),
+   CHAN2G(3,  2422, 0),
+   CHAN2G(4,  2427, 0),
+   CHAN2G(5,  2432, 0),
+   CHAN2G(6,  2437, 0),
+   CHAN2G(7,  2442, 0),
+   CHAN2G(8,  2447, 0),
+   CHAN2G(9,  2452, 0),
+   CHAN2G(10, 2457, 0),
+   CHAN2G(11, 2462, 0),
+   CHAN2G(12, 2467, 0),
+   CHAN2G(13, 2472, 0),
+   CHAN2G(14, 2484, 0),
+};
+
+static const struct ieee80211_supported_band wfx_band_2ghz = {
+   .channels = wfx_2ghz_chantable,
+   .n_channels = ARRAY_SIZE(wfx_2ghz_chantable),
+   .bitrates = wfx_rates,
+   .n_bitrates = ARRAY_SIZE(wfx_rates),
+   .ht_cap = {
+   /* Receive caps */
+   .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 |
+  IEEE80211_HT_CAP_MAX_AMSDU |
+  (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT),
+   .ht_supported = 1,
+   .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
+   .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+   .mcs = {
+   .rx_mask = { 0xFF }, /* MCS0 to MCS7 */
+   .rx_highest = cpu_to_le16(72),
+   .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+   },
+   },
+};
+
+static const struct ieee80211_iface_limit wdev_iface_limits[] = {
+   { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) },
+   { .max = 1, .types = BIT(NL80211_IFTYPE_AP) },
+};
+
+static const struct ieee80211_iface_combination wfx_iface_combinations[] = {
+   {
+   .num_different_channels = 2,
+   .max_interfaces = 2,
+   .limits = wdev_iface_limits,
+   .n_limits = ARRAY_SIZE(wdev_iface_limits),
+   }
+};
+
+static const struct ieee80211_ops wfx_ops = {
+   .start  = wfx_start,
+   .stop   = wfx_stop,
+   .add_interface  = wfx_add_interface,
+   .remove_interface   = wfx_remove_interface,
+   .config = wfx_config,
+   .tx = wfx_tx,
+   .join_ibss  = wfx_join_ibss,
+   .leave_ibss = wfx_leave_ibss,
+   .conf_tx= wfx_conf_tx,
+   .hw_scan= wfx_hw_scan,
+   .cancel_hw_scan = wfx_cancel_hw_scan,
+   .start_ap   = wfx_start_ap,
+   .stop_ap= wfx_stop_ap,
+   .sta_add  

[PATCH v4 19/24] wfx: add sta.c/sta.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/sta.c | 809 ++
 drivers/net/wireless/silabs/wfx/sta.h |  73 +++
 2 files changed, 882 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/sta.c
 create mode 100644 drivers/net/wireless/silabs/wfx/sta.h

diff --git a/drivers/net/wireless/silabs/wfx/sta.c 
b/drivers/net/wireless/silabs/wfx/sta.c
new file mode 100644
index ..81bfca8faffd
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/sta.c
@@ -0,0 +1,809 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Implementation of mac80211 API.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+
+#include "sta.h"
+#include "wfx.h"
+#include "fwio.h"
+#include "bh.h"
+#include "key.h"
+#include "scan.h"
+#include "debug.h"
+#include "hif_tx.h"
+#include "hif_tx_mib.h"
+
+#define HIF_MAX_ARP_IP_ADDRTABLE_ENTRIES 2
+
+u32 wfx_rate_mask_to_hw(struct wfx_dev *wdev, u32 rates)
+{
+   int i;
+   u32 ret = 0;
+   /* WF200 only support 2GHz */
+   struct ieee80211_supported_band *sband = 
wdev->hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+   for (i = 0; i < sband->n_bitrates; i++) {
+   if (rates & BIT(i)) {
+   if (i >= sband->n_bitrates)
+   dev_warn(wdev->dev, "unsupported basic rate\n");
+   else
+   ret |= BIT(sband->bitrates[i].hw_value);
+   }
+   }
+   return ret;
+}
+
+void wfx_cooling_timeout_work(struct work_struct *work)
+{
+   struct wfx_dev *wdev = container_of(to_delayed_work(work),
+   struct wfx_dev,
+   cooling_timeout_work);
+
+   wdev->chip_frozen = true;
+   wfx_tx_unlock(wdev);
+}
+
+void wfx_suspend_hot_dev(struct wfx_dev *wdev, enum sta_notify_cmd cmd)
+{
+   if (cmd == STA_NOTIFY_AWAKE) {
+   /* Device recover normal temperature */
+   if (cancel_delayed_work(&wdev->cooling_timeout_work))
+   wfx_tx_unlock(wdev);
+   } else {
+   /* Device is too hot */
+   schedule_delayed_work(&wdev->cooling_timeout_work, 10 * HZ);
+   wfx_tx_lock(wdev);
+   }
+}
+
+static void wfx_filter_beacon(struct wfx_vif *wvif, bool filter_beacon)
+{
+   static const struct hif_ie_table_entry filter_ies[] = {
+   {
+   .ie_id= WLAN_EID_VENDOR_SPECIFIC,
+   .has_changed  = 1,
+   .no_longer= 1,
+   .has_appeared = 1,
+   .oui  = { 0x50, 0x6F, 0x9A },
+   }, {
+   .ie_id= WLAN_EID_HT_OPERATION,
+   .has_changed  = 1,
+   .no_longer= 1,
+   .has_appeared = 1,
+   }, {
+   .ie_id= WLAN_EID_ERP_INFO,
+   .has_changed  = 1,
+   .no_longer= 1,
+   .has_appeared = 1,
+   }
+   };
+
+   if (!filter_beacon) {
+   hif_beacon_filter_control(wvif, 0, 1);
+   } else {
+   hif_set_beacon_filter_table(wvif, 3, filter_ies);
+   hif_beacon_filter_control(wvif, HIF_BEACON_FILTER_ENABLE, 0);
+   }
+}
+
+void wfx_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
+ unsigned int *total_flags, u64 unused)
+{
+   struct wfx_vif *wvif = NULL;
+   struct wfx_dev *wdev = hw->priv;
+   bool filter_bssid, filter_prbreq, filter_beacon;
+
+   /* Notes:
+*   - Probe responses (FIF_BCN_PRBRESP_PROMISC) are never filtered
+*   - PS-Poll (FIF_PSPOLL) are never filtered
+*   - RTS, CTS and Ack (FIF_CONTROL) are always filtered
+*   - Broken frames (FIF_FCSFAIL and FIF_PLCPFAIL) are always filtered
+*   - Firmware does (yet) allow to forward unicast traffic sent to
+* other stations (aka. promiscuous mode)
+*/
+   *total_flags &= FIF_BCN_PRBRESP_PROMISC | FIF_ALLMULTI | FIF_OTHER_BSS |
+   FIF_PROBE_REQ | FIF_PSPOLL;
+
+   mutex_lock(&wdev->conf_mutex);
+   while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
+   mutex_lock(&wvif->scan_lock);
+
+   /* Note: FIF_BCN_PRBRESP_PROMISC covers probe response and
+* beacons from other BSS
+*/
+   if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
+   filter_beacon = false;
+   else
+   filter_beacon = true;
+   wfx_filter_beacon(wvif, filter_beacon);
+
+   if (*total_flags & FIF_OTHER_BSS)
+   filter_bssid = false;
+  

[PATCH v4 08/24] wfx: add bus_sdio.c

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/bus_sdio.c | 258 +
 1 file changed, 258 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/bus_sdio.c

diff --git a/drivers/net/wireless/silabs/wfx/bus_sdio.c 
b/drivers/net/wireless/silabs/wfx/bus_sdio.c
new file mode 100644
index ..2f6488e36e86
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/bus_sdio.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SDIO interface.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "bus.h"
+#include "wfx.h"
+#include "hwio.h"
+#include "main.h"
+#include "bh.h"
+
+static const struct wfx_platform_data wfx_sdio_pdata = {
+   .file_fw = "wfm_wf200",
+   .file_pds = "wf200.pds",
+};
+
+struct wfx_sdio_priv {
+   struct sdio_func *func;
+   struct wfx_dev *core;
+   u8 buf_id_tx;
+   u8 buf_id_rx;
+   int of_irq;
+};
+
+static int wfx_sdio_copy_from_io(void *priv, unsigned int reg_id,
+void *dst, size_t count)
+{
+   struct wfx_sdio_priv *bus = priv;
+   unsigned int sdio_addr = reg_id << 2;
+   int ret;
+
+   WARN(reg_id > 7, "chip only has 7 registers");
+   WARN(((uintptr_t)dst) & 3, "unaligned buffer size");
+   WARN(count & 3, "unaligned buffer address");
+
+   /* Use queue mode buffers */
+   if (reg_id == WFX_REG_IN_OUT_QUEUE)
+   sdio_addr |= (bus->buf_id_rx + 1) << 7;
+   ret = sdio_memcpy_fromio(bus->func, dst, sdio_addr, count);
+   if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE)
+   bus->buf_id_rx = (bus->buf_id_rx + 1) % 4;
+
+   return ret;
+}
+
+static int wfx_sdio_copy_to_io(void *priv, unsigned int reg_id,
+  const void *src, size_t count)
+{
+   struct wfx_sdio_priv *bus = priv;
+   unsigned int sdio_addr = reg_id << 2;
+   int ret;
+
+   WARN(reg_id > 7, "chip only has 7 registers");
+   WARN(((uintptr_t)src) & 3, "unaligned buffer size");
+   WARN(count & 3, "unaligned buffer address");
+
+   /* Use queue mode buffers */
+   if (reg_id == WFX_REG_IN_OUT_QUEUE)
+   sdio_addr |= bus->buf_id_tx << 7;
+   /* FIXME: discards 'const' qualifier for src */
+   ret = sdio_memcpy_toio(bus->func, sdio_addr, (void *)src, count);
+   if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE)
+   bus->buf_id_tx = (bus->buf_id_tx + 1) % 32;
+
+   return ret;
+}
+
+static void wfx_sdio_lock(void *priv)
+{
+   struct wfx_sdio_priv *bus = priv;
+
+   sdio_claim_host(bus->func);
+}
+
+static void wfx_sdio_unlock(void *priv)
+{
+   struct wfx_sdio_priv *bus = priv;
+
+   sdio_release_host(bus->func);
+}
+
+static void wfx_sdio_irq_handler(struct sdio_func *func)
+{
+   struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
+
+   wfx_bh_request_rx(bus->core);
+}
+
+static irqreturn_t wfx_sdio_irq_handler_ext(int irq, void *priv)
+{
+   struct wfx_sdio_priv *bus = priv;
+
+   sdio_claim_host(bus->func);
+   wfx_bh_request_rx(bus->core);
+   sdio_release_host(bus->func);
+   return IRQ_HANDLED;
+}
+
+static int wfx_sdio_irq_subscribe(void *priv)
+{
+   struct wfx_sdio_priv *bus = priv;
+   u32 flags;
+   int ret;
+   u8 cccr;
+
+   if (!bus->of_irq) {
+   sdio_claim_host(bus->func);
+   ret = sdio_claim_irq(bus->func, wfx_sdio_irq_handler);
+   sdio_release_host(bus->func);
+   return ret;
+   }
+
+   sdio_claim_host(bus->func);
+   cccr = sdio_f0_readb(bus->func, SDIO_CCCR_IENx, NULL);
+   cccr |= BIT(0);
+   cccr |= BIT(bus->func->num);
+   sdio_f0_writeb(bus->func, cccr, SDIO_CCCR_IENx, NULL);
+   sdio_release_host(bus->func);
+   flags = irq_get_trigger_type(bus->of_irq);
+   if (!flags)
+   flags = IRQF_TRIGGER_HIGH;
+   flags |= IRQF_ONESHOT;
+   return devm_request_threaded_irq(&bus->func->dev, bus->of_irq, NULL,
+wfx_sdio_irq_handler_ext, flags,
+"wfx", bus);
+}
+
+static int wfx_sdio_irq_unsubscribe(void *priv)
+{
+   struct wfx_sdio_priv *bus = priv;
+   int ret;
+
+   if (bus->of_irq)
+   devm_free_irq(&bus->func->dev, bus->of_irq, bus);
+   sdio_claim_host(bus->func);
+   ret = sdio_release_irq(bus->func);
+   sdio_release_host(bus->func);
+   return ret;
+}
+
+static size_t wfx_sdio_align_size(void *priv, size_t size)
+{
+   struct wfx_sdio_priv *bus = priv;
+
+   return sdio_align_size(bus->func, size);
+}
+
+static const struct hwbus_ops wfx_sdio_hwbus_ops = {
+   .copy_from_io = wfx_sdio_copy_from_io,
+   .copy_to_io = wfx_sdio_copy_to_io,
+   .irq_subscribe = 

[PATCH v4 03/24] wfx: add Makefile/Kconfig

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/Kconfig  | 12 +++
 drivers/net/wireless/silabs/wfx/Makefile | 26 
 2 files changed, 38 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/Kconfig
 create mode 100644 drivers/net/wireless/silabs/wfx/Makefile

diff --git a/drivers/net/wireless/silabs/wfx/Kconfig 
b/drivers/net/wireless/silabs/wfx/Kconfig
new file mode 100644
index ..3be4b1e735e1
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/Kconfig
@@ -0,0 +1,12 @@
+config WFX
+   tristate "Silicon Labs wireless chips WF200 and further"
+   depends on MAC80211
+   depends on MMC || !MMC # do not allow WFX=y if MMC=m
+   depends on (SPI || MMC)
+   help
+ This is a driver for Silicons Labs WFxxx series (WF200 and further)
+ chipsets. This chip can be found on SPI or SDIO buses.
+
+ Silabs does not use a reliable SDIO vendor ID. So, to avoid conflicts,
+ the driver won't probe the device if it is not also declared in the
+ Device Tree.
diff --git a/drivers/net/wireless/silabs/wfx/Makefile 
b/drivers/net/wireless/silabs/wfx/Makefile
new file mode 100644
index ..f399962c8619
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# Necessary for CREATE_TRACE_POINTS
+CFLAGS_debug.o = -I$(src)
+
+wfx-y := \
+   bh.o \
+   hwio.o \
+   fwio.o \
+   hif_tx_mib.o \
+   hif_tx.o \
+   hif_rx.o \
+   queue.o \
+   data_tx.o \
+   data_rx.o \
+   scan.o \
+   sta.o \
+   key.o \
+   main.o \
+   sta.o \
+   debug.o
+wfx-$(CONFIG_SPI) += bus_spi.o
+# When CONFIG_MMC == m, append to 'wfx-y' (and not to 'wfx-m')
+wfx-$(subst m,y,$(CONFIG_MMC)) += bus_sdio.o
+
+obj-$(CONFIG_WFX) += wfx.o
-- 
2.29.2

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


[PATCH v4 13/24] wfx: add hif_tx*.c/hif_tx*.h

2020-12-23 Thread Jerome Pouiller
From: Jérôme Pouiller 

Signed-off-by: Jérôme Pouiller 
---
 drivers/net/wireless/silabs/wfx/hif_tx.c | 527 +++
 drivers/net/wireless/silabs/wfx/hif_tx.h |  60 +++
 drivers/net/wireless/silabs/wfx/hif_tx_mib.c | 324 
 drivers/net/wireless/silabs/wfx/hif_tx_mib.h |  49 ++
 4 files changed, 960 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/hif_tx.c
 create mode 100644 drivers/net/wireless/silabs/wfx/hif_tx.h
 create mode 100644 drivers/net/wireless/silabs/wfx/hif_tx_mib.c
 create mode 100644 drivers/net/wireless/silabs/wfx/hif_tx_mib.h

diff --git a/drivers/net/wireless/silabs/wfx/hif_tx.c 
b/drivers/net/wireless/silabs/wfx/hif_tx.c
new file mode 100644
index ..626a6b5aee1e
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/hif_tx.c
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx
+ * Split Mac (WSM) API.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include 
+
+#include "hif_tx.h"
+#include "wfx.h"
+#include "bh.h"
+#include "hwio.h"
+#include "debug.h"
+#include "sta.h"
+
+void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd)
+{
+   init_completion(&hif_cmd->ready);
+   init_completion(&hif_cmd->done);
+   mutex_init(&hif_cmd->lock);
+}
+
+static void wfx_fill_header(struct hif_msg *hif, int if_id,
+   unsigned int cmd, size_t size)
+{
+   if (if_id == -1)
+   if_id = 2;
+
+   WARN(cmd > 0x3f, "invalid WSM command %#.2x", cmd);
+   WARN(size > 0xFFF, "requested buffer is too large: %zu bytes", size);
+   WARN(if_id > 0x3, "invalid interface ID %d", if_id);
+
+   hif->len = cpu_to_le16(size + 4);
+   hif->id = cmd;
+   hif->interface = if_id;
+}
+
+static void *wfx_alloc_hif(size_t body_len, struct hif_msg **hif)
+{
+   *hif = kzalloc(sizeof(struct hif_msg) + body_len, GFP_KERNEL);
+   if (*hif)
+   return (*hif)->body;
+   else
+   return NULL;
+}
+
+int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request,
+void *reply, size_t reply_len, bool no_reply)
+{
+   const char *mib_name = "";
+   const char *mib_sep = "";
+   int cmd = request->id;
+   int vif = request->interface;
+   int ret;
+
+   /* Do not wait for any reply if chip is frozen */
+   if (wdev->chip_frozen)
+   return -ETIMEDOUT;
+
+   mutex_lock(&wdev->hif_cmd.lock);
+   WARN(wdev->hif_cmd.buf_send, "data locking error");
+
+   /* Note: call to complete() below has an implicit memory barrier that
+* hopefully protect buf_send
+*/
+   wdev->hif_cmd.buf_send = request;
+   wdev->hif_cmd.buf_recv = reply;
+   wdev->hif_cmd.len_recv = reply_len;
+   complete(&wdev->hif_cmd.ready);
+
+   wfx_bh_request_tx(wdev);
+
+   if (no_reply) {
+   /* Chip won't reply. Give enough time to the wq to send the
+* buffer.
+*/
+   msleep(100);
+   wdev->hif_cmd.buf_send = NULL;
+   mutex_unlock(&wdev->hif_cmd.lock);
+   return 0;
+   }
+
+   if (wdev->poll_irq)
+   wfx_bh_poll_irq(wdev);
+
+   ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 1 * HZ);
+   if (!ret) {
+   dev_err(wdev->dev, "chip is abnormally long to answer\n");
+   reinit_completion(&wdev->hif_cmd.ready);
+   ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 3 * HZ);
+   }
+   if (!ret) {
+   dev_err(wdev->dev, "chip did not answer\n");
+   wfx_pending_dump_old_frames(wdev, 3000);
+   wdev->chip_frozen = true;
+   reinit_completion(&wdev->hif_cmd.done);
+   ret = -ETIMEDOUT;
+   } else {
+   ret = wdev->hif_cmd.ret;
+   }
+
+   wdev->hif_cmd.buf_send = NULL;
+   mutex_unlock(&wdev->hif_cmd.lock);
+
+   if (ret &&
+   (cmd == HIF_REQ_ID_READ_MIB || cmd == HIF_REQ_ID_WRITE_MIB)) {
+   mib_name = get_mib_name(((u16 *)request)[2]);
+   mib_sep = "/";
+   }
+   if (ret < 0)
+   dev_err(wdev->dev,
+   "WSM request %s%s%s (%#.2x) on vif %d returned error 
%d\n",
+   get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret);
+   if (ret > 0)
+   dev_warn(wdev->dev,
+"WSM request %s%s%s (%#.2x) on vif %d returned status 
%d\n",
+get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret);
+
+   return ret;
+}
+
+/* This function is special. After HIF_REQ_ID_SHUT_DOWN, chip won't reply to 
any
+ * request anymore. Obviously, only call this function during device 
unregister.
+ */
+int hif_shutdown(struct wfx_dev *wdev)
+{
+   int ret;
+   struct hif_msg *h

[PATCH] staging: ralink-gdma: Fixed blank line coding style issue

2020-12-23 Thread Ayoub Soussi
Fixed coding style issue.

Signed-off-by: Ayoub Soussi 
---
 drivers/staging/ralink-gdma/ralink-gdma.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/staging/ralink-gdma/ralink-gdma.c 
b/drivers/staging/ralink-gdma/ralink-gdma.c
index 655df317d0ee..a6181a167814 100644
--- a/drivers/staging/ralink-gdma/ralink-gdma.c
+++ b/drivers/staging/ralink-gdma/ralink-gdma.c
@@ -122,6 +122,7 @@ struct gdma_dma_dev {
struct gdma_data *data;
void __iomem *base;
struct tasklet_struct task;
+
volatile unsigned long chan_issued;
atomic_t cnt;
 
-- 
2.17.1

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v2 19/48] opp: Fix adding OPP entries in a wrong order if rate is unavailable

2020-12-23 Thread Dmitry Osipenko
23.12.2020 07:34, Viresh Kumar пишет:
> On 22-12-20, 22:19, Dmitry Osipenko wrote:
>> 22.12.2020 12:12, Viresh Kumar пишет:
>>> On 17-12-20, 21:06, Dmitry Osipenko wrote:
 Fix adding OPP entries in a wrong (opposite) order if OPP rate is
 unavailable. The OPP comparison is erroneously skipped if OPP rate is
 missing, thus OPPs are left unsorted.

 Signed-off-by: Dmitry Osipenko 
 ---
  drivers/opp/core.c | 23 ---
  drivers/opp/opp.h  |  2 +-
  2 files changed, 13 insertions(+), 12 deletions(-)

 diff --git a/drivers/opp/core.c b/drivers/opp/core.c
 index 34f7e530d941..5c7f130a8de2 100644
 --- a/drivers/opp/core.c
 +++ b/drivers/opp/core.c
 @@ -1531,9 +1531,10 @@ static bool _opp_supported_by_regulators(struct 
 dev_pm_opp *opp,
return true;
  }
  
 -int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2)
 +int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2,
 +   bool rate_not_available)
  {
 -  if (opp1->rate != opp2->rate)
 +  if (!rate_not_available && opp1->rate != opp2->rate)
>>>
>>> rate will be 0 for both the OPPs here if rate_not_available is true and so 
>>> this
>>> change shouldn't be required.
>>
>> The rate_not_available is negated in the condition. This change is
>> required because both rates are 0 and then we should proceed to the
>> levels comparison.
> 
> Won't that happen without this patch ?

No

___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v2 28/48] soc/tegra: Introduce core power domain driver

2020-12-23 Thread Dmitry Osipenko
23.12.2020 08:57, Viresh Kumar пишет:
> On 22-12-20, 22:39, Dmitry Osipenko wrote:
>> 22.12.2020 22:21, Dmitry Osipenko пишет:
> + if (IS_ERR(opp)) {
> + dev_err(&genpd->dev, "failed to find OPP for level %u: %pe\n",
> + level, opp);
> + return PTR_ERR(opp);
> + }
> +
> + err = dev_pm_opp_set_voltage(&genpd->dev, opp);
 IIUC, you implemented this callback because you want to use the voltage 
 triplet
 present in the OPP table ?

 And so you are setting the regulator ("power") later in this patch ?
>>> yes
>>>
 I am not in favor of implementing this routine, as it just adds a wrapper 
 above
 the regulator API. What you should be doing rather is get the regulator by
 yourself here (instead of depending on the OPP core). And then you can do
 dev_pm_opp_get_voltage() here and set the voltage yourself. You may want to
 implement a version supporting triplet here though for the same.

 And you won't require the sync version of the API as well then.

>>> That's what I initially did for this driver. I don't mind to revert back
>>> to the initial variant in v3, it appeared to me that it will be nicer
>>> and cleaner to have OPP API managing everything here.
>>
>> I forgot one important detail (why the initial variant wasn't good)..
>> OPP entries that have unsupportable voltages should be filtered out and
>> OPP core performs the filtering only if regulator is assigned to the OPP
>> table.
>>
>> If regulator is assigned to the OPP table, then we need to use OPP API
>> for driving the regulator, hence that's why I added
>> dev_pm_opp_sync_regulators() and dev_pm_opp_set_voltage().
>>
>> Perhaps it should be possible to add dev_pm_opp_get_regulator() that
> 
> What's wrong with getting the regulator in the driver as well ? Apart from the
> OPP core ?

The voltage syncing should be done for each consumer regulator
individually [1].

Secondly, regulator core doesn't work well today if the same regulator
is requested more than one time for the same device.

>> will return the OPP table regulator in order to allow driver to use the
>> regulator directly. But I'm not sure whether this is a much better
>> option than the opp_sync_regulators() and opp_set_voltage() APIs.
> 
> set_voltage() is still fine as there is some data that the OPP core has, but
> sync_regulator() has nothing to do with OPP core.
> 
> And this may lead to more wrapper helpers in the OPP core, which I am afraid 
> of.
> And so even if it is not the best, I would like the OPP core to provide the 
> data
> and not get into this. Ofcourse there is an exception to this, opp_set_rate.
> 

The regulator_sync_voltage() should be invoked only if voltage was
changed previously [1].

The OPP core already has the info about whether voltage was changed and
it provides the necessary locking for both set_voltage() and
sync_regulator(). Perhaps I'll need to duplicate that functionality in
the PD driver, instead of making it all generic and re-usable by other
drivers.

[1]
https://elixir.bootlin.com/linux/v5.10.2/source/drivers/regulator/core.c#L4107
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v2 11/48] opp: Add dev_pm_opp_find_level_ceil()

2020-12-23 Thread Dmitry Osipenko
23.12.2020 07:19, Viresh Kumar пишет:
> On 22-12-20, 22:15, Dmitry Osipenko wrote:
>> 22.12.2020 09:42, Viresh Kumar пишет:
>>> On 17-12-20, 21:06, Dmitry Osipenko wrote:
 Add a ceil version of the dev_pm_opp_find_level(). It's handy to have if
 levels don't start from 0 in OPP table and zero usually means a minimal
 level.

 Signed-off-by: Dmitry Osipenko 
>>>
>>> Why doesn't the exact version work for you here ?
>>>
>>
>> The exact version won't find OPP for level=0 if levels don't start with
>> 0, where 0 means that minimal level is desired.
> 
> Right, but why do you need to send 0 for your platform ?
> 

To put power domain into the lowest performance state when device is idling.

https://elixir.bootlin.com/linux/v5.10-rc2/source/drivers/opp/core.c#L897

https://elixir.bootlin.com/linux/v5.10-rc2/source/drivers/opp/core.c#L785

Also please see patch 32, tegra_clock_runtime_suspend().
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v2 15/48] opp: Support set_opp() customization without requiring to use regulators

2020-12-23 Thread Dmitry Osipenko
23.12.2020 09:01, Viresh Kumar пишет:
> On 17-12-20, 21:06, Dmitry Osipenko wrote:
>> Support set_opp() customization without requiring to use regulators. This
>> is needed by drivers which want to use dev_pm_opp_set_rate() for changing
>> rates of a multiple clocks and don't need to touch regulator.
>>
>> One example is NVIDIA Tegra30/114 SoCs which have two sibling 3D hardware
>> units which should be use to the same clock rate, meanwhile voltage
>> scaling is done using a power domain. In this case OPP table doesn't have
>> a regulator, causing a NULL dereference in _set_opp_custom().
>>
>> Signed-off-by: Dmitry Osipenko 
>> ---
>>  drivers/opp/core.c | 16 
>>  1 file changed, 12 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/opp/core.c b/drivers/opp/core.c
>> index 3d02fe33630b..625dae7a5ecb 100644
>> --- a/drivers/opp/core.c
>> +++ b/drivers/opp/core.c
>> @@ -828,17 +828,25 @@ static int _set_opp_custom(const struct opp_table 
>> *opp_table,
>> struct dev_pm_opp_supply *old_supply,
>> struct dev_pm_opp_supply *new_supply)
>>  {
>> -struct dev_pm_set_opp_data *data;
>> +struct dev_pm_set_opp_data *data, tmp_data;
>> +unsigned int regulator_count;
>>  int size;
>>  
>> -data = opp_table->set_opp_data;
>> +if (opp_table->set_opp_data) {
>> +data = opp_table->set_opp_data;
>> +regulator_count = opp_table->regulator_count;
>> +} else {
>> +data = &tmp_data;
>> +regulator_count = 0;
>> +}
>> +
> 
> We should use the same structure, you can add some checks but not replace the
> structure altogether.

Well, there is no "same structure", the opp_table->set_opp_data is NULL
there.

I can re-write it like this if it looks better to you:

static int _set_opp_custom(...)
{
struct dev_pm_set_opp_data *data;
unsigned int regulator_count;
int size;

+   if (!opp_table->set_opp_data) {
+   struct dev_pm_set_opp_data freq_data = {};
+
+   freq_data.dev = dev;
+   freq_data.clk = opp_table->clk;
+   freq_data.new_opp.rate = freq;
+   freq_data.old_opp.rate = old_freq;
+
+   return opp_table->set_opp(&freq_data);
+   }
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v2 14/48] opp: Filter out OPPs based on availability of a required-OPP

2020-12-23 Thread Dmitry Osipenko
23.12.2020 07:22, Viresh Kumar пишет:
> On 22-12-20, 22:17, Dmitry Osipenko wrote:
>> 22.12.2020 11:59, Viresh Kumar пишет:
>>> On 17-12-20, 21:06, Dmitry Osipenko wrote:
 A required OPP may not be available, and thus, all OPPs which are using
 this required OPP should be unavailable too.

 Signed-off-by: Dmitry Osipenko 
 ---
  drivers/opp/core.c | 11 ++-
  1 file changed, 10 insertions(+), 1 deletion(-)
>>>
>>> Please send a separate patchset for fixes, as these can also go to 5.11 
>>> itself.
>>
>> Alright, although I don't think that this patch fixes any problems for
>> existing OPP users.
> 
> Because nobody is using this feature, but otherwise this is a fix for me.
> 
 diff --git a/drivers/opp/core.c b/drivers/opp/core.c
 index d9feb7639598..3d02fe33630b 100644
 --- a/drivers/opp/core.c
 +++ b/drivers/opp/core.c
 @@ -1588,7 +1588,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp 
 *new_opp,
 struct opp_table *opp_table, bool rate_not_available)
  {
struct list_head *head;
 -  int ret;
 +  int i, ret;
  
mutex_lock(&opp_table->lock);
head = &opp_table->opp_list;
 @@ -1615,6 +1615,15 @@ int _opp_add(struct device *dev, struct dev_pm_opp 
 *new_opp,
 __func__, new_opp->rate);
}
  
 +  for (i = 0; i < opp_table->required_opp_count && new_opp->available; 
 i++) {
 +  if (new_opp->required_opps[i]->available)
 +  continue;
 +
 +  new_opp->available = false;
 +  dev_warn(dev, "%s: OPP not supported by required OPP %pOF 
 (%lu)\n",
 +   __func__, new_opp->required_opps[i]->np, 
 new_opp->rate);
>>>
>>> Why not just break from here ?
>>
>> The new_opp could be already marked as unavailable by a previous voltage
>> check, hence this loop should be skipped entirely in that case.
> 
> Then add a separate check for that before the loop as we don't need that check
> on every iteration here.
> 

Perhaps the break will be a better option in this case, since it won't
hurt at all to print the additional message even if OPP was already
disabled by another check. I'll update it in next revision, thanks.
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v2 28/48] soc/tegra: Introduce core power domain driver

2020-12-23 Thread Dmitry Osipenko
23.12.2020 23:37, Dmitry Osipenko пишет:
> 23.12.2020 08:57, Viresh Kumar пишет:
>> On 22-12-20, 22:39, Dmitry Osipenko wrote:
>>> 22.12.2020 22:21, Dmitry Osipenko пишет:
>> +if (IS_ERR(opp)) {
>> +dev_err(&genpd->dev, "failed to find OPP for level %u: 
>> %pe\n",
>> +level, opp);
>> +return PTR_ERR(opp);
>> +}
>> +
>> +err = dev_pm_opp_set_voltage(&genpd->dev, opp);
> IIUC, you implemented this callback because you want to use the voltage 
> triplet
> present in the OPP table ?
>
> And so you are setting the regulator ("power") later in this patch ?
 yes

> I am not in favor of implementing this routine, as it just adds a wrapper 
> above
> the regulator API. What you should be doing rather is get the regulator by
> yourself here (instead of depending on the OPP core). And then you can do
> dev_pm_opp_get_voltage() here and set the voltage yourself. You may want 
> to
> implement a version supporting triplet here though for the same.
>
> And you won't require the sync version of the API as well then.
>
 That's what I initially did for this driver. I don't mind to revert back
 to the initial variant in v3, it appeared to me that it will be nicer
 and cleaner to have OPP API managing everything here.
>>>
>>> I forgot one important detail (why the initial variant wasn't good)..
>>> OPP entries that have unsupportable voltages should be filtered out and
>>> OPP core performs the filtering only if regulator is assigned to the OPP
>>> table.
>>>
>>> If regulator is assigned to the OPP table, then we need to use OPP API
>>> for driving the regulator, hence that's why I added
>>> dev_pm_opp_sync_regulators() and dev_pm_opp_set_voltage().
>>>
>>> Perhaps it should be possible to add dev_pm_opp_get_regulator() that
>>
>> What's wrong with getting the regulator in the driver as well ? Apart from 
>> the
>> OPP core ?
> 
> The voltage syncing should be done for each consumer regulator
> individually [1].
> 
> Secondly, regulator core doesn't work well today if the same regulator
> is requested more than one time for the same device.
> 
>>> will return the OPP table regulator in order to allow driver to use the
>>> regulator directly. But I'm not sure whether this is a much better
>>> option than the opp_sync_regulators() and opp_set_voltage() APIs.
>>
>> set_voltage() is still fine as there is some data that the OPP core has, but
>> sync_regulator() has nothing to do with OPP core.
>>
>> And this may lead to more wrapper helpers in the OPP core, which I am afraid 
>> of.
>> And so even if it is not the best, I would like the OPP core to provide the 
>> data
>> and not get into this. Ofcourse there is an exception to this, opp_set_rate.
>>
> 
> The regulator_sync_voltage() should be invoked only if voltage was
> changed previously [1].
> 
> The OPP core already has the info about whether voltage was changed and
> it provides the necessary locking for both set_voltage() and
> sync_regulator(). Perhaps I'll need to duplicate that functionality in
> the PD driver, instead of making it all generic and re-usable by other
> drivers.
> 
> [1]
> https://elixir.bootlin.com/linux/v5.10.2/source/drivers/regulator/core.c#L4107
> 

I just realized that the locking is missing in the v2 patches, something
to fix in the next revision :)

Still I'm in favor of extending the OPP API with the new common
functions. But if you have a strong opinion about that, then I'll try to
work around it in the PD driver in v3.
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v2 15/48] opp: Support set_opp() customization without requiring to use regulators

2020-12-23 Thread Viresh Kumar
On 23-12-20, 23:38, Dmitry Osipenko wrote:
> Well, there is no "same structure", the opp_table->set_opp_data is NULL
> there.

Right, I saw that yesterday. What I meant was that we need to start allocating
the structure for this case now.

-- 
viresh
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v2 19/48] opp: Fix adding OPP entries in a wrong order if rate is unavailable

2020-12-23 Thread Viresh Kumar
On 23-12-20, 23:36, Dmitry Osipenko wrote:
> 23.12.2020 07:34, Viresh Kumar пишет:
> > On 22-12-20, 22:19, Dmitry Osipenko wrote:
> >> 22.12.2020 12:12, Viresh Kumar пишет:
> >>> rate will be 0 for both the OPPs here if rate_not_available is true and 
> >>> so this
> >>> change shouldn't be required.
> >>
> >> The rate_not_available is negated in the condition. This change is
> >> required because both rates are 0 and then we should proceed to the
> >> levels comparison.
> > 
> > Won't that happen without this patch ?
> 
> No

This is how the code looks like currently:

int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2)
{
if (opp1->rate != opp2->rate)
return opp1->rate < opp2->rate ? -1 : 1;
if (opp1->bandwidth && opp2->bandwidth &&
opp1->bandwidth[0].peak != opp2->bandwidth[0].peak)
return opp1->bandwidth[0].peak < opp2->bandwidth[0].peak ? -1 : 
1;
if (opp1->level != opp2->level)
return opp1->level < opp2->level ? -1 : 1;
return 0;
}

Lets consider the case you are focussing on, where rate is 0 for both the OPPs,
bandwidth isn't there and we want to run the level comparison here.

Since both the rates are 0, (opp1->rate != opp2->rate) will fail and so we will
move to bandwidth check which will fail too. And so we will get to the level
comparison.

What am I missing here ? I am sure there is something for sure as you won't have
missed this..

-- 
viresh
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v2 11/48] opp: Add dev_pm_opp_find_level_ceil()

2020-12-23 Thread Viresh Kumar
On 23-12-20, 23:37, Dmitry Osipenko wrote:
> 23.12.2020 07:19, Viresh Kumar пишет:
> > On 22-12-20, 22:15, Dmitry Osipenko wrote:
> >> 22.12.2020 09:42, Viresh Kumar пишет:
> >>> On 17-12-20, 21:06, Dmitry Osipenko wrote:
>  Add a ceil version of the dev_pm_opp_find_level(). It's handy to have if
>  levels don't start from 0 in OPP table and zero usually means a minimal
>  level.
> 
>  Signed-off-by: Dmitry Osipenko 
> >>>
> >>> Why doesn't the exact version work for you here ?
> >>>
> >>
> >> The exact version won't find OPP for level=0 if levels don't start with
> >> 0, where 0 means that minimal level is desired.
> > 
> > Right, but why do you need to send 0 for your platform ?
> > 
> 
> To put power domain into the lowest performance state when device is idling.

I see. So you really want to set it to the lowest state or just take the vote
out ? Which may end up powering off the domain in the worst case ?

-- 
viresh
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH v2 28/48] soc/tegra: Introduce core power domain driver

2020-12-23 Thread Viresh Kumar
On 23-12-20, 23:37, Dmitry Osipenko wrote:
> 23.12.2020 08:57, Viresh Kumar пишет:
> > What's wrong with getting the regulator in the driver as well ? Apart from 
> > the
> > OPP core ?
> 
> The voltage syncing should be done for each consumer regulator
> individually [1].
> 
> Secondly, regulator core doesn't work well today if the same regulator
> is requested more than one time for the same device.

Hmm...

> >> will return the OPP table regulator in order to allow driver to use the
> >> regulator directly. But I'm not sure whether this is a much better
> >> option than the opp_sync_regulators() and opp_set_voltage() APIs.
> > 
> > set_voltage() is still fine as there is some data that the OPP core has, but
> > sync_regulator() has nothing to do with OPP core.
> > 
> > And this may lead to more wrapper helpers in the OPP core, which I am 
> > afraid of.
> > And so even if it is not the best, I would like the OPP core to provide the 
> > data
> > and not get into this. Ofcourse there is an exception to this, opp_set_rate.
> > 
> 
> The regulator_sync_voltage() should be invoked only if voltage was
> changed previously [1].
> 
> The OPP core already has the info about whether voltage was changed and
> it provides the necessary locking for both set_voltage() and
> sync_regulator(). Perhaps I'll need to duplicate that functionality in
> the PD driver, instead of making it all generic and re-usable by other
> drivers.
> 
> [1]
> https://elixir.bootlin.com/linux/v5.10.2/source/drivers/regulator/core.c#L4107

Lets do it in the OPP core and see where we go.

-- 
viresh
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel


Re: [PATCH] staging: ralink-gdma: Fixed blank line coding style issue

2020-12-23 Thread Greg KH
On Wed, Dec 23, 2020 at 09:22:30PM +0100, Ayoub Soussi wrote:
> Fixed coding style issue.
> 
> Signed-off-by: Ayoub Soussi 
> ---
>  drivers/staging/ralink-gdma/ralink-gdma.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/staging/ralink-gdma/ralink-gdma.c 
> b/drivers/staging/ralink-gdma/ralink-gdma.c
> index 655df317d0ee..a6181a167814 100644
> --- a/drivers/staging/ralink-gdma/ralink-gdma.c
> +++ b/drivers/staging/ralink-gdma/ralink-gdma.c
> @@ -122,6 +122,7 @@ struct gdma_dma_dev {
>   struct gdma_data *data;
>   void __iomem *base;
>   struct tasklet_struct task;
> +
>   volatile unsigned long chan_issued;
>   atomic_t cnt;

With your knowledge of C, does this change look correct?

thanks,

greg k-h
___
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel