diff mbox series

[v7,09/24] wfx: add hwio.c/hwio.h

Message ID 20210920161136.2398632-10-Jerome.Pouiller@silabs.com (mailing list archive)
State Changes Requested
Delegated to: Kalle Valo
Headers show
Series wfx: get out from the staging area | expand

Commit Message

Jérôme Pouiller Sept. 20, 2021, 4:11 p.m. UTC
From: Jérôme Pouiller <jerome.pouiller@silabs.com>

Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
---
 drivers/net/wireless/silabs/wfx/hwio.c | 340 +++++++++++++++++++++++++
 drivers/net/wireless/silabs/wfx/hwio.h |  79 ++++++
 2 files changed, 419 insertions(+)
 create mode 100644 drivers/net/wireless/silabs/wfx/hwio.c
 create mode 100644 drivers/net/wireless/silabs/wfx/hwio.h

Comments

Kalle Valo Oct. 1, 2021, 9:52 a.m. UTC | #1
Jerome Pouiller <Jerome.Pouiller@silabs.com> writes:

> From: Jérôme Pouiller <jerome.pouiller@silabs.com>
>
> Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
> ---
>  drivers/net/wireless/silabs/wfx/hwio.c | 340 +++++++++++++++++++++++++
>  drivers/net/wireless/silabs/wfx/hwio.h |  79 ++++++

[...]

> +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);

A define for the magic value, please. I see this 0x2000 limit multiple
times.

> +	WARN_ON(reg != WFX_REG_AHB_DPORT && reg != WFX_REG_SRAM_DPORT);

I see quite a lot of WARN() and WARN_ON() in the driver. Do note that
WARN() and WARN_ON() are a bit dangerous to use in the data path as an
attacker, or even just a bug, might easily spam the kernel log which
might result to host reboots due to watchdog triggering or other
resource starvation. I recommend using some ratelimited versions of
printk() macros, for example dev_*() if they have ratelimits. Not a
blocker, but wanted to point out anyway.

> +int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t len)
> +{
> +	int ret;
> +
> +	WARN((long)buf & 3, "%s: unaligned buffer", __func__);

IS_ALIGNED()?

> +	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__);

IS_ALIGNED()?

> --- /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 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);

"wfx_" prefix missing from these functions.

> +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);

And these too.
Kalle Valo Oct. 1, 2021, 12:39 p.m. UTC | #2
Kalle Valo <kvalo@codeaurora.org> writes:

>> --- /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 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);
>
> "wfx_" prefix missing from these functions.

I actually saw quite a few functions without wfx_ prefix. My preference
is that all function names would have that prefix.
diff mbox series

Patch

diff --git a/drivers/net/wireless/silabs/wfx/hwio.c b/drivers/net/wireless/silabs/wfx/hwio.c
new file mode 100644
index 000000000000..393bcb1e2f4e
--- /dev/null
+++ b/drivers/net/wireless/silabs/wfx/hwio.c
@@ -0,0 +1,340 @@ 
+// 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"
+
+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..d34baae47017
--- /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 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