linux/drivers/net/wireless/microchip/wilc1000/sdio.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

1076 lines
23 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/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio.h>
#include <linux/of_irq.h>
#include "netdev.h"
#include "cfg80211.h"
#define SDIO_MODALIAS "wilc1000_sdio"
static const struct sdio_device_id wilc_sdio_ids[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_MICROCHIP_WILC, SDIO_DEVICE_ID_MICROCHIP_WILC1000) },
{ },
};
MODULE_DEVICE_TABLE(sdio, wilc_sdio_ids);
#define WILC_SDIO_BLOCK_SIZE 512
static int wilc_sdio_init(struct wilc *wilc, bool resume);
static int wilc_sdio_deinit(struct wilc *wilc);
struct wilc_sdio {
bool irq_gpio;
u32 block_size;
bool isinit;
u8 *cmd53_buf;
};
struct sdio_cmd52 {
u32 read_write: 1;
u32 function: 3;
u32 raw: 1;
u32 address: 17;
u32 data: 8;
};
struct sdio_cmd53 {
u32 read_write: 1;
u32 function: 3;
u32 block_mode: 1;
u32 increment: 1;
u32 address: 17;
u32 count: 9;
u8 *buffer;
u32 block_size;
bool use_global_buf;
};
static const struct wilc_hif_func wilc_hif_sdio;
static void wilc_sdio_interrupt(struct sdio_func *func)
{
sdio_release_host(func);
wilc_handle_isr(sdio_get_drvdata(func));
sdio_claim_host(func);
}
static int wilc_sdio_cmd52(struct wilc *wilc, struct sdio_cmd52 *cmd)
{
struct sdio_func *func = container_of(wilc->dev, struct sdio_func, dev);
int ret;
u8 data;
sdio_claim_host(func);
func->num = cmd->function;
if (cmd->read_write) { /* write */
if (cmd->raw) {
sdio_writeb(func, cmd->data, cmd->address, &ret);
data = sdio_readb(func, cmd->address, &ret);
cmd->data = data;
} else {
sdio_writeb(func, cmd->data, cmd->address, &ret);
}
} else { /* read */
data = sdio_readb(func, cmd->address, &ret);
cmd->data = data;
}
sdio_release_host(func);
if (ret)
dev_err(&func->dev, "%s..failed, err(%d)\n", __func__, ret);
return ret;
}
static int wilc_sdio_cmd53(struct wilc *wilc, struct sdio_cmd53 *cmd)
{
struct sdio_func *func = container_of(wilc->dev, struct sdio_func, dev);
int size, ret;
struct wilc_sdio *sdio_priv = wilc->bus_data;
u8 *buf = cmd->buffer;
sdio_claim_host(func);
func->num = cmd->function;
func->cur_blksize = cmd->block_size;
if (cmd->block_mode)
size = cmd->count * cmd->block_size;
else
size = cmd->count;
if (cmd->use_global_buf) {
if (size > sizeof(u32)) {
ret = -EINVAL;
goto out;
}
buf = sdio_priv->cmd53_buf;
}
if (cmd->read_write) { /* write */
if (cmd->use_global_buf)
memcpy(buf, cmd->buffer, size);
ret = sdio_memcpy_toio(func, cmd->address, buf, size);
} else { /* read */
ret = sdio_memcpy_fromio(func, buf, cmd->address, size);
if (cmd->use_global_buf)
memcpy(cmd->buffer, buf, size);
}
out:
sdio_release_host(func);
if (ret)
dev_err(&func->dev, "%s..failed, err(%d)\n", __func__, ret);
return ret;
}
static int wilc_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct wilc_sdio *sdio_priv;
struct wilc_vif *vif;
struct wilc *wilc;
int ret;
sdio_priv = kzalloc(sizeof(*sdio_priv), GFP_KERNEL);
if (!sdio_priv)
return -ENOMEM;
sdio_priv->cmd53_buf = kzalloc(sizeof(u32), GFP_KERNEL);
if (!sdio_priv->cmd53_buf) {
ret = -ENOMEM;
goto free;
}
ret = wilc_cfg80211_init(&wilc, &func->dev, WILC_HIF_SDIO,
&wilc_hif_sdio);
if (ret)
goto free;
if (IS_ENABLED(CONFIG_WILC1000_HW_OOB_INTR)) {
struct device_node *np = func->card->dev.of_node;
int irq_num = of_irq_get(np, 0);
if (irq_num > 0) {
wilc->dev_irq_num = irq_num;
sdio_priv->irq_gpio = true;
}
}
sdio_set_drvdata(func, wilc);
wilc->bus_data = sdio_priv;
wilc->dev = &func->dev;
wilc->rtc_clk = devm_clk_get_optional_enabled(&func->card->dev, "rtc");
if (IS_ERR(wilc->rtc_clk)) {
ret = PTR_ERR(wilc->rtc_clk);
goto dispose_irq;
}
wilc_sdio_init(wilc, false);
ret = wilc_get_chipid(wilc);
if (ret)
goto dispose_irq;
ret = wilc_cfg80211_register(wilc);
if (ret)
goto dispose_irq;
ret = wilc_load_mac_from_nv(wilc);
if (ret) {
pr_err("Can not retrieve MAC address from chip\n");
goto unregister_wiphy;
}
wilc_sdio_deinit(wilc);
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;
}
dev_info(&func->dev, "Driver Initializing success\n");
return 0;
unregister_wiphy:
wiphy_unregister(wilc->wiphy);
dispose_irq:
irq_dispose_mapping(wilc->dev_irq_num);
wilc_netdev_cleanup(wilc);
wiphy_free(wilc->wiphy);
free:
kfree(sdio_priv->cmd53_buf);
kfree(sdio_priv);
return ret;
}
static void wilc_sdio_remove(struct sdio_func *func)
{
struct wilc *wilc = sdio_get_drvdata(func);
struct wilc_sdio *sdio_priv = wilc->bus_data;
wilc_netdev_cleanup(wilc);
wiphy_unregister(wilc->wiphy);
wiphy_free(wilc->wiphy);
kfree(sdio_priv->cmd53_buf);
kfree(sdio_priv);
}
static int wilc_sdio_reset(struct wilc *wilc)
{
struct sdio_cmd52 cmd;
int ret;
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 0;
cmd.address = SDIO_CCCR_ABORT;
cmd.data = WILC_SDIO_CCCR_ABORT_RESET;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev, "Fail cmd 52, reset cmd ...\n");
return ret;
}
return 0;
}
static bool wilc_sdio_is_init(struct wilc *wilc)
{
struct wilc_sdio *sdio_priv = wilc->bus_data;
return sdio_priv->isinit;
}
static int wilc_sdio_enable_interrupt(struct wilc *dev)
{
struct sdio_func *func = container_of(dev->dev, struct sdio_func, dev);
int ret = 0;
sdio_claim_host(func);
ret = sdio_claim_irq(func, wilc_sdio_interrupt);
sdio_release_host(func);
if (ret < 0) {
dev_err(&func->dev, "can't claim sdio_irq, err(%d)\n", ret);
ret = -EIO;
}
return ret;
}
static void wilc_sdio_disable_interrupt(struct wilc *dev)
{
struct sdio_func *func = container_of(dev->dev, struct sdio_func, dev);
int ret;
sdio_claim_host(func);
ret = sdio_release_irq(func);
if (ret < 0)
dev_err(&func->dev, "can't release sdio_irq, err(%d)\n", ret);
sdio_release_host(func);
}
/********************************************
*
* Function 0
*
********************************************/
static int wilc_sdio_set_func0_csa_address(struct wilc *wilc, u32 adr)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct sdio_cmd52 cmd;
int ret;
/**
* Review: BIG ENDIAN
**/
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 0;
cmd.address = WILC_SDIO_FBR_CSA_REG;
cmd.data = (u8)adr;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev, "Failed cmd52, set %04x data...\n",
cmd.address);
return ret;
}
cmd.address = WILC_SDIO_FBR_CSA_REG + 1;
cmd.data = (u8)(adr >> 8);
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev, "Failed cmd52, set %04x data...\n",
cmd.address);
return ret;
}
cmd.address = WILC_SDIO_FBR_CSA_REG + 2;
cmd.data = (u8)(adr >> 16);
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev, "Failed cmd52, set %04x data...\n",
cmd.address);
return ret;
}
return 0;
}
static int wilc_sdio_set_block_size(struct wilc *wilc, u8 func_num,
u32 block_size)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct sdio_cmd52 cmd;
int ret;
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 0;
cmd.address = SDIO_FBR_BASE(func_num) + SDIO_CCCR_BLKSIZE;
cmd.data = (u8)block_size;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev, "Failed cmd52, set %04x data...\n",
cmd.address);
return ret;
}
cmd.address = SDIO_FBR_BASE(func_num) + SDIO_CCCR_BLKSIZE + 1;
cmd.data = (u8)(block_size >> 8);
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev, "Failed cmd52, set %04x data...\n",
cmd.address);
return ret;
}
return 0;
}
/********************************************
*
* Sdio interfaces
*
********************************************/
static int wilc_sdio_write_reg(struct wilc *wilc, u32 addr, u32 data)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct wilc_sdio *sdio_priv = wilc->bus_data;
int ret;
cpu_to_le32s(&data);
if (addr >= 0xf0 && addr <= 0xff) { /* only vendor specific registers */
struct sdio_cmd52 cmd;
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 0;
cmd.address = addr;
cmd.data = data;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret)
dev_err(&func->dev,
"Failed cmd 52, read reg (%08x) ...\n", addr);
} else {
struct sdio_cmd53 cmd;
/**
* set the AHB address
**/
ret = wilc_sdio_set_func0_csa_address(wilc, addr);
if (ret)
return ret;
cmd.read_write = 1;
cmd.function = 0;
cmd.address = WILC_SDIO_FBR_DATA_REG;
cmd.block_mode = 0;
cmd.increment = 1;
cmd.count = sizeof(u32);
cmd.buffer = (u8 *)&data;
cmd.use_global_buf = true;
cmd.block_size = sdio_priv->block_size;
ret = wilc_sdio_cmd53(wilc, &cmd);
if (ret)
dev_err(&func->dev,
"Failed cmd53, write reg (%08x)...\n", addr);
}
return ret;
}
static int wilc_sdio_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct wilc_sdio *sdio_priv = wilc->bus_data;
u32 block_size = sdio_priv->block_size;
struct sdio_cmd53 cmd;
int nblk, nleft, ret;
cmd.read_write = 1;
if (addr > 0) {
/**
* func 0 access
**/
cmd.function = 0;
cmd.address = WILC_SDIO_FBR_DATA_REG;
} else {
/**
* func 1 access
**/
cmd.function = 1;
cmd.address = WILC_SDIO_F1_DATA_REG;
}
size = ALIGN(size, 4);
nblk = size / block_size;
nleft = size % block_size;
cmd.use_global_buf = false;
if (nblk > 0) {
cmd.block_mode = 1;
cmd.increment = 1;
cmd.count = nblk;
cmd.buffer = buf;
cmd.block_size = block_size;
if (addr > 0) {
ret = wilc_sdio_set_func0_csa_address(wilc, addr);
if (ret)
return ret;
}
ret = wilc_sdio_cmd53(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed cmd53 [%x], block send...\n", addr);
return ret;
}
if (addr > 0)
addr += nblk * block_size;
buf += nblk * block_size;
}
if (nleft > 0) {
cmd.block_mode = 0;
cmd.increment = 1;
cmd.count = nleft;
cmd.buffer = buf;
cmd.block_size = block_size;
if (addr > 0) {
ret = wilc_sdio_set_func0_csa_address(wilc, addr);
if (ret)
return ret;
}
ret = wilc_sdio_cmd53(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed cmd53 [%x], bytes send...\n", addr);
return ret;
}
}
return 0;
}
static int wilc_sdio_read_reg(struct wilc *wilc, u32 addr, u32 *data)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct wilc_sdio *sdio_priv = wilc->bus_data;
int ret;
if (addr >= 0xf0 && addr <= 0xff) { /* only vendor specific registers */
struct sdio_cmd52 cmd;
cmd.read_write = 0;
cmd.function = 0;
cmd.raw = 0;
cmd.address = addr;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed cmd 52, read reg (%08x) ...\n", addr);
return ret;
}
*data = cmd.data;
} else {
struct sdio_cmd53 cmd;
ret = wilc_sdio_set_func0_csa_address(wilc, addr);
if (ret)
return ret;
cmd.read_write = 0;
cmd.function = 0;
cmd.address = WILC_SDIO_FBR_DATA_REG;
cmd.block_mode = 0;
cmd.increment = 1;
cmd.count = sizeof(u32);
cmd.buffer = (u8 *)data;
cmd.use_global_buf = true;
cmd.block_size = sdio_priv->block_size;
ret = wilc_sdio_cmd53(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed cmd53, read reg (%08x)...\n", addr);
return ret;
}
}
le32_to_cpus(data);
return 0;
}
static int wilc_sdio_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct wilc_sdio *sdio_priv = wilc->bus_data;
u32 block_size = sdio_priv->block_size;
struct sdio_cmd53 cmd;
int nblk, nleft, ret;
cmd.read_write = 0;
if (addr > 0) {
/**
* func 0 access
**/
cmd.function = 0;
cmd.address = WILC_SDIO_FBR_DATA_REG;
} else {
/**
* func 1 access
**/
cmd.function = 1;
cmd.address = WILC_SDIO_F1_DATA_REG;
}
size = ALIGN(size, 4);
nblk = size / block_size;
nleft = size % block_size;
cmd.use_global_buf = false;
if (nblk > 0) {
cmd.block_mode = 1;
cmd.increment = 1;
cmd.count = nblk;
cmd.buffer = buf;
cmd.block_size = block_size;
if (addr > 0) {
ret = wilc_sdio_set_func0_csa_address(wilc, addr);
if (ret)
return ret;
}
ret = wilc_sdio_cmd53(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed cmd53 [%x], block read...\n", addr);
return ret;
}
if (addr > 0)
addr += nblk * block_size;
buf += nblk * block_size;
} /* if (nblk > 0) */
if (nleft > 0) {
cmd.block_mode = 0;
cmd.increment = 1;
cmd.count = nleft;
cmd.buffer = buf;
cmd.block_size = block_size;
if (addr > 0) {
ret = wilc_sdio_set_func0_csa_address(wilc, addr);
if (ret)
return ret;
}
ret = wilc_sdio_cmd53(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed cmd53 [%x], bytes read...\n", addr);
return ret;
}
}
return 0;
}
/********************************************
*
* Bus interfaces
*
********************************************/
static int wilc_sdio_deinit(struct wilc *wilc)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct wilc_sdio *sdio_priv = wilc->bus_data;
struct sdio_cmd52 cmd;
int ret;
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 1;
/* Disable all functions interrupts */
cmd.address = SDIO_CCCR_IENx;
cmd.data = 0;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev, "Failed to disable functions interrupts\n");
return ret;
}
/* Disable all functions */
cmd.address = SDIO_CCCR_IOEx;
cmd.data = 0;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed to reset all functions\n");
return ret;
}
/* Disable CSA */
cmd.read_write = 0;
cmd.address = SDIO_FBR_BASE(1);
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed to read CSA for function 1\n");
return ret;
}
cmd.read_write = 1;
cmd.address = SDIO_FBR_BASE(1);
cmd.data &= ~SDIO_FBR_ENABLE_CSA;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed to disable CSA for function 1\n");
return ret;
}
sdio_priv->isinit = false;
return 0;
}
static int wilc_sdio_init(struct wilc *wilc, bool resume)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct wilc_sdio *sdio_priv = wilc->bus_data;
struct sdio_cmd52 cmd;
int loop, ret;
/**
* function 0 csa enable
**/
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 1;
cmd.address = SDIO_FBR_BASE(1);
cmd.data = SDIO_FBR_ENABLE_CSA;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev, "Fail cmd 52, enable csa...\n");
return ret;
}
/**
* function 0 block size
**/
ret = wilc_sdio_set_block_size(wilc, 0, WILC_SDIO_BLOCK_SIZE);
if (ret) {
dev_err(&func->dev, "Fail cmd 52, set func 0 block size...\n");
return ret;
}
sdio_priv->block_size = WILC_SDIO_BLOCK_SIZE;
/**
* enable func1 IO
**/
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 1;
cmd.address = SDIO_CCCR_IOEx;
cmd.data = WILC_SDIO_CCCR_IO_EN_FUNC1;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Fail cmd 52, set IOE register...\n");
return ret;
}
/**
* make sure func 1 is up
**/
cmd.read_write = 0;
cmd.function = 0;
cmd.raw = 0;
cmd.address = SDIO_CCCR_IORx;
loop = 3;
do {
cmd.data = 0;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Fail cmd 52, get IOR register...\n");
return ret;
}
if (cmd.data == WILC_SDIO_CCCR_IO_EN_FUNC1)
break;
} while (loop--);
if (loop <= 0) {
dev_err(&func->dev, "Fail func 1 is not ready...\n");
return -EINVAL;
}
/**
* func 1 is ready, set func 1 block size
**/
ret = wilc_sdio_set_block_size(wilc, 1, WILC_SDIO_BLOCK_SIZE);
if (ret) {
dev_err(&func->dev, "Fail set func 1 block size...\n");
return ret;
}
/**
* func 1 interrupt enable
**/
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 1;
cmd.address = SDIO_CCCR_IENx;
cmd.data = WILC_SDIO_CCCR_IEN_MASTER | WILC_SDIO_CCCR_IEN_FUNC1;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev, "Fail cmd 52, set IEN register...\n");
return ret;
}
sdio_priv->isinit = true;
return 0;
}
static int wilc_sdio_read_size(struct wilc *wilc, u32 *size)
{
u32 tmp;
struct sdio_cmd52 cmd;
/**
* Read DMA count in words
**/
cmd.read_write = 0;
cmd.function = 0;
cmd.raw = 0;
cmd.address = WILC_SDIO_INTERRUPT_DATA_SZ_REG;
cmd.data = 0;
wilc_sdio_cmd52(wilc, &cmd);
tmp = cmd.data;
cmd.address = WILC_SDIO_INTERRUPT_DATA_SZ_REG + 1;
cmd.data = 0;
wilc_sdio_cmd52(wilc, &cmd);
tmp |= (cmd.data << 8);
*size = tmp;
return 0;
}
static int wilc_sdio_read_int(struct wilc *wilc, u32 *int_status)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct wilc_sdio *sdio_priv = wilc->bus_data;
u32 tmp;
u8 irq_flags;
struct sdio_cmd52 cmd;
wilc_sdio_read_size(wilc, &tmp);
/**
* Read IRQ flags
**/
if (!sdio_priv->irq_gpio) {
cmd.function = 1;
cmd.address = WILC_SDIO_EXT_IRQ_FLAG_REG;
} else {
cmd.function = 0;
cmd.address = is_wilc1000(wilc->chipid) ?
WILC1000_SDIO_IRQ_FLAG_REG :
WILC3000_SDIO_IRQ_FLAG_REG;
}
cmd.raw = 0;
cmd.read_write = 0;
cmd.data = 0;
wilc_sdio_cmd52(wilc, &cmd);
irq_flags = cmd.data;
if (sdio_priv->irq_gpio)
irq_flags &= is_wilc1000(wilc->chipid) ? 0x1f : 0x0f;
tmp |= FIELD_PREP(IRG_FLAGS_MASK, cmd.data);
if (FIELD_GET(UNHANDLED_IRQ_MASK, irq_flags))
dev_err(&func->dev, "Unexpected interrupt (1) int=%lx\n",
FIELD_GET(UNHANDLED_IRQ_MASK, irq_flags));
*int_status = tmp;
return 0;
}
static int wilc_sdio_clear_int_ext(struct wilc *wilc, u32 val)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct wilc_sdio *sdio_priv = wilc->bus_data;
int ret;
u32 reg = 0;
if (sdio_priv->irq_gpio)
reg = val & (BIT(MAX_NUM_INT) - 1);
if (is_wilc1000(wilc->chipid)) {
/* select VMM table 0 */
if (val & SEL_VMM_TBL0)
reg |= BIT(5);
/* select VMM table 1 */
if (val & SEL_VMM_TBL1)
reg |= BIT(6);
/* enable VMM */
if (val & EN_VMM)
reg |= BIT(7);
} else {
if (sdio_priv->irq_gpio && reg) {
struct sdio_cmd52 cmd;
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 0;
cmd.address = WILC3000_SDIO_IRQ_FLAG_REG;
cmd.data = reg;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed cmd52, set 0xfe data (%d) ...\n",
__LINE__);
return ret;
}
}
reg = 0;
/* select VMM table 0 */
if (val & SEL_VMM_TBL0)
reg |= BIT(0);
/* select VMM table 1 */
if (val & SEL_VMM_TBL1)
reg |= BIT(1);
/* enable VMM */
if (val & EN_VMM)
reg |= BIT(2);
}
if (reg) {
struct sdio_cmd52 cmd;
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 0;
cmd.address = is_wilc1000(wilc->chipid) ?
WILC1000_SDIO_IRQ_CLEAR_FLAG_REG :
WILC3000_SDIO_VMM_TBL_CTRL_REG;
cmd.data = reg;
ret = wilc_sdio_cmd52(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
"Failed cmd52, set (%02x) data (%d) ...\n",
cmd.address, __LINE__);
return ret;
}
}
return 0;
}
static int wilc_sdio_sync_ext(struct wilc *wilc, int nint)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
struct wilc_sdio *sdio_priv = wilc->bus_data;
if (nint > MAX_NUM_INT) {
dev_err(&func->dev, "Too many interrupts (%d)...\n", nint);
return -EINVAL;
}
if (sdio_priv->irq_gpio) {
u32 reg;
int ret, i;
/**
* interrupt pin mux select
**/
ret = wilc_sdio_read_reg(wilc, WILC_PIN_MUX_0, &reg);
if (ret) {
dev_err(&func->dev, "Failed read reg (%08x)...\n",
WILC_PIN_MUX_0);
return ret;
}
reg |= BIT(8);
ret = wilc_sdio_write_reg(wilc, WILC_PIN_MUX_0, reg);
if (ret) {
dev_err(&func->dev, "Failed write reg (%08x)...\n",
WILC_PIN_MUX_0);
return ret;
}
/**
* interrupt enable
**/
ret = wilc_sdio_read_reg(wilc, WILC_INTR_ENABLE, &reg);
if (ret) {
dev_err(&func->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_sdio_write_reg(wilc, WILC_INTR_ENABLE, reg);
if (ret) {
dev_err(&func->dev, "Failed write reg (%08x)...\n",
WILC_INTR_ENABLE);
return ret;
}
if (nint) {
ret = wilc_sdio_read_reg(wilc, WILC_INTR2_ENABLE, &reg);
if (ret) {
dev_err(&func->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_sdio_write_reg(wilc, WILC_INTR2_ENABLE, reg);
if (ret) {
dev_err(&func->dev,
"Failed write reg (%08x)...\n",
WILC_INTR2_ENABLE);
return ret;
}
}
}
return 0;
}
/* Global sdio HIF function table */
static const struct wilc_hif_func wilc_hif_sdio = {
.hif_init = wilc_sdio_init,
.hif_deinit = wilc_sdio_deinit,
.hif_read_reg = wilc_sdio_read_reg,
.hif_write_reg = wilc_sdio_write_reg,
.hif_block_rx = wilc_sdio_read,
.hif_block_tx = wilc_sdio_write,
.hif_read_int = wilc_sdio_read_int,
.hif_clear_int_ext = wilc_sdio_clear_int_ext,
.hif_read_size = wilc_sdio_read_size,
.hif_block_tx_ext = wilc_sdio_write,
.hif_block_rx_ext = wilc_sdio_read,
.hif_sync_ext = wilc_sdio_sync_ext,
.enable_interrupt = wilc_sdio_enable_interrupt,
.disable_interrupt = wilc_sdio_disable_interrupt,
.hif_reset = wilc_sdio_reset,
.hif_is_init = wilc_sdio_is_init,
};
static int wilc_sdio_suspend(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
struct wilc *wilc = sdio_get_drvdata(func);
int ret;
dev_info(dev, "sdio suspend\n");
if (!wilc->initialized)
return 0;
if (!IS_ERR(wilc->rtc_clk))
clk_disable_unprepare(wilc->rtc_clk);
ret = host_sleep_notify(wilc);
if (ret) {
clk_prepare_enable(wilc->rtc_clk);
return ret;
}
wilc_sdio_disable_interrupt(wilc);
return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
}
static int wilc_sdio_resume(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
struct wilc *wilc = sdio_get_drvdata(func);
dev_info(dev, "sdio resume\n");
if (!wilc->initialized)
return 0;
if (!IS_ERR(wilc->rtc_clk))
clk_prepare_enable(wilc->rtc_clk);
wilc_sdio_init(wilc, true);
wilc_sdio_enable_interrupt(wilc);
return host_wakeup_notify(wilc);
}
static const struct of_device_id wilc_of_match[] = {
{ .compatible = "microchip,wilc1000", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, wilc_of_match);
static const struct dev_pm_ops wilc_sdio_pm_ops = {
.suspend = wilc_sdio_suspend,
.resume = wilc_sdio_resume,
};
static struct sdio_driver wilc_sdio_driver = {
.name = SDIO_MODALIAS,
.id_table = wilc_sdio_ids,
.probe = wilc_sdio_probe,
.remove = wilc_sdio_remove,
.drv = {
.pm = &wilc_sdio_pm_ops,
.of_match_table = wilc_of_match,
}
};
module_sdio_driver(wilc_sdio_driver);
MODULE_DESCRIPTION("Atmel WILC1000 SDIO wireless driver");
MODULE_LICENSE("GPL");