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.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};
274