529 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * sdricoh_cs.c - driver for Ricoh Secure Digital Card Readers that can be4 * found on some Ricoh RL5c476 II cardbus bridge5 *6 * Copyright (C) 2006 - 2008 Sascha Sommer <saschasommer@freenet.de>7 */8 9/*10#define DEBUG11#define VERBOSE_DEBUG12*/13#include <linux/delay.h>14#include <linux/highmem.h>15#include <linux/module.h>16#include <linux/pci.h>17#include <linux/ioport.h>18#include <linux/iopoll.h>19#include <linux/scatterlist.h>20 21#include <pcmcia/cistpl.h>22#include <pcmcia/ds.h>23#include <linux/io.h>24 25#include <linux/mmc/host.h>26#include <linux/mmc/mmc.h>27 28#define DRIVER_NAME "sdricoh_cs"29 30static unsigned int switchlocked;31 32/* i/o region */33#define SDRICOH_PCI_REGION 034#define SDRICOH_PCI_REGION_SIZE 0x100035 36/* registers */37#define R104_VERSION 0x10438#define R200_CMD 0x20039#define R204_CMD_ARG 0x20440#define R208_DATAIO 0x20841#define R20C_RESP 0x20c42#define R21C_STATUS 0x21c43#define R2E0_INIT 0x2e044#define R2E4_STATUS_RESP 0x2e445#define R2F0_RESET 0x2f046#define R224_MODE 0x22447#define R226_BLOCKSIZE 0x22648#define R228_POWER 0x22849#define R230_DATA 0x23050 51/* flags for the R21C_STATUS register */52#define STATUS_CMD_FINISHED 0x0000000153#define STATUS_TRANSFER_FINISHED 0x0000000454#define STATUS_CARD_INSERTED 0x0000002055#define STATUS_CARD_LOCKED 0x0000008056#define STATUS_CMD_TIMEOUT 0x0040000057#define STATUS_READY_TO_READ 0x0100000058#define STATUS_READY_TO_WRITE 0x0200000059#define STATUS_BUSY 0x4000000060 61/* timeouts */62#define SDRICOH_CMD_TIMEOUT_US 100000063#define SDRICOH_DATA_TIMEOUT_US 100000064 65/* list of supported pcmcia devices */66static const struct pcmcia_device_id pcmcia_ids[] = {67 /* vendor and device strings followed by their crc32 hashes */68 PCMCIA_DEVICE_PROD_ID12("RICOH", "Bay1Controller", 0xd9f522ed,69 0xc3901202),70 PCMCIA_DEVICE_PROD_ID12("RICOH", "Bay Controller", 0xd9f522ed,71 0xace80909),72 PCMCIA_DEVICE_NULL,73};74 75MODULE_DEVICE_TABLE(pcmcia, pcmcia_ids);76 77/* mmc privdata */78struct sdricoh_host {79 struct device *dev;80 struct mmc_host *mmc; /* MMC structure */81 unsigned char __iomem *iobase;82 struct pci_dev *pci_dev;83 int app_cmd;84};85 86/***************** register i/o helper functions *****************************/87 88static inline unsigned int sdricoh_readl(struct sdricoh_host *host,89 unsigned int reg)90{91 unsigned int value = readl(host->iobase + reg);92 dev_vdbg(host->dev, "rl %x 0x%x\n", reg, value);93 return value;94}95 96static inline void sdricoh_writel(struct sdricoh_host *host, unsigned int reg,97 unsigned int value)98{99 writel(value, host->iobase + reg);100 dev_vdbg(host->dev, "wl %x 0x%x\n", reg, value);101 102}103 104static inline void sdricoh_writew(struct sdricoh_host *host, unsigned int reg,105 unsigned short value)106{107 writew(value, host->iobase + reg);108 dev_vdbg(host->dev, "ww %x 0x%x\n", reg, value);109}110 111static inline unsigned int sdricoh_readb(struct sdricoh_host *host,112 unsigned int reg)113{114 unsigned int value = readb(host->iobase + reg);115 dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value);116 return value;117}118 119static bool sdricoh_status_ok(struct sdricoh_host *host, unsigned int status,120 unsigned int wanted)121{122 sdricoh_writel(host, R2E4_STATUS_RESP, status);123 return status & wanted;124}125 126static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted)127{128 int ret;129 unsigned int status = 0;130 struct device *dev = host->dev;131 132 ret = read_poll_timeout(sdricoh_readl, status,133 sdricoh_status_ok(host, status, wanted),134 32, SDRICOH_DATA_TIMEOUT_US, false,135 host, R21C_STATUS);136 if (ret) {137 dev_err(dev, "query_status: timeout waiting for %x\n", wanted);138 return -ETIMEDOUT;139 }140 141 /* do not do this check in the loop as some commands fail otherwise */142 if (status & 0x7F0000) {143 dev_err(dev, "waiting for status bit %x failed\n", wanted);144 return -EINVAL;145 }146 return 0;147 148}149 150static int sdricoh_mmc_cmd(struct sdricoh_host *host, struct mmc_command *cmd)151{152 unsigned int status, timeout_us;153 int ret;154 unsigned char opcode = cmd->opcode;155 156 /* reset status reg? */157 sdricoh_writel(host, R21C_STATUS, 0x18);158 159 /* MMC_APP_CMDs need some special handling */160 if (host->app_cmd) {161 opcode |= 64;162 host->app_cmd = 0;163 } else if (opcode == MMC_APP_CMD)164 host->app_cmd = 1;165 166 /* fill parameters */167 sdricoh_writel(host, R204_CMD_ARG, cmd->arg);168 sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode);169 170 /* wait for command completion */171 if (!opcode)172 return 0;173 174 timeout_us = cmd->busy_timeout ? cmd->busy_timeout * 1000 :175 SDRICOH_CMD_TIMEOUT_US;176 177 ret = read_poll_timeout(sdricoh_readl, status,178 sdricoh_status_ok(host, status, STATUS_CMD_FINISHED),179 32, timeout_us, false,180 host, R21C_STATUS);181 182 /*183 * Don't check for timeout status in the loop, as it's not always reset184 * correctly.185 */186 if (ret || status & STATUS_CMD_TIMEOUT)187 return -ETIMEDOUT;188 189 return 0;190}191 192static int sdricoh_reset(struct sdricoh_host *host)193{194 dev_dbg(host->dev, "reset\n");195 sdricoh_writel(host, R2F0_RESET, 0x10001);196 sdricoh_writel(host, R2E0_INIT, 0x10000);197 if (sdricoh_readl(host, R2E0_INIT) != 0x10000)198 return -EIO;199 sdricoh_writel(host, R2E0_INIT, 0x10007);200 201 sdricoh_writel(host, R224_MODE, 0x2000000);202 sdricoh_writel(host, R228_POWER, 0xe0);203 204 205 /* status register ? */206 sdricoh_writel(host, R21C_STATUS, 0x18);207 208 return 0;209}210 211static int sdricoh_blockio(struct sdricoh_host *host, int read,212 u8 *buf, int len)213{214 int size;215 u32 data = 0;216 /* wait until the data is available */217 if (read) {218 if (sdricoh_query_status(host, STATUS_READY_TO_READ))219 return -ETIMEDOUT;220 sdricoh_writel(host, R21C_STATUS, 0x18);221 /* read data */222 while (len) {223 data = sdricoh_readl(host, R230_DATA);224 size = min(len, 4);225 len -= size;226 while (size) {227 *buf = data & 0xFF;228 buf++;229 data >>= 8;230 size--;231 }232 }233 } else {234 if (sdricoh_query_status(host, STATUS_READY_TO_WRITE))235 return -ETIMEDOUT;236 sdricoh_writel(host, R21C_STATUS, 0x18);237 /* write data */238 while (len) {239 size = min(len, 4);240 len -= size;241 while (size) {242 data >>= 8;243 data |= (u32)*buf << 24;244 buf++;245 size--;246 }247 sdricoh_writel(host, R230_DATA, data);248 }249 }250 251 return 0;252}253 254static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq)255{256 struct sdricoh_host *host = mmc_priv(mmc);257 struct mmc_command *cmd = mrq->cmd;258 struct mmc_data *data = cmd->data;259 struct device *dev = host->dev;260 int i;261 262 dev_dbg(dev, "=============================\n");263 dev_dbg(dev, "sdricoh_request opcode=%i\n", cmd->opcode);264 265 sdricoh_writel(host, R21C_STATUS, 0x18);266 267 /* read/write commands seem to require this */268 if (data) {269 sdricoh_writew(host, R226_BLOCKSIZE, data->blksz);270 sdricoh_writel(host, R208_DATAIO, 0);271 }272 273 cmd->error = sdricoh_mmc_cmd(host, cmd);274 275 /* read response buffer */276 if (cmd->flags & MMC_RSP_PRESENT) {277 if (cmd->flags & MMC_RSP_136) {278 /* CRC is stripped so we need to do some shifting. */279 for (i = 0; i < 4; i++) {280 cmd->resp[i] =281 sdricoh_readl(host,282 R20C_RESP + (3 - i) * 4) << 8;283 if (i != 3)284 cmd->resp[i] |=285 sdricoh_readb(host, R20C_RESP +286 (3 - i) * 4 - 1);287 }288 } else289 cmd->resp[0] = sdricoh_readl(host, R20C_RESP);290 }291 292 /* transfer data */293 if (data && cmd->error == 0) {294 dev_dbg(dev, "transfer: blksz %i blocks %i sg_len %i "295 "sg length %i\n", data->blksz, data->blocks,296 data->sg_len, data->sg->length);297 298 /* enter data reading mode */299 sdricoh_writel(host, R21C_STATUS, 0x837f031e);300 for (i = 0; i < data->blocks; i++) {301 size_t len = data->blksz;302 u8 *buf;303 struct page *page;304 int result;305 page = sg_page(data->sg);306 307 buf = kmap(page) + data->sg->offset + (len * i);308 result =309 sdricoh_blockio(host,310 data->flags & MMC_DATA_READ, buf, len);311 kunmap(page);312 flush_dcache_page(page);313 if (result) {314 dev_err(dev, "sdricoh_request: cmd %i "315 "block transfer failed\n", cmd->opcode);316 cmd->error = result;317 break;318 } else319 data->bytes_xfered += len;320 }321 322 sdricoh_writel(host, R208_DATAIO, 1);323 324 if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED)) {325 dev_err(dev, "sdricoh_request: transfer end error\n");326 cmd->error = -EINVAL;327 }328 }329 /* FIXME check busy flag */330 331 mmc_request_done(mmc, mrq);332 dev_dbg(dev, "=============================\n");333}334 335static void sdricoh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)336{337 struct sdricoh_host *host = mmc_priv(mmc);338 dev_dbg(host->dev, "set_ios\n");339 340 if (ios->power_mode == MMC_POWER_ON) {341 sdricoh_writel(host, R228_POWER, 0xc0e0);342 343 if (ios->bus_width == MMC_BUS_WIDTH_4) {344 sdricoh_writel(host, R224_MODE, 0x2000300);345 sdricoh_writel(host, R228_POWER, 0x40e0);346 } else {347 sdricoh_writel(host, R224_MODE, 0x2000340);348 }349 350 } else if (ios->power_mode == MMC_POWER_UP) {351 sdricoh_writel(host, R224_MODE, 0x2000320);352 sdricoh_writel(host, R228_POWER, 0xe0);353 }354}355 356static int sdricoh_get_ro(struct mmc_host *mmc)357{358 struct sdricoh_host *host = mmc_priv(mmc);359 unsigned int status;360 361 status = sdricoh_readl(host, R21C_STATUS);362 sdricoh_writel(host, R2E4_STATUS_RESP, status);363 364 /* some notebooks seem to have the locked flag switched */365 if (switchlocked)366 return !(status & STATUS_CARD_LOCKED);367 368 return (status & STATUS_CARD_LOCKED);369}370 371static const struct mmc_host_ops sdricoh_ops = {372 .request = sdricoh_request,373 .set_ios = sdricoh_set_ios,374 .get_ro = sdricoh_get_ro,375};376 377/* initialize the control and register it to the mmc framework */378static int sdricoh_init_mmc(struct pci_dev *pci_dev,379 struct pcmcia_device *pcmcia_dev)380{381 int result;382 void __iomem *iobase;383 struct mmc_host *mmc;384 struct sdricoh_host *host;385 struct device *dev = &pcmcia_dev->dev;386 /* map iomem */387 if (pci_resource_len(pci_dev, SDRICOH_PCI_REGION) !=388 SDRICOH_PCI_REGION_SIZE) {389 dev_dbg(dev, "unexpected pci resource len\n");390 return -ENODEV;391 }392 iobase =393 pci_iomap(pci_dev, SDRICOH_PCI_REGION, SDRICOH_PCI_REGION_SIZE);394 if (!iobase) {395 dev_err(dev, "unable to map iobase\n");396 return -ENODEV;397 }398 /* check version? */399 if (readl(iobase + R104_VERSION) != 0x4000) {400 dev_dbg(dev, "no supported mmc controller found\n");401 result = -ENODEV;402 goto unmap_io;403 }404 /* allocate privdata */405 mmc = pcmcia_dev->priv =406 mmc_alloc_host(sizeof(struct sdricoh_host), &pcmcia_dev->dev);407 if (!mmc) {408 dev_err(dev, "mmc_alloc_host failed\n");409 result = -ENOMEM;410 goto unmap_io;411 }412 host = mmc_priv(mmc);413 414 host->iobase = iobase;415 host->dev = dev;416 host->pci_dev = pci_dev;417 418 mmc->ops = &sdricoh_ops;419 420 /* FIXME: frequency and voltage handling is done by the controller421 */422 mmc->f_min = 450000;423 mmc->f_max = 24000000;424 mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;425 mmc->caps |= MMC_CAP_4_BIT_DATA;426 427 mmc->max_seg_size = 1024 * 512;428 mmc->max_blk_size = 512;429 430 /* reset the controller */431 if (sdricoh_reset(host)) {432 dev_dbg(dev, "could not reset\n");433 result = -EIO;434 goto free_host;435 }436 437 result = mmc_add_host(mmc);438 439 if (!result) {440 dev_dbg(dev, "mmc host registered\n");441 return 0;442 }443free_host:444 mmc_free_host(mmc);445unmap_io:446 pci_iounmap(pci_dev, iobase);447 return result;448}449 450/* search for supported mmc controllers */451static int sdricoh_pcmcia_probe(struct pcmcia_device *pcmcia_dev)452{453 struct pci_dev *pci_dev = NULL;454 455 dev_info(&pcmcia_dev->dev, "Searching MMC controller for pcmcia device"456 " %s %s ...\n", pcmcia_dev->prod_id[0], pcmcia_dev->prod_id[1]);457 458 /* search pci cardbus bridge that contains the mmc controller */459 /* the io region is already claimed by yenta_socket... */460 while ((pci_dev =461 pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476,462 pci_dev))) {463 /* try to init the device */464 if (!sdricoh_init_mmc(pci_dev, pcmcia_dev)) {465 dev_info(&pcmcia_dev->dev, "MMC controller found\n");466 return 0;467 }468 469 }470 dev_err(&pcmcia_dev->dev, "No MMC controller was found.\n");471 return -ENODEV;472}473 474static void sdricoh_pcmcia_detach(struct pcmcia_device *link)475{476 struct mmc_host *mmc = link->priv;477 478 dev_dbg(&link->dev, "detach\n");479 480 /* remove mmc host */481 if (mmc) {482 struct sdricoh_host *host = mmc_priv(mmc);483 mmc_remove_host(mmc);484 pci_iounmap(host->pci_dev, host->iobase);485 pci_dev_put(host->pci_dev);486 mmc_free_host(mmc);487 }488 pcmcia_disable_device(link);489 490}491 492#ifdef CONFIG_PM493static int sdricoh_pcmcia_suspend(struct pcmcia_device *link)494{495 dev_dbg(&link->dev, "suspend\n");496 return 0;497}498 499static int sdricoh_pcmcia_resume(struct pcmcia_device *link)500{501 struct mmc_host *mmc = link->priv;502 dev_dbg(&link->dev, "resume\n");503 sdricoh_reset(mmc_priv(mmc));504 return 0;505}506#else507#define sdricoh_pcmcia_suspend NULL508#define sdricoh_pcmcia_resume NULL509#endif510 511static struct pcmcia_driver sdricoh_driver = {512 .name = DRIVER_NAME,513 .probe = sdricoh_pcmcia_probe,514 .remove = sdricoh_pcmcia_detach,515 .id_table = pcmcia_ids,516 .suspend = sdricoh_pcmcia_suspend,517 .resume = sdricoh_pcmcia_resume,518};519module_pcmcia_driver(sdricoh_driver);520 521module_param(switchlocked, uint, 0444);522 523MODULE_AUTHOR("Sascha Sommer <saschasommer@freenet.de>");524MODULE_DESCRIPTION("Ricoh PCMCIA Secure Digital Interface driver");525MODULE_LICENSE("GPL");526 527MODULE_PARM_DESC(switchlocked, "Switch the cards locked status."528 "Use this when unlocked cards are shown readonly (default 0)");529