Commit a794e8b6 authored by Jérôme Pouiller's avatar Jérôme Pouiller Committed by Greg Kroah-Hartman
Browse files

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: default avatarJérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://lore.kernel.org/r/20190919142527.31797-4-Jerome.Pouiller@silabs.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0096214a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
# 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 −0
Original line number Diff line number Diff line
// 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);
}
+27 −0
Original line number Diff line number Diff line
@@ -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 */