mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-09 06:41:06 -04:00
staging: wfx: add I/O API
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> Link: https://lore.kernel.org/r/20190919142527.31797-4-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
0096214a59
commit
a794e8b6fa
@@ -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
|
||||
|
||||
327
drivers/staging/wfx/hwio.c
Normal file
327
drivers/staging/wfx/hwio.c
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user