aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb
diff options
context:
space:
mode:
authorUri Shkolnik <uris@siano-ms.com>2009-03-27 13:16:57 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-06-16 18:14:17 -0400
commit319afbf97f209e3a907981f76e382c02ea3ecff3 (patch)
treeea3b8db486aea3cfc1c7005cf8438d0f0f35f81a /drivers/media/dvb
parentecfe0cfa3cae9a8402df12d81b159d851b61cf29 (diff)
V4L/DVB (11240): siano: add high level SDIO interface driver for SMS based cards
This patch provides SDIO interface driver for SMS (Siano Mobile Silicon) based devices. The patch includes SMS high level SDIO driver and requires patching the kernel SDIO stack, those stack patches had been provided previously. I would like to thank Pierre Ossman, MMC maintainer, who wrote this driver. Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> Signed-off-by: Uri Shkolnik <uris@siano-ms.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/dvb')
-rw-r--r--drivers/media/dvb/siano/smssdio.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c
new file mode 100644
index 000000000000..31ba8c5af5bc
--- /dev/null
+++ b/drivers/media/dvb/siano/smssdio.c
@@ -0,0 +1,356 @@
1/*
2 * smssdio.c - Siano 1xxx SDIO interface driver
3 *
4 * Copyright 2008 Pierre Ossman
5 *
6 * Based on code by Siano Mobile Silicon, Inc.,
7 * Copyright (C) 2006-2008, Uri Shkolnik
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or (at
12 * your option) any later version.
13 *
14 *
15 * This hardware is a bit odd in that all transfers should be done
16 * to/from the SMSSDIO_DATA register, yet the "increase address" bit
17 * always needs to be set.
18 *
19 * Also, buffers from the card are always aligned to 128 byte
20 * boundaries.
21 */
22
23/*
24 * General cleanup notes:
25 *
26 * - only typedefs should be name *_t
27 *
28 * - use ERR_PTR and friends for smscore_register_device()
29 *
30 * - smscore_getbuffer should zero fields
31 *
32 * Fix stop command
33 */
34
35#include <linux/moduleparam.h>
36#include <linux/firmware.h>
37#include <linux/delay.h>
38#include <linux/mmc/card.h>
39#include <linux/mmc/sdio_func.h>
40#include <linux/mmc/sdio_ids.h>
41
42#include "smscoreapi.h"
43#include "sms-cards.h"
44
45/* Registers */
46
47#define SMSSDIO_DATA 0x00
48#define SMSSDIO_INT 0x04
49
50static 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 { /* end: all zeroes */ },
62};
63
64MODULE_DEVICE_TABLE(sdio, smssdio_ids);
65
66struct smssdio_device {
67 struct sdio_func *func;
68
69 struct smscore_device_t *coredev;
70
71 struct smscore_buffer_t *split_cb;
72};
73
74/*******************************************************************/
75/* Siano core callbacks */
76/*******************************************************************/
77
78static int smssdio_sendrequest(void *context, void *buffer, size_t size)
79{
80 int ret;
81 struct smssdio_device *smsdev;
82
83 smsdev = context;
84
85 sdio_claim_host(smsdev->func);
86
87 while (size >= smsdev->func->cur_blksize) {
88 ret = sdio_write_blocks(smsdev->func, SMSSDIO_DATA, buffer, 1);
89 if (ret)
90 goto out;
91
92 buffer += smsdev->func->cur_blksize;
93 size -= smsdev->func->cur_blksize;
94 }
95
96 if (size) {
97 ret = sdio_write_bytes(smsdev->func, SMSSDIO_DATA,
98 buffer, size);
99 if (ret)
100 goto out;
101 }
102
103out:
104 sdio_release_host(smsdev->func);
105
106 return ret;
107}
108
109/*******************************************************************/
110/* SDIO callbacks */
111/*******************************************************************/
112
113static void smssdio_interrupt(struct sdio_func *func)
114{
115 int ret, isr;
116
117 struct smssdio_device *smsdev;
118 struct smscore_buffer_t *cb;
119 struct SmsMsgHdr_ST *hdr;
120 size_t size;
121
122 smsdev = sdio_get_drvdata(func);
123
124 /*
125 * The interrupt register has no defined meaning. It is just
126 * a way of turning of the level triggered interrupt.
127 */
128 isr = sdio_readb(func, SMSSDIO_INT, &ret);
129 if (ret) {
130 dev_err(&smsdev->func->dev,
131 "Unable to read interrupt register!\n");
132 return;
133 }
134
135 if (smsdev->split_cb == NULL) {
136 cb = smscore_getbuffer(smsdev->coredev);
137 if (!cb) {
138 dev_err(&smsdev->func->dev,
139 "Unable to allocate data buffer!\n");
140 return;
141 }
142
143 ret = sdio_read_blocks(smsdev->func, cb->p, SMSSDIO_DATA, 1);
144 if (ret) {
145 dev_err(&smsdev->func->dev,
146 "Error %d reading initial block!\n", ret);
147 return;
148 }
149
150 hdr = cb->p;
151
152 if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) {
153 smsdev->split_cb = cb;
154 return;
155 }
156
157 size = hdr->msgLength - smsdev->func->cur_blksize;
158 } else {
159 cb = smsdev->split_cb;
160 hdr = cb->p;
161
162 size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST);
163
164 smsdev->split_cb = NULL;
165 }
166
167 if (hdr->msgLength > smsdev->func->cur_blksize) {
168 void *buffer;
169
170 size = ALIGN(size, 128);
171 buffer = cb->p + hdr->msgLength;
172
173 BUG_ON(smsdev->func->cur_blksize != 128);
174
175 /*
176 * First attempt to transfer all of it in one go...
177 */
178 ret = sdio_read_blocks(smsdev->func, buffer,
179 SMSSDIO_DATA, size / 128);
180 if (ret && ret != -EINVAL) {
181 smscore_putbuffer(smsdev->coredev, cb);
182 dev_err(&smsdev->func->dev,
183 "Error %d reading data from card!\n", ret);
184 return;
185 }
186
187 /*
188 * ..then fall back to one block at a time if that is
189 * not possible...
190 *
191 * (we have to do this manually because of the
192 * problem with the "increase address" bit)
193 */
194 if (ret == -EINVAL) {
195 while (size) {
196 ret = sdio_read_blocks(smsdev->func,
197 buffer, SMSSDIO_DATA, 1);
198 if (ret) {
199 smscore_putbuffer(smsdev->coredev, cb);
200 dev_err(&smsdev->func->dev,
201 "Error %d reading "
202 "data from card!\n", ret);
203 return;
204 }
205
206 buffer += smsdev->func->cur_blksize;
207 if (size > smsdev->func->cur_blksize)
208 size -= smsdev->func->cur_blksize;
209 else
210 size = 0;
211 }
212 }
213 }
214
215 cb->size = hdr->msgLength;
216 cb->offset = 0;
217
218 smscore_onresponse(smsdev->coredev, cb);
219}
220
221static int smssdio_probe(struct sdio_func *func,
222 const struct sdio_device_id *id)
223{
224 int ret;
225
226 int board_id;
227 struct smssdio_device *smsdev;
228 struct smsdevice_params_t params;
229
230 board_id = id->driver_data;
231
232 smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL);
233 if (!smsdev)
234 return -ENOMEM;
235
236 smsdev->func = func;
237
238 memset(&params, 0, sizeof(struct smsdevice_params_t));
239
240 params.device = &func->dev;
241 params.buffer_size = 0x5000; /* ?? */
242 params.num_buffers = 22; /* ?? */
243 params.context = smsdev;
244
245 snprintf(params.devpath, sizeof(params.devpath),
246 "sdio\\%s", sdio_func_id(func));
247
248 params.sendrequest_handler = smssdio_sendrequest;
249
250 params.device_type = sms_get_board(board_id)->type;
251
252 if (params.device_type != SMS_STELLAR)
253 params.flags |= SMS_DEVICE_FAMILY2;
254 else {
255 /*
256 * FIXME: Stellar needs special handling...
257 */
258 ret = -ENODEV;
259 goto free;
260 }
261
262 ret = smscore_register_device(&params, &smsdev->coredev);
263 if (ret < 0)
264 goto free;
265
266 smscore_set_board_id(smsdev->coredev, board_id);
267
268 sdio_claim_host(func);
269
270 ret = sdio_enable_func(func);
271 if (ret)
272 goto release;
273
274 ret = sdio_set_block_size(func, 128);
275 if (ret)
276 goto disable;
277
278 ret = sdio_claim_irq(func, smssdio_interrupt);
279 if (ret)
280 goto disable;
281
282 sdio_set_drvdata(func, smsdev);
283
284 sdio_release_host(func);
285
286 ret = smscore_start_device(smsdev->coredev);
287 if (ret < 0)
288 goto reclaim;
289
290 return 0;
291
292reclaim:
293 sdio_claim_host(func);
294 sdio_release_irq(func);
295disable:
296 sdio_disable_func(func);
297release:
298 sdio_release_host(func);
299 smscore_unregister_device(smsdev->coredev);
300free:
301 kfree(smsdev);
302
303 return ret;
304}
305
306static void smssdio_remove(struct sdio_func *func)
307{
308 struct smssdio_device *smsdev;
309
310 smsdev = sdio_get_drvdata(func);
311
312 /* FIXME: racy! */
313 if (smsdev->split_cb)
314 smscore_putbuffer(smsdev->coredev, smsdev->split_cb);
315
316 smscore_unregister_device(smsdev->coredev);
317
318 sdio_claim_host(func);
319 sdio_release_irq(func);
320 sdio_disable_func(func);
321 sdio_release_host(func);
322
323 kfree(smsdev);
324}
325
326static struct sdio_driver smssdio_driver = {
327 .name = "smssdio",
328 .id_table = smssdio_ids,
329 .probe = smssdio_probe,
330 .remove = smssdio_remove,
331};
332
333/*******************************************************************/
334/* Module functions */
335/*******************************************************************/
336
337int smssdio_register(void)
338{
339 int ret = 0;
340
341 printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
342 printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
343
344 ret = sdio_register_driver(&smssdio_driver);
345
346 return ret;
347}
348
349void smssdio_unregister(void)
350{
351 sdio_unregister_driver(&smssdio_driver);
352}
353
354MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
355MODULE_AUTHOR("Pierre Ossman");
356MODULE_LICENSE("GPL");