1/*
2 * Support code for the SCOOP interface found on various Sharp PDAs
3 *
4 * Copyright (c) 2004 Richard Purdie
5 *
6 *	Based on code written by Sharp/Lineo for 2.4 kernels
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 */
13
14#include <linux/device.h>
15#include <linux/gpio.h>
16#include <linux/string.h>
17#include <linux/slab.h>
18#include <linux/platform_device.h>
19#include <linux/export.h>
20#include <linux/io.h>
21#include <asm/hardware/scoop.h>
22
23/* PCMCIA to Scoop linkage
24
25   There is no easy way to link multiple scoop devices into one
26   single entity for the pxa2xx_pcmcia device so this structure
27   is used which is setup by the platform code.
28
29   This file is never modular so this symbol is always
30   accessile to the board support files.
31*/
32struct scoop_pcmcia_config *platform_scoop_config;
33EXPORT_SYMBOL(platform_scoop_config);
34
35struct  scoop_dev {
36	void __iomem *base;
37	struct gpio_chip gpio;
38	spinlock_t scoop_lock;
39	unsigned short suspend_clr;
40	unsigned short suspend_set;
41	u32 scoop_gpwr;
42};
43
44void reset_scoop(struct device *dev)
45{
46	struct scoop_dev *sdev = dev_get_drvdata(dev);
47
48	iowrite16(0x0100, sdev->base + SCOOP_MCR);  /* 00 */
49	iowrite16(0x0000, sdev->base + SCOOP_CDR);  /* 04 */
50	iowrite16(0x0000, sdev->base + SCOOP_CCR);  /* 10 */
51	iowrite16(0x0000, sdev->base + SCOOP_IMR);  /* 18 */
52	iowrite16(0x00FF, sdev->base + SCOOP_IRM);  /* 14 */
53	iowrite16(0x0000, sdev->base + SCOOP_ISR);  /* 1C */
54	iowrite16(0x0000, sdev->base + SCOOP_IRM);
55}
56
57static void __scoop_gpio_set(struct scoop_dev *sdev,
58			unsigned offset, int value)
59{
60	unsigned short gpwr;
61
62	gpwr = ioread16(sdev->base + SCOOP_GPWR);
63	if (value)
64		gpwr |= 1 << (offset + 1);
65	else
66		gpwr &= ~(1 << (offset + 1));
67	iowrite16(gpwr, sdev->base + SCOOP_GPWR);
68}
69
70static void scoop_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
71{
72	struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
73	unsigned long flags;
74
75	spin_lock_irqsave(&sdev->scoop_lock, flags);
76
77	__scoop_gpio_set(sdev, offset, value);
78
79	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
80}
81
82static int scoop_gpio_get(struct gpio_chip *chip, unsigned offset)
83{
84	struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
85
86	/* XXX: I'm unsure, but it seems so */
87	return ioread16(sdev->base + SCOOP_GPRR) & (1 << (offset + 1));
88}
89
90static int scoop_gpio_direction_input(struct gpio_chip *chip,
91			unsigned offset)
92{
93	struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
94	unsigned long flags;
95	unsigned short gpcr;
96
97	spin_lock_irqsave(&sdev->scoop_lock, flags);
98
99	gpcr = ioread16(sdev->base + SCOOP_GPCR);
100	gpcr &= ~(1 << (offset + 1));
101	iowrite16(gpcr, sdev->base + SCOOP_GPCR);
102
103	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
104
105	return 0;
106}
107
108static int scoop_gpio_direction_output(struct gpio_chip *chip,
109			unsigned offset, int value)
110{
111	struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
112	unsigned long flags;
113	unsigned short gpcr;
114
115	spin_lock_irqsave(&sdev->scoop_lock, flags);
116
117	__scoop_gpio_set(sdev, offset, value);
118
119	gpcr = ioread16(sdev->base + SCOOP_GPCR);
120	gpcr |= 1 << (offset + 1);
121	iowrite16(gpcr, sdev->base + SCOOP_GPCR);
122
123	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
124
125	return 0;
126}
127
128unsigned short read_scoop_reg(struct device *dev, unsigned short reg)
129{
130	struct scoop_dev *sdev = dev_get_drvdata(dev);
131	return ioread16(sdev->base + reg);
132}
133
134void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
135{
136	struct scoop_dev *sdev = dev_get_drvdata(dev);
137	iowrite16(data, sdev->base + reg);
138}
139
140EXPORT_SYMBOL(reset_scoop);
141EXPORT_SYMBOL(read_scoop_reg);
142EXPORT_SYMBOL(write_scoop_reg);
143
144#ifdef CONFIG_PM
145static void check_scoop_reg(struct scoop_dev *sdev)
146{
147	unsigned short mcr;
148
149	mcr = ioread16(sdev->base + SCOOP_MCR);
150	if ((mcr & 0x100) == 0)
151		iowrite16(0x0101, sdev->base + SCOOP_MCR);
152}
153
154static int scoop_suspend(struct platform_device *dev, pm_message_t state)
155{
156	struct scoop_dev *sdev = platform_get_drvdata(dev);
157
158	check_scoop_reg(sdev);
159	sdev->scoop_gpwr = ioread16(sdev->base + SCOOP_GPWR);
160	iowrite16((sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set, sdev->base + SCOOP_GPWR);
161
162	return 0;
163}
164
165static int scoop_resume(struct platform_device *dev)
166{
167	struct scoop_dev *sdev = platform_get_drvdata(dev);
168
169	check_scoop_reg(sdev);
170	iowrite16(sdev->scoop_gpwr, sdev->base + SCOOP_GPWR);
171
172	return 0;
173}
174#else
175#define scoop_suspend	NULL
176#define scoop_resume	NULL
177#endif
178
179static int scoop_probe(struct platform_device *pdev)
180{
181	struct scoop_dev *devptr;
182	struct scoop_config *inf;
183	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
184	int ret;
185
186	if (!mem)
187		return -EINVAL;
188
189	devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL);
190	if (!devptr)
191		return -ENOMEM;
192
193	spin_lock_init(&devptr->scoop_lock);
194
195	inf = pdev->dev.platform_data;
196	devptr->base = ioremap(mem->start, resource_size(mem));
197
198	if (!devptr->base) {
199		ret = -ENOMEM;
200		goto err_ioremap;
201	}
202
203	platform_set_drvdata(pdev, devptr);
204
205	printk("Sharp Scoop Device found at 0x%08x -> 0x%8p\n",(unsigned int)mem->start, devptr->base);
206
207	iowrite16(0x0140, devptr->base + SCOOP_MCR);
208	reset_scoop(&pdev->dev);
209	iowrite16(0x0000, devptr->base + SCOOP_CPR);
210	iowrite16(inf->io_dir & 0xffff, devptr->base + SCOOP_GPCR);
211	iowrite16(inf->io_out & 0xffff, devptr->base + SCOOP_GPWR);
212
213	devptr->suspend_clr = inf->suspend_clr;
214	devptr->suspend_set = inf->suspend_set;
215
216	devptr->gpio.base = -1;
217
218	if (inf->gpio_base != 0) {
219		devptr->gpio.label = dev_name(&pdev->dev);
220		devptr->gpio.base = inf->gpio_base;
221		devptr->gpio.ngpio = 12; /* PA11 = 0, PA12 = 1, etc. up to PA22 = 11 */
222		devptr->gpio.set = scoop_gpio_set;
223		devptr->gpio.get = scoop_gpio_get;
224		devptr->gpio.direction_input = scoop_gpio_direction_input;
225		devptr->gpio.direction_output = scoop_gpio_direction_output;
226
227		ret = gpiochip_add(&devptr->gpio);
228		if (ret)
229			goto err_gpio;
230	}
231
232	return 0;
233
234err_gpio:
235	platform_set_drvdata(pdev, NULL);
236err_ioremap:
237	iounmap(devptr->base);
238	kfree(devptr);
239
240	return ret;
241}
242
243static int scoop_remove(struct platform_device *pdev)
244{
245	struct scoop_dev *sdev = platform_get_drvdata(pdev);
246
247	if (!sdev)
248		return -EINVAL;
249
250	if (sdev->gpio.base != -1)
251		gpiochip_remove(&sdev->gpio);
252
253	platform_set_drvdata(pdev, NULL);
254	iounmap(sdev->base);
255	kfree(sdev);
256
257	return 0;
258}
259
260static struct platform_driver scoop_driver = {
261	.probe		= scoop_probe,
262	.remove		= scoop_remove,
263	.suspend	= scoop_suspend,
264	.resume		= scoop_resume,
265	.driver		= {
266		.name	= "sharp-scoop",
267	},
268};
269
270static int __init scoop_init(void)
271{
272	return platform_driver_register(&scoop_driver);
273}
274
275subsys_initcall(scoop_init);
276