Linux-libre 5.7.6-gnu
[librecmc/linux-libre.git] / drivers / media / mmc / siano / smssdio.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  smssdio.c - Siano 1xxx SDIO interface driver
4  *
5  *  Copyright 2008 Pierre Ossman
6  *
7  * Based on code by Siano Mobile Silicon, Inc.,
8  * Copyright (C) 2006-2008, Uri Shkolnik
9  *
10  * This hardware is a bit odd in that all transfers should be done
11  * to/from the SMSSDIO_DATA register, yet the "increase address" bit
12  * always needs to be set.
13  *
14  * Also, buffers from the card are always aligned to 128 byte
15  * boundaries.
16  */
17
18 /*
19  * General cleanup notes:
20  *
21  * - only typedefs should be name *_t
22  *
23  * - use ERR_PTR and friends for smscore_register_device()
24  *
25  * - smscore_getbuffer should zero fields
26  *
27  * Fix stop command
28  */
29
30 #include "smscoreapi.h"
31
32 #include <linux/moduleparam.h>
33 #include <linux/slab.h>
34 #include <linux/firmware.h>
35 #include <linux/delay.h>
36 #include <linux/mmc/card.h>
37 #include <linux/mmc/sdio_func.h>
38 #include <linux/mmc/sdio_ids.h>
39 #include <linux/module.h>
40
41 #include "sms-cards.h"
42 #include "smsendian.h"
43
44 /* Registers */
45
46 #define SMSSDIO_DATA            0x00
47 #define SMSSDIO_INT             0x04
48 #define SMSSDIO_BLOCK_SIZE      128
49
50 static const struct sdio_device_id smssdio_ids[] = {
51         {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),
52          .driver_data = SMS1XXX_BOARD_SIANO_STELLAR},
53         {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0),
54          .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A},
55         {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0),
56          .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B},
57         {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0),
58          .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
59         {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
60          .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
61         {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x302),
62         .driver_data = SMS1XXX_BOARD_SIANO_MING},
63         {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x500),
64         .driver_data = SMS1XXX_BOARD_SIANO_PELE},
65         {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x600),
66         .driver_data = SMS1XXX_BOARD_SIANO_RIO},
67         {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x700),
68         .driver_data = SMS1XXX_BOARD_SIANO_DENVER_2160},
69         {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x800),
70         .driver_data = SMS1XXX_BOARD_SIANO_DENVER_1530},
71         { /* end: all zeroes */ },
72 };
73
74 MODULE_DEVICE_TABLE(sdio, smssdio_ids);
75
76 struct smssdio_device {
77         struct sdio_func *func;
78
79         struct smscore_device_t *coredev;
80
81         struct smscore_buffer_t *split_cb;
82 };
83
84 /*******************************************************************/
85 /* Siano core callbacks                                            */
86 /*******************************************************************/
87
88 static int smssdio_sendrequest(void *context, void *buffer, size_t size)
89 {
90         int ret = 0;
91         struct smssdio_device *smsdev;
92
93         smsdev = context;
94
95         sdio_claim_host(smsdev->func);
96
97         smsendian_handle_tx_message((struct sms_msg_data *) buffer);
98         while (size >= smsdev->func->cur_blksize) {
99                 ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,
100                                         buffer, smsdev->func->cur_blksize);
101                 if (ret)
102                         goto out;
103
104                 buffer += smsdev->func->cur_blksize;
105                 size -= smsdev->func->cur_blksize;
106         }
107
108         if (size) {
109                 ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,
110                                         buffer, size);
111         }
112
113 out:
114         sdio_release_host(smsdev->func);
115
116         return ret;
117 }
118
119 /*******************************************************************/
120 /* SDIO callbacks                                                  */
121 /*******************************************************************/
122
123 static void smssdio_interrupt(struct sdio_func *func)
124 {
125         int ret;
126
127         struct smssdio_device *smsdev;
128         struct smscore_buffer_t *cb;
129         struct sms_msg_hdr *hdr;
130         size_t size;
131
132         smsdev = sdio_get_drvdata(func);
133
134         /*
135          * The interrupt register has no defined meaning. It is just
136          * a way of turning of the level triggered interrupt.
137          */
138         (void)sdio_readb(func, SMSSDIO_INT, &ret);
139         if (ret) {
140                 pr_err("Unable to read interrupt register!\n");
141                 return;
142         }
143
144         if (smsdev->split_cb == NULL) {
145                 cb = smscore_getbuffer(smsdev->coredev);
146                 if (!cb) {
147                         pr_err("Unable to allocate data buffer!\n");
148                         return;
149                 }
150
151                 ret = sdio_memcpy_fromio(smsdev->func,
152                                          cb->p,
153                                          SMSSDIO_DATA,
154                                          SMSSDIO_BLOCK_SIZE);
155                 if (ret) {
156                         pr_err("Error %d reading initial block!\n", ret);
157                         return;
158                 }
159
160                 hdr = cb->p;
161
162                 if (hdr->msg_flags & MSG_HDR_FLAG_SPLIT_MSG) {
163                         smsdev->split_cb = cb;
164                         return;
165                 }
166
167                 if (hdr->msg_length > smsdev->func->cur_blksize)
168                         size = hdr->msg_length - smsdev->func->cur_blksize;
169                 else
170                         size = 0;
171         } else {
172                 cb = smsdev->split_cb;
173                 hdr = cb->p;
174
175                 size = hdr->msg_length - sizeof(struct sms_msg_hdr);
176
177                 smsdev->split_cb = NULL;
178         }
179
180         if (size) {
181                 void *buffer;
182
183                 buffer = cb->p + (hdr->msg_length - size);
184                 size = ALIGN(size, SMSSDIO_BLOCK_SIZE);
185
186                 BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE);
187
188                 /*
189                  * First attempt to transfer all of it in one go...
190                  */
191                 ret = sdio_memcpy_fromio(smsdev->func,
192                                          buffer,
193                                          SMSSDIO_DATA,
194                                          size);
195                 if (ret && ret != -EINVAL) {
196                         smscore_putbuffer(smsdev->coredev, cb);
197                         pr_err("Error %d reading data from card!\n", ret);
198                         return;
199                 }
200
201                 /*
202                  * ..then fall back to one block at a time if that is
203                  * not possible...
204                  *
205                  * (we have to do this manually because of the
206                  * problem with the "increase address" bit)
207                  */
208                 if (ret == -EINVAL) {
209                         while (size) {
210                                 ret = sdio_memcpy_fromio(smsdev->func,
211                                                   buffer, SMSSDIO_DATA,
212                                                   smsdev->func->cur_blksize);
213                                 if (ret) {
214                                         smscore_putbuffer(smsdev->coredev, cb);
215                                         pr_err("Error %d reading data from card!\n",
216                                                ret);
217                                         return;
218                                 }
219
220                                 buffer += smsdev->func->cur_blksize;
221                                 if (size > smsdev->func->cur_blksize)
222                                         size -= smsdev->func->cur_blksize;
223                                 else
224                                         size = 0;
225                         }
226                 }
227         }
228
229         cb->size = hdr->msg_length;
230         cb->offset = 0;
231
232         smsendian_handle_rx_message((struct sms_msg_data *) cb->p);
233         smscore_onresponse(smsdev->coredev, cb);
234 }
235
236 static int smssdio_probe(struct sdio_func *func,
237                          const struct sdio_device_id *id)
238 {
239         int ret;
240
241         int board_id;
242         struct smssdio_device *smsdev;
243         struct smsdevice_params_t params;
244
245         board_id = id->driver_data;
246
247         smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL);
248         if (!smsdev)
249                 return -ENOMEM;
250
251         smsdev->func = func;
252
253         memset(&params, 0, sizeof(struct smsdevice_params_t));
254
255         params.device = &func->dev;
256         params.buffer_size = 0x5000;    /* ?? */
257         params.num_buffers = 22;        /* ?? */
258         params.context = smsdev;
259
260         snprintf(params.devpath, sizeof(params.devpath),
261                  "sdio\\%s", sdio_func_id(func));
262
263         params.sendrequest_handler = smssdio_sendrequest;
264
265         params.device_type = sms_get_board(board_id)->type;
266
267         if (params.device_type != SMS_STELLAR)
268                 params.flags |= SMS_DEVICE_FAMILY2;
269         else {
270                 /*
271                  * FIXME: Stellar needs special handling...
272                  */
273                 ret = -ENODEV;
274                 goto free;
275         }
276
277         ret = smscore_register_device(&params, &smsdev->coredev, GFP_DMA, NULL);
278         if (ret < 0)
279                 goto free;
280
281         smscore_set_board_id(smsdev->coredev, board_id);
282
283         sdio_claim_host(func);
284
285         ret = sdio_enable_func(func);
286         if (ret)
287                 goto release;
288
289         ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE);
290         if (ret)
291                 goto disable;
292
293         ret = sdio_claim_irq(func, smssdio_interrupt);
294         if (ret)
295                 goto disable;
296
297         sdio_set_drvdata(func, smsdev);
298
299         sdio_release_host(func);
300
301         ret = smscore_start_device(smsdev->coredev);
302         if (ret < 0)
303                 goto reclaim;
304
305         return 0;
306
307 reclaim:
308         sdio_claim_host(func);
309         sdio_release_irq(func);
310 disable:
311         sdio_disable_func(func);
312 release:
313         sdio_release_host(func);
314         smscore_unregister_device(smsdev->coredev);
315 free:
316         kfree(smsdev);
317
318         return ret;
319 }
320
321 static void smssdio_remove(struct sdio_func *func)
322 {
323         struct smssdio_device *smsdev;
324
325         smsdev = sdio_get_drvdata(func);
326
327         /* FIXME: racy! */
328         if (smsdev->split_cb)
329                 smscore_putbuffer(smsdev->coredev, smsdev->split_cb);
330
331         smscore_unregister_device(smsdev->coredev);
332
333         sdio_claim_host(func);
334         sdio_release_irq(func);
335         sdio_disable_func(func);
336         sdio_release_host(func);
337
338         kfree(smsdev);
339 }
340
341 static struct sdio_driver smssdio_driver = {
342         .name = "smssdio",
343         .id_table = smssdio_ids,
344         .probe = smssdio_probe,
345         .remove = smssdio_remove,
346 };
347
348 /*******************************************************************/
349 /* Module functions                                                */
350 /*******************************************************************/
351
352 static int __init smssdio_module_init(void)
353 {
354         int ret = 0;
355
356         printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
357         printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
358
359         ret = sdio_register_driver(&smssdio_driver);
360
361         return ret;
362 }
363
364 static void __exit smssdio_module_exit(void)
365 {
366         sdio_unregister_driver(&smssdio_driver);
367 }
368
369 module_init(smssdio_module_init);
370 module_exit(smssdio_module_exit);
371
372 MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
373 MODULE_AUTHOR("Pierre Ossman");
374 MODULE_LICENSE("GPL");