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