// 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/host.h>
#include <linux/mmc/sdio.h>
#include <linux/of_irq.h>
#include "netdev.h"
#include "cfg80211.h"
#define SDIO_MODALIAS "wilc1000_sdio"
#define SDIO_VENDOR_ID_WILC 0x0296
#define SDIO_DEVICE_ID_WILC 0x5347
static const struct sdio_device_id wilc_sdio_ids[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_WILC, SDIO_DEVICE_ID_WILC) },
{ },
};
#define WILC_SDIO_BLOCK_SIZE 512
struct wilc_sdio {
bool irq_gpio;
u32 block_size;
int has_thrpt_enh3;
};
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;
};
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;
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->read_write) { /* write */
ret = sdio_memcpy_toio(func, cmd->address,
(void *)cmd->buffer, size);
} else { /* read */
ret = sdio_memcpy_fromio(func, (void *)cmd->buffer,
cmd->address, size);
}
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 *wilc;
int ret;
struct wilc_sdio *sdio_priv;
sdio_priv = kzalloc(sizeof(*sdio_priv), GFP_KERNEL);
if (!sdio_priv)
return -ENOMEM;
ret = wilc_cfg80211_init(&wilc, &func->dev, WILC_HIF_SDIO,
&wilc_hif_sdio);
if (ret) {
kfree(sdio_priv);
return ret;
}
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(&func->card->dev, "rtc");
if (PTR_ERR_OR_ZERO(wilc->rtc_clk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
else if (!IS_ERR(wilc->rtc_clk))
clk_prepare_enable(wilc->rtc_clk);
dev_info(&func->dev, "Driver Initializing success\n");
return 0;
}
static void wilc_sdio_remove(struct sdio_func *func)
{
struct wilc *wilc = sdio_get_drvdata(func);
if (!IS_ERR(