1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * TI FlashMedia driver 4 * 5 * Copyright (C) 2007 Alex Dubov <oakad@yahoo.com> 6 * 7 * Special thanks to Carlos Corbacho for providing various MemoryStick cards 8 * 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 21 static bool no_dma; 22 module_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 0x04000 30 #define TIFM_MS_STAT_MSINT 0x02000 31 #define TIFM_MS_STAT_RDY 0x01000 32 #define TIFM_MS_STAT_CRC 0x00200 33 #define TIFM_MS_STAT_TOE 0x00100 34 #define TIFM_MS_STAT_EMP 0x00020 35 #define TIFM_MS_STAT_FUL 0x00010 36 #define TIFM_MS_STAT_CED 0x00008 37 #define TIFM_MS_STAT_ERR 0x00004 38 #define TIFM_MS_STAT_BRQ 0x00002 39 #define TIFM_MS_STAT_CNK 0x00001 40 41 #define TIFM_MS_SYS_DMA 0x10000 42 #define TIFM_MS_SYS_RESET 0x08000 43 #define TIFM_MS_SYS_SRAC 0x04000 44 #define TIFM_MS_SYS_INTEN 0x02000 45 #define TIFM_MS_SYS_NOCRC 0x01000 46 #define TIFM_MS_SYS_INTCLR 0x00800 47 #define TIFM_MS_SYS_MSIEN 0x00400 48 #define TIFM_MS_SYS_FCLR 0x00200 49 #define TIFM_MS_SYS_FDIR 0x00100 50 #define TIFM_MS_SYS_DAM 0x00080 51 #define TIFM_MS_SYS_DRM 0x00040 52 #define TIFM_MS_SYS_DRQSL 0x00020 53 #define TIFM_MS_SYS_REI 0x00010 54 #define TIFM_MS_SYS_REO 0x00008 55 #define TIFM_MS_SYS_BSY_MASK 0x00007 56 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 */ 61 enum { 62 CMD_READY = 0x01, 63 FIFO_READY = 0x02, 64 CARD_INT = 0x04 65 }; 66 67 struct 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 82 static 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->addr 102 + SOCK_MS_DATA); 103 length -= 4; 104 off += 4; 105 } 106 107 if (length 108 && !(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 122 static 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 == 4 137 && !(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 180 static 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 = sg_page(&host->req->sg) + (off >> PAGE_SHIFT); 205 p_off = offset_in_page(off); 206 p_cnt = PAGE_SIZE - p_off; 207 p_cnt = min(p_cnt, length); 208 209 local_irq_save(flags); 210 buf = kmap_atomic(pg) + p_off; 211 } else { 212 buf = host->req->data + host->block_pos; 213 p_cnt = host->req->data_len - host->block_pos; 214 } 215 216 t_size = host->req->data_dir == WRITE 217 ? tifm_ms_write_data(host, buf, p_cnt) 218 : tifm_ms_read_data(host, buf, p_cnt); 219 220 if (host->req->long_data) { 221 kunmap_atomic(buf - p_off); 222 local_irq_restore(flags); 223 } 224 225 if (!t_size) 226 break; 227 host->block_pos += t_size; 228 length -= t_size; 229 off += t_size; 230 } 231 232 dev_dbg(&sock->dev, "fifo data transfer, %d remaining\n", length); 233 if (!length && (host->req->data_dir == WRITE)) { 234 if (host->io_pos) { 235 writel(TIFM_MS_SYS_FDIR 236 | readl(sock->addr + SOCK_MS_SYSTEM), 237 sock->addr + SOCK_MS_SYSTEM); 238 writel(host->io_word, sock->addr + SOCK_MS_DATA); 239 } 240 writel(TIFM_MS_SYS_FDIR 241 | readl(sock->addr + SOCK_MS_SYSTEM), 242 sock->addr + SOCK_MS_SYSTEM); 243 writel(0, sock->addr + SOCK_MS_DATA); 244 } else { 245 readl(sock->addr + SOCK_MS_DATA); 246 } 247 248 return length; 249 } 250 251 static int tifm_ms_issue_cmd(struct tifm_ms *host) 252 { 253 struct tifm_dev *sock = host->dev; 254 unsigned int data_len, cmd, sys_param; 255 256 host->cmd_flags = 0; 257 host->block_pos = 0; 258 host->io_pos = 0; 259 host->io_word = 0; 260 host->cmd_flags = 0; 261 262 host->use_dma = !no_dma; 263 264 if (host->req->long_data) { 265 data_len = host->req->sg.length; 266 if (!is_power_of_2(data_len)) 267 host->use_dma = 0; 268 } else { 269 data_len = host->req->data_len; 270 host->use_dma = 0; 271 } 272 273 writel(TIFM_FIFO_INT_SETALL, 274 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); 275 writel(TIFM_FIFO_ENABLE, 276 sock->addr + SOCK_FIFO_CONTROL); 277 278 if (host->use_dma) { 279 if (1 != tifm_map_sg(sock, &host->req->sg, 1, 280 host->req->data_dir == READ 281 ? DMA_FROM_DEVICE 282 : DMA_TO_DEVICE)) { 283 host->req->error = -ENOMEM; 284 return host->req->error; 285 } 286 data_len = sg_dma_len(&host->req->sg); 287 288 writel(ilog2(data_len) - 2, 289 sock->addr + SOCK_FIFO_PAGE_SIZE); 290 writel(TIFM_FIFO_INTMASK, 291 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); 292 sys_param = TIFM_DMA_EN | (1 << 8); 293 if (host->req->data_dir == WRITE) 294 sys_param |= TIFM_DMA_TX; 295 296 writel(TIFM_FIFO_INTMASK, 297 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); 298 299 writel(sg_dma_address(&host->req->sg), 300 sock->addr + SOCK_DMA_ADDRESS); 301 writel(sys_param, sock->addr + SOCK_DMA_CONTROL); 302 } else { 303 writel(host->mode_mask | TIFM_MS_SYS_FIFO, 304 sock->addr + SOCK_MS_SYSTEM); 305 306 writel(TIFM_FIFO_MORE, 307 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); 308 } 309 310 mod_timer(&host->timer, jiffies + host->timeout_jiffies); 311 writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), 312 sock->addr + SOCK_CONTROL); 313 host->req->error = 0; 314 315 sys_param = readl(sock->addr + SOCK_MS_SYSTEM); 316 sys_param |= TIFM_MS_SYS_INTCLR; 317 318 if (host->use_dma) 319 sys_param |= TIFM_MS_SYS_DMA; 320 else 321 sys_param &= ~TIFM_MS_SYS_DMA; 322 323 writel(sys_param, sock->addr + SOCK_MS_SYSTEM); 324 325 cmd = (host->req->tpc & 0xf) << 12; 326 cmd |= data_len; 327 writel(cmd, sock->addr + SOCK_MS_COMMAND); 328 329 dev_dbg(&sock->dev, "executing TPC %x, %x\n", cmd, sys_param); 330 return 0; 331 } 332 333 static void tifm_ms_complete_cmd(struct tifm_ms *host) 334 { 335 struct tifm_dev *sock = host->dev; 336 struct memstick_host *msh = tifm_get_drvdata(sock); 337 int rc; 338 339 timer_delete(&host->timer); 340 341 host->req->int_reg = readl(sock->addr + SOCK_MS_STATUS) & 0xff; 342 host->req->int_reg = (host->req->int_reg & 1) 343 | ((host->req->int_reg << 4) & 0xe0); 344 345 writel(TIFM_FIFO_INT_SETALL, 346 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); 347 writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); 348 349 if (host->use_dma) { 350 tifm_unmap_sg(sock, &host->req->sg, 1, 351 host->req->data_dir == READ 352 ? DMA_FROM_DEVICE 353 : DMA_TO_DEVICE); 354 } 355 356 writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), 357 sock->addr + SOCK_CONTROL); 358 359 dev_dbg(&sock->dev, "TPC complete\n"); 360 do { 361 rc = memstick_next_req(msh, &host->req); 362 } while (!rc && tifm_ms_issue_cmd(host)); 363 } 364 365 static int tifm_ms_check_status(struct tifm_ms *host) 366 { 367 if (!host->req->error) { 368 if (!(host->cmd_flags & CMD_READY)) 369 return 1; 370 if (!(host->cmd_flags & FIFO_READY)) 371 return 1; 372 if (host->req->need_card_int 373 && !(host->cmd_flags & CARD_INT)) 374 return 1; 375 } 376 return 0; 377 } 378 379 /* Called from interrupt handler */ 380 static void tifm_ms_data_event(struct tifm_dev *sock) 381 { 382 struct tifm_ms *host; 383 unsigned int fifo_status = 0, host_status = 0; 384 int rc = 1; 385 386 spin_lock(&sock->lock); 387 host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock)); 388 fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); 389 host_status = readl(sock->addr + SOCK_MS_STATUS); 390 dev_dbg(&sock->dev, 391 "data event: fifo_status %x, host_status %x, flags %x\n", 392 fifo_status, host_status, host->cmd_flags); 393 394 if (host->req) { 395 if (host->use_dma && (fifo_status & 1)) { 396 host->cmd_flags |= FIFO_READY; 397 rc = tifm_ms_check_status(host); 398 } 399 if (!host->use_dma && (fifo_status & TIFM_FIFO_MORE)) { 400 if (!tifm_ms_transfer_data(host)) { 401 host->cmd_flags |= FIFO_READY; 402 rc = tifm_ms_check_status(host); 403 } 404 } 405 } 406 407 writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); 408 if (!rc) 409 tifm_ms_complete_cmd(host); 410 411 spin_unlock(&sock->lock); 412 } 413 414 415 /* Called from interrupt handler */ 416 static void tifm_ms_card_event(struct tifm_dev *sock) 417 { 418 struct tifm_ms *host; 419 unsigned int host_status = 0; 420 int rc = 1; 421 422 spin_lock(&sock->lock); 423 host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock)); 424 host_status = readl(sock->addr + SOCK_MS_STATUS); 425 dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n", 426 host_status, host->cmd_flags); 427 428 if (host->req) { 429 if (host_status & TIFM_MS_STAT_TOE) 430 host->req->error = -ETIME; 431 else if (host_status & TIFM_MS_STAT_CRC) 432 host->req->error = -EILSEQ; 433 434 if (host_status & TIFM_MS_STAT_RDY) 435 host->cmd_flags |= CMD_READY; 436 437 if (host_status & TIFM_MS_STAT_MSINT) 438 host->cmd_flags |= CARD_INT; 439 440 rc = tifm_ms_check_status(host); 441 442 } 443 444 writel(TIFM_MS_SYS_INTCLR | readl(sock->addr + SOCK_MS_SYSTEM), 445 sock->addr + SOCK_MS_SYSTEM); 446 447 if (!rc) 448 tifm_ms_complete_cmd(host); 449 450 spin_unlock(&sock->lock); 451 return; 452 } 453 454 static void tifm_ms_req_tasklet(unsigned long data) 455 { 456 struct memstick_host *msh = (struct memstick_host *)data; 457 struct tifm_ms *host = memstick_priv(msh); 458 struct tifm_dev *sock = host->dev; 459 unsigned long flags; 460 int rc; 461 462 spin_lock_irqsave(&sock->lock, flags); 463 if (!host->req) { 464 if (host->eject) { 465 do { 466 rc = memstick_next_req(msh, &host->req); 467 if (!rc) 468 host->req->error = -ETIME; 469 } while (!rc); 470 spin_unlock_irqrestore(&sock->lock, flags); 471 return; 472 } 473 474 do { 475 rc = memstick_next_req(msh, &host->req); 476 } while (!rc && tifm_ms_issue_cmd(host)); 477 } 478 spin_unlock_irqrestore(&sock->lock, flags); 479 } 480 481 static void tifm_ms_dummy_submit(struct memstick_host *msh) 482 { 483 return; 484 } 485 486 static void tifm_ms_submit_req(struct memstick_host *msh) 487 { 488 struct tifm_ms *host = memstick_priv(msh); 489 490 tasklet_schedule(&host->notify); 491 } 492 493 static int tifm_ms_set_param(struct memstick_host *msh, 494 enum memstick_param param, 495 int value) 496 { 497 struct tifm_ms *host = memstick_priv(msh); 498 struct tifm_dev *sock = host->dev; 499 500 switch (param) { 501 case MEMSTICK_POWER: 502 /* also affected by media detection mechanism */ 503 if (value == MEMSTICK_POWER_ON) { 504 host->mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI; 505 writel(TIFM_MS_SYS_RESET, sock->addr + SOCK_MS_SYSTEM); 506 writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR, 507 sock->addr + SOCK_MS_SYSTEM); 508 writel(0xffffffff, sock->addr + SOCK_MS_STATUS); 509 } else if (value == MEMSTICK_POWER_OFF) { 510 writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR, 511 sock->addr + SOCK_MS_SYSTEM); 512 writel(0xffffffff, sock->addr + SOCK_MS_STATUS); 513 } else 514 return -EINVAL; 515 break; 516 case MEMSTICK_INTERFACE: 517 if (value == MEMSTICK_SERIAL) { 518 host->mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI; 519 writel((~TIFM_CTRL_FAST_CLK) 520 & readl(sock->addr + SOCK_CONTROL), 521 sock->addr + SOCK_CONTROL); 522 } else if (value == MEMSTICK_PAR4) { 523 host->mode_mask = 0; 524 writel(TIFM_CTRL_FAST_CLK 525 | readl(sock->addr + SOCK_CONTROL), 526 sock->addr + SOCK_CONTROL); 527 } else 528 return -EINVAL; 529 break; 530 } 531 532 return 0; 533 } 534 535 static void tifm_ms_abort(struct timer_list *t) 536 { 537 struct tifm_ms *host = timer_container_of(host, t, timer); 538 539 dev_dbg(&host->dev->dev, "status %x\n", 540 readl(host->dev->addr + SOCK_MS_STATUS)); 541 printk(KERN_ERR 542 "%s : card failed to respond for a long period of time " 543 "(%x, %x)\n", 544 dev_name(&host->dev->dev), host->req ? host->req->tpc : 0, 545 host->cmd_flags); 546 547 tifm_eject(host->dev); 548 } 549 550 static int tifm_ms_probe(struct tifm_dev *sock) 551 { 552 struct memstick_host *msh; 553 struct tifm_ms *host; 554 int rc = -EIO; 555 556 if (!(TIFM_SOCK_STATE_OCCUPIED 557 & readl(sock->addr + SOCK_PRESENT_STATE))) { 558 printk(KERN_WARNING "%s : card gone, unexpectedly\n", 559 dev_name(&sock->dev)); 560 return rc; 561 } 562 563 msh = memstick_alloc_host(sizeof(struct tifm_ms), &sock->dev); 564 if (!msh) 565 return -ENOMEM; 566 567 host = memstick_priv(msh); 568 tifm_set_drvdata(sock, msh); 569 host->dev = sock; 570 host->timeout_jiffies = msecs_to_jiffies(1000); 571 572 timer_setup(&host->timer, tifm_ms_abort, 0); 573 tasklet_init(&host->notify, tifm_ms_req_tasklet, (unsigned long)msh); 574 575 msh->request = tifm_ms_submit_req; 576 msh->set_param = tifm_ms_set_param; 577 sock->card_event = tifm_ms_card_event; 578 sock->data_event = tifm_ms_data_event; 579 if (tifm_has_ms_pif(sock)) 580 msh->caps |= MEMSTICK_CAP_PAR4; 581 582 rc = memstick_add_host(msh); 583 if (!rc) 584 return 0; 585 586 memstick_free_host(msh); 587 return rc; 588 } 589 590 static void tifm_ms_remove(struct tifm_dev *sock) 591 { 592 struct memstick_host *msh = tifm_get_drvdata(sock); 593 struct tifm_ms *host = memstick_priv(msh); 594 int rc = 0; 595 unsigned long flags; 596 597 msh->request = tifm_ms_dummy_submit; 598 tasklet_kill(&host->notify); 599 spin_lock_irqsave(&sock->lock, flags); 600 host->eject = 1; 601 if (host->req) { 602 timer_delete(&host->timer); 603 writel(TIFM_FIFO_INT_SETALL, 604 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); 605 writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); 606 if (host->use_dma) 607 tifm_unmap_sg(sock, &host->req->sg, 1, 608 host->req->data_dir == READ 609 ? DMA_TO_DEVICE 610 : DMA_FROM_DEVICE); 611 host->req->error = -ETIME; 612 613 do { 614 rc = memstick_next_req(msh, &host->req); 615 if (!rc) 616 host->req->error = -ETIME; 617 } while (!rc); 618 } 619 spin_unlock_irqrestore(&sock->lock, flags); 620 621 memstick_remove_host(msh); 622 memstick_free_host(msh); 623 } 624 625 #ifdef CONFIG_PM 626 627 static int tifm_ms_suspend(struct tifm_dev *sock, pm_message_t state) 628 { 629 struct memstick_host *msh = tifm_get_drvdata(sock); 630 631 memstick_suspend_host(msh); 632 return 0; 633 } 634 635 static int tifm_ms_resume(struct tifm_dev *sock) 636 { 637 struct memstick_host *msh = tifm_get_drvdata(sock); 638 639 memstick_resume_host(msh); 640 return 0; 641 } 642 643 #else 644 645 #define tifm_ms_suspend NULL 646 #define tifm_ms_resume NULL 647 648 #endif /* CONFIG_PM */ 649 650 static struct tifm_device_id tifm_ms_id_tbl[] = { 651 { TIFM_TYPE_MS }, { 0 } 652 }; 653 654 static struct tifm_driver tifm_ms_driver = { 655 .driver = { 656 .name = DRIVER_NAME, 657 .owner = THIS_MODULE 658 }, 659 .id_table = tifm_ms_id_tbl, 660 .probe = tifm_ms_probe, 661 .remove = tifm_ms_remove, 662 .suspend = tifm_ms_suspend, 663 .resume = tifm_ms_resume 664 }; 665 666 static int __init tifm_ms_init(void) 667 { 668 return tifm_register_driver(&tifm_ms_driver); 669 } 670 671 static void __exit tifm_ms_exit(void) 672 { 673 tifm_unregister_driver(&tifm_ms_driver); 674 } 675 676 MODULE_AUTHOR("Alex Dubov"); 677 MODULE_DESCRIPTION("TI FlashMedia MemoryStick driver"); 678 MODULE_LICENSE("GPL"); 679 MODULE_DEVICE_TABLE(tifm, tifm_ms_id_tbl); 680 681 module_init(tifm_ms_init); 682 module_exit(tifm_ms_exit); 683