lkml.org 
[lkml]   [2019]   [Sep]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 03/20] staging: wfx: add I/O API
    Date
    From: Jérôme Pouiller <jerome.pouiller@silabs.com>

    hwio.c provides an abstraction to access different types of register of
    the chip.

    Note that only data register (aka FRAME_OUT) and control register are
    used normal communication. Other registers are only used during chip
    start up.

    Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
    ---
    drivers/staging/wfx/Makefile | 1 +
    drivers/staging/wfx/hwio.c | 327 +++++++++++++++++++++++++++++++++++
    drivers/staging/wfx/hwio.h | 27 +++
    3 files changed, 355 insertions(+)
    create mode 100644 drivers/staging/wfx/hwio.c

    diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile
    index 74939a5a0a1c..e860845186cf 100644
    --- a/drivers/staging/wfx/Makefile
    +++ b/drivers/staging/wfx/Makefile
    @@ -1,6 +1,7 @@
    # SPDX-License-Identifier: GPL-2.0

    wfx-y := \
    + hwio.o \
    main.o
    wfx-$(CONFIG_SPI) += bus_spi.o
    wfx-$(subst m,y,$(CONFIG_MMC)) += bus_sdio.o
    diff --git a/drivers/staging/wfx/hwio.c b/drivers/staging/wfx/hwio.c
    new file mode 100644
    index 000000000000..fa626a49dd8a
    --- /dev/null
    +++ b/drivers/staging/wfx/hwio.c
    @@ -0,0 +1,327 @@
    +// SPDX-License-Identifier: GPL-2.0-only
    +/*
    + * Low-level I/O functions.
    + *
    + * Copyright (c) 2017-2019, 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"
    +
    +/*
    + * 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);
    + 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);
    + 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);
    + if (ret < 0)
    + goto err;
    + val_w = (val_r & ~mask) | val;
    + if (val_w != val_r) {
    + ret = write32(wdev, 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;
    + udelay(200);
    + }
    + 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);
    + 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);
    + 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 = cpu_to_le32(*tmp);
    + 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));
    + 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);
    + 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);
    + 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/staging/wfx/hwio.h b/drivers/staging/wfx/hwio.h
    index c490014c1df8..906524f71fd1 100644
    --- a/drivers/staging/wfx/hwio.h
    +++ b/drivers/staging/wfx/hwio.h
    @@ -8,6 +8,25 @@
    #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
    @@ -36,13 +55,21 @@
    #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.20.1
    \
     
     \ /
      Last update: 2019-09-19 12:56    [W:4.182 / U:0.184 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site