Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/media/dvb/siano/smssdio.c
15112 views
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/slab.h>
37
#include <linux/firmware.h>
38
#include <linux/delay.h>
39
#include <linux/mmc/card.h>
40
#include <linux/mmc/sdio_func.h>
41
#include <linux/mmc/sdio_ids.h>
42
43
#include "smscoreapi.h"
44
#include "sms-cards.h"
45
46
/* Registers */
47
48
#define SMSSDIO_DATA 0x00
49
#define SMSSDIO_INT 0x04
50
#define SMSSDIO_BLOCK_SIZE 128
51
52
static const struct sdio_device_id smssdio_ids[] __devinitconst = {
53
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),
54
.driver_data = SMS1XXX_BOARD_SIANO_STELLAR},
55
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0),
56
.driver_data = SMS1XXX_BOARD_SIANO_NOVA_A},
57
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0),
58
.driver_data = SMS1XXX_BOARD_SIANO_NOVA_B},
59
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0),
60
.driver_data = SMS1XXX_BOARD_SIANO_VEGA},
61
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
62
.driver_data = SMS1XXX_BOARD_SIANO_VEGA},
63
{ /* end: all zeroes */ },
64
};
65
66
MODULE_DEVICE_TABLE(sdio, smssdio_ids);
67
68
struct smssdio_device {
69
struct sdio_func *func;
70
71
struct smscore_device_t *coredev;
72
73
struct smscore_buffer_t *split_cb;
74
};
75
76
/*******************************************************************/
77
/* Siano core callbacks */
78
/*******************************************************************/
79
80
static int smssdio_sendrequest(void *context, void *buffer, size_t size)
81
{
82
int ret = 0;
83
struct smssdio_device *smsdev;
84
85
smsdev = context;
86
87
sdio_claim_host(smsdev->func);
88
89
while (size >= smsdev->func->cur_blksize) {
90
ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,
91
buffer, smsdev->func->cur_blksize);
92
if (ret)
93
goto out;
94
95
buffer += smsdev->func->cur_blksize;
96
size -= smsdev->func->cur_blksize;
97
}
98
99
if (size) {
100
ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,
101
buffer, size);
102
}
103
104
out:
105
sdio_release_host(smsdev->func);
106
107
return ret;
108
}
109
110
/*******************************************************************/
111
/* SDIO callbacks */
112
/*******************************************************************/
113
114
static void smssdio_interrupt(struct sdio_func *func)
115
{
116
int ret, isr;
117
118
struct smssdio_device *smsdev;
119
struct smscore_buffer_t *cb;
120
struct SmsMsgHdr_ST *hdr;
121
size_t size;
122
123
smsdev = sdio_get_drvdata(func);
124
125
/*
126
* The interrupt register has no defined meaning. It is just
127
* a way of turning of the level triggered interrupt.
128
*/
129
isr = sdio_readb(func, SMSSDIO_INT, &ret);
130
if (ret) {
131
sms_err("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
sms_err("Unable to allocate data buffer!\n");
139
return;
140
}
141
142
ret = sdio_memcpy_fromio(smsdev->func,
143
cb->p,
144
SMSSDIO_DATA,
145
SMSSDIO_BLOCK_SIZE);
146
if (ret) {
147
sms_err("Error %d reading initial block!\n", ret);
148
return;
149
}
150
151
hdr = cb->p;
152
153
if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) {
154
smsdev->split_cb = cb;
155
return;
156
}
157
158
if (hdr->msgLength > smsdev->func->cur_blksize)
159
size = hdr->msgLength - smsdev->func->cur_blksize;
160
else
161
size = 0;
162
} else {
163
cb = smsdev->split_cb;
164
hdr = cb->p;
165
166
size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST);
167
168
smsdev->split_cb = NULL;
169
}
170
171
if (size) {
172
void *buffer;
173
174
buffer = cb->p + (hdr->msgLength - size);
175
size = ALIGN(size, SMSSDIO_BLOCK_SIZE);
176
177
BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE);
178
179
/*
180
* First attempt to transfer all of it in one go...
181
*/
182
ret = sdio_memcpy_fromio(smsdev->func,
183
buffer,
184
SMSSDIO_DATA,
185
size);
186
if (ret && ret != -EINVAL) {
187
smscore_putbuffer(smsdev->coredev, cb);
188
sms_err("Error %d reading data from card!\n", ret);
189
return;
190
}
191
192
/*
193
* ..then fall back to one block at a time if that is
194
* not possible...
195
*
196
* (we have to do this manually because of the
197
* problem with the "increase address" bit)
198
*/
199
if (ret == -EINVAL) {
200
while (size) {
201
ret = sdio_memcpy_fromio(smsdev->func,
202
buffer, SMSSDIO_DATA,
203
smsdev->func->cur_blksize);
204
if (ret) {
205
smscore_putbuffer(smsdev->coredev, cb);
206
sms_err("Error %d reading "
207
"data from card!\n", ret);
208
return;
209
}
210
211
buffer += smsdev->func->cur_blksize;
212
if (size > smsdev->func->cur_blksize)
213
size -= smsdev->func->cur_blksize;
214
else
215
size = 0;
216
}
217
}
218
}
219
220
cb->size = hdr->msgLength;
221
cb->offset = 0;
222
223
smscore_onresponse(smsdev->coredev, cb);
224
}
225
226
static int __devinit smssdio_probe(struct sdio_func *func,
227
const struct sdio_device_id *id)
228
{
229
int ret;
230
231
int board_id;
232
struct smssdio_device *smsdev;
233
struct smsdevice_params_t params;
234
235
board_id = id->driver_data;
236
237
smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL);
238
if (!smsdev)
239
return -ENOMEM;
240
241
smsdev->func = func;
242
243
memset(&params, 0, sizeof(struct smsdevice_params_t));
244
245
params.device = &func->dev;
246
params.buffer_size = 0x5000; /* ?? */
247
params.num_buffers = 22; /* ?? */
248
params.context = smsdev;
249
250
snprintf(params.devpath, sizeof(params.devpath),
251
"sdio\\%s", sdio_func_id(func));
252
253
params.sendrequest_handler = smssdio_sendrequest;
254
255
params.device_type = sms_get_board(board_id)->type;
256
257
if (params.device_type != SMS_STELLAR)
258
params.flags |= SMS_DEVICE_FAMILY2;
259
else {
260
/*
261
* FIXME: Stellar needs special handling...
262
*/
263
ret = -ENODEV;
264
goto free;
265
}
266
267
ret = smscore_register_device(&params, &smsdev->coredev);
268
if (ret < 0)
269
goto free;
270
271
smscore_set_board_id(smsdev->coredev, board_id);
272
273
sdio_claim_host(func);
274
275
ret = sdio_enable_func(func);
276
if (ret)
277
goto release;
278
279
ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE);
280
if (ret)
281
goto disable;
282
283
ret = sdio_claim_irq(func, smssdio_interrupt);
284
if (ret)
285
goto disable;
286
287
sdio_set_drvdata(func, smsdev);
288
289
sdio_release_host(func);
290
291
ret = smscore_start_device(smsdev->coredev);
292
if (ret < 0)
293
goto reclaim;
294
295
return 0;
296
297
reclaim:
298
sdio_claim_host(func);
299
sdio_release_irq(func);
300
disable:
301
sdio_disable_func(func);
302
release:
303
sdio_release_host(func);
304
smscore_unregister_device(smsdev->coredev);
305
free:
306
kfree(smsdev);
307
308
return ret;
309
}
310
311
static void smssdio_remove(struct sdio_func *func)
312
{
313
struct smssdio_device *smsdev;
314
315
smsdev = sdio_get_drvdata(func);
316
317
/* FIXME: racy! */
318
if (smsdev->split_cb)
319
smscore_putbuffer(smsdev->coredev, smsdev->split_cb);
320
321
smscore_unregister_device(smsdev->coredev);
322
323
sdio_claim_host(func);
324
sdio_release_irq(func);
325
sdio_disable_func(func);
326
sdio_release_host(func);
327
328
kfree(smsdev);
329
}
330
331
static struct sdio_driver smssdio_driver = {
332
.name = "smssdio",
333
.id_table = smssdio_ids,
334
.probe = smssdio_probe,
335
.remove = smssdio_remove,
336
};
337
338
/*******************************************************************/
339
/* Module functions */
340
/*******************************************************************/
341
342
static int __init smssdio_module_init(void)
343
{
344
int ret = 0;
345
346
printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
347
printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
348
349
ret = sdio_register_driver(&smssdio_driver);
350
351
return ret;
352
}
353
354
static void __exit smssdio_module_exit(void)
355
{
356
sdio_unregister_driver(&smssdio_driver);
357
}
358
359
module_init(smssdio_module_init);
360
module_exit(smssdio_module_exit);
361
362
MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
363
MODULE_AUTHOR("Pierre Ossman");
364
MODULE_LICENSE("GPL");
365
366