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 |  79 ++++++
 2 files changed, 431 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..c53b36d36687
--- /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 <linux/align.h>
+
+#include "hwio.h"
+#include "wfx.h"
+#include "bus.h"
+#include "traces.h"
+
+#define WFX_HIF_BUFFER_SIZE 0x2000
+
+static int wfx_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 wfx_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 wfx_read32_locked(struct wfx_dev *wdev, int reg, u32 *val)
+{
+       int ret;
+
+       wdev->hwbus_ops->lock(wdev->hwbus_priv);
+       ret = wfx_read32(wdev, reg, val);
+       _trace_io_read32(reg, *val);
+       wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+       return ret;
+}
+
+static int wfx_write32_locked(struct wfx_dev *wdev, int reg, u32 val)
+{
+       int ret;
+
+       wdev->hwbus_ops->lock(wdev->hwbus_priv);
+       ret = wfx_write32(wdev, reg, val);
+       _trace_io_write32(reg, val);
+       wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+       return ret;
+}
+
+static int wfx_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 = wfx_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 = wfx_write32(wdev, reg, val_w);
+               _trace_io_write32(reg, val_w);
+       }
+err:
+       wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+       return ret;
+}
+
+static int wfx_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 >= WFX_HIF_BUFFER_SIZE);
+       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 = wfx_write32(wdev, WFX_REG_BASE_ADDR, addr);
+       if (ret < 0)
+               goto err;
+
+       ret = wfx_read32(wdev, WFX_REG_CONFIG, &cfg);
+       if (ret < 0)
+               goto err;
+
+       ret = wfx_write32(wdev, WFX_REG_CONFIG, cfg | prefetch);
+       if (ret < 0)
+               goto err;
+
+       for (i = 0; i < 20; i++) {
+               ret = wfx_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 wfx_indirect_write(struct wfx_dev *wdev, int reg, u32 addr,
+                             const void *buf, size_t len)
+{
+       int ret;
+
+       WARN_ON(len >= WFX_HIF_BUFFER_SIZE);
+       WARN_ON(reg != WFX_REG_AHB_DPORT && reg != WFX_REG_SRAM_DPORT);
+       ret = wfx_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 wfx_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 = wfx_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 wfx_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 = wfx_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 wfx_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 = wfx_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 wfx_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 = wfx_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(!IS_ALIGNED((uintptr_t)buf, 4), "unaligned buffer");
+       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(!IS_ALIGNED((uintptr_t)buf, 4), "unaligned buffer");
+       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 wfx_sram_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len)
+{
+       return wfx_indirect_read_locked(wdev, WFX_REG_SRAM_DPORT,
+                                       addr, buf, len);
+}
+
+int wfx_ahb_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len)
+{
+       return wfx_indirect_read_locked(wdev, WFX_REG_AHB_DPORT,
+                                       addr, buf, len);
+}
+
+int wfx_sram_buf_write(struct wfx_dev *wdev, u32 addr,
+                      const void *buf, size_t len)
+{
+       return wfx_indirect_write_locked(wdev, WFX_REG_SRAM_DPORT,
+                                        addr, buf, len);
+}
+
+int wfx_ahb_buf_write(struct wfx_dev *wdev, u32 addr,
+                     const void *buf, size_t len)
+{
+       return wfx_indirect_write_locked(wdev, WFX_REG_AHB_DPORT,
+                                        addr, buf, len);
+}
+
+int wfx_sram_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val)
+{
+       return wfx_indirect_read32_locked(wdev, WFX_REG_SRAM_DPORT,
+                                         addr, val);
+}
+
+int wfx_ahb_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val)
+{
+       return wfx_indirect_read32_locked(wdev, WFX_REG_AHB_DPORT,
+                                         addr, val);
+}
+
+int wfx_sram_reg_write(struct wfx_dev *wdev, u32 addr, u32 val)
+{
+       return wfx_indirect_write32_locked(wdev, WFX_REG_SRAM_DPORT, addr, val);
+}
+
+int wfx_ahb_reg_write(struct wfx_dev *wdev, u32 addr, u32 val)
+{
+       return wfx_indirect_write32_locked(wdev, WFX_REG_AHB_DPORT, addr, val);
+}
+
+int wfx_config_reg_read(struct wfx_dev *wdev, u32 *val)
+{
+       return wfx_read32_locked(wdev, WFX_REG_CONFIG, val);
+}
+
+int wfx_config_reg_write(struct wfx_dev *wdev, u32 val)
+{
+       return wfx_write32_locked(wdev, WFX_REG_CONFIG, val);
+}
+
+int wfx_config_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val)
+{
+       return wfx_write32_bits_locked(wdev, WFX_REG_CONFIG, mask, val);
+}
+
+int wfx_control_reg_read(struct wfx_dev *wdev, u32 *val)
+{
+       return wfx_read32_locked(wdev, WFX_REG_CONTROL, val);
+}
+
+int wfx_control_reg_write(struct wfx_dev *wdev, u32 val)
+{
+       return wfx_write32_locked(wdev, WFX_REG_CONTROL, val);
+}
+
+int wfx_control_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val)
+{
+       return wfx_write32_bits_locked(wdev, WFX_REG_CONTROL, mask, val);
+}
+
+int wfx_igpr_reg_read(struct wfx_dev *wdev, int index, u32 *val)
+{
+       int ret;
+
+       *val = ~0; /* Never return undefined value */
+       ret = wfx_write32_locked(wdev, WFX_REG_SET_GEN_R_W, IGPR_RW | index << 
24);
+       if (ret)
+               return ret;
+       ret = wfx_read32_locked(wdev, WFX_REG_SET_GEN_R_W, val);
+       if (ret)
+               return ret;
+       *val &= IGPR_VALUE;
+       return ret;
+}
+
+int wfx_igpr_reg_write(struct wfx_dev *wdev, int index, u32 val)
+{
+       return wfx_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..8c8fe76871f8
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/hwio.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Low-level I/O functions.
+ *
+ * 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;
+
+/* Caution: in the functions below, 'buf' will used with a DMA. So, it must be
+ * kmalloc'd (do not use stack allocated buffers). In doubt, enable
+ * CONFIG_DEBUG_SG to detect badly located buffer.
+ */
+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 wfx_sram_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len);
+int wfx_sram_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t 
len);
+
+int wfx_ahb_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len);
+int wfx_ahb_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t 
len);
+
+int wfx_sram_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val);
+int wfx_sram_reg_write(struct wfx_dev *wdev, u32 addr, u32 val);
+
+int wfx_ahb_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val);
+int wfx_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 wfx_config_reg_read(struct wfx_dev *wdev, u32 *val);
+int wfx_config_reg_write(struct wfx_dev *wdev, u32 val);
+int wfx_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 wfx_control_reg_read(struct wfx_dev *wdev, u32 *val);
+int wfx_control_reg_write(struct wfx_dev *wdev, u32 val);
+int wfx_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 wfx_igpr_reg_read(struct wfx_dev *wdev, int index, u32 *val);
+int wfx_igpr_reg_write(struct wfx_dev *wdev, int index, u32 val);
+
+#endif
-- 
2.33.0

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

Reply via email to