Blame view

drivers/pcmcia/omap_cf.c 8.54 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
f74e48a51   David Brownell   [PATCH] pcmcia: O...
2
3
4
5
  /*
   * omap_cf.c -- OMAP 16xx CompactFlash controller driver
   *
   * Copyright (c) 2005 David Brownell
f74e48a51   David Brownell   [PATCH] pcmcia: O...
6
7
8
9
   */
  
  #include <linux/module.h>
  #include <linux/kernel.h>
d052d1bef   Russell King   Create platform_d...
10
  #include <linux/platform_device.h>
f74e48a51   David Brownell   [PATCH] pcmcia: O...
11
12
13
14
  #include <linux/errno.h>
  #include <linux/init.h>
  #include <linux/delay.h>
  #include <linux/interrupt.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
15
  #include <linux/slab.h>
f74e48a51   David Brownell   [PATCH] pcmcia: O...
16
17
  
  #include <pcmcia/ss.h>
a09e64fbc   Russell King   [ARM] Move includ...
18
  #include <mach/hardware.h>
f74e48a51   David Brownell   [PATCH] pcmcia: O...
19
  #include <asm/io.h>
87dfb311b   Masahiro Yamada   treewide: replace...
20
  #include <linux/sizes.h>
f74e48a51   David Brownell   [PATCH] pcmcia: O...
21

70c494c31   Tony Lindgren   ARM: OMAP1: Make ...
22
  #include <mach/mux.h>
54b693d46   Tony Lindgren   ARM: OMAP1: Move ...
23
  #include <mach/tc.h>
f74e48a51   David Brownell   [PATCH] pcmcia: O...
24
25
26
27
28
29
30
31
32
33
34
35
36
  
  
  /* 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...
37
  #define CF_STATUS			(CF_BASE + 0x00)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
38
39
40
41
42
  #	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...
43
  #define CF_CFG				(CF_BASE + 0x02)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
44
45
  
  /* card reset */
030b15457   Tony Lindgren   ARM: OMAP: Change...
46
  #define CF_CONTROL			(CF_BASE + 0x04)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
47
  #	define	CF_CONTROL_RESET	(1 << 0)
030b15457   Tony Lindgren   ARM: OMAP: Change...
48
  #define omap_cf_present() (!(omap_readw(CF_STATUS) & CF_STATUS_CARD_DETECT))
f74e48a51   David Brownell   [PATCH] pcmcia: O...
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  
  /*--------------------------------------------------------------------------*/
  
  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...
64
  	struct resource		iomem;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
65
66
67
  };
  
  #define	POLL_INTERVAL		(2 * HZ)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
68
69
70
71
72
73
74
75
  /*--------------------------------------------------------------------------*/
  
  static int omap_cf_ss_init(struct pcmcia_socket *s)
  {
  	return 0;
  }
  
  /* the timer is primarily to kick this socket's pccardd */
41760d0e0   Kees Cook   drivers/pcmcia: C...
76
  static void omap_cf_timer(struct timer_list *t)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
77
  {
41760d0e0   Kees Cook   drivers/pcmcia: C...
78
  	struct omap_cf_socket	*cf = from_timer(cf, t, timer);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  	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...
97
  static irqreturn_t omap_cf_irq(int irq, void *_cf)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
98
  {
439dc05fb   Kees Cook   drivers/pcmcia: o...
99
100
101
  	struct omap_cf_socket *cf = (struct omap_cf_socket *)_cf;
  
  	omap_cf_timer(&cf->timer);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
102
103
104
105
106
107
108
  	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...
109
  	/* NOTE CF is always 3VCARD */
f74e48a51   David Brownell   [PATCH] pcmcia: O...
110
111
112
113
114
  	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...
115
  		s->pcmcia_irq = 0;
dcb9c3923   David Brownell   [PATCH] omap_cf w...
116
  		s->pci_irq = cf->irq;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
117
118
119
120
121
122
123
124
125
  	} 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...
126
  	/* REVISIT some non-OSK boards may support power switching */
f74e48a51   David Brownell   [PATCH] pcmcia: O...
127
128
129
130
131
132
133
  	switch (s->Vcc) {
  	case 0:
  	case 33:
  		break;
  	default:
  		return -EINVAL;
  	}
030b15457   Tony Lindgren   ARM: OMAP: Change...
134
  	control = omap_readw(CF_CONTROL);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
135
  	if (s->flags & SS_RESET)
030b15457   Tony Lindgren   ARM: OMAP: Change...
136
  		omap_writew(CF_CONTROL_RESET, CF_CONTROL);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
137
  	else
030b15457   Tony Lindgren   ARM: OMAP: Change...
138
  		omap_writew(0, CF_CONTROL);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
139
140
141
142
143
144
145
146
147
148
  
  	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...
149
150
  	pr_debug("%s: %s
  ", driver_name, __func__);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
151
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
  	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: ...
198
  static int __init omap_cf_probe(struct platform_device *pdev)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
199
200
201
  {
  	unsigned		seg;
  	struct omap_cf_socket	*cf;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
202
203
  	int			irq;
  	int			status;
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
204
  	seg = (int) pdev->dev.platform_data;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
205
206
207
208
209
  	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...
210
  	if (irq < 0)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
211
  		return -EINVAL;
cd8612808   Robert P. J. Day   [PATCH] Fix numer...
212
  	cf = kzalloc(sizeof *cf, GFP_KERNEL);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
213
214
  	if (!cf)
  		return -ENOMEM;
41760d0e0   Kees Cook   drivers/pcmcia: C...
215
  	timer_setup(&cf->timer, omap_cf_timer, 0);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
216
217
  
  	cf->pdev = pdev;
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
218
  	platform_set_drvdata(pdev, cf);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
219
220
  
  	/* this primarily just shuts up irq handling noise */
dace14537   Thomas Gleixner   [PATCH] irq-flags...
221
  	status = request_irq(irq, omap_cf_irq, IRQF_SHARED,
f74e48a51   David Brownell   [PATCH] pcmcia: O...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  			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...
242
243
244
  	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...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  
  	/* 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...
261
  	omap_writew(~(1 << seg), CF_CFG);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
262
263
264
265
266
267
268
269
270
271
  
  	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...
272
273
274
  		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...
275
276
277
278
279
  
  	/* 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...
280
281
  		omap_readw(CF_STATUS), omap_readw(CF_CFG),
  		omap_readw(CF_CONTROL),
f74e48a51   David Brownell   [PATCH] pcmcia: O...
282
283
284
  		omap_cf_present() ? "present" : "(not present)");
  
  	cf->socket.owner = THIS_MODULE;
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
285
  	cf->socket.dev.parent = &pdev->dev;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
286
287
288
289
290
  	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...
291
  	cf->socket.io[0].res = &cf->iomem;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
292
293
294
295
296
297
298
299
300
301
  
  	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...
302
303
  	release_mem_region(cf->phys_cf, SZ_8K);
  fail1:
3efa9970b   Amol Lad   [PATCH] ioremap b...
304
305
  	if (cf->socket.io_offset)
  		iounmap((void __iomem *) cf->socket.io_offset);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
306
307
308
309
310
  	free_irq(irq, cf);
  fail0:
  	kfree(cf);
  	return status;
  }
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
311
  static int __exit omap_cf_remove(struct platform_device *pdev)
f74e48a51   David Brownell   [PATCH] pcmcia: O...
312
  {
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
313
  	struct omap_cf_socket *cf = platform_get_drvdata(pdev);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
314
315
316
317
318
319
320
321
322
323
  
  	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: ...
324
325
  static struct platform_driver omap_cf_driver = {
  	.driver = {
7c8c5673f   Corentin Labbe   pcmcia: omap: rem...
326
  		.name	= driver_name,
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
327
328
  	},
  	.remove		= __exit_p(omap_cf_remove),
f74e48a51   David Brownell   [PATCH] pcmcia: O...
329
330
331
332
333
  };
  
  static int __init omap_cf_init(void)
  {
  	if (cpu_is_omap16xx())
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
334
  		return platform_driver_probe(&omap_cf_driver, omap_cf_probe);
dcb9c3923   David Brownell   [PATCH] omap_cf w...
335
  	return -ENODEV;
f74e48a51   David Brownell   [PATCH] pcmcia: O...
336
337
338
339
340
  }
  
  static void __exit omap_cf_exit(void)
  {
  	if (cpu_is_omap16xx())
b6d2cccb5   David Brownell   [PATCH] omap_cf: ...
341
  		platform_driver_unregister(&omap_cf_driver);
f74e48a51   David Brownell   [PATCH] pcmcia: O...
342
343
344
345
346
347
348
  }
  
  module_init(omap_cf_init);
  module_exit(omap_cf_exit);
  
  MODULE_DESCRIPTION("OMAP CF Driver");
  MODULE_LICENSE("GPL");
12c2c019e   Kay Sievers   pcmcia: fix platf...
349
  MODULE_ALIAS("platform:omap_cf");