Blame view

drivers/pcmcia/omap_cf.c 8.77 KB
f74e48a51   David Brownell   [PATCH] pcmcia: O...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * omap_cf.c -- OMAP 16xx CompactFlash controller driver
   *
   * Copyright (c) 2005 David Brownell
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   */
  
  #include <linux/module.h>
  #include <linux/kernel.h>
d052d1bef   Russell King   Create platform_d...
14
  #include <linux/platform_device.h>
f74e48a51   David Brownell   [PATCH] pcmcia: O...
15
16
17
18
  #include <linux/errno.h>
  #include <linux/init.h>
  #include <linux/delay.h>
  #include <linux/interrupt.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
19
  #include <linux/slab.h>
f74e48a51   David Brownell   [PATCH] pcmcia: O...
20
21
  
  #include <pcmcia/ss.h>
a09e64fbc   Russell King   [ARM] Move includ...
22
  #include <mach/hardware.h>
f74e48a51   David Brownell   [PATCH] pcmcia: O...
23
  #include <asm/io.h>
f74e48a51   David Brownell   [PATCH] pcmcia: O...
24
  #include <asm/sizes.h>
ce491cf85   Tony Lindgren   omap: headers: Mo...
25
26
  #include <plat/mux.h>
  #include <plat/tc.h>
f74e48a51   David Brownell   [PATCH] pcmcia: O...
27
28
29
30
31
32
33
34
35
36
37
38
39
  
  
  /* NOTE:  don't expect this to support many I/O cards.  The 16xx chips have
   * hard-wired timings to support Compact Flash memory cards; they won't work
   * with various other devices (like WLAN adapters) without some external
   * logic to help out.
   *
   * NOTE:  CF controller docs disagree with address space docs as to where
   * CF_BASE really lives; this is a doc erratum.
   */
  #define	CF_BASE	0xfffe2800
  
  /* status; read after IRQ */
030b15457   Tony Lindgren   ARM: OMAP: Change...
40
  #define CF_STATUS			(CF_BASE + 0x00)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
41
42
43
44
45
  #	define	CF_STATUS_BAD_READ	(1 << 2)
  #	define	CF_STATUS_BAD_WRITE	(1 << 1)
  #	define	CF_STATUS_CARD_DETECT	(1 << 0)
  
  /* which chipselect (CS0..CS3) is used for CF (active low) */
030b15457   Tony Lindgren   ARM: OMAP: Change...
46
  #define CF_CFG				(CF_BASE + 0x02)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
47
48
  
  /* card reset */
030b15457   Tony Lindgren   ARM: OMAP: Change...
49
  #define CF_CONTROL			(CF_BASE + 0x04)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
50
  #	define	CF_CONTROL_RESET	(1 << 0)
030b15457   Tony Lindgren   ARM: OMAP: Change...
51
  #define omap_cf_present() (!(omap_readw(CF_STATUS) & CF_STATUS_CARD_DETECT))
f74e48a51   David Brownell   [PATCH] pcmcia: O...
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  
  /*--------------------------------------------------------------------------*/
  
  static const char driver_name[] = "omap_cf";
  
  struct omap_cf_socket {
  	struct pcmcia_socket	socket;
  
  	struct timer_list	timer;
  	unsigned		present:1;
  	unsigned		active:1;
  
  	struct platform_device	*pdev;
  	unsigned long		phys_cf;
  	u_int			irq;
dcb9c3923   David Brownell   [PATCH] omap_cf w...
67
  	struct resource		iomem;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
68
69
70
  };
  
  #define	POLL_INTERVAL		(2 * HZ)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  /*--------------------------------------------------------------------------*/
  
  static int omap_cf_ss_init(struct pcmcia_socket *s)
  {
  	return 0;
  }
  
  /* the timer is primarily to kick this socket's pccardd */
  static void omap_cf_timer(unsigned long _cf)
  {
  	struct omap_cf_socket	*cf = (void *) _cf;
  	unsigned		present = omap_cf_present();
  
  	if (present != cf->present) {
  		cf->present = present;
  		pr_debug("%s: card %s
  ", driver_name,
  			present ? "present" : "gone");
  		pcmcia_parse_events(&cf->socket, SS_DETECT);
  	}
  
  	if (cf->active)
  		mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
  }
  
  /* This irq handler prevents "irqNNN: nobody cared" messages as drivers
   * claim the card's IRQ.  It may also detect some card insertions, but
   * not removals; it can't always eliminate timer irqs.
   */
7d12e780e   David Howells   IRQ: Maintain reg...
100
  static irqreturn_t omap_cf_irq(int irq, void *_cf)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
101
102
103
104
105
106
107
108
109
  {
  	omap_cf_timer((unsigned long)_cf);
  	return IRQ_HANDLED;
  }
  
  static int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp)
  {
  	if (!sp)
  		return -EINVAL;
dcb9c3923   David Brownell   [PATCH] omap_cf w...
110
  	/* NOTE CF is always 3VCARD */
f74e48a51   David Brownell   [PATCH] pcmcia: O...
111
112
113
114
115
  	if (omap_cf_present()) {
  		struct omap_cf_socket	*cf;
  
  		*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
  		cf = container_of(s, struct omap_cf_socket, socket);
6f840afb4   Dominik Brodowski   pcmcia: replace s...
116
  		s->pcmcia_irq = 0;
dcb9c3923   David Brownell   [PATCH] omap_cf w...
117
  		s->pci_irq = cf->irq;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
118
119
120
121
122
123
124
125
126
  	} else
  		*sp = 0;
  	return 0;
  }
  
  static int
  omap_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
  {
  	u16		control;
dcb9c3923   David Brownell   [PATCH] omap_cf w...
127
  	/* REVISIT some non-OSK boards may support power switching */
f74e48a51   David Brownell   [PATCH] pcmcia: O...
128
129
130
131
132
133
134
  	switch (s->Vcc) {
  	case 0:
  	case 33:
  		break;
  	default:
  		return -EINVAL;
  	}
030b15457   Tony Lindgren   ARM: OMAP: Change...
135
  	control = omap_readw(CF_CONTROL);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
136
  	if (s->flags & SS_RESET)
030b15457   Tony Lindgren   ARM: OMAP: Change...
137
  		omap_writew(CF_CONTROL_RESET, CF_CONTROL);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
138
  	else
030b15457   Tony Lindgren   ARM: OMAP: Change...
139
  		omap_writew(0, CF_CONTROL);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
140
141
142
143
144
145
146
147
148
149
  
  	pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x
  ",
  		driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask);
  
  	return 0;
  }
  
  static int omap_cf_ss_suspend(struct pcmcia_socket *s)
  {
2e11cb4c5   Harvey Harrison   pcmcia: replace r...
150
151
  	pr_debug("%s: %s
  ", driver_name, __func__);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
  	return omap_cf_set_socket(s, &dead_socket);
  }
  
  /* regions are 2K each:  mem, attrib, io (and reserved-for-ide) */
  
  static int
  omap_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
  {
  	struct omap_cf_socket	*cf;
  
  	cf = container_of(s, struct omap_cf_socket, socket);
  	io->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT;
  	io->start = cf->phys_cf + SZ_4K;
  	io->stop = io->start + SZ_2K - 1;
  	return 0;
  }
  
  static int
  omap_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
  {
  	struct omap_cf_socket	*cf;
  
  	if (map->card_start)
  		return -EINVAL;
  	cf = container_of(s, struct omap_cf_socket, socket);
  	map->static_start = cf->phys_cf;
  	map->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT;
  	if (map->flags & MAP_ATTRIB)
  		map->static_start += SZ_2K;
  	return 0;
  }
  
  static struct pccard_operations omap_cf_ops = {
  	.init			= omap_cf_ss_init,
  	.suspend		= omap_cf_ss_suspend,
  	.get_status		= omap_cf_get_status,
  	.set_socket		= omap_cf_set_socket,
  	.set_io_map		= omap_cf_set_io_map,
  	.set_mem_map		= omap_cf_set_mem_map,
  };
  
  /*--------------------------------------------------------------------------*/
  
  /*
   * NOTE:  right now the only board-specific platform_data is
   * "what chipselect is used".  Boards could want more.
   */
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
199
  static int __init omap_cf_probe(struct platform_device *pdev)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
200
201
202
  {
  	unsigned		seg;
  	struct omap_cf_socket	*cf;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
203
204
  	int			irq;
  	int			status;
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
205
  	seg = (int) pdev->dev.platform_data;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
206
207
208
209
210
  	if (seg == 0 || seg > 3)
  		return -ENODEV;
  
  	/* either CFLASH.IREQ (INT_1610_CF) or some GPIO */
  	irq = platform_get_irq(pdev, 0);
489447380   David Vrabel   [PATCH] handle er...
211
  	if (irq < 0)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
212
  		return -EINVAL;
cd8612808   Robert P. J. Day   [PATCH] Fix numer...
213
  	cf = kzalloc(sizeof *cf, GFP_KERNEL);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
214
215
216
217
218
219
220
  	if (!cf)
  		return -ENOMEM;
  	init_timer(&cf->timer);
  	cf->timer.function = omap_cf_timer;
  	cf->timer.data = (unsigned long) cf;
  
  	cf->pdev = pdev;
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
221
  	platform_set_drvdata(pdev, cf);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
222
223
  
  	/* this primarily just shuts up irq handling noise */
dace14537   Thomas Gleixner   [PATCH] irq-flags...
224
  	status = request_irq(irq, omap_cf_irq, IRQF_SHARED,
f74e48a51   David Brownell   [PATCH] pcmcia: O...
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  			driver_name, cf);
  	if (status < 0)
  		goto fail0;
  	cf->irq = irq;
  	cf->socket.pci_irq = irq;
  
  	switch (seg) {
  	/* NOTE: CS0 could be configured too ... */
  	case 1:
  		cf->phys_cf = OMAP_CS1_PHYS;
  		break;
  	case 2:
  		cf->phys_cf = OMAP_CS2_PHYS;
  		break;
  	case 3:
  		cf->phys_cf = omap_cs3_phys();
  		break;
  	default:
  		goto  fail1;
  	}
dcb9c3923   David Brownell   [PATCH] omap_cf w...
245
246
247
  	cf->iomem.start = cf->phys_cf;
  	cf->iomem.end = cf->iomem.end + SZ_8K - 1;
  	cf->iomem.flags = IORESOURCE_MEM;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  
  	/* pcmcia layer only remaps "real" memory */
  	cf->socket.io_offset = (unsigned long)
  			ioremap(cf->phys_cf + SZ_4K, SZ_2K);
  	if (!cf->socket.io_offset)
  		goto fail1;
  
  	if (!request_mem_region(cf->phys_cf, SZ_8K, driver_name))
  		goto fail1;
  
  	/* NOTE:  CF conflicts with MMC1 */
  	omap_cfg_reg(W11_1610_CF_CD1);
  	omap_cfg_reg(P11_1610_CF_CD2);
  	omap_cfg_reg(R11_1610_CF_IOIS16);
  	omap_cfg_reg(V10_1610_CF_IREQ);
  	omap_cfg_reg(W10_1610_CF_RESET);
030b15457   Tony Lindgren   ARM: OMAP: Change...
264
  	omap_writew(~(1 << seg), CF_CFG);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
265
266
267
268
269
270
271
272
273
274
  
  	pr_info("%s: cs%d on irq %d
  ", driver_name, seg, irq);
  
  	/* NOTE:  better EMIFS setup might support more cards; but the
  	 * TRM only shows how to affect regular flash signals, not their
  	 * CF/PCMCIA variants...
  	 */
  	pr_debug("%s: cs%d, previous ccs %08x acs %08x
  ", driver_name,
030b15457   Tony Lindgren   ARM: OMAP: Change...
275
276
277
  		seg, omap_readl(EMIFS_CCS(seg)), omap_readl(EMIFS_ACS(seg)));
  	omap_writel(0x0004a1b3, EMIFS_CCS(seg));	/* synch mode 4 etc */
  	omap_writel(0x00000000, EMIFS_ACS(seg));	/* OE hold/setup */
f74e48a51   David Brownell   [PATCH] pcmcia: O...
278
279
280
281
282
  
  	/* CF uses armxor_ck, which is "always" available */
  
  	pr_debug("%s: sts %04x cfg %04x control %04x %s
  ", driver_name,
030b15457   Tony Lindgren   ARM: OMAP: Change...
283
284
  		omap_readw(CF_STATUS), omap_readw(CF_CFG),
  		omap_readw(CF_CONTROL),
f74e48a51   David Brownell   [PATCH] pcmcia: O...
285
286
287
  		omap_cf_present() ? "present" : "(not present)");
  
  	cf->socket.owner = THIS_MODULE;
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
288
  	cf->socket.dev.parent = &pdev->dev;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
289
290
291
292
293
  	cf->socket.ops = &omap_cf_ops;
  	cf->socket.resource_ops = &pccard_static_ops;
  	cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
  				| SS_CAP_MEM_ALIGN;
  	cf->socket.map_size = SZ_2K;
dcb9c3923   David Brownell   [PATCH] omap_cf w...
294
  	cf->socket.io[0].res = &cf->iomem;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
295
296
297
298
299
300
301
302
303
304
  
  	status = pcmcia_register_socket(&cf->socket);
  	if (status < 0)
  		goto fail2;
  
  	cf->active = 1;
  	mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
  	return 0;
  
  fail2:
f74e48a51   David Brownell   [PATCH] pcmcia: O...
305
306
  	release_mem_region(cf->phys_cf, SZ_8K);
  fail1:
3efa9970b   Amol Lad   [PATCH] ioremap b...
307
308
  	if (cf->socket.io_offset)
  		iounmap((void __iomem *) cf->socket.io_offset);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
309
310
311
312
313
  	free_irq(irq, cf);
  fail0:
  	kfree(cf);
  	return status;
  }
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
314
  static int __exit omap_cf_remove(struct platform_device *pdev)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
315
  {
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
316
  	struct omap_cf_socket *cf = platform_get_drvdata(pdev);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
317
318
319
320
321
322
323
324
325
326
  
  	cf->active = 0;
  	pcmcia_unregister_socket(&cf->socket);
  	del_timer_sync(&cf->timer);
  	iounmap((void __iomem *) cf->socket.io_offset);
  	release_mem_region(cf->phys_cf, SZ_8K);
  	free_irq(cf->irq, cf);
  	kfree(cf);
  	return 0;
  }
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
327
328
329
  static struct platform_driver omap_cf_driver = {
  	.driver = {
  		.name	= (char *) driver_name,
12c2c019e   Kay Sievers   pcmcia: fix platf...
330
  		.owner	= THIS_MODULE,
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
331
332
  	},
  	.remove		= __exit_p(omap_cf_remove),
f74e48a51   David Brownell   [PATCH] pcmcia: O...
333
334
335
336
337
  };
  
  static int __init omap_cf_init(void)
  {
  	if (cpu_is_omap16xx())
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
338
  		return platform_driver_probe(&omap_cf_driver, omap_cf_probe);
dcb9c3923   David Brownell   [PATCH] omap_cf w...
339
  	return -ENODEV;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
340
341
342
343
344
  }
  
  static void __exit omap_cf_exit(void)
  {
  	if (cpu_is_omap16xx())
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
345
  		platform_driver_unregister(&omap_cf_driver);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
346
347
348
349
350
351
352
  }
  
  module_init(omap_cf_init);
  module_exit(omap_cf_exit);
  
  MODULE_DESCRIPTION("OMAP CF Driver");
  MODULE_LICENSE("GPL");
12c2c019e   Kay Sievers   pcmcia: fix platf...
353
  MODULE_ALIAS("platform:omap_cf");