blob: 9012004321dde0e9e2780899c79d53d03b083b5a [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>
Russell Kingfced80c2008-09-06 12:10:45 +010018#include <linux/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
André Goddard Rosaaf901ca2009-11-14 13:09:05 -020085 /* XXX: I'm unsure, but it seems so */
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +010086 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 read_scoop_reg(struct device *dev, unsigned short reg)
128{
129 struct scoop_dev *sdev = dev_get_drvdata(dev);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100130 return ioread16(sdev->base + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131}
132
133void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
134{
135 struct scoop_dev *sdev = dev_get_drvdata(dev);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100136 iowrite16(data, sdev->base + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137}
138
139EXPORT_SYMBOL(reset_scoop);
140EXPORT_SYMBOL(read_scoop_reg);
141EXPORT_SYMBOL(write_scoop_reg);
142
Stefan Schmidtcfab57e2010-02-16 22:41:52 +0100143#ifdef CONFIG_PM
Richard Purdie7c398982005-10-10 10:20:06 +0100144static void check_scoop_reg(struct scoop_dev *sdev)
145{
146 unsigned short mcr;
147
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100148 mcr = ioread16(sdev->base + SCOOP_MCR);
Richard Purdie7c398982005-10-10 10:20:06 +0100149 if ((mcr & 0x100) == 0)
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100150 iowrite16(0x0101, sdev->base + SCOOP_MCR);
Richard Purdie7c398982005-10-10 10:20:06 +0100151}
152
Russell King3ae5eae2005-11-09 22:32:44 +0000153static int scoop_suspend(struct platform_device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154{
Russell King3ae5eae2005-11-09 22:32:44 +0000155 struct scoop_dev *sdev = platform_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
Russell King9480e302005-10-28 09:52:56 -0700157 check_scoop_reg(sdev);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100158 sdev->scoop_gpwr = ioread16(sdev->base + SCOOP_GPWR);
159 iowrite16((sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set, sdev->base + SCOOP_GPWR);
Russell King9480e302005-10-28 09:52:56 -0700160
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 return 0;
162}
163
Russell King3ae5eae2005-11-09 22:32:44 +0000164static int scoop_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165{
Russell King3ae5eae2005-11-09 22:32:44 +0000166 struct scoop_dev *sdev = platform_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
Russell King9480e302005-10-28 09:52:56 -0700168 check_scoop_reg(sdev);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100169 iowrite16(sdev->scoop_gpwr, sdev->base + SCOOP_GPWR);
Russell King9480e302005-10-28 09:52:56 -0700170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 return 0;
172}
173#else
174#define scoop_suspend NULL
175#define scoop_resume NULL
176#endif
177
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +0100178static int __devinit scoop_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179{
180 struct scoop_dev *devptr;
181 struct scoop_config *inf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100183 int ret;
184 int temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
186 if (!mem)
187 return -EINVAL;
188
Russell Kingd2a02b92006-03-20 19:46:41 +0000189 devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 if (!devptr)
Russell Kingd2a02b92006-03-20 19:46:41 +0000191 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 spin_lock_init(&devptr->scoop_lock);
194
Russell King3ae5eae2005-11-09 22:32:44 +0000195 inf = pdev->dev.platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 devptr->base = ioremap(mem->start, mem->end - mem->start + 1);
197
198 if (!devptr->base) {
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100199 ret = -ENOMEM;
200 goto err_ioremap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 }
202
Russell King3ae5eae2005-11-09 22:32:44 +0000203 platform_set_drvdata(pdev, devptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +0100205 printk("Sharp Scoop Device found at 0x%08x -> 0x%8p\n",(unsigned int)mem->start, devptr->base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100207 iowrite16(0x0140, devptr->base + SCOOP_MCR);
Pavel Machekc35bf4a2005-11-12 20:25:25 +0000208 reset_scoop(&pdev->dev);
Dmitry Baryshkovc353faa2008-04-09 23:05:09 +0100209 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
Richard Purdie7c398982005-10-10 10:20:06 +0100213 devptr->suspend_clr = inf->suspend_clr;
214 devptr->suspend_set = inf->suspend_set;
215
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100216 devptr->gpio.base = -1;
217
218 if (inf->gpio_base != 0) {
Kay Sievers3f978702008-05-30 17:42:11 +0200219 devptr->gpio.label = dev_name(&pdev->dev);
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100220 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 return 0;
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100233
234 if (devptr->gpio.base != -1)
235 temp = gpiochip_remove(&devptr->gpio);
236err_gpio:
237 platform_set_drvdata(pdev, NULL);
238err_ioremap:
239 iounmap(devptr->base);
240 kfree(devptr);
241
242 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243}
244
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +0100245static int __devexit scoop_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246{
Russell King3ae5eae2005-11-09 22:32:44 +0000247 struct scoop_dev *sdev = platform_get_drvdata(pdev);
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100248 int ret;
249
250 if (!sdev)
251 return -EINVAL;
252
253 if (sdev->gpio.base != -1) {
254 ret = gpiochip_remove(&sdev->gpio);
255 if (ret) {
256 dev_err(&pdev->dev, "Can't remove gpio chip: %d\n", ret);
257 return ret;
258 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 }
Dmitry Baryshkovb43a9e62008-04-10 13:36:53 +0100260
261 platform_set_drvdata(pdev, NULL);
262 iounmap(sdev->base);
263 kfree(sdev);
264
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 return 0;
266}
267
Russell King3ae5eae2005-11-09 22:32:44 +0000268static struct platform_driver scoop_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 .probe = scoop_probe,
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +0100270 .remove = __devexit_p(scoop_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 .suspend = scoop_suspend,
272 .resume = scoop_resume,
Russell King3ae5eae2005-11-09 22:32:44 +0000273 .driver = {
274 .name = "sharp-scoop",
275 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276};
277
Dmitry Baryshkov2f8c5142008-04-09 22:43:37 +0100278static int __init scoop_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279{
Russell King3ae5eae2005-11-09 22:32:44 +0000280 return platform_driver_register(&scoop_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281}
282
283subsys_initcall(scoop_init);