cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bus_sdio.c (6710B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * SDIO interface.
      4 *
      5 * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
      6 * Copyright (c) 2010, ST-Ericsson
      7 */
      8#include <linux/module.h>
      9#include <linux/mmc/sdio.h>
     10#include <linux/mmc/sdio_func.h>
     11#include <linux/mmc/card.h>
     12#include <linux/interrupt.h>
     13#include <linux/of_device.h>
     14#include <linux/of_irq.h>
     15#include <linux/irq.h>
     16#include <linux/align.h>
     17
     18#include "bus.h"
     19#include "wfx.h"
     20#include "hwio.h"
     21#include "main.h"
     22#include "bh.h"
     23
     24static const struct wfx_platform_data pdata_wf200 = {
     25	.file_fw = "wfx/wfm_wf200",
     26	.file_pds = "wfx/wf200.pds",
     27};
     28
     29static const struct wfx_platform_data pdata_brd4001a = {
     30	.file_fw = "wfx/wfm_wf200",
     31	.file_pds = "wfx/brd4001a.pds",
     32};
     33
     34static const struct wfx_platform_data pdata_brd8022a = {
     35	.file_fw = "wfx/wfm_wf200",
     36	.file_pds = "wfx/brd8022a.pds",
     37};
     38
     39static const struct wfx_platform_data pdata_brd8023a = {
     40	.file_fw = "wfx/wfm_wf200",
     41	.file_pds = "wfx/brd8023a.pds",
     42};
     43
     44struct wfx_sdio_priv {
     45	struct sdio_func *func;
     46	struct wfx_dev *core;
     47	u8 buf_id_tx;
     48	u8 buf_id_rx;
     49	int of_irq;
     50};
     51
     52static int wfx_sdio_copy_from_io(void *priv, unsigned int reg_id, void *dst, size_t count)
     53{
     54	struct wfx_sdio_priv *bus = priv;
     55	unsigned int sdio_addr = reg_id << 2;
     56	int ret;
     57
     58	WARN(reg_id > 7, "chip only has 7 registers");
     59	WARN(!IS_ALIGNED((uintptr_t)dst, 4), "unaligned buffer address");
     60	WARN(!IS_ALIGNED(count, 4), "unaligned buffer size");
     61
     62	/* Use queue mode buffers */
     63	if (reg_id == WFX_REG_IN_OUT_QUEUE)
     64		sdio_addr |= (bus->buf_id_rx + 1) << 7;
     65	ret = sdio_memcpy_fromio(bus->func, dst, sdio_addr, count);
     66	if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE)
     67		bus->buf_id_rx = (bus->buf_id_rx + 1) % 4;
     68
     69	return ret;
     70}
     71
     72static int wfx_sdio_copy_to_io(void *priv, unsigned int reg_id, const void *src, size_t count)
     73{
     74	struct wfx_sdio_priv *bus = priv;
     75	unsigned int sdio_addr = reg_id << 2;
     76	int ret;
     77
     78	WARN(reg_id > 7, "chip only has 7 registers");
     79	WARN(!IS_ALIGNED((uintptr_t)src, 4), "unaligned buffer address");
     80	WARN(!IS_ALIGNED(count, 4), "unaligned buffer size");
     81
     82	/* Use queue mode buffers */
     83	if (reg_id == WFX_REG_IN_OUT_QUEUE)
     84		sdio_addr |= bus->buf_id_tx << 7;
     85	/* FIXME: discards 'const' qualifier for src */
     86	ret = sdio_memcpy_toio(bus->func, sdio_addr, (void *)src, count);
     87	if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE)
     88		bus->buf_id_tx = (bus->buf_id_tx + 1) % 32;
     89
     90	return ret;
     91}
     92
     93static void wfx_sdio_lock(void *priv)
     94{
     95	struct wfx_sdio_priv *bus = priv;
     96
     97	sdio_claim_host(bus->func);
     98}
     99
    100static void wfx_sdio_unlock(void *priv)
    101{
    102	struct wfx_sdio_priv *bus = priv;
    103
    104	sdio_release_host(bus->func);
    105}
    106
    107static void wfx_sdio_irq_handler(struct sdio_func *func)
    108{
    109	struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
    110
    111	wfx_bh_request_rx(bus->core);
    112}
    113
    114static irqreturn_t wfx_sdio_irq_handler_ext(int irq, void *priv)
    115{
    116	struct wfx_sdio_priv *bus = priv;
    117
    118	sdio_claim_host(bus->func);
    119	wfx_bh_request_rx(bus->core);
    120	sdio_release_host(bus->func);
    121	return IRQ_HANDLED;
    122}
    123
    124static int wfx_sdio_irq_subscribe(void *priv)
    125{
    126	struct wfx_sdio_priv *bus = priv;
    127	u32 flags;
    128	int ret;
    129	u8 cccr;
    130
    131	if (!bus->of_irq) {
    132		sdio_claim_host(bus->func);
    133		ret = sdio_claim_irq(bus->func, wfx_sdio_irq_handler);
    134		sdio_release_host(bus->func);
    135		return ret;
    136	}
    137
    138	flags = irq_get_trigger_type(bus->of_irq);
    139	if (!flags)
    140		flags = IRQF_TRIGGER_HIGH;
    141	flags |= IRQF_ONESHOT;
    142	ret = devm_request_threaded_irq(&bus->func->dev, bus->of_irq, NULL,
    143					wfx_sdio_irq_handler_ext, flags, "wfx", bus);
    144	if (ret)
    145		return ret;
    146	sdio_claim_host(bus->func);
    147	cccr = sdio_f0_readb(bus->func, SDIO_CCCR_IENx, NULL);
    148	cccr |= BIT(0);
    149	cccr |= BIT(bus->func->num);
    150	sdio_f0_writeb(bus->func, cccr, SDIO_CCCR_IENx, NULL);
    151	sdio_release_host(bus->func);
    152	return 0;
    153}
    154
    155static int wfx_sdio_irq_unsubscribe(void *priv)
    156{
    157	struct wfx_sdio_priv *bus = priv;
    158	int ret;
    159
    160	if (bus->of_irq)
    161		devm_free_irq(&bus->func->dev, bus->of_irq, bus);
    162	sdio_claim_host(bus->func);
    163	ret = sdio_release_irq(bus->func);
    164	sdio_release_host(bus->func);
    165	return ret;
    166}
    167
    168static size_t wfx_sdio_align_size(void *priv, size_t size)
    169{
    170	struct wfx_sdio_priv *bus = priv;
    171
    172	return sdio_align_size(bus->func, size);
    173}
    174
    175static const struct wfx_hwbus_ops wfx_sdio_hwbus_ops = {
    176	.copy_from_io    = wfx_sdio_copy_from_io,
    177	.copy_to_io      = wfx_sdio_copy_to_io,
    178	.irq_subscribe   = wfx_sdio_irq_subscribe,
    179	.irq_unsubscribe = wfx_sdio_irq_unsubscribe,
    180	.lock            = wfx_sdio_lock,
    181	.unlock          = wfx_sdio_unlock,
    182	.align_size      = wfx_sdio_align_size,
    183};
    184
    185static const struct of_device_id wfx_sdio_of_match[] = {
    186	{ .compatible = "silabs,wf200",    .data = &pdata_wf200 },
    187	{ .compatible = "silabs,brd4001a", .data = &pdata_brd4001a },
    188	{ .compatible = "silabs,brd8022a", .data = &pdata_brd8022a },
    189	{ .compatible = "silabs,brd8023a", .data = &pdata_brd8023a },
    190	{ },
    191};
    192MODULE_DEVICE_TABLE(of, wfx_sdio_of_match);
    193
    194static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
    195{
    196	const struct wfx_platform_data *pdata = of_device_get_match_data(&func->dev);
    197	struct device_node *np = func->dev.of_node;
    198	struct wfx_sdio_priv *bus;
    199	int ret;
    200
    201	if (func->num != 1) {
    202		dev_err(&func->dev, "SDIO function number is %d while it should always be 1 (unsupported chip?)\n",
    203			func->num);
    204		return -ENODEV;
    205	}
    206
    207	if (!pdata) {
    208		dev_warn(&func->dev, "no compatible device found in DT\n");
    209		return -ENODEV;
    210	}
    211
    212	bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL);
    213	if (!bus)
    214		return -ENOMEM;
    215
    216	bus->func = func;
    217	bus->of_irq = irq_of_parse_and_map(np, 0);
    218	sdio_set_drvdata(func, bus);
    219
    220	sdio_claim_host(func);
    221	ret = sdio_enable_func(func);
    222	/* Block of 64 bytes is more efficient than 512B for frame sizes < 4k */
    223	sdio_set_block_size(func, 64);
    224	sdio_release_host(func);
    225	if (ret)
    226		return ret;
    227
    228	bus->core = wfx_init_common(&func->dev, pdata, &wfx_sdio_hwbus_ops, bus);
    229	if (!bus->core) {
    230		ret = -EIO;
    231		goto sdio_release;
    232	}
    233
    234	ret = wfx_probe(bus->core);
    235	if (ret)
    236		goto sdio_release;
    237
    238	return 0;
    239
    240sdio_release:
    241	sdio_claim_host(func);
    242	sdio_disable_func(func);
    243	sdio_release_host(func);
    244	return ret;
    245}
    246
    247static void wfx_sdio_remove(struct sdio_func *func)
    248{
    249	struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
    250
    251	wfx_release(bus->core);
    252	sdio_claim_host(func);
    253	sdio_disable_func(func);
    254	sdio_release_host(func);
    255}
    256
    257static const struct sdio_device_id wfx_sdio_ids[] = {
    258	/* WF200 does not have official VID/PID */
    259	{ SDIO_DEVICE(0x0000, 0x1000) },
    260	{ },
    261};
    262MODULE_DEVICE_TABLE(sdio, wfx_sdio_ids);
    263
    264struct sdio_driver wfx_sdio_driver = {
    265	.name = "wfx-sdio",
    266	.id_table = wfx_sdio_ids,
    267	.probe = wfx_sdio_probe,
    268	.remove = wfx_sdio_remove,
    269	.drv = {
    270		.owner = THIS_MODULE,
    271		.of_match_table = wfx_sdio_of_match,
    272	}
    273};