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