From: Jérôme Pouiller <jerome.pouil...@silabs.com>

Signed-off-by: Jérôme Pouiller <jerome.pouil...@silabs.com>
---
 drivers/net/wireless/silabs/wfx/hwio.c | 352 +++++++++++++++++++++++++
 drivers/net/wireless/silabs/wfx/hwio.h |  75 ++++++
 2 files changed, 427 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 000000000000..36fbc5b5d64c
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/hwio.c
@@ -0,0 +1,352 @@
+// 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 <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "hwio.h"
+#include "wfx.h"
+#include "bus.h"
+#include "traces.h"
+
+/*
+ * 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.
+ */
+
+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, reg, buf, len);
+}
+
+static int indirect_read_locked(struct wfx_dev *wdev, int reg, u32 addr,
+                               void *buf, size_t len)
+{
+       int ret;
+
+       wdev->hwbus_ops->lock(wdev->hwbus_priv);
+       ret = indirect_read(wdev, reg, addr, buf, len);
+       _trace_io_ind_read(reg, addr, buf, len);
+       wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+       return ret;
+}
+
+static int indirect_write_locked(struct wfx_dev *wdev, int reg, u32 addr,
+                                const void *buf, size_t len)
+{
+       int ret;
+
+       wdev->hwbus_ops->lock(wdev->hwbus_priv);
+       ret = indirect_write(wdev, reg, addr, buf, len);
+       _trace_io_ind_write(reg, addr, buf, len);
+       wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+       return ret;
+}
+
+static int indirect_read32_locked(struct wfx_dev *wdev, int reg,
+                                 u32 addr, u32 *val)
+{
+       int ret;
+       __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL);
+
+       if (!tmp)
+               return -ENOMEM;
+       wdev->hwbus_ops->lock(wdev->hwbus_priv);
+       ret = indirect_read(wdev, reg, addr, tmp, sizeof(u32));
+       *val = le32_to_cpu(*tmp);
+       _trace_io_ind_read32(reg, addr, *val);
+       wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+       kfree(tmp);
+       return ret;
+}
+
+static int indirect_write32_locked(struct wfx_dev *wdev, int reg,
+                                  u32 addr, u32 val)
+{
+       int ret;
+       __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL);
+
+       if (!tmp)
+               return -ENOMEM;
+       *tmp = cpu_to_le32(val);
+       wdev->hwbus_ops->lock(wdev->hwbus_priv);
+       ret = indirect_write(wdev, reg, addr, tmp, sizeof(u32));
+       _trace_io_ind_write32(reg, addr, val);
+       wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+       kfree(tmp);
+       return ret;
+}
+
+int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t len)
+{
+       int ret;
+
+       WARN((long)buf & 3, "%s: unaligned buffer", __func__);
+       wdev->hwbus_ops->lock(wdev->hwbus_priv);
+       ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv,
+                                           WFX_REG_IN_OUT_QUEUE, buf, len);
+       _trace_io_read(WFX_REG_IN_OUT_QUEUE, buf, len);
+       wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+       if (ret)
+               dev_err(wdev->dev, "%s: bus communication error: %d\n",
+                       __func__, ret);
+       return ret;
+}
+
+int wfx_data_write(struct wfx_dev *wdev, const void *buf, size_t len)
+{
+       int ret;
+
+       WARN((long)buf & 3, "%s: unaligned buffer", __func__);
+       wdev->hwbus_ops->lock(wdev->hwbus_priv);
+       ret = wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv,
+                                         WFX_REG_IN_OUT_QUEUE, buf, len);
+       _trace_io_write(WFX_REG_IN_OUT_QUEUE, buf, len);
+       wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+       if (ret)
+               dev_err(wdev->dev, "%s: bus communication error: %d\n",
+                       __func__, ret);
+       return ret;
+}
+
+int sram_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len)
+{
+       return indirect_read_locked(wdev, WFX_REG_SRAM_DPORT, addr, buf, len);
+}
+
+int ahb_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len)
+{
+       return indirect_read_locked(wdev, WFX_REG_AHB_DPORT, addr, buf, len);
+}
+
+int sram_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len)
+{
+       return indirect_write_locked(wdev, WFX_REG_SRAM_DPORT, addr, buf, len);
+}
+
+int ahb_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len)
+{
+       return indirect_write_locked(wdev, WFX_REG_AHB_DPORT, addr, buf, len);
+}
+
+int sram_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val)
+{
+       return indirect_read32_locked(wdev, WFX_REG_SRAM_DPORT, addr, val);
+}
+
+int ahb_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val)
+{
+       return indirect_read32_locked(wdev, WFX_REG_AHB_DPORT, addr, val);
+}
+
+int sram_reg_write(struct wfx_dev *wdev, u32 addr, u32 val)
+{
+       return indirect_write32_locked(wdev, WFX_REG_SRAM_DPORT, addr, val);
+}
+
+int ahb_reg_write(struct wfx_dev *wdev, u32 addr, u32 val)
+{
+       return indirect_write32_locked(wdev, WFX_REG_AHB_DPORT, addr, val);
+}
+
+int config_reg_read(struct wfx_dev *wdev, u32 *val)
+{
+       return read32_locked(wdev, WFX_REG_CONFIG, val);
+}
+
+int config_reg_write(struct wfx_dev *wdev, u32 val)
+{
+       return write32_locked(wdev, WFX_REG_CONFIG, val);
+}
+
+int config_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val)
+{
+       return write32_bits_locked(wdev, WFX_REG_CONFIG, mask, val);
+}
+
+int control_reg_read(struct wfx_dev *wdev, u32 *val)
+{
+       return read32_locked(wdev, WFX_REG_CONTROL, val);
+}
+
+int control_reg_write(struct wfx_dev *wdev, u32 val)
+{
+       return write32_locked(wdev, WFX_REG_CONTROL, val);
+}
+
+int control_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val)
+{
+       return write32_bits_locked(wdev, WFX_REG_CONTROL, mask, val);
+}
+
+int igpr_reg_read(struct wfx_dev *wdev, int index, u32 *val)
+{
+       int ret;
+
+       *val = ~0; // Never return undefined value
+       ret = write32_locked(wdev, WFX_REG_SET_GEN_R_W, IGPR_RW | index << 24);
+       if (ret)
+               return ret;
+       ret = read32_locked(wdev, WFX_REG_SET_GEN_R_W, val);
+       if (ret)
+               return ret;
+       *val &= IGPR_VALUE;
+       return ret;
+}
+
+int igpr_reg_write(struct wfx_dev *wdev, int index, u32 val)
+{
+       return write32_locked(wdev, WFX_REG_SET_GEN_R_W, index << 24 | val);
+}
diff --git a/drivers/net/wireless/silabs/wfx/hwio.h 
b/drivers/net/wireless/silabs/wfx/hwio.h
new file mode 100644
index 000000000000..0b8e4f7157df
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/hwio.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Low-level API.
+ *
+ * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#ifndef WFX_HWIO_H
+#define WFX_HWIO_H
+
+#include <linux/types.h>
+
+struct wfx_dev;
+
+int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t buf_len);
+int wfx_data_write(struct wfx_dev *wdev, const void *buf, size_t buf_len);
+
+int sram_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len);
+int sram_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t 
len);
+
+int ahb_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len);
+int ahb_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len);
+
+int sram_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val);
+int sram_reg_write(struct wfx_dev *wdev, u32 addr, u32 val);
+
+int ahb_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val);
+int ahb_reg_write(struct wfx_dev *wdev, u32 addr, u32 val);
+
+#define CFG_ERR_SPI_FRAME          0x00000001 // only with SPI
+#define CFG_ERR_SDIO_BUF_MISMATCH  0x00000001 // only with SDIO
+#define CFG_ERR_BUF_UNDERRUN       0x00000002
+#define CFG_ERR_DATA_IN_TOO_LARGE  0x00000004
+#define CFG_ERR_HOST_NO_OUT_QUEUE  0x00000008
+#define CFG_ERR_BUF_OVERRUN        0x00000010
+#define CFG_ERR_DATA_OUT_TOO_LARGE 0x00000020
+#define CFG_ERR_HOST_NO_IN_QUEUE   0x00000040
+#define CFG_ERR_HOST_CRC_MISS      0x00000080 // only with SDIO
+#define CFG_SPI_IGNORE_CS          0x00000080 // only with SPI
+#define CFG_BYTE_ORDER_MASK        0x00000300 // only writable with SPI
+#define     CFG_BYTE_ORDER_BADC    0x00000000
+#define     CFG_BYTE_ORDER_DCBA    0x00000100
+#define     CFG_BYTE_ORDER_ABCD    0x00000200 // SDIO always use this value
+#define CFG_DIRECT_ACCESS_MODE     0x00000400
+#define CFG_PREFETCH_AHB           0x00000800
+#define CFG_DISABLE_CPU_CLK        0x00001000
+#define CFG_PREFETCH_SRAM          0x00002000
+#define CFG_CPU_RESET              0x00004000
+#define CFG_SDIO_DISABLE_IRQ       0x00008000 // only with SDIO
+#define CFG_IRQ_ENABLE_DATA        0x00010000
+#define CFG_IRQ_ENABLE_WRDY        0x00020000
+#define CFG_CLK_RISE_EDGE          0x00040000
+#define CFG_SDIO_DISABLE_CRC_CHK   0x00080000 // only with SDIO
+#define CFG_RESERVED               0x00F00000
+#define CFG_DEVICE_ID_MAJOR        0x07000000
+#define CFG_DEVICE_ID_RESERVED     0x78000000
+#define CFG_DEVICE_ID_TYPE         0x80000000
+int config_reg_read(struct wfx_dev *wdev, u32 *val);
+int config_reg_write(struct wfx_dev *wdev, u32 val);
+int config_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val);
+
+#define CTRL_NEXT_LEN_MASK   0x00000FFF
+#define CTRL_WLAN_WAKEUP     0x00001000
+#define CTRL_WLAN_READY      0x00002000
+int control_reg_read(struct wfx_dev *wdev, u32 *val);
+int control_reg_write(struct wfx_dev *wdev, u32 val);
+int control_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val);
+
+#define IGPR_RW          0x80000000
+#define IGPR_INDEX       0x7F000000
+#define IGPR_VALUE       0x00FFFFFF
+int igpr_reg_read(struct wfx_dev *wdev, int index, u32 *val);
+int igpr_reg_write(struct wfx_dev *wdev, int index, u32 val);
+
+#endif /* WFX_HWIO_H */
-- 
2.28.0

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

Reply via email to