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