Blame view

drivers/pcmcia/electra_cf.c 7.64 KB
1a59d1b8e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
2b571a066   Olof Johansson   pcmcia: CompactFl...
2
3
4
5
6
7
  /*
   * Copyright (C) 2007 PA Semi, Inc
   *
   * Maintained by: Olof Johansson <olof@lixom.net>
   *
   * Based on drivers/pcmcia/omap_cf.c
2b571a066   Olof Johansson   pcmcia: CompactFl...
8
9
10
11
12
13
14
15
16
17
   */
  
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/sched.h>
  #include <linux/platform_device.h>
  #include <linux/errno.h>
  #include <linux/init.h>
  #include <linux/delay.h>
  #include <linux/interrupt.h>
27ac792ca   Andrea Righi   PAGE_ALIGN(): cor...
18
  #include <linux/mm.h>
2b571a066   Olof Johansson   pcmcia: CompactFl...
19
  #include <linux/vmalloc.h>
5af507300   Rob Herring   drivers: clean-up...
20
21
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
ad19dbdb4   Stephen Rothwell   pcmcia: Use linux...
22
  #include <linux/of_platform.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
23
  #include <linux/slab.h>
2b571a066   Olof Johansson   pcmcia: CompactFl...
24
25
  
  #include <pcmcia/ss.h>
2b571a066   Olof Johansson   pcmcia: CompactFl...
26
27
28
29
30
31
32
33
34
  
  static const char driver_name[] = "electra-cf";
  
  struct electra_cf_socket {
  	struct pcmcia_socket	socket;
  
  	struct timer_list	timer;
  	unsigned		present:1;
  	unsigned		active:1;
2dc115813   Grant Likely   of/device: Replac...
35
  	struct platform_device	*ofdev;
2b571a066   Olof Johansson   pcmcia: CompactFl...
36
  	unsigned long		mem_phys;
0f06abd38   Laurent Navet   drivers: pcmcia: ...
37
  	void __iomem		*mem_base;
2b571a066   Olof Johansson   pcmcia: CompactFl...
38
  	unsigned long		mem_size;
0f06abd38   Laurent Navet   drivers: pcmcia: ...
39
  	void __iomem		*io_virt;
2b571a066   Olof Johansson   pcmcia: CompactFl...
40
41
42
43
  	unsigned int		io_base;
  	unsigned int		io_size;
  	u_int			irq;
  	struct resource		iomem;
0f06abd38   Laurent Navet   drivers: pcmcia: ...
44
  	void __iomem		*gpio_base;
2b571a066   Olof Johansson   pcmcia: CompactFl...
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
  	int			gpio_detect;
  	int			gpio_vsense;
  	int			gpio_3v;
  	int			gpio_5v;
  };
  
  #define	POLL_INTERVAL		(2 * HZ)
  
  
  static int electra_cf_present(struct electra_cf_socket *cf)
  {
  	unsigned int gpio;
  
  	gpio = in_le32(cf->gpio_base+0x40);
  	return !(gpio & (1 << cf->gpio_detect));
  }
  
  static int electra_cf_ss_init(struct pcmcia_socket *s)
  {
  	return 0;
  }
  
  /* the timer is primarily to kick this socket's pccardd */
80c5a20b5   Kees Cook   pcmcia/electra_cf...
68
  static void electra_cf_timer(struct timer_list *t)
2b571a066   Olof Johansson   pcmcia: CompactFl...
69
  {
80c5a20b5   Kees Cook   pcmcia/electra_cf...
70
  	struct electra_cf_socket *cf = from_timer(cf, t, timer);
2b571a066   Olof Johansson   pcmcia: CompactFl...
71
72
73
74
75
76
77
78
79
80
81
82
83
  	int present = electra_cf_present(cf);
  
  	if (present != cf->present) {
  		cf->present = present;
  		pcmcia_parse_events(&cf->socket, SS_DETECT);
  	}
  
  	if (cf->active)
  		mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
  }
  
  static irqreturn_t electra_cf_irq(int irq, void *_cf)
  {
80c5a20b5   Kees Cook   pcmcia/electra_cf...
84
85
86
  	struct electra_cf_socket *cf = _cf;
  
  	electra_cf_timer(&cf->timer);
2b571a066   Olof Johansson   pcmcia: CompactFl...
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  	return IRQ_HANDLED;
  }
  
  static int electra_cf_get_status(struct pcmcia_socket *s, u_int *sp)
  {
  	struct electra_cf_socket *cf;
  
  	if (!sp)
  		return -EINVAL;
  
  	cf = container_of(s, struct electra_cf_socket, socket);
  
  	/* NOTE CF is always 3VCARD */
  	if (electra_cf_present(cf)) {
  		*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
  
  		s->pci_irq = cf->irq;
  	} else
  		*sp = 0;
  	return 0;
  }
  
  static int electra_cf_set_socket(struct pcmcia_socket *sock,
  				 struct socket_state_t *s)
  {
  	unsigned int gpio;
  	unsigned int vcc;
  	struct electra_cf_socket *cf;
  
  	cf = container_of(sock, struct electra_cf_socket, socket);
  
  	/* "reset" means no power in our case */
  	vcc = (s->flags & SS_RESET) ? 0 : s->Vcc;
  
  	switch (vcc) {
  	case 0:
  		gpio = 0;
  		break;
  	case 33:
  		gpio = (1 << cf->gpio_3v);
  		break;
  	case 5:
  		gpio = (1 << cf->gpio_5v);
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	gpio |= 1 << (cf->gpio_3v + 16); /* enwr */
  	gpio |= 1 << (cf->gpio_5v + 16); /* enwr */
  	out_le32(cf->gpio_base+0x90, gpio);
  
  	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 electra_cf_set_io_map(struct pcmcia_socket *s,
  				 struct pccard_io_map *io)
  {
  	return 0;
  }
  
  static int electra_cf_set_mem_map(struct pcmcia_socket *s,
  				  struct pccard_mem_map *map)
  {
  	struct electra_cf_socket *cf;
  
  	if (map->card_start)
  		return -EINVAL;
  	cf = container_of(s, struct electra_cf_socket, socket);
  	map->static_start = cf->mem_phys;
  	map->flags &= MAP_ACTIVE|MAP_ATTRIB;
  	if (!(map->flags & MAP_ATTRIB))
  		map->static_start += 0x800;
  	return 0;
  }
  
  static struct pccard_operations electra_cf_ops = {
  	.init			= electra_cf_ss_init,
  	.get_status		= electra_cf_get_status,
  	.set_socket		= electra_cf_set_socket,
  	.set_io_map		= electra_cf_set_io_map,
  	.set_mem_map		= electra_cf_set_mem_map,
  };
34cdf25a1   Bill Pemberton   pcmcia: remove us...
174
  static int electra_cf_probe(struct platform_device *ofdev)
2b571a066   Olof Johansson   pcmcia: CompactFl...
175
176
  {
  	struct device *device = &ofdev->dev;
61c7a080a   Grant Likely   of: Always use 's...
177
  	struct device_node *np = ofdev->dev.of_node;
2b571a066   Olof Johansson   pcmcia: CompactFl...
178
179
  	struct electra_cf_socket   *cf;
  	struct resource mem, io;
b274014c6   Christoph Hellwig   powerpc: add an i...
180
  	int status = -ENOMEM;
2b571a066   Olof Johansson   pcmcia: CompactFl...
181
182
  	const unsigned int *prop;
  	int err;
2b571a066   Olof Johansson   pcmcia: CompactFl...
183
184
185
186
187
188
189
190
  
  	err = of_address_to_resource(np, 0, &mem);
  	if (err)
  		return -EINVAL;
  
  	err = of_address_to_resource(np, 1, &io);
  	if (err)
  		return -EINVAL;
0f06abd38   Laurent Navet   drivers: pcmcia: ...
191
  	cf = kzalloc(sizeof(*cf), GFP_KERNEL);
2b571a066   Olof Johansson   pcmcia: CompactFl...
192
193
  	if (!cf)
  		return -ENOMEM;
80c5a20b5   Kees Cook   pcmcia/electra_cf...
194
  	timer_setup(&cf->timer, electra_cf_timer, 0);
6c8343e82   Michael Ellerman   drivers/pcmcia: N...
195
  	cf->irq = 0;
2b571a066   Olof Johansson   pcmcia: CompactFl...
196
197
198
  
  	cf->ofdev = ofdev;
  	cf->mem_phys = mem.start;
28f65c11f   Joe Perches   treewide: Convert...
199
  	cf->mem_size = PAGE_ALIGN(resource_size(&mem));
2b571a066   Olof Johansson   pcmcia: CompactFl...
200
  	cf->mem_base = ioremap(cf->mem_phys, cf->mem_size);
b274014c6   Christoph Hellwig   powerpc: add an i...
201
202
  	if (!cf->mem_base)
  		goto out_free_cf;
28f65c11f   Joe Perches   treewide: Convert...
203
  	cf->io_size = PAGE_ALIGN(resource_size(&io));
b274014c6   Christoph Hellwig   powerpc: add an i...
204
205
206
  	cf->io_virt = ioremap_phb(io.start, cf->io_size);
  	if (!cf->io_virt)
  		goto out_unmap_mem;
2b571a066   Olof Johansson   pcmcia: CompactFl...
207
208
  
  	cf->gpio_base = ioremap(0xfc103000, 0x1000);
b274014c6   Christoph Hellwig   powerpc: add an i...
209
210
  	if (!cf->gpio_base)
  		goto out_unmap_virt;
2b571a066   Olof Johansson   pcmcia: CompactFl...
211
  	dev_set_drvdata(device, cf);
2b571a066   Olof Johansson   pcmcia: CompactFl...
212
  	cf->io_base = (unsigned long)cf->io_virt - VMALLOC_END;
2b571a066   Olof Johansson   pcmcia: CompactFl...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
  	cf->iomem.start = (unsigned long)cf->mem_base;
  	cf->iomem.end = (unsigned long)cf->mem_base + (mem.end - mem.start);
  	cf->iomem.flags = IORESOURCE_MEM;
  
  	cf->irq = irq_of_parse_and_map(np, 0);
  
  	status = request_irq(cf->irq, electra_cf_irq, IRQF_SHARED,
  			     driver_name, cf);
  	if (status < 0) {
  		dev_err(device, "request_irq failed
  ");
  		goto fail1;
  	}
  
  	cf->socket.pci_irq = cf->irq;
  
  	prop = of_get_property(np, "card-detect-gpio", NULL);
  	if (!prop)
  		goto fail1;
  	cf->gpio_detect = *prop;
  
  	prop = of_get_property(np, "card-vsense-gpio", NULL);
  	if (!prop)
  		goto fail1;
  	cf->gpio_vsense = *prop;
  
  	prop = of_get_property(np, "card-3v-gpio", NULL);
  	if (!prop)
  		goto fail1;
  	cf->gpio_3v = *prop;
  
  	prop = of_get_property(np, "card-5v-gpio", NULL);
  	if (!prop)
  		goto fail1;
  	cf->gpio_5v = *prop;
  
  	cf->socket.io_offset = cf->io_base;
  
  	/* reserve chip-select regions */
  	if (!request_mem_region(cf->mem_phys, cf->mem_size, driver_name)) {
  		status = -ENXIO;
  		dev_err(device, "Can't claim memory region
  ");
  		goto fail1;
  	}
  
  	if (!request_region(cf->io_base, cf->io_size, driver_name)) {
  		status = -ENXIO;
  		dev_err(device, "Can't claim I/O region
  ");
  		goto fail2;
  	}
  
  	cf->socket.owner = THIS_MODULE;
  	cf->socket.dev.parent = &ofdev->dev;
  	cf->socket.ops = &electra_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 = 0x800;
  
  	status = pcmcia_register_socket(&cf->socket);
  	if (status < 0) {
  		dev_err(device, "pcmcia_register_socket failed
  ");
  		goto fail3;
  	}
fe333321e   Ingo Molnar   powerpc: Change u...
280
281
  	dev_info(device, "at mem 0x%lx io 0x%llx irq %d
  ",
2b571a066   Olof Johansson   pcmcia: CompactFl...
282
283
284
  		 cf->mem_phys, io.start, cf->irq);
  
  	cf->active = 1;
80c5a20b5   Kees Cook   pcmcia/electra_cf...
285
  	electra_cf_timer(&cf->timer);
2b571a066   Olof Johansson   pcmcia: CompactFl...
286
287
288
289
290
291
292
  	return 0;
  
  fail3:
  	release_region(cf->io_base, cf->io_size);
  fail2:
  	release_mem_region(cf->mem_phys, cf->mem_size);
  fail1:
6c8343e82   Michael Ellerman   drivers/pcmcia: N...
293
  	if (cf->irq)
2b571a066   Olof Johansson   pcmcia: CompactFl...
294
  		free_irq(cf->irq, cf);
b274014c6   Christoph Hellwig   powerpc: add an i...
295
296
297
298
299
300
301
  	iounmap(cf->gpio_base);
  out_unmap_virt:
  	device_init_wakeup(&ofdev->dev, 0);
  	iounmap(cf->io_virt);
  out_unmap_mem:
  	iounmap(cf->mem_base);
  out_free_cf:
2b571a066   Olof Johansson   pcmcia: CompactFl...
302
303
304
305
  	kfree(cf);
  	return status;
  
  }
e765a02cb   Bill Pemberton   pcmcia: remove us...
306
  static int electra_cf_remove(struct platform_device *ofdev)
2b571a066   Olof Johansson   pcmcia: CompactFl...
307
308
309
310
311
312
313
314
315
316
  {
  	struct device *device = &ofdev->dev;
  	struct electra_cf_socket *cf;
  
  	cf = dev_get_drvdata(device);
  
  	cf->active = 0;
  	pcmcia_unregister_socket(&cf->socket);
  	free_irq(cf->irq, cf);
  	del_timer_sync(&cf->timer);
b274014c6   Christoph Hellwig   powerpc: add an i...
317
  	iounmap(cf->io_virt);
2b571a066   Olof Johansson   pcmcia: CompactFl...
318
319
320
321
322
323
324
325
326
  	iounmap(cf->mem_base);
  	iounmap(cf->gpio_base);
  	release_mem_region(cf->mem_phys, cf->mem_size);
  	release_region(cf->io_base, cf->io_size);
  
  	kfree(cf);
  
  	return 0;
  }
63c9a8b30   Márton Németh   pcmcia: make Open...
327
  static const struct of_device_id electra_cf_match[] = {
2b571a066   Olof Johansson   pcmcia: CompactFl...
328
329
330
331
332
  	{
  		.compatible   = "electra-cf",
  	},
  	{},
  };
c433a1b64   Olof Johansson   electra_cf: Add M...
333
  MODULE_DEVICE_TABLE(of, electra_cf_match);
2b571a066   Olof Johansson   pcmcia: CompactFl...
334

1c48a5c93   Grant Likely   dt: Eliminate of_...
335
  static struct platform_driver electra_cf_driver = {
4018294b5   Grant Likely   of: Remove duplic...
336
  	.driver = {
4455d9fd4   Geert Uytterhoeven   pcmcia: Remove su...
337
  		.name = driver_name,
4018294b5   Grant Likely   of: Remove duplic...
338
339
  		.of_match_table = electra_cf_match,
  	},
2b571a066   Olof Johansson   pcmcia: CompactFl...
340
341
342
  	.probe	  = electra_cf_probe,
  	.remove   = electra_cf_remove,
  };
5d95f8e2d   Axel Lin   pcmcia: convert d...
343
  module_platform_driver(electra_cf_driver);
2b571a066   Olof Johansson   pcmcia: CompactFl...
344
345
  
  MODULE_LICENSE("GPL");
0f06abd38   Laurent Navet   drivers: pcmcia: ...
346
  MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
2b571a066   Olof Johansson   pcmcia: CompactFl...
347
  MODULE_DESCRIPTION("PA Semi Electra CF driver");