684 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * TI FlashMedia driver4 *5 * Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>6 *7 * Special thanks to Carlos Corbacho for providing various MemoryStick cards8 * that made this driver possible.9 */10 11#include <linux/tifm.h>12#include <linux/memstick.h>13#include <linux/highmem.h>14#include <linux/scatterlist.h>15#include <linux/log2.h>16#include <linux/module.h>17#include <asm/io.h>18 19#define DRIVER_NAME "tifm_ms"20 21static bool no_dma;22module_param(no_dma, bool, 0644);23 24/*25 * Some control bits of TIFM appear to conform to Sony's reference design,26 * so I'm just assuming they all are.27 */28 29#define TIFM_MS_STAT_DRQ 0x0400030#define TIFM_MS_STAT_MSINT 0x0200031#define TIFM_MS_STAT_RDY 0x0100032#define TIFM_MS_STAT_CRC 0x0020033#define TIFM_MS_STAT_TOE 0x0010034#define TIFM_MS_STAT_EMP 0x0002035#define TIFM_MS_STAT_FUL 0x0001036#define TIFM_MS_STAT_CED 0x0000837#define TIFM_MS_STAT_ERR 0x0000438#define TIFM_MS_STAT_BRQ 0x0000239#define TIFM_MS_STAT_CNK 0x0000140 41#define TIFM_MS_SYS_DMA 0x1000042#define TIFM_MS_SYS_RESET 0x0800043#define TIFM_MS_SYS_SRAC 0x0400044#define TIFM_MS_SYS_INTEN 0x0200045#define TIFM_MS_SYS_NOCRC 0x0100046#define TIFM_MS_SYS_INTCLR 0x0080047#define TIFM_MS_SYS_MSIEN 0x0040048#define TIFM_MS_SYS_FCLR 0x0020049#define TIFM_MS_SYS_FDIR 0x0010050#define TIFM_MS_SYS_DAM 0x0008051#define TIFM_MS_SYS_DRM 0x0004052#define TIFM_MS_SYS_DRQSL 0x0002053#define TIFM_MS_SYS_REI 0x0001054#define TIFM_MS_SYS_REO 0x0000855#define TIFM_MS_SYS_BSY_MASK 0x0000756 57#define TIFM_MS_SYS_FIFO (TIFM_MS_SYS_INTEN | TIFM_MS_SYS_MSIEN \58 | TIFM_MS_SYS_FCLR | TIFM_MS_SYS_BSY_MASK)59 60/* Hardware flags */61enum {62 CMD_READY = 0x01,63 FIFO_READY = 0x02,64 CARD_INT = 0x0465};66 67struct tifm_ms {68 struct tifm_dev *dev;69 struct timer_list timer;70 struct memstick_request *req;71 struct tasklet_struct notify;72 unsigned int mode_mask;73 unsigned int block_pos;74 unsigned long timeout_jiffies;75 unsigned char eject:1,76 use_dma:1;77 unsigned char cmd_flags;78 unsigned char io_pos;79 unsigned int io_word;80};81 82static unsigned int tifm_ms_read_data(struct tifm_ms *host,83 unsigned char *buf, unsigned int length)84{85 struct tifm_dev *sock = host->dev;86 unsigned int off = 0;87 88 while (host->io_pos && length) {89 buf[off++] = host->io_word & 0xff;90 host->io_word >>= 8;91 length--;92 host->io_pos--;93 }94 95 if (!length)96 return off;97 98 while (!(TIFM_MS_STAT_EMP & readl(sock->addr + SOCK_MS_STATUS))) {99 if (length < 4)100 break;101 *(unsigned int *)(buf + off) = __raw_readl(sock->addr102 + SOCK_MS_DATA);103 length -= 4;104 off += 4;105 }106 107 if (length108 && !(TIFM_MS_STAT_EMP & readl(sock->addr + SOCK_MS_STATUS))) {109 host->io_word = readl(sock->addr + SOCK_MS_DATA);110 for (host->io_pos = 4; host->io_pos; --host->io_pos) {111 buf[off++] = host->io_word & 0xff;112 host->io_word >>= 8;113 length--;114 if (!length)115 break;116 }117 }118 119 return off;120}121 122static unsigned int tifm_ms_write_data(struct tifm_ms *host,123 unsigned char *buf, unsigned int length)124{125 struct tifm_dev *sock = host->dev;126 unsigned int off = 0;127 128 if (host->io_pos) {129 while (host->io_pos < 4 && length) {130 host->io_word |= buf[off++] << (host->io_pos * 8);131 host->io_pos++;132 length--;133 }134 }135 136 if (host->io_pos == 4137 && !(TIFM_MS_STAT_FUL & readl(sock->addr + SOCK_MS_STATUS))) {138 writel(TIFM_MS_SYS_FDIR | readl(sock->addr + SOCK_MS_SYSTEM),139 sock->addr + SOCK_MS_SYSTEM);140 writel(host->io_word, sock->addr + SOCK_MS_DATA);141 host->io_pos = 0;142 host->io_word = 0;143 } else if (host->io_pos) {144 return off;145 }146 147 if (!length)148 return off;149 150 while (!(TIFM_MS_STAT_FUL & readl(sock->addr + SOCK_MS_STATUS))) {151 if (length < 4)152 break;153 writel(TIFM_MS_SYS_FDIR | readl(sock->addr + SOCK_MS_SYSTEM),154 sock->addr + SOCK_MS_SYSTEM);155 __raw_writel(*(unsigned int *)(buf + off),156 sock->addr + SOCK_MS_DATA);157 length -= 4;158 off += 4;159 }160 161 switch (length) {162 case 3:163 host->io_word |= buf[off + 2] << 16;164 host->io_pos++;165 fallthrough;166 case 2:167 host->io_word |= buf[off + 1] << 8;168 host->io_pos++;169 fallthrough;170 case 1:171 host->io_word |= buf[off];172 host->io_pos++;173 }174 175 off += host->io_pos;176 177 return off;178}179 180static unsigned int tifm_ms_transfer_data(struct tifm_ms *host)181{182 struct tifm_dev *sock = host->dev;183 unsigned int length;184 unsigned int off;185 unsigned int t_size, p_cnt;186 unsigned char *buf;187 struct page *pg;188 unsigned long flags = 0;189 190 if (host->req->long_data) {191 length = host->req->sg.length - host->block_pos;192 off = host->req->sg.offset + host->block_pos;193 } else {194 length = host->req->data_len - host->block_pos;195 off = 0;196 }197 dev_dbg(&sock->dev, "fifo data transfer, %d, %d\n", length,198 host->block_pos);199 200 while (length) {201 unsigned int p_off;202 203 if (host->req->long_data) {204 pg = nth_page(sg_page(&host->req->sg),205 off >> PAGE_SHIFT);206 p_off = offset_in_page(off);207 p_cnt = PAGE_SIZE - p_off;208 p_cnt = min(p_cnt, length);209 210 local_irq_save(flags);211 buf = kmap_atomic(pg) + p_off;212 } else {213 buf = host->req->data + host->block_pos;214 p_cnt = host->req->data_len - host->block_pos;215 }216 217 t_size = host->req->data_dir == WRITE218 ? tifm_ms_write_data(host, buf, p_cnt)219 : tifm_ms_read_data(host, buf, p_cnt);220 221 if (host->req->long_data) {222 kunmap_atomic(buf - p_off);223 local_irq_restore(flags);224 }225 226 if (!t_size)227 break;228 host->block_pos += t_size;229 length -= t_size;230 off += t_size;231 }232 233 dev_dbg(&sock->dev, "fifo data transfer, %d remaining\n", length);234 if (!length && (host->req->data_dir == WRITE)) {235 if (host->io_pos) {236 writel(TIFM_MS_SYS_FDIR237 | readl(sock->addr + SOCK_MS_SYSTEM),238 sock->addr + SOCK_MS_SYSTEM);239 writel(host->io_word, sock->addr + SOCK_MS_DATA);240 }241 writel(TIFM_MS_SYS_FDIR242 | readl(sock->addr + SOCK_MS_SYSTEM),243 sock->addr + SOCK_MS_SYSTEM);244 writel(0, sock->addr + SOCK_MS_DATA);245 } else {246 readl(sock->addr + SOCK_MS_DATA);247 }248 249 return length;250}251 252static int tifm_ms_issue_cmd(struct tifm_ms *host)253{254 struct tifm_dev *sock = host->dev;255 unsigned int data_len, cmd, sys_param;256 257 host->cmd_flags = 0;258 host->block_pos = 0;259 host->io_pos = 0;260 host->io_word = 0;261 host->cmd_flags = 0;262 263 host->use_dma = !no_dma;264 265 if (host->req->long_data) {266 data_len = host->req->sg.length;267 if (!is_power_of_2(data_len))268 host->use_dma = 0;269 } else {270 data_len = host->req->data_len;271 host->use_dma = 0;272 }273 274 writel(TIFM_FIFO_INT_SETALL,275 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);276 writel(TIFM_FIFO_ENABLE,277 sock->addr + SOCK_FIFO_CONTROL);278 279 if (host->use_dma) {280 if (1 != tifm_map_sg(sock, &host->req->sg, 1,281 host->req->data_dir == READ282 ? DMA_FROM_DEVICE283 : DMA_TO_DEVICE)) {284 host->req->error = -ENOMEM;285 return host->req->error;286 }287 data_len = sg_dma_len(&host->req->sg);288 289 writel(ilog2(data_len) - 2,290 sock->addr + SOCK_FIFO_PAGE_SIZE);291 writel(TIFM_FIFO_INTMASK,292 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);293 sys_param = TIFM_DMA_EN | (1 << 8);294 if (host->req->data_dir == WRITE)295 sys_param |= TIFM_DMA_TX;296 297 writel(TIFM_FIFO_INTMASK,298 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);299 300 writel(sg_dma_address(&host->req->sg),301 sock->addr + SOCK_DMA_ADDRESS);302 writel(sys_param, sock->addr + SOCK_DMA_CONTROL);303 } else {304 writel(host->mode_mask | TIFM_MS_SYS_FIFO,305 sock->addr + SOCK_MS_SYSTEM);306 307 writel(TIFM_FIFO_MORE,308 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);309 }310 311 mod_timer(&host->timer, jiffies + host->timeout_jiffies);312 writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),313 sock->addr + SOCK_CONTROL);314 host->req->error = 0;315 316 sys_param = readl(sock->addr + SOCK_MS_SYSTEM);317 sys_param |= TIFM_MS_SYS_INTCLR;318 319 if (host->use_dma)320 sys_param |= TIFM_MS_SYS_DMA;321 else322 sys_param &= ~TIFM_MS_SYS_DMA;323 324 writel(sys_param, sock->addr + SOCK_MS_SYSTEM);325 326 cmd = (host->req->tpc & 0xf) << 12;327 cmd |= data_len;328 writel(cmd, sock->addr + SOCK_MS_COMMAND);329 330 dev_dbg(&sock->dev, "executing TPC %x, %x\n", cmd, sys_param);331 return 0;332}333 334static void tifm_ms_complete_cmd(struct tifm_ms *host)335{336 struct tifm_dev *sock = host->dev;337 struct memstick_host *msh = tifm_get_drvdata(sock);338 int rc;339 340 del_timer(&host->timer);341 342 host->req->int_reg = readl(sock->addr + SOCK_MS_STATUS) & 0xff;343 host->req->int_reg = (host->req->int_reg & 1)344 | ((host->req->int_reg << 4) & 0xe0);345 346 writel(TIFM_FIFO_INT_SETALL,347 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);348 writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);349 350 if (host->use_dma) {351 tifm_unmap_sg(sock, &host->req->sg, 1,352 host->req->data_dir == READ353 ? DMA_FROM_DEVICE354 : DMA_TO_DEVICE);355 }356 357 writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),358 sock->addr + SOCK_CONTROL);359 360 dev_dbg(&sock->dev, "TPC complete\n");361 do {362 rc = memstick_next_req(msh, &host->req);363 } while (!rc && tifm_ms_issue_cmd(host));364}365 366static int tifm_ms_check_status(struct tifm_ms *host)367{368 if (!host->req->error) {369 if (!(host->cmd_flags & CMD_READY))370 return 1;371 if (!(host->cmd_flags & FIFO_READY))372 return 1;373 if (host->req->need_card_int374 && !(host->cmd_flags & CARD_INT))375 return 1;376 }377 return 0;378}379 380/* Called from interrupt handler */381static void tifm_ms_data_event(struct tifm_dev *sock)382{383 struct tifm_ms *host;384 unsigned int fifo_status = 0, host_status = 0;385 int rc = 1;386 387 spin_lock(&sock->lock);388 host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));389 fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);390 host_status = readl(sock->addr + SOCK_MS_STATUS);391 dev_dbg(&sock->dev,392 "data event: fifo_status %x, host_status %x, flags %x\n",393 fifo_status, host_status, host->cmd_flags);394 395 if (host->req) {396 if (host->use_dma && (fifo_status & 1)) {397 host->cmd_flags |= FIFO_READY;398 rc = tifm_ms_check_status(host);399 }400 if (!host->use_dma && (fifo_status & TIFM_FIFO_MORE)) {401 if (!tifm_ms_transfer_data(host)) {402 host->cmd_flags |= FIFO_READY;403 rc = tifm_ms_check_status(host);404 }405 }406 }407 408 writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);409 if (!rc)410 tifm_ms_complete_cmd(host);411 412 spin_unlock(&sock->lock);413}414 415 416/* Called from interrupt handler */417static void tifm_ms_card_event(struct tifm_dev *sock)418{419 struct tifm_ms *host;420 unsigned int host_status = 0;421 int rc = 1;422 423 spin_lock(&sock->lock);424 host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));425 host_status = readl(sock->addr + SOCK_MS_STATUS);426 dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n",427 host_status, host->cmd_flags);428 429 if (host->req) {430 if (host_status & TIFM_MS_STAT_TOE)431 host->req->error = -ETIME;432 else if (host_status & TIFM_MS_STAT_CRC)433 host->req->error = -EILSEQ;434 435 if (host_status & TIFM_MS_STAT_RDY)436 host->cmd_flags |= CMD_READY;437 438 if (host_status & TIFM_MS_STAT_MSINT)439 host->cmd_flags |= CARD_INT;440 441 rc = tifm_ms_check_status(host);442 443 }444 445 writel(TIFM_MS_SYS_INTCLR | readl(sock->addr + SOCK_MS_SYSTEM),446 sock->addr + SOCK_MS_SYSTEM);447 448 if (!rc)449 tifm_ms_complete_cmd(host);450 451 spin_unlock(&sock->lock);452 return;453}454 455static void tifm_ms_req_tasklet(unsigned long data)456{457 struct memstick_host *msh = (struct memstick_host *)data;458 struct tifm_ms *host = memstick_priv(msh);459 struct tifm_dev *sock = host->dev;460 unsigned long flags;461 int rc;462 463 spin_lock_irqsave(&sock->lock, flags);464 if (!host->req) {465 if (host->eject) {466 do {467 rc = memstick_next_req(msh, &host->req);468 if (!rc)469 host->req->error = -ETIME;470 } while (!rc);471 spin_unlock_irqrestore(&sock->lock, flags);472 return;473 }474 475 do {476 rc = memstick_next_req(msh, &host->req);477 } while (!rc && tifm_ms_issue_cmd(host));478 }479 spin_unlock_irqrestore(&sock->lock, flags);480}481 482static void tifm_ms_dummy_submit(struct memstick_host *msh)483{484 return;485}486 487static void tifm_ms_submit_req(struct memstick_host *msh)488{489 struct tifm_ms *host = memstick_priv(msh);490 491 tasklet_schedule(&host->notify);492}493 494static int tifm_ms_set_param(struct memstick_host *msh,495 enum memstick_param param,496 int value)497{498 struct tifm_ms *host = memstick_priv(msh);499 struct tifm_dev *sock = host->dev;500 501 switch (param) {502 case MEMSTICK_POWER:503 /* also affected by media detection mechanism */504 if (value == MEMSTICK_POWER_ON) {505 host->mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI;506 writel(TIFM_MS_SYS_RESET, sock->addr + SOCK_MS_SYSTEM);507 writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR,508 sock->addr + SOCK_MS_SYSTEM);509 writel(0xffffffff, sock->addr + SOCK_MS_STATUS);510 } else if (value == MEMSTICK_POWER_OFF) {511 writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR,512 sock->addr + SOCK_MS_SYSTEM);513 writel(0xffffffff, sock->addr + SOCK_MS_STATUS);514 } else515 return -EINVAL;516 break;517 case MEMSTICK_INTERFACE:518 if (value == MEMSTICK_SERIAL) {519 host->mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI;520 writel((~TIFM_CTRL_FAST_CLK)521 & readl(sock->addr + SOCK_CONTROL),522 sock->addr + SOCK_CONTROL);523 } else if (value == MEMSTICK_PAR4) {524 host->mode_mask = 0;525 writel(TIFM_CTRL_FAST_CLK526 | readl(sock->addr + SOCK_CONTROL),527 sock->addr + SOCK_CONTROL);528 } else529 return -EINVAL;530 break;531 }532 533 return 0;534}535 536static void tifm_ms_abort(struct timer_list *t)537{538 struct tifm_ms *host = from_timer(host, t, timer);539 540 dev_dbg(&host->dev->dev, "status %x\n",541 readl(host->dev->addr + SOCK_MS_STATUS));542 printk(KERN_ERR543 "%s : card failed to respond for a long period of time "544 "(%x, %x)\n",545 dev_name(&host->dev->dev), host->req ? host->req->tpc : 0,546 host->cmd_flags);547 548 tifm_eject(host->dev);549}550 551static int tifm_ms_probe(struct tifm_dev *sock)552{553 struct memstick_host *msh;554 struct tifm_ms *host;555 int rc = -EIO;556 557 if (!(TIFM_SOCK_STATE_OCCUPIED558 & readl(sock->addr + SOCK_PRESENT_STATE))) {559 printk(KERN_WARNING "%s : card gone, unexpectedly\n",560 dev_name(&sock->dev));561 return rc;562 }563 564 msh = memstick_alloc_host(sizeof(struct tifm_ms), &sock->dev);565 if (!msh)566 return -ENOMEM;567 568 host = memstick_priv(msh);569 tifm_set_drvdata(sock, msh);570 host->dev = sock;571 host->timeout_jiffies = msecs_to_jiffies(1000);572 573 timer_setup(&host->timer, tifm_ms_abort, 0);574 tasklet_init(&host->notify, tifm_ms_req_tasklet, (unsigned long)msh);575 576 msh->request = tifm_ms_submit_req;577 msh->set_param = tifm_ms_set_param;578 sock->card_event = tifm_ms_card_event;579 sock->data_event = tifm_ms_data_event;580 if (tifm_has_ms_pif(sock))581 msh->caps |= MEMSTICK_CAP_PAR4;582 583 rc = memstick_add_host(msh);584 if (!rc)585 return 0;586 587 memstick_free_host(msh);588 return rc;589}590 591static void tifm_ms_remove(struct tifm_dev *sock)592{593 struct memstick_host *msh = tifm_get_drvdata(sock);594 struct tifm_ms *host = memstick_priv(msh);595 int rc = 0;596 unsigned long flags;597 598 msh->request = tifm_ms_dummy_submit;599 tasklet_kill(&host->notify);600 spin_lock_irqsave(&sock->lock, flags);601 host->eject = 1;602 if (host->req) {603 del_timer(&host->timer);604 writel(TIFM_FIFO_INT_SETALL,605 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);606 writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);607 if (host->use_dma)608 tifm_unmap_sg(sock, &host->req->sg, 1,609 host->req->data_dir == READ610 ? DMA_TO_DEVICE611 : DMA_FROM_DEVICE);612 host->req->error = -ETIME;613 614 do {615 rc = memstick_next_req(msh, &host->req);616 if (!rc)617 host->req->error = -ETIME;618 } while (!rc);619 }620 spin_unlock_irqrestore(&sock->lock, flags);621 622 memstick_remove_host(msh);623 memstick_free_host(msh);624}625 626#ifdef CONFIG_PM627 628static int tifm_ms_suspend(struct tifm_dev *sock, pm_message_t state)629{630 struct memstick_host *msh = tifm_get_drvdata(sock);631 632 memstick_suspend_host(msh);633 return 0;634}635 636static int tifm_ms_resume(struct tifm_dev *sock)637{638 struct memstick_host *msh = tifm_get_drvdata(sock);639 640 memstick_resume_host(msh);641 return 0;642}643 644#else645 646#define tifm_ms_suspend NULL647#define tifm_ms_resume NULL648 649#endif /* CONFIG_PM */650 651static struct tifm_device_id tifm_ms_id_tbl[] = {652 { TIFM_TYPE_MS }, { 0 }653};654 655static struct tifm_driver tifm_ms_driver = {656 .driver = {657 .name = DRIVER_NAME,658 .owner = THIS_MODULE659 },660 .id_table = tifm_ms_id_tbl,661 .probe = tifm_ms_probe,662 .remove = tifm_ms_remove,663 .suspend = tifm_ms_suspend,664 .resume = tifm_ms_resume665};666 667static int __init tifm_ms_init(void)668{669 return tifm_register_driver(&tifm_ms_driver);670}671 672static void __exit tifm_ms_exit(void)673{674 tifm_unregister_driver(&tifm_ms_driver);675}676 677MODULE_AUTHOR("Alex Dubov");678MODULE_DESCRIPTION("TI FlashMedia MemoryStick driver");679MODULE_LICENSE("GPL");680MODULE_DEVICE_TABLE(tifm, tifm_ms_id_tbl);681 682module_init(tifm_ms_init);683module_exit(tifm_ms_exit);684