162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TI FlashMedia driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 Alex Dubov <oakad@yahoo.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Special thanks to Carlos Corbacho for providing various MemoryStick cards 862306a36Sopenharmony_ci * that made this driver possible. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/tifm.h> 1262306a36Sopenharmony_ci#include <linux/memstick.h> 1362306a36Sopenharmony_ci#include <linux/highmem.h> 1462306a36Sopenharmony_ci#include <linux/scatterlist.h> 1562306a36Sopenharmony_ci#include <linux/log2.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <asm/io.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define DRIVER_NAME "tifm_ms" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic bool no_dma; 2262306a36Sopenharmony_cimodule_param(no_dma, bool, 0644); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Some control bits of TIFM appear to conform to Sony's reference design, 2662306a36Sopenharmony_ci * so I'm just assuming they all are. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define TIFM_MS_STAT_DRQ 0x04000 3062306a36Sopenharmony_ci#define TIFM_MS_STAT_MSINT 0x02000 3162306a36Sopenharmony_ci#define TIFM_MS_STAT_RDY 0x01000 3262306a36Sopenharmony_ci#define TIFM_MS_STAT_CRC 0x00200 3362306a36Sopenharmony_ci#define TIFM_MS_STAT_TOE 0x00100 3462306a36Sopenharmony_ci#define TIFM_MS_STAT_EMP 0x00020 3562306a36Sopenharmony_ci#define TIFM_MS_STAT_FUL 0x00010 3662306a36Sopenharmony_ci#define TIFM_MS_STAT_CED 0x00008 3762306a36Sopenharmony_ci#define TIFM_MS_STAT_ERR 0x00004 3862306a36Sopenharmony_ci#define TIFM_MS_STAT_BRQ 0x00002 3962306a36Sopenharmony_ci#define TIFM_MS_STAT_CNK 0x00001 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define TIFM_MS_SYS_DMA 0x10000 4262306a36Sopenharmony_ci#define TIFM_MS_SYS_RESET 0x08000 4362306a36Sopenharmony_ci#define TIFM_MS_SYS_SRAC 0x04000 4462306a36Sopenharmony_ci#define TIFM_MS_SYS_INTEN 0x02000 4562306a36Sopenharmony_ci#define TIFM_MS_SYS_NOCRC 0x01000 4662306a36Sopenharmony_ci#define TIFM_MS_SYS_INTCLR 0x00800 4762306a36Sopenharmony_ci#define TIFM_MS_SYS_MSIEN 0x00400 4862306a36Sopenharmony_ci#define TIFM_MS_SYS_FCLR 0x00200 4962306a36Sopenharmony_ci#define TIFM_MS_SYS_FDIR 0x00100 5062306a36Sopenharmony_ci#define TIFM_MS_SYS_DAM 0x00080 5162306a36Sopenharmony_ci#define TIFM_MS_SYS_DRM 0x00040 5262306a36Sopenharmony_ci#define TIFM_MS_SYS_DRQSL 0x00020 5362306a36Sopenharmony_ci#define TIFM_MS_SYS_REI 0x00010 5462306a36Sopenharmony_ci#define TIFM_MS_SYS_REO 0x00008 5562306a36Sopenharmony_ci#define TIFM_MS_SYS_BSY_MASK 0x00007 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define TIFM_MS_SYS_FIFO (TIFM_MS_SYS_INTEN | TIFM_MS_SYS_MSIEN \ 5862306a36Sopenharmony_ci | TIFM_MS_SYS_FCLR | TIFM_MS_SYS_BSY_MASK) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* Hardware flags */ 6162306a36Sopenharmony_cienum { 6262306a36Sopenharmony_ci CMD_READY = 0x01, 6362306a36Sopenharmony_ci FIFO_READY = 0x02, 6462306a36Sopenharmony_ci CARD_INT = 0x04 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct tifm_ms { 6862306a36Sopenharmony_ci struct tifm_dev *dev; 6962306a36Sopenharmony_ci struct timer_list timer; 7062306a36Sopenharmony_ci struct memstick_request *req; 7162306a36Sopenharmony_ci struct tasklet_struct notify; 7262306a36Sopenharmony_ci unsigned int mode_mask; 7362306a36Sopenharmony_ci unsigned int block_pos; 7462306a36Sopenharmony_ci unsigned long timeout_jiffies; 7562306a36Sopenharmony_ci unsigned char eject:1, 7662306a36Sopenharmony_ci use_dma:1; 7762306a36Sopenharmony_ci unsigned char cmd_flags; 7862306a36Sopenharmony_ci unsigned char io_pos; 7962306a36Sopenharmony_ci unsigned int io_word; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic unsigned int tifm_ms_read_data(struct tifm_ms *host, 8362306a36Sopenharmony_ci unsigned char *buf, unsigned int length) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct tifm_dev *sock = host->dev; 8662306a36Sopenharmony_ci unsigned int off = 0; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci while (host->io_pos && length) { 8962306a36Sopenharmony_ci buf[off++] = host->io_word & 0xff; 9062306a36Sopenharmony_ci host->io_word >>= 8; 9162306a36Sopenharmony_ci length--; 9262306a36Sopenharmony_ci host->io_pos--; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (!length) 9662306a36Sopenharmony_ci return off; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci while (!(TIFM_MS_STAT_EMP & readl(sock->addr + SOCK_MS_STATUS))) { 9962306a36Sopenharmony_ci if (length < 4) 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci *(unsigned int *)(buf + off) = __raw_readl(sock->addr 10262306a36Sopenharmony_ci + SOCK_MS_DATA); 10362306a36Sopenharmony_ci length -= 4; 10462306a36Sopenharmony_ci off += 4; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (length 10862306a36Sopenharmony_ci && !(TIFM_MS_STAT_EMP & readl(sock->addr + SOCK_MS_STATUS))) { 10962306a36Sopenharmony_ci host->io_word = readl(sock->addr + SOCK_MS_DATA); 11062306a36Sopenharmony_ci for (host->io_pos = 4; host->io_pos; --host->io_pos) { 11162306a36Sopenharmony_ci buf[off++] = host->io_word & 0xff; 11262306a36Sopenharmony_ci host->io_word >>= 8; 11362306a36Sopenharmony_ci length--; 11462306a36Sopenharmony_ci if (!length) 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return off; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic unsigned int tifm_ms_write_data(struct tifm_ms *host, 12362306a36Sopenharmony_ci unsigned char *buf, unsigned int length) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct tifm_dev *sock = host->dev; 12662306a36Sopenharmony_ci unsigned int off = 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (host->io_pos) { 12962306a36Sopenharmony_ci while (host->io_pos < 4 && length) { 13062306a36Sopenharmony_ci host->io_word |= buf[off++] << (host->io_pos * 8); 13162306a36Sopenharmony_ci host->io_pos++; 13262306a36Sopenharmony_ci length--; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (host->io_pos == 4 13762306a36Sopenharmony_ci && !(TIFM_MS_STAT_FUL & readl(sock->addr + SOCK_MS_STATUS))) { 13862306a36Sopenharmony_ci writel(TIFM_MS_SYS_FDIR | readl(sock->addr + SOCK_MS_SYSTEM), 13962306a36Sopenharmony_ci sock->addr + SOCK_MS_SYSTEM); 14062306a36Sopenharmony_ci writel(host->io_word, sock->addr + SOCK_MS_DATA); 14162306a36Sopenharmony_ci host->io_pos = 0; 14262306a36Sopenharmony_ci host->io_word = 0; 14362306a36Sopenharmony_ci } else if (host->io_pos) { 14462306a36Sopenharmony_ci return off; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!length) 14862306a36Sopenharmony_ci return off; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci while (!(TIFM_MS_STAT_FUL & readl(sock->addr + SOCK_MS_STATUS))) { 15162306a36Sopenharmony_ci if (length < 4) 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci writel(TIFM_MS_SYS_FDIR | readl(sock->addr + SOCK_MS_SYSTEM), 15462306a36Sopenharmony_ci sock->addr + SOCK_MS_SYSTEM); 15562306a36Sopenharmony_ci __raw_writel(*(unsigned int *)(buf + off), 15662306a36Sopenharmony_ci sock->addr + SOCK_MS_DATA); 15762306a36Sopenharmony_ci length -= 4; 15862306a36Sopenharmony_ci off += 4; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci switch (length) { 16262306a36Sopenharmony_ci case 3: 16362306a36Sopenharmony_ci host->io_word |= buf[off + 2] << 16; 16462306a36Sopenharmony_ci host->io_pos++; 16562306a36Sopenharmony_ci fallthrough; 16662306a36Sopenharmony_ci case 2: 16762306a36Sopenharmony_ci host->io_word |= buf[off + 1] << 8; 16862306a36Sopenharmony_ci host->io_pos++; 16962306a36Sopenharmony_ci fallthrough; 17062306a36Sopenharmony_ci case 1: 17162306a36Sopenharmony_ci host->io_word |= buf[off]; 17262306a36Sopenharmony_ci host->io_pos++; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci off += host->io_pos; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return off; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic unsigned int tifm_ms_transfer_data(struct tifm_ms *host) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct tifm_dev *sock = host->dev; 18362306a36Sopenharmony_ci unsigned int length; 18462306a36Sopenharmony_ci unsigned int off; 18562306a36Sopenharmony_ci unsigned int t_size, p_cnt; 18662306a36Sopenharmony_ci unsigned char *buf; 18762306a36Sopenharmony_ci struct page *pg; 18862306a36Sopenharmony_ci unsigned long flags = 0; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (host->req->long_data) { 19162306a36Sopenharmony_ci length = host->req->sg.length - host->block_pos; 19262306a36Sopenharmony_ci off = host->req->sg.offset + host->block_pos; 19362306a36Sopenharmony_ci } else { 19462306a36Sopenharmony_ci length = host->req->data_len - host->block_pos; 19562306a36Sopenharmony_ci off = 0; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci dev_dbg(&sock->dev, "fifo data transfer, %d, %d\n", length, 19862306a36Sopenharmony_ci host->block_pos); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci while (length) { 20162306a36Sopenharmony_ci unsigned int p_off; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (host->req->long_data) { 20462306a36Sopenharmony_ci pg = nth_page(sg_page(&host->req->sg), 20562306a36Sopenharmony_ci off >> PAGE_SHIFT); 20662306a36Sopenharmony_ci p_off = offset_in_page(off); 20762306a36Sopenharmony_ci p_cnt = PAGE_SIZE - p_off; 20862306a36Sopenharmony_ci p_cnt = min(p_cnt, length); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci local_irq_save(flags); 21162306a36Sopenharmony_ci buf = kmap_atomic(pg) + p_off; 21262306a36Sopenharmony_ci } else { 21362306a36Sopenharmony_ci buf = host->req->data + host->block_pos; 21462306a36Sopenharmony_ci p_cnt = host->req->data_len - host->block_pos; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci t_size = host->req->data_dir == WRITE 21862306a36Sopenharmony_ci ? tifm_ms_write_data(host, buf, p_cnt) 21962306a36Sopenharmony_ci : tifm_ms_read_data(host, buf, p_cnt); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (host->req->long_data) { 22262306a36Sopenharmony_ci kunmap_atomic(buf - p_off); 22362306a36Sopenharmony_ci local_irq_restore(flags); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (!t_size) 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci host->block_pos += t_size; 22962306a36Sopenharmony_ci length -= t_size; 23062306a36Sopenharmony_ci off += t_size; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci dev_dbg(&sock->dev, "fifo data transfer, %d remaining\n", length); 23462306a36Sopenharmony_ci if (!length && (host->req->data_dir == WRITE)) { 23562306a36Sopenharmony_ci if (host->io_pos) { 23662306a36Sopenharmony_ci writel(TIFM_MS_SYS_FDIR 23762306a36Sopenharmony_ci | readl(sock->addr + SOCK_MS_SYSTEM), 23862306a36Sopenharmony_ci sock->addr + SOCK_MS_SYSTEM); 23962306a36Sopenharmony_ci writel(host->io_word, sock->addr + SOCK_MS_DATA); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci writel(TIFM_MS_SYS_FDIR 24262306a36Sopenharmony_ci | readl(sock->addr + SOCK_MS_SYSTEM), 24362306a36Sopenharmony_ci sock->addr + SOCK_MS_SYSTEM); 24462306a36Sopenharmony_ci writel(0, sock->addr + SOCK_MS_DATA); 24562306a36Sopenharmony_ci } else { 24662306a36Sopenharmony_ci readl(sock->addr + SOCK_MS_DATA); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return length; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int tifm_ms_issue_cmd(struct tifm_ms *host) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct tifm_dev *sock = host->dev; 25562306a36Sopenharmony_ci unsigned int data_len, cmd, sys_param; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci host->cmd_flags = 0; 25862306a36Sopenharmony_ci host->block_pos = 0; 25962306a36Sopenharmony_ci host->io_pos = 0; 26062306a36Sopenharmony_ci host->io_word = 0; 26162306a36Sopenharmony_ci host->cmd_flags = 0; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci host->use_dma = !no_dma; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (host->req->long_data) { 26662306a36Sopenharmony_ci data_len = host->req->sg.length; 26762306a36Sopenharmony_ci if (!is_power_of_2(data_len)) 26862306a36Sopenharmony_ci host->use_dma = 0; 26962306a36Sopenharmony_ci } else { 27062306a36Sopenharmony_ci data_len = host->req->data_len; 27162306a36Sopenharmony_ci host->use_dma = 0; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci writel(TIFM_FIFO_INT_SETALL, 27562306a36Sopenharmony_ci sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); 27662306a36Sopenharmony_ci writel(TIFM_FIFO_ENABLE, 27762306a36Sopenharmony_ci sock->addr + SOCK_FIFO_CONTROL); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (host->use_dma) { 28062306a36Sopenharmony_ci if (1 != tifm_map_sg(sock, &host->req->sg, 1, 28162306a36Sopenharmony_ci host->req->data_dir == READ 28262306a36Sopenharmony_ci ? DMA_FROM_DEVICE 28362306a36Sopenharmony_ci : DMA_TO_DEVICE)) { 28462306a36Sopenharmony_ci host->req->error = -ENOMEM; 28562306a36Sopenharmony_ci return host->req->error; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci data_len = sg_dma_len(&host->req->sg); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci writel(ilog2(data_len) - 2, 29062306a36Sopenharmony_ci sock->addr + SOCK_FIFO_PAGE_SIZE); 29162306a36Sopenharmony_ci writel(TIFM_FIFO_INTMASK, 29262306a36Sopenharmony_ci sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); 29362306a36Sopenharmony_ci sys_param = TIFM_DMA_EN | (1 << 8); 29462306a36Sopenharmony_ci if (host->req->data_dir == WRITE) 29562306a36Sopenharmony_ci sys_param |= TIFM_DMA_TX; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci writel(TIFM_FIFO_INTMASK, 29862306a36Sopenharmony_ci sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci writel(sg_dma_address(&host->req->sg), 30162306a36Sopenharmony_ci sock->addr + SOCK_DMA_ADDRESS); 30262306a36Sopenharmony_ci writel(sys_param, sock->addr + SOCK_DMA_CONTROL); 30362306a36Sopenharmony_ci } else { 30462306a36Sopenharmony_ci writel(host->mode_mask | TIFM_MS_SYS_FIFO, 30562306a36Sopenharmony_ci sock->addr + SOCK_MS_SYSTEM); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci writel(TIFM_FIFO_MORE, 30862306a36Sopenharmony_ci sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci mod_timer(&host->timer, jiffies + host->timeout_jiffies); 31262306a36Sopenharmony_ci writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), 31362306a36Sopenharmony_ci sock->addr + SOCK_CONTROL); 31462306a36Sopenharmony_ci host->req->error = 0; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci sys_param = readl(sock->addr + SOCK_MS_SYSTEM); 31762306a36Sopenharmony_ci sys_param |= TIFM_MS_SYS_INTCLR; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (host->use_dma) 32062306a36Sopenharmony_ci sys_param |= TIFM_MS_SYS_DMA; 32162306a36Sopenharmony_ci else 32262306a36Sopenharmony_ci sys_param &= ~TIFM_MS_SYS_DMA; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci writel(sys_param, sock->addr + SOCK_MS_SYSTEM); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci cmd = (host->req->tpc & 0xf) << 12; 32762306a36Sopenharmony_ci cmd |= data_len; 32862306a36Sopenharmony_ci writel(cmd, sock->addr + SOCK_MS_COMMAND); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci dev_dbg(&sock->dev, "executing TPC %x, %x\n", cmd, sys_param); 33162306a36Sopenharmony_ci return 0; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic void tifm_ms_complete_cmd(struct tifm_ms *host) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct tifm_dev *sock = host->dev; 33762306a36Sopenharmony_ci struct memstick_host *msh = tifm_get_drvdata(sock); 33862306a36Sopenharmony_ci int rc; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci del_timer(&host->timer); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci host->req->int_reg = readl(sock->addr + SOCK_MS_STATUS) & 0xff; 34362306a36Sopenharmony_ci host->req->int_reg = (host->req->int_reg & 1) 34462306a36Sopenharmony_ci | ((host->req->int_reg << 4) & 0xe0); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci writel(TIFM_FIFO_INT_SETALL, 34762306a36Sopenharmony_ci sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); 34862306a36Sopenharmony_ci writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (host->use_dma) { 35162306a36Sopenharmony_ci tifm_unmap_sg(sock, &host->req->sg, 1, 35262306a36Sopenharmony_ci host->req->data_dir == READ 35362306a36Sopenharmony_ci ? DMA_FROM_DEVICE 35462306a36Sopenharmony_ci : DMA_TO_DEVICE); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), 35862306a36Sopenharmony_ci sock->addr + SOCK_CONTROL); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci dev_dbg(&sock->dev, "TPC complete\n"); 36162306a36Sopenharmony_ci do { 36262306a36Sopenharmony_ci rc = memstick_next_req(msh, &host->req); 36362306a36Sopenharmony_ci } while (!rc && tifm_ms_issue_cmd(host)); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int tifm_ms_check_status(struct tifm_ms *host) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci if (!host->req->error) { 36962306a36Sopenharmony_ci if (!(host->cmd_flags & CMD_READY)) 37062306a36Sopenharmony_ci return 1; 37162306a36Sopenharmony_ci if (!(host->cmd_flags & FIFO_READY)) 37262306a36Sopenharmony_ci return 1; 37362306a36Sopenharmony_ci if (host->req->need_card_int 37462306a36Sopenharmony_ci && !(host->cmd_flags & CARD_INT)) 37562306a36Sopenharmony_ci return 1; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* Called from interrupt handler */ 38162306a36Sopenharmony_cistatic void tifm_ms_data_event(struct tifm_dev *sock) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct tifm_ms *host; 38462306a36Sopenharmony_ci unsigned int fifo_status = 0, host_status = 0; 38562306a36Sopenharmony_ci int rc = 1; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci spin_lock(&sock->lock); 38862306a36Sopenharmony_ci host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock)); 38962306a36Sopenharmony_ci fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); 39062306a36Sopenharmony_ci host_status = readl(sock->addr + SOCK_MS_STATUS); 39162306a36Sopenharmony_ci dev_dbg(&sock->dev, 39262306a36Sopenharmony_ci "data event: fifo_status %x, host_status %x, flags %x\n", 39362306a36Sopenharmony_ci fifo_status, host_status, host->cmd_flags); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (host->req) { 39662306a36Sopenharmony_ci if (host->use_dma && (fifo_status & 1)) { 39762306a36Sopenharmony_ci host->cmd_flags |= FIFO_READY; 39862306a36Sopenharmony_ci rc = tifm_ms_check_status(host); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci if (!host->use_dma && (fifo_status & TIFM_FIFO_MORE)) { 40162306a36Sopenharmony_ci if (!tifm_ms_transfer_data(host)) { 40262306a36Sopenharmony_ci host->cmd_flags |= FIFO_READY; 40362306a36Sopenharmony_ci rc = tifm_ms_check_status(host); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); 40962306a36Sopenharmony_ci if (!rc) 41062306a36Sopenharmony_ci tifm_ms_complete_cmd(host); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci spin_unlock(&sock->lock); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/* Called from interrupt handler */ 41762306a36Sopenharmony_cistatic void tifm_ms_card_event(struct tifm_dev *sock) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct tifm_ms *host; 42062306a36Sopenharmony_ci unsigned int host_status = 0; 42162306a36Sopenharmony_ci int rc = 1; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci spin_lock(&sock->lock); 42462306a36Sopenharmony_ci host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock)); 42562306a36Sopenharmony_ci host_status = readl(sock->addr + SOCK_MS_STATUS); 42662306a36Sopenharmony_ci dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n", 42762306a36Sopenharmony_ci host_status, host->cmd_flags); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (host->req) { 43062306a36Sopenharmony_ci if (host_status & TIFM_MS_STAT_TOE) 43162306a36Sopenharmony_ci host->req->error = -ETIME; 43262306a36Sopenharmony_ci else if (host_status & TIFM_MS_STAT_CRC) 43362306a36Sopenharmony_ci host->req->error = -EILSEQ; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (host_status & TIFM_MS_STAT_RDY) 43662306a36Sopenharmony_ci host->cmd_flags |= CMD_READY; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (host_status & TIFM_MS_STAT_MSINT) 43962306a36Sopenharmony_ci host->cmd_flags |= CARD_INT; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci rc = tifm_ms_check_status(host); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci writel(TIFM_MS_SYS_INTCLR | readl(sock->addr + SOCK_MS_SYSTEM), 44662306a36Sopenharmony_ci sock->addr + SOCK_MS_SYSTEM); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (!rc) 44962306a36Sopenharmony_ci tifm_ms_complete_cmd(host); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci spin_unlock(&sock->lock); 45262306a36Sopenharmony_ci return; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic void tifm_ms_req_tasklet(unsigned long data) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct memstick_host *msh = (struct memstick_host *)data; 45862306a36Sopenharmony_ci struct tifm_ms *host = memstick_priv(msh); 45962306a36Sopenharmony_ci struct tifm_dev *sock = host->dev; 46062306a36Sopenharmony_ci unsigned long flags; 46162306a36Sopenharmony_ci int rc; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci spin_lock_irqsave(&sock->lock, flags); 46462306a36Sopenharmony_ci if (!host->req) { 46562306a36Sopenharmony_ci if (host->eject) { 46662306a36Sopenharmony_ci do { 46762306a36Sopenharmony_ci rc = memstick_next_req(msh, &host->req); 46862306a36Sopenharmony_ci if (!rc) 46962306a36Sopenharmony_ci host->req->error = -ETIME; 47062306a36Sopenharmony_ci } while (!rc); 47162306a36Sopenharmony_ci spin_unlock_irqrestore(&sock->lock, flags); 47262306a36Sopenharmony_ci return; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci do { 47662306a36Sopenharmony_ci rc = memstick_next_req(msh, &host->req); 47762306a36Sopenharmony_ci } while (!rc && tifm_ms_issue_cmd(host)); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci spin_unlock_irqrestore(&sock->lock, flags); 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic void tifm_ms_dummy_submit(struct memstick_host *msh) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci return; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic void tifm_ms_submit_req(struct memstick_host *msh) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct tifm_ms *host = memstick_priv(msh); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci tasklet_schedule(&host->notify); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int tifm_ms_set_param(struct memstick_host *msh, 49562306a36Sopenharmony_ci enum memstick_param param, 49662306a36Sopenharmony_ci int value) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct tifm_ms *host = memstick_priv(msh); 49962306a36Sopenharmony_ci struct tifm_dev *sock = host->dev; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci switch (param) { 50262306a36Sopenharmony_ci case MEMSTICK_POWER: 50362306a36Sopenharmony_ci /* also affected by media detection mechanism */ 50462306a36Sopenharmony_ci if (value == MEMSTICK_POWER_ON) { 50562306a36Sopenharmony_ci host->mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI; 50662306a36Sopenharmony_ci writel(TIFM_MS_SYS_RESET, sock->addr + SOCK_MS_SYSTEM); 50762306a36Sopenharmony_ci writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR, 50862306a36Sopenharmony_ci sock->addr + SOCK_MS_SYSTEM); 50962306a36Sopenharmony_ci writel(0xffffffff, sock->addr + SOCK_MS_STATUS); 51062306a36Sopenharmony_ci } else if (value == MEMSTICK_POWER_OFF) { 51162306a36Sopenharmony_ci writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR, 51262306a36Sopenharmony_ci sock->addr + SOCK_MS_SYSTEM); 51362306a36Sopenharmony_ci writel(0xffffffff, sock->addr + SOCK_MS_STATUS); 51462306a36Sopenharmony_ci } else 51562306a36Sopenharmony_ci return -EINVAL; 51662306a36Sopenharmony_ci break; 51762306a36Sopenharmony_ci case MEMSTICK_INTERFACE: 51862306a36Sopenharmony_ci if (value == MEMSTICK_SERIAL) { 51962306a36Sopenharmony_ci host->mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI; 52062306a36Sopenharmony_ci writel((~TIFM_CTRL_FAST_CLK) 52162306a36Sopenharmony_ci & readl(sock->addr + SOCK_CONTROL), 52262306a36Sopenharmony_ci sock->addr + SOCK_CONTROL); 52362306a36Sopenharmony_ci } else if (value == MEMSTICK_PAR4) { 52462306a36Sopenharmony_ci host->mode_mask = 0; 52562306a36Sopenharmony_ci writel(TIFM_CTRL_FAST_CLK 52662306a36Sopenharmony_ci | readl(sock->addr + SOCK_CONTROL), 52762306a36Sopenharmony_ci sock->addr + SOCK_CONTROL); 52862306a36Sopenharmony_ci } else 52962306a36Sopenharmony_ci return -EINVAL; 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic void tifm_ms_abort(struct timer_list *t) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct tifm_ms *host = from_timer(host, t, timer); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci dev_dbg(&host->dev->dev, "status %x\n", 54162306a36Sopenharmony_ci readl(host->dev->addr + SOCK_MS_STATUS)); 54262306a36Sopenharmony_ci printk(KERN_ERR 54362306a36Sopenharmony_ci "%s : card failed to respond for a long period of time " 54462306a36Sopenharmony_ci "(%x, %x)\n", 54562306a36Sopenharmony_ci dev_name(&host->dev->dev), host->req ? host->req->tpc : 0, 54662306a36Sopenharmony_ci host->cmd_flags); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci tifm_eject(host->dev); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int tifm_ms_probe(struct tifm_dev *sock) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct memstick_host *msh; 55462306a36Sopenharmony_ci struct tifm_ms *host; 55562306a36Sopenharmony_ci int rc = -EIO; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (!(TIFM_SOCK_STATE_OCCUPIED 55862306a36Sopenharmony_ci & readl(sock->addr + SOCK_PRESENT_STATE))) { 55962306a36Sopenharmony_ci printk(KERN_WARNING "%s : card gone, unexpectedly\n", 56062306a36Sopenharmony_ci dev_name(&sock->dev)); 56162306a36Sopenharmony_ci return rc; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci msh = memstick_alloc_host(sizeof(struct tifm_ms), &sock->dev); 56562306a36Sopenharmony_ci if (!msh) 56662306a36Sopenharmony_ci return -ENOMEM; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci host = memstick_priv(msh); 56962306a36Sopenharmony_ci tifm_set_drvdata(sock, msh); 57062306a36Sopenharmony_ci host->dev = sock; 57162306a36Sopenharmony_ci host->timeout_jiffies = msecs_to_jiffies(1000); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci timer_setup(&host->timer, tifm_ms_abort, 0); 57462306a36Sopenharmony_ci tasklet_init(&host->notify, tifm_ms_req_tasklet, (unsigned long)msh); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci msh->request = tifm_ms_submit_req; 57762306a36Sopenharmony_ci msh->set_param = tifm_ms_set_param; 57862306a36Sopenharmony_ci sock->card_event = tifm_ms_card_event; 57962306a36Sopenharmony_ci sock->data_event = tifm_ms_data_event; 58062306a36Sopenharmony_ci if (tifm_has_ms_pif(sock)) 58162306a36Sopenharmony_ci msh->caps |= MEMSTICK_CAP_PAR4; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci rc = memstick_add_host(msh); 58462306a36Sopenharmony_ci if (!rc) 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci memstick_free_host(msh); 58862306a36Sopenharmony_ci return rc; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void tifm_ms_remove(struct tifm_dev *sock) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct memstick_host *msh = tifm_get_drvdata(sock); 59462306a36Sopenharmony_ci struct tifm_ms *host = memstick_priv(msh); 59562306a36Sopenharmony_ci int rc = 0; 59662306a36Sopenharmony_ci unsigned long flags; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci msh->request = tifm_ms_dummy_submit; 59962306a36Sopenharmony_ci tasklet_kill(&host->notify); 60062306a36Sopenharmony_ci spin_lock_irqsave(&sock->lock, flags); 60162306a36Sopenharmony_ci host->eject = 1; 60262306a36Sopenharmony_ci if (host->req) { 60362306a36Sopenharmony_ci del_timer(&host->timer); 60462306a36Sopenharmony_ci writel(TIFM_FIFO_INT_SETALL, 60562306a36Sopenharmony_ci sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); 60662306a36Sopenharmony_ci writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); 60762306a36Sopenharmony_ci if (host->use_dma) 60862306a36Sopenharmony_ci tifm_unmap_sg(sock, &host->req->sg, 1, 60962306a36Sopenharmony_ci host->req->data_dir == READ 61062306a36Sopenharmony_ci ? DMA_TO_DEVICE 61162306a36Sopenharmony_ci : DMA_FROM_DEVICE); 61262306a36Sopenharmony_ci host->req->error = -ETIME; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci do { 61562306a36Sopenharmony_ci rc = memstick_next_req(msh, &host->req); 61662306a36Sopenharmony_ci if (!rc) 61762306a36Sopenharmony_ci host->req->error = -ETIME; 61862306a36Sopenharmony_ci } while (!rc); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci spin_unlock_irqrestore(&sock->lock, flags); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci memstick_remove_host(msh); 62362306a36Sopenharmony_ci memstick_free_host(msh); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci#ifdef CONFIG_PM 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int tifm_ms_suspend(struct tifm_dev *sock, pm_message_t state) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct memstick_host *msh = tifm_get_drvdata(sock); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci memstick_suspend_host(msh); 63362306a36Sopenharmony_ci return 0; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int tifm_ms_resume(struct tifm_dev *sock) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct memstick_host *msh = tifm_get_drvdata(sock); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci memstick_resume_host(msh); 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci#else 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci#define tifm_ms_suspend NULL 64762306a36Sopenharmony_ci#define tifm_ms_resume NULL 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci#endif /* CONFIG_PM */ 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic struct tifm_device_id tifm_ms_id_tbl[] = { 65262306a36Sopenharmony_ci { TIFM_TYPE_MS }, { 0 } 65362306a36Sopenharmony_ci}; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic struct tifm_driver tifm_ms_driver = { 65662306a36Sopenharmony_ci .driver = { 65762306a36Sopenharmony_ci .name = DRIVER_NAME, 65862306a36Sopenharmony_ci .owner = THIS_MODULE 65962306a36Sopenharmony_ci }, 66062306a36Sopenharmony_ci .id_table = tifm_ms_id_tbl, 66162306a36Sopenharmony_ci .probe = tifm_ms_probe, 66262306a36Sopenharmony_ci .remove = tifm_ms_remove, 66362306a36Sopenharmony_ci .suspend = tifm_ms_suspend, 66462306a36Sopenharmony_ci .resume = tifm_ms_resume 66562306a36Sopenharmony_ci}; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic int __init tifm_ms_init(void) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci return tifm_register_driver(&tifm_ms_driver); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic void __exit tifm_ms_exit(void) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci tifm_unregister_driver(&tifm_ms_driver); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ciMODULE_AUTHOR("Alex Dubov"); 67862306a36Sopenharmony_ciMODULE_DESCRIPTION("TI FlashMedia MemoryStick driver"); 67962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 68062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(tifm, tifm_ms_id_tbl); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cimodule_init(tifm_ms_init); 68362306a36Sopenharmony_cimodule_exit(tifm_ms_exit); 684