1/*
2 * PCMCIA socket code for the MyCable XXS1500 system.
3 *
4 * Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com>
5 *
6 */
7
8#include <linux/delay.h>
9#include <linux/gpio.h>
10#include <linux/interrupt.h>
11#include <linux/io.h>
12#include <linux/ioport.h>
13#include <linux/mm.h>
14#include <linux/platform_device.h>
15#include <linux/pm.h>
16#include <linux/resource.h>
17#include <linux/slab.h>
18#include <linux/spinlock.h>
19
20#include <pcmcia/ss.h>
21#include <pcmcia/cistpl.h>
22
23#include <asm/irq.h>
24#include <asm/mach-au1x00/au1000.h>
25
26#define MEM_MAP_SIZE	0x400000
27#define IO_MAP_SIZE	0x1000
28
29
30/*
31 * 3.3V cards only; all interfacing is done via gpios:
32 *
33 * 0/1:  carddetect (00 = card present, xx = huh)
34 * 4:	 card irq
35 * 204:  reset (high-act)
36 * 205:  buffer enable (low-act)
37 * 208/209: card voltage key (00,01,10,11)
38 * 210:  battwarn
39 * 211:  batdead
40 * 214:  power (low-act)
41 */
42#define GPIO_CDA	0
43#define GPIO_CDB	1
44#define GPIO_CARDIRQ	4
45#define GPIO_RESET	204
46#define GPIO_OUTEN	205
47#define GPIO_VSL	208
48#define GPIO_VSH	209
49#define GPIO_BATTDEAD	210
50#define GPIO_BATTWARN	211
51#define GPIO_POWER	214
52
53struct xxs1500_pcmcia_sock {
54	struct pcmcia_socket	socket;
55	void		*virt_io;
56
57	phys_addr_t	phys_io;
58	phys_addr_t	phys_attr;
59	phys_addr_t	phys_mem;
60
61	/* previous flags for set_socket() */
62	unsigned int old_flags;
63};
64
65#define to_xxs_socket(x) container_of(x, struct xxs1500_pcmcia_sock, socket)
66
67static irqreturn_t cdirq(int irq, void *data)
68{
69	struct xxs1500_pcmcia_sock *sock = data;
70
71	pcmcia_parse_events(&sock->socket, SS_DETECT);
72
73	return IRQ_HANDLED;
74}
75
76static int xxs1500_pcmcia_configure(struct pcmcia_socket *skt,
77				    struct socket_state_t *state)
78{
79	struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt);
80	unsigned int changed;
81
82	/* power control */
83	switch (state->Vcc) {
84	case 0:
85		gpio_set_value(GPIO_POWER, 1);	/* power off */
86		break;
87	case 33:
88		gpio_set_value(GPIO_POWER, 0);	/* power on */
89		break;
90	case 50:
91	default:
92		return -EINVAL;
93	}
94
95	changed = state->flags ^ sock->old_flags;
96
97	if (changed & SS_RESET) {
98		if (state->flags & SS_RESET) {
99			gpio_set_value(GPIO_RESET, 1);	/* assert reset */
100			gpio_set_value(GPIO_OUTEN, 1);	/* buffers off */
101		} else {
102			gpio_set_value(GPIO_RESET, 0);	/* deassert reset */
103			gpio_set_value(GPIO_OUTEN, 0);	/* buffers on */
104			msleep(500);
105		}
106	}
107
108	sock->old_flags = state->flags;
109
110	return 0;
111}
112
113static int xxs1500_pcmcia_get_status(struct pcmcia_socket *skt,
114				     unsigned int *value)
115{
116	unsigned int status;
117	int i;
118
119	status = 0;
120
121	/* check carddetects: GPIO[0:1] must both be low */
122	if (!gpio_get_value(GPIO_CDA) && !gpio_get_value(GPIO_CDB))
123		status |= SS_DETECT;
124
125	/* determine card voltage: GPIO[208:209] binary value */
126	i = (!!gpio_get_value(GPIO_VSL)) | ((!!gpio_get_value(GPIO_VSH)) << 1);
127
128	switch (i) {
129	case 0:
130	case 1:
131	case 2:
132		status |= SS_3VCARD;	/* 3V card */
133		break;
134	case 3:				/* 5V card, unsupported */
135	default:
136		status |= SS_XVCARD;	/* treated as unsupported in core */
137	}
138
139	/* GPIO214: low active power switch */
140	status |= gpio_get_value(GPIO_POWER) ? 0 : SS_POWERON;
141
142	/* GPIO204: high-active reset line */
143	status |= gpio_get_value(GPIO_RESET) ? SS_RESET : SS_READY;
144
145	/* other stuff */
146	status |= gpio_get_value(GPIO_BATTDEAD) ? 0 : SS_BATDEAD;
147	status |= gpio_get_value(GPIO_BATTWARN) ? 0 : SS_BATWARN;
148
149	*value = status;
150
151	return 0;
152}
153
154static int xxs1500_pcmcia_sock_init(struct pcmcia_socket *skt)
155{
156	gpio_direction_input(GPIO_CDA);
157	gpio_direction_input(GPIO_CDB);
158	gpio_direction_input(GPIO_VSL);
159	gpio_direction_input(GPIO_VSH);
160	gpio_direction_input(GPIO_BATTDEAD);
161	gpio_direction_input(GPIO_BATTWARN);
162	gpio_direction_output(GPIO_RESET, 1);	/* assert reset */
163	gpio_direction_output(GPIO_OUTEN, 1);	/* disable buffers */
164	gpio_direction_output(GPIO_POWER, 1);	/* power off */
165
166	return 0;
167}
168
169static int xxs1500_pcmcia_sock_suspend(struct pcmcia_socket *skt)
170{
171	return 0;
172}
173
174static int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt,
175				    struct pccard_io_map *map)
176{
177	struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt);
178
179	map->start = (u32)sock->virt_io;
180	map->stop = map->start + IO_MAP_SIZE;
181
182	return 0;
183}
184
185static int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt,
186				     struct pccard_mem_map *map)
187{
188	struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt);
189
190	if (map->flags & MAP_ATTRIB)
191		map->static_start = sock->phys_attr + map->card_start;
192	else
193		map->static_start = sock->phys_mem + map->card_start;
194
195	return 0;
196}
197
198static struct pccard_operations xxs1500_pcmcia_operations = {
199	.init			= xxs1500_pcmcia_sock_init,
200	.suspend		= xxs1500_pcmcia_sock_suspend,
201	.get_status		= xxs1500_pcmcia_get_status,
202	.set_socket		= xxs1500_pcmcia_configure,
203	.set_io_map		= au1x00_pcmcia_set_io_map,
204	.set_mem_map		= au1x00_pcmcia_set_mem_map,
205};
206
207static int xxs1500_pcmcia_probe(struct platform_device *pdev)
208{
209	struct xxs1500_pcmcia_sock *sock;
210	struct resource *r;
211	int ret, irq;
212
213	sock = kzalloc(sizeof(struct xxs1500_pcmcia_sock), GFP_KERNEL);
214	if (!sock)
215		return -ENOMEM;
216
217	ret = -ENODEV;
218
219	/* 36bit PCMCIA Attribute area address */
220	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr");
221	if (!r) {
222		dev_err(&pdev->dev, "missing 'pcmcia-attr' resource!\n");
223		goto out0;
224	}
225	sock->phys_attr = r->start;
226
227	/* 36bit PCMCIA Memory area address */
228	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem");
229	if (!r) {
230		dev_err(&pdev->dev, "missing 'pcmcia-mem' resource!\n");
231		goto out0;
232	}
233	sock->phys_mem = r->start;
234
235	/* 36bit PCMCIA IO area address */
236	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io");
237	if (!r) {
238		dev_err(&pdev->dev, "missing 'pcmcia-io' resource!\n");
239		goto out0;
240	}
241	sock->phys_io = r->start;
242
243
244	/*
245	 * PCMCIA client drivers use the inb/outb macros to access
246	 * the IO registers.  Since mips_io_port_base is added
247	 * to the access address of the mips implementation of
248	 * inb/outb, we need to subtract it here because we want
249	 * to access the I/O or MEM address directly, without
250	 * going through this "mips_io_port_base" mechanism.
251	 */
252	sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) -
253				 mips_io_port_base);
254
255	if (!sock->virt_io) {
256		dev_err(&pdev->dev, "cannot remap IO area\n");
257		ret = -ENOMEM;
258		goto out0;
259	}
260
261	sock->socket.ops	= &xxs1500_pcmcia_operations;
262	sock->socket.owner	= THIS_MODULE;
263	sock->socket.pci_irq	= gpio_to_irq(GPIO_CARDIRQ);
264	sock->socket.features	= SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
265	sock->socket.map_size	= MEM_MAP_SIZE;
266	sock->socket.io_offset	= (unsigned long)sock->virt_io;
267	sock->socket.dev.parent	= &pdev->dev;
268	sock->socket.resource_ops = &pccard_static_ops;
269
270	platform_set_drvdata(pdev, sock);
271
272	/* setup carddetect irq: use one of the 2 GPIOs as an
273	 * edge detector.
274	 */
275	irq = gpio_to_irq(GPIO_CDA);
276	irq_set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
277	ret = request_irq(irq, cdirq, 0, "pcmcia_carddetect", sock);
278	if (ret) {
279		dev_err(&pdev->dev, "cannot setup cd irq\n");
280		goto out1;
281	}
282
283	ret = pcmcia_register_socket(&sock->socket);
284	if (ret) {
285		dev_err(&pdev->dev, "failed to register\n");
286		goto out2;
287	}
288
289	printk(KERN_INFO "MyCable XXS1500 PCMCIA socket services\n");
290
291	return 0;
292
293out2:
294	free_irq(gpio_to_irq(GPIO_CDA), sock);
295out1:
296	iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
297out0:
298	kfree(sock);
299	return ret;
300}
301
302static int xxs1500_pcmcia_remove(struct platform_device *pdev)
303{
304	struct xxs1500_pcmcia_sock *sock = platform_get_drvdata(pdev);
305
306	pcmcia_unregister_socket(&sock->socket);
307	free_irq(gpio_to_irq(GPIO_CDA), sock);
308	iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
309	kfree(sock);
310
311	return 0;
312}
313
314static struct platform_driver xxs1500_pcmcia_socket_driver = {
315	.driver	= {
316		.name	= "xxs1500_pcmcia",
317	},
318	.probe		= xxs1500_pcmcia_probe,
319	.remove		= xxs1500_pcmcia_remove,
320};
321
322module_platform_driver(xxs1500_pcmcia_socket_driver);
323
324MODULE_LICENSE("GPL");
325MODULE_DESCRIPTION("PCMCIA Socket Services for MyCable XXS1500 systems");
326MODULE_AUTHOR("Manuel Lauss");
327