linux/drivers/net/wireless/microchip/wilc1000/spi.c
Alexis Lothoré 208dea9107 wifi: wilc1000: unregister wiphy only after netdev registration
wiphy_unregister()/wiphy_free() has been recently decoupled from
wilc_netdev_cleanup() to fix a faulty error path in sdio/spi probe
functions. However this change introduced a new failure when simply
loading then unloading the driver:

  $ modprobe wilc1000-sdio; modprobe -r wilc1000-sdio
  WARNING: CPU: 0 PID: 115 at net/wireless/core.c:1145 wiphy_unregister+0x904/0xc40 [cfg80211]
  Modules linked in: wilc1000_sdio(-) wilc1000 cfg80211 bluetooth ecdh_generic ecc
  CPU: 0 UID: 0 PID: 115 Comm: modprobe Not tainted 6.13.0-rc6+ #45
  Hardware name: Atmel SAMA5
  Call trace:
   unwind_backtrace from show_stack+0x18/0x1c
   show_stack from dump_stack_lvl+0x44/0x70
   dump_stack_lvl from __warn+0x118/0x27c
   __warn from warn_slowpath_fmt+0xcc/0x140
   warn_slowpath_fmt from wiphy_unregister+0x904/0xc40 [cfg80211]
   wiphy_unregister [cfg80211] from wilc_sdio_remove+0xb0/0x15c [wilc1000_sdio]
   wilc_sdio_remove [wilc1000_sdio] from sdio_bus_remove+0x104/0x3f0
   sdio_bus_remove from device_release_driver_internal+0x424/0x5dc
   device_release_driver_internal from driver_detach+0x120/0x224
   driver_detach from bus_remove_driver+0x17c/0x314
   bus_remove_driver from sys_delete_module+0x310/0x46c
   sys_delete_module from ret_fast_syscall+0x0/0x1c
  Exception stack(0xd0acbfa8 to 0xd0acbff0)
  bfa0:                   0044b210 0044b210 0044b24c 00000800 00000000 00000000
  bfc0: 0044b210 0044b210 00000000 00000081 00000000 0044b210 00000000 00000000
  bfe0: 00448e24 b6af99c4 0043ea0d aea2e12c
  irq event stamp: 0
  hardirqs last  enabled at (0): [<00000000>] 0x0
  hardirqs last disabled at (0): [<c01588f0>] copy_process+0x1c4c/0x7bec
  softirqs last  enabled at (0): [<c0158944>] copy_process+0x1ca0/0x7bec
  softirqs last disabled at (0): [<00000000>] 0x0

The warning is triggered by the fact that there is still a
wireless_device linked to the wiphy we are unregistering, due to
wiphy_unregister() now being called after net device unregister (performed
in wilc_netdev_cleanup()). Fix this warning by moving wiphy_unregister()
after wilc_netdev_cleanup() is nominal paths (ie: driver removal).
wilc_netdev_cleanup() ordering is left untouched in error paths in probe
function because net device is not registered in those paths (so the
warning can not trigger), yet the wiphy can still be registered, and we
still some cleanup steps from wilc_netdev_cleanup().

Fixes: 1be94490b6 ("wifi: wilc1000: unregister wiphy only if it has been registered")
Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://patch.msgid.link/20250114-wilc1000_modprobe-v1-1-ad19d46f0c07@bootlin.com
2025-01-15 18:24:33 +02:00

1374 lines
32 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
* All rights reserved.
*/
#include <linux/clk.h>
#include <linux/spi/spi.h>
#include <linux/crc7.h>
#include <linux/crc-itu-t.h>
#include <linux/gpio/consumer.h>
#include "netdev.h"
#include "cfg80211.h"
#define SPI_MODALIAS "wilc1000_spi"
static bool enable_crc7; /* protect SPI commands with CRC7 */
module_param(enable_crc7, bool, 0644);
MODULE_PARM_DESC(enable_crc7,
"Enable CRC7 checksum to protect command transfers\n"
"\t\t\tagainst corruption during the SPI transfer.\n"
"\t\t\tCommand transfers are short and the CPU-cycle cost\n"
"\t\t\tof enabling this is small.");
static bool enable_crc16; /* protect SPI data with CRC16 */
module_param(enable_crc16, bool, 0644);
MODULE_PARM_DESC(enable_crc16,
"Enable CRC16 checksum to protect data transfers\n"
"\t\t\tagainst corruption during the SPI transfer.\n"
"\t\t\tData transfers can be large and the CPU-cycle cost\n"
"\t\t\tof enabling this may be substantial.");
/*
* For CMD_SINGLE_READ and CMD_INTERNAL_READ, WILC may insert one or
* more zero bytes between the command response and the DATA Start tag
* (0xf3). This behavior appears to be undocumented in "ATWILC1000
* USER GUIDE" (https://tinyurl.com/4hhshdts) but we have observed 1-4
* zero bytes when the SPI bus operates at 48MHz and none when it
* operates at 1MHz.
*/
#define WILC_SPI_RSP_HDR_EXTRA_DATA 8
struct wilc_spi {
bool isinit; /* true if wilc_spi_init was successful */
bool probing_crc; /* true if we're probing chip's CRC config */
bool crc7_enabled; /* true if crc7 is currently enabled */
bool crc16_enabled; /* true if crc16 is currently enabled */
struct wilc_gpios {
struct gpio_desc *enable; /* ENABLE GPIO or NULL */
struct gpio_desc *reset; /* RESET GPIO or NULL */
} gpios;
};
static const struct wilc_hif_func wilc_hif_spi;
static int wilc_spi_reset(struct wilc *wilc);
static int wilc_spi_configure_bus_protocol(struct wilc *wilc);
static int wilc_validate_chipid(struct wilc *wilc);
/********************************************
*
* Spi protocol Function
*
********************************************/
#define CMD_DMA_WRITE 0xc1
#define CMD_DMA_READ 0xc2
#define CMD_INTERNAL_WRITE 0xc3
#define CMD_INTERNAL_READ 0xc4
#define CMD_TERMINATE 0xc5
#define CMD_REPEAT 0xc6
#define CMD_DMA_EXT_WRITE 0xc7
#define CMD_DMA_EXT_READ 0xc8
#define CMD_SINGLE_WRITE 0xc9
#define CMD_SINGLE_READ 0xca
#define CMD_RESET 0xcf
#define SPI_RETRY_MAX_LIMIT 10
#define SPI_ENABLE_VMM_RETRY_LIMIT 2
/* SPI response fields (section 11.1.2 in ATWILC1000 User Guide): */
#define RSP_START_FIELD GENMASK(7, 4)
#define RSP_TYPE_FIELD GENMASK(3, 0)
/* SPI response values for the response fields: */
#define RSP_START_TAG 0xc
#define RSP_TYPE_FIRST_PACKET 0x1
#define RSP_TYPE_INNER_PACKET 0x2
#define RSP_TYPE_LAST_PACKET 0x3
#define RSP_STATE_NO_ERROR 0x00
#define PROTOCOL_REG_PKT_SZ_MASK GENMASK(6, 4)
#define PROTOCOL_REG_CRC16_MASK GENMASK(3, 3)
#define PROTOCOL_REG_CRC7_MASK GENMASK(2, 2)
/*
* The SPI data packet size may be any integer power of two in the
* range from 256 to 8192 bytes.
*/
#define DATA_PKT_LOG_SZ_MIN 8 /* 256 B */
#define DATA_PKT_LOG_SZ_MAX 13 /* 8 KiB */
/*
* Select the data packet size (log2 of number of bytes): Use the
* maximum data packet size. We only retransmit complete packets, so
* there is no benefit from using smaller data packets.
*/
#define DATA_PKT_LOG_SZ DATA_PKT_LOG_SZ_MAX
#define DATA_PKT_SZ (1 << DATA_PKT_LOG_SZ)
#define WILC_SPI_COMMAND_STAT_SUCCESS 0
#define WILC_GET_RESP_HDR_START(h) (((h) >> 4) & 0xf)
struct wilc_spi_cmd {
u8 cmd_type;
union {
struct {
u8 addr[3];
u8 crc[];
} __packed simple_cmd;
struct {
u8 addr[3];
u8 size[2];
u8 crc[];
} __packed dma_cmd;
struct {
u8 addr[3];
u8 size[3];
u8 crc[];
} __packed dma_cmd_ext;
struct {
u8 addr[2];
__be32 data;
u8 crc[];
} __packed internal_w_cmd;
struct {
u8 addr[3];
__be32 data;
u8 crc[];
} __packed w_cmd;
} u;
} __packed;
struct wilc_spi_read_rsp_data {
u8 header;
u8 data[4];
u8 crc[];
} __packed;
struct wilc_spi_rsp_data {
u8 rsp_cmd_type;
u8 status;
u8 data[];
} __packed;
struct wilc_spi_special_cmd_rsp {
u8 skip_byte;
u8 rsp_cmd_type;
u8 status;
} __packed;
static int wilc_parse_gpios(struct wilc *wilc)
{
struct spi_device *spi = to_spi_device(wilc->dev);
struct wilc_spi *spi_priv = wilc->bus_data;
struct wilc_gpios *gpios = &spi_priv->gpios;
/* get ENABLE pin and deassert it (if it is defined): */
gpios->enable = devm_gpiod_get_optional(&spi->dev,
"enable", GPIOD_OUT_LOW);
/* get RESET pin and assert it (if it is defined): */
if (gpios->enable) {
/* if enable pin exists, reset must exist as well */
gpios->reset = devm_gpiod_get(&spi->dev,
"reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpios->reset)) {
dev_err(&spi->dev, "missing reset gpio.\n");
return PTR_ERR(gpios->reset);
}
} else {
gpios->reset = devm_gpiod_get_optional(&spi->dev,
"reset", GPIOD_OUT_HIGH);
}
return 0;
}
static void wilc_wlan_power(struct wilc *wilc, bool on)
{
struct wilc_spi *spi_priv = wilc->bus_data;
struct wilc_gpios *gpios = &spi_priv->gpios;
if (on) {
/* assert ENABLE: */
gpiod_set_value(gpios->enable, 1);
mdelay(5);
/* deassert RESET: */
gpiod_set_value(gpios->reset, 0);
} else {
/* assert RESET: */
gpiod_set_value(gpios->reset, 1);
/* deassert ENABLE: */
gpiod_set_value(gpios->enable, 0);
}
}
static int wilc_bus_probe(struct spi_device *spi)
{
struct wilc_spi *spi_priv;
struct wilc_vif *vif;
struct wilc *wilc;
int ret;
spi_priv = kzalloc(sizeof(*spi_priv), GFP_KERNEL);
if (!spi_priv)
return -ENOMEM;
ret = wilc_cfg80211_init(&wilc, &spi->dev, WILC_HIF_SPI, &wilc_hif_spi);
if (ret)
goto free;
spi_set_drvdata(spi, wilc);
wilc->dev = &spi->dev;
wilc->bus_data = spi_priv;
wilc->dev_irq_num = spi->irq;
ret = wilc_parse_gpios(wilc);
if (ret < 0)
goto netdev_cleanup;
wilc->rtc_clk = devm_clk_get_optional_enabled(&spi->dev, "rtc");
if (IS_ERR(wilc->rtc_clk)) {
ret = PTR_ERR(wilc->rtc_clk);
goto netdev_cleanup;
}
dev_info(&spi->dev, "Selected CRC config: crc7=%s, crc16=%s\n",
enable_crc7 ? "on" : "off", enable_crc16 ? "on" : "off");
/* we need power to configure the bus protocol and to read the chip id: */
wilc_wlan_power(wilc, true);
ret = wilc_spi_configure_bus_protocol(wilc);
if (ret)
goto power_down;
ret = wilc_get_chipid(wilc);
if (ret)
goto power_down;
ret = wilc_cfg80211_register(wilc);
if (ret)
goto power_down;
ret = wilc_load_mac_from_nv(wilc);
if (ret) {
pr_err("Can not retrieve MAC address from chip\n");
goto unregister_wiphy;
}
wilc_wlan_power(wilc, false);
vif = wilc_netdev_ifc_init(wilc, "wlan%d", WILC_STATION_MODE,
NL80211_IFTYPE_STATION, false);
if (IS_ERR(vif)) {
ret = PTR_ERR(vif);
goto unregister_wiphy;
}
return 0;
unregister_wiphy:
wiphy_unregister(wilc->wiphy);
power_down:
wilc_wlan_power(wilc, false);
netdev_cleanup:
wilc_netdev_cleanup(wilc);
wiphy_free(wilc->wiphy);
free:
kfree(spi_priv);
return ret;
}
static void wilc_bus_remove(struct spi_device *spi)
{
struct wilc *wilc = spi_get_drvdata(spi);
struct wilc_spi *spi_priv = wilc->bus_data;
wilc_netdev_cleanup(wilc);
wiphy_unregister(wilc->wiphy);
wiphy_free(wilc->wiphy);
kfree(spi_priv);
}
static const struct of_device_id wilc_of_match[] = {
{ .compatible = "microchip,wilc1000", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, wilc_of_match);
static const struct spi_device_id wilc_spi_id[] = {
{ "wilc1000", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, wilc_spi_id);
static struct spi_driver wilc_spi_driver = {
.driver = {
.name = SPI_MODALIAS,
.of_match_table = wilc_of_match,
},
.id_table = wilc_spi_id,
.probe = wilc_bus_probe,
.remove = wilc_bus_remove,
};
module_spi_driver(wilc_spi_driver);
MODULE_DESCRIPTION("Atmel WILC1000 SPI wireless driver");
MODULE_LICENSE("GPL");
static int wilc_spi_tx(struct wilc *wilc, u8 *b, u32 len)
{
struct spi_device *spi = to_spi_device(wilc->dev);
int ret;
struct spi_message msg;
if (len > 0 && b) {
struct spi_transfer tr = {
.tx_buf = b,
.len = len,
.delay = {
.value = 0,
.unit = SPI_DELAY_UNIT_USECS
},
};
char *r_buffer = kzalloc(len, GFP_KERNEL);
if (!r_buffer)
return -ENOMEM;
tr.rx_buf = r_buffer;
dev_dbg(&spi->dev, "Request writing %d bytes\n", len);
memset(&msg, 0, sizeof(msg));
spi_message_init(&msg);
spi_message_add_tail(&tr, &msg);
ret = spi_sync(spi, &msg);
if (ret < 0)
dev_err(&spi->dev, "SPI transaction failed\n");
kfree(r_buffer);
} else {
dev_err(&spi->dev,
"can't write data with the following length: %d\n",
len);
ret = -EINVAL;
}
return ret;
}
static int wilc_spi_rx(struct wilc *wilc, u8 *rb, u32 rlen)
{
struct spi_device *spi = to_spi_device(wilc->dev);
int ret;
if (rlen > 0) {
struct spi_message msg;
struct spi_transfer tr = {
.rx_buf = rb,
.len = rlen,
.delay = {
.value = 0,
.unit = SPI_DELAY_UNIT_USECS
},
};
char *t_buffer = kzalloc(rlen, GFP_KERNEL);
if (!t_buffer)
return -ENOMEM;
tr.tx_buf = t_buffer;
memset(&msg, 0, sizeof(msg));
spi_message_init(&msg);
spi_message_add_tail(&tr, &msg);
ret = spi_sync(spi, &msg);
if (ret < 0)
dev_err(&spi->dev, "SPI transaction failed\n");
kfree(t_buffer);
} else {
dev_err(&spi->dev,
"can't read data with the following length: %u\n",
rlen);
ret = -EINVAL;
}
return ret;
}
static int wilc_spi_tx_rx(struct wilc *wilc, u8 *wb, u8 *rb, u32 rlen)
{
struct spi_device *spi = to_spi_device(wilc->dev);
int ret;
if (rlen > 0) {
struct spi_message msg;
struct spi_transfer tr = {
.rx_buf = rb,
.tx_buf = wb,
.len = rlen,
.bits_per_word = 8,
.delay = {
.value = 0,
.unit = SPI_DELAY_UNIT_USECS
},
};
memset(&msg, 0, sizeof(msg));
spi_message_init(&msg);
spi_message_add_tail(&tr, &msg);
ret = spi_sync(spi, &msg);
if (ret < 0)
dev_err(&spi->dev, "SPI transaction failed\n");
} else {
dev_err(&spi->dev,
"can't read data with the following length: %u\n",
rlen);
ret = -EINVAL;
}
return ret;
}
static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz)
{
struct spi_device *spi = to_spi_device(wilc->dev);
struct wilc_spi *spi_priv = wilc->bus_data;
int ix, nbytes;
int result = 0;
u8 cmd, order, crc[2];
u16 crc_calc;
/*
* Data
*/
ix = 0;
do {
if (sz <= DATA_PKT_SZ) {
nbytes = sz;
order = 0x3;
} else {
nbytes = DATA_PKT_SZ;
if (ix == 0)
order = 0x1;
else
order = 0x02;
}
/*
* Write command
*/
cmd = 0xf0;
cmd |= order;
if (wilc_spi_tx(wilc, &cmd, 1)) {
dev_err(&spi->dev,
"Failed data block cmd write, bus error...\n");
result = -EINVAL;
break;
}
/*
* Write data
*/
if (wilc_spi_tx(wilc, &b[ix], nbytes)) {
dev_err(&spi->dev,
"Failed data block write, bus error...\n");
result = -EINVAL;
break;
}
/*
* Write CRC
*/
if (spi_priv->crc16_enabled) {
crc_calc = crc_itu_t(0xffff, &b[ix], nbytes);
crc[0] = crc_calc >> 8;
crc[1] = crc_calc;
if (wilc_spi_tx(wilc, crc, 2)) {
dev_err(&spi->dev, "Failed data block crc write, bus error...\n");
result = -EINVAL;
break;
}
}
/*
* No need to wait for response
*/
ix += nbytes;
sz -= nbytes;
} while (sz);
return result;
}
/********************************************
*
* Spi Internal Read/Write Function
*
********************************************/
static u8 wilc_get_crc7(u8 *buffer, u32 len)
{
return crc7_be(0xfe, buffer, len) | 0x01;
}
static int wilc_spi_single_read(struct wilc *wilc, u8 cmd, u32 adr, void *b,
u8 clockless)
{
struct spi_device *spi = to_spi_device(wilc->dev);
struct wilc_spi *spi_priv = wilc->bus_data;
u8 wb[32], rb[32];
int cmd_len, resp_len, i;
u16 crc_calc, crc_recv;
struct wilc_spi_cmd *c;
struct wilc_spi_rsp_data *r;
struct wilc_spi_read_rsp_data *r_data;
memset(wb, 0x0, sizeof(wb));
memset(rb, 0x0, sizeof(rb));
c = (struct wilc_spi_cmd *)wb;
c->cmd_type = cmd;
if (cmd == CMD_SINGLE_READ) {
c->u.simple_cmd.addr[0] = adr >> 16;
c->u.simple_cmd.addr[1] = adr >> 8;
c->u.simple_cmd.addr[2] = adr;
} else if (cmd == CMD_INTERNAL_READ) {
c->u.simple_cmd.addr[0] = adr >> 8;
if (clockless == 1)
c->u.simple_cmd.addr[0] |= BIT(7);
c->u.simple_cmd.addr[1] = adr;
c->u.simple_cmd.addr[2] = 0x0;
} else {
dev_err(&spi->dev, "cmd [%x] not supported\n", cmd);
return -EINVAL;
}
cmd_len = offsetof(struct wilc_spi_cmd, u.simple_cmd.crc);
resp_len = sizeof(*r) + sizeof(*r_data) + WILC_SPI_RSP_HDR_EXTRA_DATA;
if (spi_priv->crc7_enabled) {
c->u.simple_cmd.crc[0] = wilc_get_crc7(wb, cmd_len);
cmd_len += 1;
resp_len += 2;
}
if (cmd_len + resp_len > ARRAY_SIZE(wb)) {
dev_err(&spi->dev,
"spi buffer size too small (%d) (%d) (%zu)\n",
cmd_len, resp_len, ARRAY_SIZE(wb));
return -EINVAL;
}
if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) {
dev_err(&spi->dev, "Failed cmd write, bus error...\n");
return -EINVAL;
}
r = (struct wilc_spi_rsp_data *)&rb[cmd_len];
if (r->rsp_cmd_type != cmd && !clockless) {
if (!spi_priv->probing_crc)
dev_err(&spi->dev,
"Failed cmd, cmd (%02x), resp (%02x)\n",
cmd, r->rsp_cmd_type);
return -EINVAL;
}
if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS && !clockless) {
dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
r->status);
return -EINVAL;
}
for (i = 0; i < WILC_SPI_RSP_HDR_EXTRA_DATA; ++i)
if (WILC_GET_RESP_HDR_START(r->data[i]) == 0xf)
break;
if (i >= WILC_SPI_RSP_HDR_EXTRA_DATA) {
dev_err(&spi->dev, "Error, data start missing\n");
return -EINVAL;
}
r_data = (struct wilc_spi_read_rsp_data *)&r->data[i];
if (b)
memcpy(b, r_data->data, 4);
if (!clockless && spi_priv->crc16_enabled) {
crc_recv = (r_data->crc[0] << 8) | r_data->crc[1];
crc_calc = crc_itu_t(0xffff, r_data->data, 4);
if (crc_recv != crc_calc) {
dev_err(&spi->dev, "%s: bad CRC 0x%04x "
"(calculated 0x%04x)\n", __func__,
crc_recv, crc_calc);
return -EINVAL;
}
}
return 0;
}
static int wilc_spi_write_cmd(struct wilc *wilc, u8 cmd, u32 adr, u32 data,
u8 clockless)
{
struct spi_device *spi = to_spi_device(wilc->dev);
struct wilc_spi *spi_priv = wilc->bus_data;
u8 wb[32], rb[32];
int cmd_len, resp_len;
struct wilc_spi_cmd *c;
struct wilc_spi_rsp_data *r;
memset(wb, 0x0, sizeof(wb));
memset(rb, 0x0, sizeof(rb));
c = (struct wilc_spi_cmd *)wb;
c->cmd_type = cmd;
if (cmd == CMD_INTERNAL_WRITE) {
c->u.internal_w_cmd.addr[0] = adr >> 8;
if (clockless == 1)
c->u.internal_w_cmd.addr[0] |= BIT(7);
c->u.internal_w_cmd.addr[1] = adr;
c->u.internal_w_cmd.data = cpu_to_be32(data);
cmd_len = offsetof(struct wilc_spi_cmd, u.internal_w_cmd.crc);
if (spi_priv->crc7_enabled)
c->u.internal_w_cmd.crc[0] = wilc_get_crc7(wb, cmd_len);
} else if (cmd == CMD_SINGLE_WRITE) {
c->u.w_cmd.addr[0] = adr >> 16;
c->u.w_cmd.addr[1] = adr >> 8;
c->u.w_cmd.addr[2] = adr;
c->u.w_cmd.data = cpu_to_be32(data);
cmd_len = offsetof(struct wilc_spi_cmd, u.w_cmd.crc);
if (spi_priv->crc7_enabled)
c->u.w_cmd.crc[0] = wilc_get_crc7(wb, cmd_len);
} else {
dev_err(&spi->dev, "write cmd [%x] not supported\n", cmd);
return -EINVAL;
}
if (spi_priv->crc7_enabled)
cmd_len += 1;
resp_len = sizeof(*r);
if (cmd_len + resp_len > ARRAY_SIZE(wb)) {
dev_err(&spi->dev,
"spi buffer size too small (%d) (%d) (%zu)\n",
cmd_len, resp_len, ARRAY_SIZE(wb));
return -EINVAL;
}
if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) {
dev_err(&spi->dev, "Failed cmd write, bus error...\n");
return -EINVAL;
}
r = (struct wilc_spi_rsp_data *)&rb[cmd_len];
/*
* Clockless registers operations might return unexptected responses,
* even if successful.
*/
if (r->rsp_cmd_type != cmd && !clockless) {
dev_err(&spi->dev,
"Failed cmd response, cmd (%02x), resp (%02x)\n",
cmd, r->rsp_cmd_type);
return -EINVAL;
}
if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS && !clockless) {
dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
r->status);
return -EINVAL;
}
return 0;
}
static int wilc_spi_dma_rw(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz)
{
struct spi_device *spi = to_spi_device(wilc->dev);
struct wilc_spi *spi_priv = wilc->bus_data;
u16 crc_recv, crc_calc;
u8 wb[32], rb[32];
int cmd_len, resp_len;
int retry, ix = 0;
u8 crc[2];
struct wilc_spi_cmd *c;
struct wilc_spi_rsp_data *r;
memset(wb, 0x0, sizeof(wb));
memset(rb, 0x0, sizeof(rb));
c = (struct wilc_spi_cmd *)wb;
c->cmd_type = cmd;
if (cmd == CMD_DMA_WRITE || cmd == CMD_DMA_READ) {
c->u.dma_cmd.addr[0] = adr >> 16;
c->u.dma_cmd.addr[1] = adr >> 8;
c->u.dma_cmd.addr[2] = adr;
c->u.dma_cmd.size[0] = sz >> 8;
c->u.dma_cmd.size[1] = sz;
cmd_len = offsetof(struct wilc_spi_cmd, u.dma_cmd.crc);
if (spi_priv->crc7_enabled)
c->u.dma_cmd.crc[0] = wilc_get_crc7(wb, cmd_len);
} else if (cmd == CMD_DMA_EXT_WRITE || cmd == CMD_DMA_EXT_READ) {
c->u.dma_cmd_ext.addr[0] = adr >> 16;
c->u.dma_cmd_ext.addr[1] = adr >> 8;
c->u.dma_cmd_ext.addr[2] = adr;
c->u.dma_cmd_ext.size[0] = sz >> 16;
c->u.dma_cmd_ext.size[1] = sz >> 8;
c->u.dma_cmd_ext.size[2] = sz;
cmd_len = offsetof(struct wilc_spi_cmd, u.dma_cmd_ext.crc);
if (spi_priv->crc7_enabled)
c->u.dma_cmd_ext.crc[0] = wilc_get_crc7(wb, cmd_len);
} else {
dev_err(&spi->dev, "dma read write cmd [%x] not supported\n",
cmd);
return -EINVAL;
}
if (spi_priv->crc7_enabled)
cmd_len += 1;
resp_len = sizeof(*r);
if (cmd_len + resp_len > ARRAY_SIZE(wb)) {
dev_err(&spi->dev, "spi buffer size too small (%d)(%d) (%zu)\n",
cmd_len, resp_len, ARRAY_SIZE(wb));
return -EINVAL;
}
if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) {
dev_err(&spi->dev, "Failed cmd write, bus error...\n");
return -EINVAL;
}
r = (struct wilc_spi_rsp_data *)&rb[cmd_len];
if (r->rsp_cmd_type != cmd) {
dev_err(&spi->dev,
"Failed cmd response, cmd (%02x), resp (%02x)\n",
cmd, r->rsp_cmd_type);
return -EINVAL;
}
if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS) {
dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
r->status);
return -EINVAL;
}
if (cmd == CMD_DMA_WRITE || cmd == CMD_DMA_EXT_WRITE)
return 0;
while (sz > 0) {
int nbytes;
u8 rsp;
nbytes = min_t(u32, sz, DATA_PKT_SZ);
/*
* Data Response header
*/
retry = 100;
do {
if (wilc_spi_rx(wilc, &rsp, 1)) {
dev_err(&spi->dev,
"Failed resp read, bus err\n");
return -EINVAL;
}
if (WILC_GET_RESP_HDR_START(rsp) == 0xf)
break;
} while (retry--);
/*
* Read bytes
*/
if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
dev_err(&spi->dev,
"Failed block read, bus err\n");
return -EINVAL;
}
/*
* Read CRC
*/
if (spi_priv->crc16_enabled) {
if (wilc_spi_rx(wilc, crc, 2)) {
dev_err(&spi->dev,
"Failed block CRC read, bus err\n");
return -EINVAL;
}
crc_recv = (crc[0] << 8) | crc[1];
crc_calc = crc_itu_t(0xffff, &b[ix], nbytes);
if (crc_recv != crc_calc) {
dev_err(&spi->dev, "%s: bad CRC 0x%04x "
"(calculated 0x%04x)\n", __func__,
crc_recv, crc_calc);
return -EINVAL;
}
}
ix += nbytes;
sz -= nbytes;
}
return 0;
}
static int wilc_spi_special_cmd(struct wilc *wilc, u8 cmd)
{
struct spi_device *spi = to_spi_device(wilc->dev);
struct wilc_spi *spi_priv = wilc->bus_data;
u8 wb[32], rb[32];
int cmd_len, resp_len = 0;
struct wilc_spi_cmd *c;
struct wilc_spi_special_cmd_rsp *r;
if (cmd != CMD_TERMINATE && cmd != CMD_REPEAT && cmd != CMD_RESET)
return -EINVAL;
memset(wb, 0x0, sizeof(wb));
memset(rb, 0x0, sizeof(rb));
c = (struct wilc_spi_cmd *)wb;
c->cmd_type = cmd;
if (cmd == CMD_RESET)
memset(c->u.simple_cmd.addr, 0xFF, 3);
cmd_len = offsetof(struct wilc_spi_cmd, u.simple_cmd.crc);
resp_len = sizeof(*r);
if (spi_priv->crc7_enabled) {
c->u.simple_cmd.crc[0] = wilc_get_crc7(wb, cmd_len);
cmd_len += 1;
}
if (cmd_len + resp_len > ARRAY_SIZE(wb)) {
dev_err(&spi->dev, "spi buffer size too small (%d) (%d) (%zu)\n",
cmd_len, resp_len, ARRAY_SIZE(wb));
return -EINVAL;
}
if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) {
dev_err(&spi->dev, "Failed cmd write, bus error...\n");
return -EINVAL;
}
r = (struct wilc_spi_special_cmd_rsp *)&rb[cmd_len];
if (r->rsp_cmd_type != cmd) {
if (!spi_priv->probing_crc)
dev_err(&spi->dev,
"Failed cmd response, cmd (%02x), resp (%02x)\n",
cmd, r->rsp_cmd_type);
return -EINVAL;
}
if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS) {
dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
r->status);
return -EINVAL;
}
return 0;
}
static void wilc_spi_reset_cmd_sequence(struct wilc *wl, u8 attempt, u32 addr)
{
struct spi_device *spi = to_spi_device(wl->dev);
struct wilc_spi *spi_priv = wl->bus_data;
if (!spi_priv->probing_crc)
dev_err(&spi->dev, "Reset and retry %d %x\n", attempt, addr);
usleep_range(1000, 1100);
wilc_spi_reset(wl);
usleep_range(1000, 1100);
}
static int wilc_spi_read_reg(struct wilc *wilc, u32 addr, u32 *data)
{
struct spi_device *spi = to_spi_device(wilc->dev);
int result;
u8 cmd = CMD_SINGLE_READ;
u8 clockless = 0;
u8 i;
if (addr <= WILC_SPI_CLOCKLESS_ADDR_LIMIT) {
/* Clockless register */
cmd = CMD_INTERNAL_READ;
clockless = 1;
}
for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
result = wilc_spi_single_read(wilc, cmd, addr, data, clockless);
if (!result) {
le32_to_cpus(data);
return 0;
}
/* retry is not applicable for clockless registers */
if (clockless)
break;
dev_err(&spi->dev, "Failed cmd, read reg (%08x)...\n", addr);
wilc_spi_reset_cmd_sequence(wilc, i, addr);
}
return result;
}
static int wilc_spi_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
{
struct spi_device *spi = to_spi_device(wilc->dev);
int result;
u8 i;
if (size <= 4)
return -EINVAL;
for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
result = wilc_spi_dma_rw(wilc, CMD_DMA_EXT_READ, addr,
buf, size);
if (!result)
return 0;
dev_err(&spi->dev, "Failed cmd, read block (%08x)...\n", addr);
wilc_spi_reset_cmd_sequence(wilc, i, addr);
}
return result;
}
static int spi_internal_write(struct wilc *wilc, u32 adr, u32 dat)
{
struct spi_device *spi = to_spi_device(wilc->dev);
int result;
u8 i;
for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
result = wilc_spi_write_cmd(wilc, CMD_INTERNAL_WRITE, adr,
dat, 0);
if (!result)
return 0;
dev_err(&spi->dev, "Failed internal write cmd...\n");
wilc_spi_reset_cmd_sequence(wilc, i, adr);
}
return result;
}
static int spi_internal_read(struct wilc *wilc, u32 adr, u32 *data)
{
struct spi_device *spi = to_spi_device(wilc->dev);
struct wilc_spi *spi_priv = wilc->bus_data;
int result;
u8 i;
for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
result = wilc_spi_single_read(wilc, CMD_INTERNAL_READ, adr,
data, 0);
if (!result) {
le32_to_cpus(data);
return 0;
}
if (!spi_priv->probing_crc)
dev_err(&spi->dev, "Failed internal read cmd...\n");
wilc_spi_reset_cmd_sequence(wilc, i, adr);
}
return result;
}
/********************************************
*
* Spi interfaces
*
********************************************/
static int wilc_spi_write_reg(struct wilc *wilc, u32 addr, u32 data)
{
struct spi_device *spi = to_spi_device(wilc->dev);
int result;
u8 cmd = CMD_SINGLE_WRITE;
u8 clockless = 0;
u8 i;
if (addr <= WILC_SPI_CLOCKLESS_ADDR_LIMIT) {
/* Clockless register */
cmd = CMD_INTERNAL_WRITE;
clockless = 1;
}
for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
result = wilc_spi_write_cmd(wilc, cmd, addr, data, clockless);
if (!result)
return 0;
dev_err(&spi->dev, "Failed cmd, write reg (%08x)...\n", addr);
if (clockless)
break;
wilc_spi_reset_cmd_sequence(wilc, i, addr);
}
return result;
}
static int spi_data_rsp(struct wilc *wilc, u8 cmd)
{
struct spi_device *spi = to_spi_device(wilc->dev);
int result, i;
u8 rsp[4];
/*
* The response to data packets is two bytes long. For
* efficiency's sake, wilc_spi_write() wisely ignores the
* responses for all packets but the final one. The downside
* of that optimization is that when the final data packet is
* short, we may receive (part of) the response to the
* second-to-last packet before the one for the final packet.
* To handle this, we always read 4 bytes and then search for
* the last byte that contains the "Response Start" code (0xc
* in the top 4 bits). We then know that this byte is the
* first response byte of the final data packet.
*/
result = wilc_spi_rx(wilc, rsp, sizeof(rsp));
if (result) {
dev_err(&spi->dev, "Failed bus error...\n");
return result;
}
for (i = sizeof(rsp) - 2; i >= 0; --i)
if (FIELD_GET(RSP_START_FIELD, rsp[i]) == RSP_START_TAG)
break;
if (i < 0) {
dev_err(&spi->dev,
"Data packet response missing (%02x %02x %02x %02x)\n",
rsp[0], rsp[1], rsp[2], rsp[3]);
return -1;
}
/* rsp[i] is the last response start byte */
if (FIELD_GET(RSP_TYPE_FIELD, rsp[i]) != RSP_TYPE_LAST_PACKET
|| rsp[i + 1] != RSP_STATE_NO_ERROR) {
dev_err(&spi->dev, "Data response error (%02x %02x)\n",
rsp[i], rsp[i + 1]);
return -1;
}
return 0;
}
static int wilc_spi_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
{
struct spi_device *spi = to_spi_device(wilc->dev);
int result;
u8 i;
/*
* has to be greated than 4
*/
if (size <= 4)
return -EINVAL;
for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
result = wilc_spi_dma_rw(wilc, CMD_DMA_EXT_WRITE, addr,
NULL, size);
if (result) {
dev_err(&spi->dev,
"Failed cmd, write block (%08x)...\n", addr);
wilc_spi_reset_cmd_sequence(wilc, i, addr);
continue;
}
/*
* Data
*/
result = spi_data_write(wilc, buf, size);
if (result) {
dev_err(&spi->dev, "Failed block data write...\n");
wilc_spi_reset_cmd_sequence(wilc, i, addr);
continue;
}
/*
* Data response
*/
result = spi_data_rsp(wilc, CMD_DMA_EXT_WRITE);
if (result) {
dev_err(&spi->dev, "Failed block data rsp...\n");
wilc_spi_reset_cmd_sequence(wilc, i, addr);
continue;
}
break;
}
return result;
}
/********************************************
*
* Bus interfaces
*
********************************************/
static int wilc_spi_reset(struct wilc *wilc)
{
struct spi_device *spi = to_spi_device(wilc->dev);
struct wilc_spi *spi_priv = wilc->bus_data;
int result;
result = wilc_spi_special_cmd(wilc, CMD_RESET);
if (result && !spi_priv->probing_crc)
dev_err(&spi->dev, "Failed cmd reset\n");
return result;
}
static bool wilc_spi_is_init(struct wilc *wilc)
{
struct wilc_spi *spi_priv = wilc->bus_data;
return spi_priv->isinit;
}
static int wilc_spi_deinit(struct wilc *wilc)
{
struct wilc_spi *spi_priv = wilc->bus_data;
spi_priv->isinit = false;
wilc_wlan_power(wilc, false);
return 0;
}
static int wilc_spi_init(struct wilc *wilc, bool resume)
{
struct wilc_spi *spi_priv = wilc->bus_data;
int ret;
if (spi_priv->isinit) {
/* Confirm we can read chipid register without error: */
if (wilc_validate_chipid(wilc) == 0)
return 0;
}
wilc_wlan_power(wilc, true);
ret = wilc_spi_configure_bus_protocol(wilc);
if (ret) {
wilc_wlan_power(wilc, false);
return ret;
}
spi_priv->isinit = true;
return 0;
}
static int wilc_spi_configure_bus_protocol(struct wilc *wilc)
{
struct spi_device *spi = to_spi_device(wilc->dev);
struct wilc_spi *spi_priv = wilc->bus_data;
u32 reg;
int ret, i;
/*
* Infer the CRC settings that are currently in effect. This
* is necessary because we can't be sure that the chip has
* been RESET (e.g, after module unload and reload).
*/
spi_priv->probing_crc = true;
spi_priv->crc7_enabled = enable_crc7;
spi_priv->crc16_enabled = false; /* don't check CRC16 during probing */
for (i = 0; i < 2; ++i) {
ret = spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, &reg);
if (ret == 0)
break;
spi_priv->crc7_enabled = !enable_crc7;
}
if (ret) {
dev_err(&spi->dev, "Failed with CRC7 on and off.\n");
return ret;
}
/* set up the desired CRC configuration: */
reg &= ~(PROTOCOL_REG_CRC7_MASK | PROTOCOL_REG_CRC16_MASK);
if (enable_crc7)
reg |= PROTOCOL_REG_CRC7_MASK;
if (enable_crc16)
reg |= PROTOCOL_REG_CRC16_MASK;
/* set up the data packet size: */
BUILD_BUG_ON(DATA_PKT_LOG_SZ < DATA_PKT_LOG_SZ_MIN
|| DATA_PKT_LOG_SZ > DATA_PKT_LOG_SZ_MAX);
reg &= ~PROTOCOL_REG_PKT_SZ_MASK;
reg |= FIELD_PREP(PROTOCOL_REG_PKT_SZ_MASK,
DATA_PKT_LOG_SZ - DATA_PKT_LOG_SZ_MIN);
/* establish the new setup: */
ret = spi_internal_write(wilc, WILC_SPI_PROTOCOL_OFFSET, reg);
if (ret) {
dev_err(&spi->dev,
"[wilc spi %d]: Failed internal write reg\n",
__LINE__);
return ret;
}
/* update our state to match new protocol settings: */
spi_priv->crc7_enabled = enable_crc7;
spi_priv->crc16_enabled = enable_crc16;
/* re-read to make sure new settings are in effect: */
spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, &reg);
spi_priv->probing_crc = false;
return 0;
}
static int wilc_validate_chipid(struct wilc *wilc)
{
struct spi_device *spi = to_spi_device(wilc->dev);
u32 chipid;
int ret;
/*
* make sure can read chip id without protocol error
*/
ret = wilc_spi_read_reg(wilc, WILC_CHIPID, &chipid);
if (ret) {
dev_err(&spi->dev, "Fail cmd read chip id...\n");
return ret;
}
if (!is_wilc1000(chipid) && !is_wilc3000(chipid)) {
dev_err(&spi->dev, "Unknown chip id 0x%x\n", chipid);
return -ENODEV;
}
return 0;
}
static int wilc_spi_read_size(struct wilc *wilc, u32 *size)
{
int ret;
ret = spi_internal_read(wilc,
WILC_SPI_INT_STATUS - WILC_SPI_REG_BASE, size);
*size = FIELD_GET(IRQ_DMA_WD_CNT_MASK, *size);
return ret;
}
static int wilc_spi_read_int(struct wilc *wilc, u32 *int_status)
{
return spi_internal_read(wilc, WILC_SPI_INT_STATUS - WILC_SPI_REG_BASE,
int_status);
}
static int wilc_spi_clear_int_ext(struct wilc *wilc, u32 val)
{
int ret;
int retry = SPI_ENABLE_VMM_RETRY_LIMIT;
u32 check;
while (retry) {
ret = spi_internal_write(wilc,
WILC_SPI_INT_CLEAR - WILC_SPI_REG_BASE,
val);
if (ret)
break;
ret = spi_internal_read(wilc,
WILC_SPI_INT_CLEAR - WILC_SPI_REG_BASE,
&check);
if (ret || ((check & EN_VMM) == (val & EN_VMM)))
break;
retry--;
}
return ret;
}
static int wilc_spi_sync_ext(struct wilc *wilc, int nint)
{
struct spi_device *spi = to_spi_device(wilc->dev);
u32 reg;
int ret, i;
if (nint > MAX_NUM_INT) {
dev_err(&spi->dev, "Too many interrupts (%d)...\n", nint);
return -EINVAL;
}
/*
* interrupt pin mux select
*/
ret = wilc_spi_read_reg(wilc, WILC_PIN_MUX_0, &reg);
if (ret) {
dev_err(&spi->dev, "Failed read reg (%08x)...\n",
WILC_PIN_MUX_0);
return ret;
}
reg |= BIT(8);
ret = wilc_spi_write_reg(wilc, WILC_PIN_MUX_0, reg);
if (ret) {
dev_err(&spi->dev, "Failed write reg (%08x)...\n",
WILC_PIN_MUX_0);
return ret;
}
/*
* interrupt enable
*/
ret = wilc_spi_read_reg(wilc, WILC_INTR_ENABLE, &reg);
if (ret) {
dev_err(&spi->dev, "Failed read reg (%08x)...\n",
WILC_INTR_ENABLE);
return ret;
}
for (i = 0; (i < 5) && (nint > 0); i++, nint--)
reg |= (BIT((27 + i)));
ret = wilc_spi_write_reg(wilc, WILC_INTR_ENABLE, reg);
if (ret) {
dev_err(&spi->dev, "Failed write reg (%08x)...\n",
WILC_INTR_ENABLE);
return ret;
}
if (nint) {
ret = wilc_spi_read_reg(wilc, WILC_INTR2_ENABLE, &reg);
if (ret) {
dev_err(&spi->dev, "Failed read reg (%08x)...\n",
WILC_INTR2_ENABLE);
return ret;
}
for (i = 0; (i < 3) && (nint > 0); i++, nint--)
reg |= BIT(i);
ret = wilc_spi_write_reg(wilc, WILC_INTR2_ENABLE, reg);
if (ret) {
dev_err(&spi->dev, "Failed write reg (%08x)...\n",
WILC_INTR2_ENABLE);
return ret;
}
}
return 0;
}
/* Global spi HIF function table */
static const struct wilc_hif_func wilc_hif_spi = {
.hif_init = wilc_spi_init,
.hif_deinit = wilc_spi_deinit,
.hif_read_reg = wilc_spi_read_reg,
.hif_write_reg = wilc_spi_write_reg,
.hif_block_rx = wilc_spi_read,
.hif_block_tx = wilc_spi_write,
.hif_read_int = wilc_spi_read_int,
.hif_clear_int_ext = wilc_spi_clear_int_ext,
.hif_read_size = wilc_spi_read_size,
.hif_block_tx_ext = wilc_spi_write,
.hif_block_rx_ext = wilc_spi_read,
.hif_sync_ext = wilc_spi_sync_ext,
.hif_reset = wilc_spi_reset,
.hif_is_init = wilc_spi_is_init,
};