blob: bc299b07a6fa8a949ed72fdc352e2cea817f51ac [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
Tim Schmielau4e57b682005-10-30 15:03:48 -080015#include <linux/string.h>
Tim Schmielaude259682006-01-08 01:02:05 -080016#include <linux/slab.h>
Russell Kingd052d1b2005-10-29 19:07:23 +010017#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <asm/io.h>
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +010019#include <asm/gpio.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <asm/hardware/scoop.h>
21
Richard Purdie7ea3bbb2006-04-18 23:18:53 +010022/* 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*/
31struct scoop_pcmcia_config *platform_scoop_config;
32EXPORT_SYMBOL(platform_scoop_config);
33
Linus Torvalds1da177e2005-04-16 15:20:36 -070034struct scoop_dev {
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +010035 void __iomem *base;
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +010036 struct gpio_chip gpio;
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 spinlock_t scoop_lock;
Richard Purdie7c398982005-10-10 10:20:06 +010038 unsigned short suspend_clr;
39 unsigned short suspend_set;
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 u32 scoop_gpwr;
41};
42
43void reset_scoop(struct device *dev)
44{
45 struct scoop_dev *sdev = dev_get_drvdata(dev);
46
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +010047 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -070054}
55
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +010056static 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
69static 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
81static 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
89static 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
107static 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127unsigned 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);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100134 gpio_bit = ioread16(sdev->base + SCOOP_GPWR) | bit;
135 iowrite16(gpio_bit, sdev->base + SCOOP_GPWR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 spin_unlock_irqrestore(&sdev->scoop_lock, flag);
137
138 return gpio_bit;
139}
140
141unsigned 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);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100148 gpio_bit = ioread16(sdev->base + SCOOP_GPWR) & ~bit;
149 iowrite16(gpio_bit, sdev->base + SCOOP_GPWR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 spin_unlock_irqrestore(&sdev->scoop_lock, flag);
151
152 return gpio_bit;
153}
154
155EXPORT_SYMBOL(set_scoop_gpio);
156EXPORT_SYMBOL(reset_scoop_gpio);
157
158unsigned short read_scoop_reg(struct device *dev, unsigned short reg)
159{
160 struct scoop_dev *sdev = dev_get_drvdata(dev);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100161 return ioread16(sdev->base + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162}
163
164void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
165{
166 struct scoop_dev *sdev = dev_get_drvdata(dev);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100167 iowrite16(data, sdev->base + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168}
169
170EXPORT_SYMBOL(reset_scoop);
171EXPORT_SYMBOL(read_scoop_reg);
172EXPORT_SYMBOL(write_scoop_reg);
173
Richard Purdie7c398982005-10-10 10:20:06 +0100174static void check_scoop_reg(struct scoop_dev *sdev)
175{
176 unsigned short mcr;
177
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100178 mcr = ioread16(sdev->base + SCOOP_MCR);
Richard Purdie7c398982005-10-10 10:20:06 +0100179 if ((mcr & 0x100) == 0)
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100180 iowrite16(0x0101, sdev->base + SCOOP_MCR);
Richard Purdie7c398982005-10-10 10:20:06 +0100181}
182
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183#ifdef CONFIG_PM
Russell King3ae5eae2005-11-09 22:32:44 +0000184static int scoop_suspend(struct platform_device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185{
Russell King3ae5eae2005-11-09 22:32:44 +0000186 struct scoop_dev *sdev = platform_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
Russell King9480e302005-10-28 09:52:56 -0700188 check_scoop_reg(sdev);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100189 sdev->scoop_gpwr = ioread16(sdev->base + SCOOP_GPWR);
190 iowrite16((sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set, sdev->base + SCOOP_GPWR);
Russell King9480e302005-10-28 09:52:56 -0700191
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 return 0;
193}
194
Russell King3ae5eae2005-11-09 22:32:44 +0000195static int scoop_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196{
Russell King3ae5eae2005-11-09 22:32:44 +0000197 struct scoop_dev *sdev = platform_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198
Russell King9480e302005-10-28 09:52:56 -0700199 check_scoop_reg(sdev);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100200 iowrite16(sdev->scoop_gpwr, sdev->base + SCOOP_GPWR);
Russell King9480e302005-10-28 09:52:56 -0700201
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 return 0;
203}
204#else
205#define scoop_suspend NULL
206#define scoop_resume NULL
207#endif
208
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +0100209static int __devinit scoop_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210{
211 struct scoop_dev *devptr;
212 struct scoop_config *inf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100214 int ret;
215 int temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
217 if (!mem)
218 return -EINVAL;
219
Russell Kingd2a02b92006-03-20 19:46:41 +0000220 devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 if (!devptr)
Russell Kingd2a02b92006-03-20 19:46:41 +0000222 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 spin_lock_init(&devptr->scoop_lock);
225
Russell King3ae5eae2005-11-09 22:32:44 +0000226 inf = pdev->dev.platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 devptr->base = ioremap(mem->start, mem->end - mem->start + 1);
228
229 if (!devptr->base) {
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100230 ret = -ENOMEM;
231 goto err_ioremap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 }
233
Russell King3ae5eae2005-11-09 22:32:44 +0000234 platform_set_drvdata(pdev, devptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +0100236 printk("Sharp Scoop Device found at 0x%08x -> 0x%8p\n",(unsigned int)mem->start, devptr->base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100238 iowrite16(0x0140, devptr->base + SCOOP_MCR);
Pavel Machekc35bf4a2005-11-12 20:25:25 +0000239 reset_scoop(&pdev->dev);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100240 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243
Richard Purdie7c398982005-10-10 10:20:06 +0100244 devptr->suspend_clr = inf->suspend_clr;
245 devptr->suspend_set = inf->suspend_set;
246
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100247 devptr->gpio.base = -1;
248
249 if (inf->gpio_base != 0) {
250 devptr->gpio.label = pdev->dev.bus_id;
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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 return 0;
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100264
265 if (devptr->gpio.base != -1)
266 temp = gpiochip_remove(&devptr->gpio);
267err_gpio:
268 platform_set_drvdata(pdev, NULL);
269err_ioremap:
270 iounmap(devptr->base);
271 kfree(devptr);
272
273 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274}
275
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +0100276static int __devexit scoop_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277{
Russell King3ae5eae2005-11-09 22:32:44 +0000278 struct scoop_dev *sdev = platform_get_drvdata(pdev);
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100279 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 }
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100291
292 platform_set_drvdata(pdev, NULL);
293 iounmap(sdev->base);
294 kfree(sdev);
295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 return 0;
297}
298
Russell King3ae5eae2005-11-09 22:32:44 +0000299static struct platform_driver scoop_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 .probe = scoop_probe,
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +0100301 .remove = __devexit_p(scoop_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 .suspend = scoop_suspend,
303 .resume = scoop_resume,
Russell King3ae5eae2005-11-09 22:32:44 +0000304 .driver = {
305 .name = "sharp-scoop",
306 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307};
308
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +0100309static int __init scoop_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310{
Russell King3ae5eae2005-11-09 22:32:44 +0000311 return platform_driver_register(&scoop_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312}
313
314subsys_initcall(scoop_init);