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