Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/mmc/host/sdricoh_cs.c
15111 views
1
/*
2
* sdricoh_cs.c - driver for Ricoh Secure Digital Card Readers that can be
3
* found on some Ricoh RL5c476 II cardbus bridge
4
*
5
* Copyright (C) 2006 - 2008 Sascha Sommer <[email protected]>
6
*
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
11
*
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
16
*
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
*
21
*/
22
23
/*
24
#define DEBUG
25
#define VERBOSE_DEBUG
26
*/
27
#include <linux/delay.h>
28
#include <linux/highmem.h>
29
#include <linux/pci.h>
30
#include <linux/ioport.h>
31
#include <linux/scatterlist.h>
32
33
#include <pcmcia/cistpl.h>
34
#include <pcmcia/ds.h>
35
#include <linux/io.h>
36
37
#include <linux/mmc/host.h>
38
39
#define DRIVER_NAME "sdricoh_cs"
40
41
static unsigned int switchlocked;
42
43
/* i/o region */
44
#define SDRICOH_PCI_REGION 0
45
#define SDRICOH_PCI_REGION_SIZE 0x1000
46
47
/* registers */
48
#define R104_VERSION 0x104
49
#define R200_CMD 0x200
50
#define R204_CMD_ARG 0x204
51
#define R208_DATAIO 0x208
52
#define R20C_RESP 0x20c
53
#define R21C_STATUS 0x21c
54
#define R2E0_INIT 0x2e0
55
#define R2E4_STATUS_RESP 0x2e4
56
#define R2F0_RESET 0x2f0
57
#define R224_MODE 0x224
58
#define R226_BLOCKSIZE 0x226
59
#define R228_POWER 0x228
60
#define R230_DATA 0x230
61
62
/* flags for the R21C_STATUS register */
63
#define STATUS_CMD_FINISHED 0x00000001
64
#define STATUS_TRANSFER_FINISHED 0x00000004
65
#define STATUS_CARD_INSERTED 0x00000020
66
#define STATUS_CARD_LOCKED 0x00000080
67
#define STATUS_CMD_TIMEOUT 0x00400000
68
#define STATUS_READY_TO_READ 0x01000000
69
#define STATUS_READY_TO_WRITE 0x02000000
70
#define STATUS_BUSY 0x40000000
71
72
/* timeouts */
73
#define INIT_TIMEOUT 100
74
#define CMD_TIMEOUT 100000
75
#define TRANSFER_TIMEOUT 100000
76
#define BUSY_TIMEOUT 32767
77
78
/* list of supported pcmcia devices */
79
static const struct pcmcia_device_id pcmcia_ids[] = {
80
/* vendor and device strings followed by their crc32 hashes */
81
PCMCIA_DEVICE_PROD_ID12("RICOH", "Bay1Controller", 0xd9f522ed,
82
0xc3901202),
83
PCMCIA_DEVICE_PROD_ID12("RICOH", "Bay Controller", 0xd9f522ed,
84
0xace80909),
85
PCMCIA_DEVICE_NULL,
86
};
87
88
MODULE_DEVICE_TABLE(pcmcia, pcmcia_ids);
89
90
/* mmc privdata */
91
struct sdricoh_host {
92
struct device *dev;
93
struct mmc_host *mmc; /* MMC structure */
94
unsigned char __iomem *iobase;
95
struct pci_dev *pci_dev;
96
int app_cmd;
97
};
98
99
/***************** register i/o helper functions *****************************/
100
101
static inline unsigned int sdricoh_readl(struct sdricoh_host *host,
102
unsigned int reg)
103
{
104
unsigned int value = readl(host->iobase + reg);
105
dev_vdbg(host->dev, "rl %x 0x%x\n", reg, value);
106
return value;
107
}
108
109
static inline void sdricoh_writel(struct sdricoh_host *host, unsigned int reg,
110
unsigned int value)
111
{
112
writel(value, host->iobase + reg);
113
dev_vdbg(host->dev, "wl %x 0x%x\n", reg, value);
114
115
}
116
117
static inline unsigned int sdricoh_readw(struct sdricoh_host *host,
118
unsigned int reg)
119
{
120
unsigned int value = readw(host->iobase + reg);
121
dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value);
122
return value;
123
}
124
125
static inline void sdricoh_writew(struct sdricoh_host *host, unsigned int reg,
126
unsigned short value)
127
{
128
writew(value, host->iobase + reg);
129
dev_vdbg(host->dev, "ww %x 0x%x\n", reg, value);
130
}
131
132
static inline unsigned int sdricoh_readb(struct sdricoh_host *host,
133
unsigned int reg)
134
{
135
unsigned int value = readb(host->iobase + reg);
136
dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value);
137
return value;
138
}
139
140
static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted,
141
unsigned int timeout){
142
unsigned int loop;
143
unsigned int status = 0;
144
struct device *dev = host->dev;
145
for (loop = 0; loop < timeout; loop++) {
146
status = sdricoh_readl(host, R21C_STATUS);
147
sdricoh_writel(host, R2E4_STATUS_RESP, status);
148
if (status & wanted)
149
break;
150
}
151
152
if (loop == timeout) {
153
dev_err(dev, "query_status: timeout waiting for %x\n", wanted);
154
return -ETIMEDOUT;
155
}
156
157
/* do not do this check in the loop as some commands fail otherwise */
158
if (status & 0x7F0000) {
159
dev_err(dev, "waiting for status bit %x failed\n", wanted);
160
return -EINVAL;
161
}
162
return 0;
163
164
}
165
166
static int sdricoh_mmc_cmd(struct sdricoh_host *host, unsigned char opcode,
167
unsigned int arg)
168
{
169
unsigned int status;
170
int result = 0;
171
unsigned int loop = 0;
172
/* reset status reg? */
173
sdricoh_writel(host, R21C_STATUS, 0x18);
174
/* fill parameters */
175
sdricoh_writel(host, R204_CMD_ARG, arg);
176
sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode);
177
/* wait for command completion */
178
if (opcode) {
179
for (loop = 0; loop < CMD_TIMEOUT; loop++) {
180
status = sdricoh_readl(host, R21C_STATUS);
181
sdricoh_writel(host, R2E4_STATUS_RESP, status);
182
if (status & STATUS_CMD_FINISHED)
183
break;
184
}
185
/* don't check for timeout in the loop it is not always
186
reset correctly
187
*/
188
if (loop == CMD_TIMEOUT || status & STATUS_CMD_TIMEOUT)
189
result = -ETIMEDOUT;
190
191
}
192
193
return result;
194
195
}
196
197
static int sdricoh_reset(struct sdricoh_host *host)
198
{
199
dev_dbg(host->dev, "reset\n");
200
sdricoh_writel(host, R2F0_RESET, 0x10001);
201
sdricoh_writel(host, R2E0_INIT, 0x10000);
202
if (sdricoh_readl(host, R2E0_INIT) != 0x10000)
203
return -EIO;
204
sdricoh_writel(host, R2E0_INIT, 0x10007);
205
206
sdricoh_writel(host, R224_MODE, 0x2000000);
207
sdricoh_writel(host, R228_POWER, 0xe0);
208
209
210
/* status register ? */
211
sdricoh_writel(host, R21C_STATUS, 0x18);
212
213
return 0;
214
}
215
216
static int sdricoh_blockio(struct sdricoh_host *host, int read,
217
u8 *buf, int len)
218
{
219
int size;
220
u32 data = 0;
221
/* wait until the data is available */
222
if (read) {
223
if (sdricoh_query_status(host, STATUS_READY_TO_READ,
224
TRANSFER_TIMEOUT))
225
return -ETIMEDOUT;
226
sdricoh_writel(host, R21C_STATUS, 0x18);
227
/* read data */
228
while (len) {
229
data = sdricoh_readl(host, R230_DATA);
230
size = min(len, 4);
231
len -= size;
232
while (size) {
233
*buf = data & 0xFF;
234
buf++;
235
data >>= 8;
236
size--;
237
}
238
}
239
} else {
240
if (sdricoh_query_status(host, STATUS_READY_TO_WRITE,
241
TRANSFER_TIMEOUT))
242
return -ETIMEDOUT;
243
sdricoh_writel(host, R21C_STATUS, 0x18);
244
/* write data */
245
while (len) {
246
size = min(len, 4);
247
len -= size;
248
while (size) {
249
data >>= 8;
250
data |= (u32)*buf << 24;
251
buf++;
252
size--;
253
}
254
sdricoh_writel(host, R230_DATA, data);
255
}
256
}
257
258
if (len)
259
return -EIO;
260
261
return 0;
262
}
263
264
static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq)
265
{
266
struct sdricoh_host *host = mmc_priv(mmc);
267
struct mmc_command *cmd = mrq->cmd;
268
struct mmc_data *data = cmd->data;
269
struct device *dev = host->dev;
270
unsigned char opcode = cmd->opcode;
271
int i;
272
273
dev_dbg(dev, "=============================\n");
274
dev_dbg(dev, "sdricoh_request opcode=%i\n", opcode);
275
276
sdricoh_writel(host, R21C_STATUS, 0x18);
277
278
/* MMC_APP_CMDs need some special handling */
279
if (host->app_cmd) {
280
opcode |= 64;
281
host->app_cmd = 0;
282
} else if (opcode == 55)
283
host->app_cmd = 1;
284
285
/* read/write commands seem to require this */
286
if (data) {
287
sdricoh_writew(host, R226_BLOCKSIZE, data->blksz);
288
sdricoh_writel(host, R208_DATAIO, 0);
289
}
290
291
cmd->error = sdricoh_mmc_cmd(host, opcode, cmd->arg);
292
293
/* read response buffer */
294
if (cmd->flags & MMC_RSP_PRESENT) {
295
if (cmd->flags & MMC_RSP_136) {
296
/* CRC is stripped so we need to do some shifting. */
297
for (i = 0; i < 4; i++) {
298
cmd->resp[i] =
299
sdricoh_readl(host,
300
R20C_RESP + (3 - i) * 4) << 8;
301
if (i != 3)
302
cmd->resp[i] |=
303
sdricoh_readb(host, R20C_RESP +
304
(3 - i) * 4 - 1);
305
}
306
} else
307
cmd->resp[0] = sdricoh_readl(host, R20C_RESP);
308
}
309
310
/* transfer data */
311
if (data && cmd->error == 0) {
312
dev_dbg(dev, "transfer: blksz %i blocks %i sg_len %i "
313
"sg length %i\n", data->blksz, data->blocks,
314
data->sg_len, data->sg->length);
315
316
/* enter data reading mode */
317
sdricoh_writel(host, R21C_STATUS, 0x837f031e);
318
for (i = 0; i < data->blocks; i++) {
319
size_t len = data->blksz;
320
u8 *buf;
321
struct page *page;
322
int result;
323
page = sg_page(data->sg);
324
325
buf = kmap(page) + data->sg->offset + (len * i);
326
result =
327
sdricoh_blockio(host,
328
data->flags & MMC_DATA_READ, buf, len);
329
kunmap(page);
330
flush_dcache_page(page);
331
if (result) {
332
dev_err(dev, "sdricoh_request: cmd %i "
333
"block transfer failed\n", cmd->opcode);
334
cmd->error = result;
335
break;
336
} else
337
data->bytes_xfered += len;
338
}
339
340
sdricoh_writel(host, R208_DATAIO, 1);
341
342
if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED,
343
TRANSFER_TIMEOUT)) {
344
dev_err(dev, "sdricoh_request: transfer end error\n");
345
cmd->error = -EINVAL;
346
}
347
}
348
/* FIXME check busy flag */
349
350
mmc_request_done(mmc, mrq);
351
dev_dbg(dev, "=============================\n");
352
}
353
354
static void sdricoh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
355
{
356
struct sdricoh_host *host = mmc_priv(mmc);
357
dev_dbg(host->dev, "set_ios\n");
358
359
if (ios->power_mode == MMC_POWER_ON) {
360
sdricoh_writel(host, R228_POWER, 0xc0e0);
361
362
if (ios->bus_width == MMC_BUS_WIDTH_4) {
363
sdricoh_writel(host, R224_MODE, 0x2000300);
364
sdricoh_writel(host, R228_POWER, 0x40e0);
365
} else {
366
sdricoh_writel(host, R224_MODE, 0x2000340);
367
}
368
369
} else if (ios->power_mode == MMC_POWER_UP) {
370
sdricoh_writel(host, R224_MODE, 0x2000320);
371
sdricoh_writel(host, R228_POWER, 0xe0);
372
}
373
}
374
375
static int sdricoh_get_ro(struct mmc_host *mmc)
376
{
377
struct sdricoh_host *host = mmc_priv(mmc);
378
unsigned int status;
379
380
status = sdricoh_readl(host, R21C_STATUS);
381
sdricoh_writel(host, R2E4_STATUS_RESP, status);
382
383
/* some notebooks seem to have the locked flag switched */
384
if (switchlocked)
385
return !(status & STATUS_CARD_LOCKED);
386
387
return (status & STATUS_CARD_LOCKED);
388
}
389
390
static struct mmc_host_ops sdricoh_ops = {
391
.request = sdricoh_request,
392
.set_ios = sdricoh_set_ios,
393
.get_ro = sdricoh_get_ro,
394
};
395
396
/* initialize the control and register it to the mmc framework */
397
static int sdricoh_init_mmc(struct pci_dev *pci_dev,
398
struct pcmcia_device *pcmcia_dev)
399
{
400
int result = 0;
401
void __iomem *iobase = NULL;
402
struct mmc_host *mmc = NULL;
403
struct sdricoh_host *host = NULL;
404
struct device *dev = &pcmcia_dev->dev;
405
/* map iomem */
406
if (pci_resource_len(pci_dev, SDRICOH_PCI_REGION) !=
407
SDRICOH_PCI_REGION_SIZE) {
408
dev_dbg(dev, "unexpected pci resource len\n");
409
return -ENODEV;
410
}
411
iobase =
412
pci_iomap(pci_dev, SDRICOH_PCI_REGION, SDRICOH_PCI_REGION_SIZE);
413
if (!iobase) {
414
dev_err(dev, "unable to map iobase\n");
415
return -ENODEV;
416
}
417
/* check version? */
418
if (readl(iobase + R104_VERSION) != 0x4000) {
419
dev_dbg(dev, "no supported mmc controller found\n");
420
result = -ENODEV;
421
goto err;
422
}
423
/* allocate privdata */
424
mmc = pcmcia_dev->priv =
425
mmc_alloc_host(sizeof(struct sdricoh_host), &pcmcia_dev->dev);
426
if (!mmc) {
427
dev_err(dev, "mmc_alloc_host failed\n");
428
result = -ENOMEM;
429
goto err;
430
}
431
host = mmc_priv(mmc);
432
433
host->iobase = iobase;
434
host->dev = dev;
435
host->pci_dev = pci_dev;
436
437
mmc->ops = &sdricoh_ops;
438
439
/* FIXME: frequency and voltage handling is done by the controller
440
*/
441
mmc->f_min = 450000;
442
mmc->f_max = 24000000;
443
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
444
mmc->caps |= MMC_CAP_4_BIT_DATA;
445
446
mmc->max_seg_size = 1024 * 512;
447
mmc->max_blk_size = 512;
448
449
/* reset the controller */
450
if (sdricoh_reset(host)) {
451
dev_dbg(dev, "could not reset\n");
452
result = -EIO;
453
goto err;
454
455
}
456
457
result = mmc_add_host(mmc);
458
459
if (!result) {
460
dev_dbg(dev, "mmc host registered\n");
461
return 0;
462
}
463
464
err:
465
if (iobase)
466
pci_iounmap(pci_dev, iobase);
467
if (mmc)
468
mmc_free_host(mmc);
469
470
return result;
471
}
472
473
/* search for supported mmc controllers */
474
static int sdricoh_pcmcia_probe(struct pcmcia_device *pcmcia_dev)
475
{
476
struct pci_dev *pci_dev = NULL;
477
478
dev_info(&pcmcia_dev->dev, "Searching MMC controller for pcmcia device"
479
" %s %s ...\n", pcmcia_dev->prod_id[0], pcmcia_dev->prod_id[1]);
480
481
/* search pci cardbus bridge that contains the mmc controller */
482
/* the io region is already claimed by yenta_socket... */
483
while ((pci_dev =
484
pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476,
485
pci_dev))) {
486
/* try to init the device */
487
if (!sdricoh_init_mmc(pci_dev, pcmcia_dev)) {
488
dev_info(&pcmcia_dev->dev, "MMC controller found\n");
489
return 0;
490
}
491
492
}
493
dev_err(&pcmcia_dev->dev, "No MMC controller was found.\n");
494
return -ENODEV;
495
}
496
497
static void sdricoh_pcmcia_detach(struct pcmcia_device *link)
498
{
499
struct mmc_host *mmc = link->priv;
500
501
dev_dbg(&link->dev, "detach\n");
502
503
/* remove mmc host */
504
if (mmc) {
505
struct sdricoh_host *host = mmc_priv(mmc);
506
mmc_remove_host(mmc);
507
pci_iounmap(host->pci_dev, host->iobase);
508
pci_dev_put(host->pci_dev);
509
mmc_free_host(mmc);
510
}
511
pcmcia_disable_device(link);
512
513
}
514
515
#ifdef CONFIG_PM
516
static int sdricoh_pcmcia_suspend(struct pcmcia_device *link)
517
{
518
struct mmc_host *mmc = link->priv;
519
dev_dbg(&link->dev, "suspend\n");
520
mmc_suspend_host(mmc);
521
return 0;
522
}
523
524
static int sdricoh_pcmcia_resume(struct pcmcia_device *link)
525
{
526
struct mmc_host *mmc = link->priv;
527
dev_dbg(&link->dev, "resume\n");
528
sdricoh_reset(mmc_priv(mmc));
529
mmc_resume_host(mmc);
530
return 0;
531
}
532
#else
533
#define sdricoh_pcmcia_suspend NULL
534
#define sdricoh_pcmcia_resume NULL
535
#endif
536
537
static struct pcmcia_driver sdricoh_driver = {
538
.name = DRIVER_NAME,
539
.probe = sdricoh_pcmcia_probe,
540
.remove = sdricoh_pcmcia_detach,
541
.id_table = pcmcia_ids,
542
.suspend = sdricoh_pcmcia_suspend,
543
.resume = sdricoh_pcmcia_resume,
544
};
545
546
/*****************************************************************************\
547
* *
548
* Driver init/exit *
549
* *
550
\*****************************************************************************/
551
552
static int __init sdricoh_drv_init(void)
553
{
554
return pcmcia_register_driver(&sdricoh_driver);
555
}
556
557
static void __exit sdricoh_drv_exit(void)
558
{
559
pcmcia_unregister_driver(&sdricoh_driver);
560
}
561
562
module_init(sdricoh_drv_init);
563
module_exit(sdricoh_drv_exit);
564
565
module_param(switchlocked, uint, 0444);
566
567
MODULE_AUTHOR("Sascha Sommer <[email protected]>");
568
MODULE_DESCRIPTION("Ricoh PCMCIA Secure Digital Interface driver");
569
MODULE_LICENSE("GPL");
570
571
MODULE_PARM_DESC(switchlocked, "Switch the cards locked status."
572
"Use this when unlocked cards are shown readonly (default 0)");
573
574