xref: /OK3568_Linux_fs/kernel/drivers/memstick/host/tifm_ms.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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