Pull novell-bugzilla-156426 into release branch
[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/hardware/scoop.h>
20
21 /* PCMCIA to Scoop linkage
22
23    There is no easy way to link multiple scoop devices into one
24    single entity for the pxa2xx_pcmcia device so this structure
25    is used which is setup by the platform code.
26
27    This file is never modular so this symbol is always
28    accessile to the board support files.
29 */
30 struct scoop_pcmcia_config *platform_scoop_config;
31 EXPORT_SYMBOL(platform_scoop_config);
32
33 #define SCOOP_REG(d,adr) (*(volatile unsigned short*)(d +(adr)))
34
35 struct  scoop_dev {
36         void  *base;
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         SCOOP_REG(sdev->base,SCOOP_MCR) = 0x0100;  // 00
48         SCOOP_REG(sdev->base,SCOOP_CDR) = 0x0000;  // 04
49         SCOOP_REG(sdev->base,SCOOP_CCR) = 0x0000;  // 10
50         SCOOP_REG(sdev->base,SCOOP_IMR) = 0x0000;  // 18
51         SCOOP_REG(sdev->base,SCOOP_IRM) = 0x00FF;  // 14
52         SCOOP_REG(sdev->base,SCOOP_ISR) = 0x0000;  // 1C
53         SCOOP_REG(sdev->base,SCOOP_IRM) = 0x0000;
54 }
55
56 unsigned short set_scoop_gpio(struct device *dev, unsigned short bit)
57 {
58         unsigned short gpio_bit;
59         unsigned long flag;
60         struct scoop_dev *sdev = dev_get_drvdata(dev);
61
62         spin_lock_irqsave(&sdev->scoop_lock, flag);
63         gpio_bit = SCOOP_REG(sdev->base, SCOOP_GPWR) | bit;
64         SCOOP_REG(sdev->base, SCOOP_GPWR) = gpio_bit;
65         spin_unlock_irqrestore(&sdev->scoop_lock, flag);
66
67         return gpio_bit;
68 }
69
70 unsigned short reset_scoop_gpio(struct device *dev, unsigned short bit)
71 {
72         unsigned short gpio_bit;
73         unsigned long flag;
74         struct scoop_dev *sdev = dev_get_drvdata(dev);
75
76         spin_lock_irqsave(&sdev->scoop_lock, flag);
77         gpio_bit = SCOOP_REG(sdev->base, SCOOP_GPWR) & ~bit;
78         SCOOP_REG(sdev->base,SCOOP_GPWR) = gpio_bit;
79         spin_unlock_irqrestore(&sdev->scoop_lock, flag);
80
81         return gpio_bit;
82 }
83
84 EXPORT_SYMBOL(set_scoop_gpio);
85 EXPORT_SYMBOL(reset_scoop_gpio);
86
87 unsigned short read_scoop_reg(struct device *dev, unsigned short reg)
88 {
89         struct scoop_dev *sdev = dev_get_drvdata(dev);
90         return SCOOP_REG(sdev->base,reg);
91 }
92
93 void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
94 {
95         struct scoop_dev *sdev = dev_get_drvdata(dev);
96         SCOOP_REG(sdev->base,reg)=data;
97 }
98
99 EXPORT_SYMBOL(reset_scoop);
100 EXPORT_SYMBOL(read_scoop_reg);
101 EXPORT_SYMBOL(write_scoop_reg);
102
103 static void check_scoop_reg(struct scoop_dev *sdev)
104 {
105         unsigned short mcr;
106
107         mcr = SCOOP_REG(sdev->base, SCOOP_MCR);
108         if ((mcr & 0x100) == 0)
109                 SCOOP_REG(sdev->base, SCOOP_MCR) = 0x0101;
110 }
111
112 #ifdef CONFIG_PM
113 static int scoop_suspend(struct platform_device *dev, pm_message_t state)
114 {
115         struct scoop_dev *sdev = platform_get_drvdata(dev);
116
117         check_scoop_reg(sdev);
118         sdev->scoop_gpwr = SCOOP_REG(sdev->base, SCOOP_GPWR);
119         SCOOP_REG(sdev->base, SCOOP_GPWR) = (sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set;
120
121         return 0;
122 }
123
124 static int scoop_resume(struct platform_device *dev)
125 {
126         struct scoop_dev *sdev = platform_get_drvdata(dev);
127
128         check_scoop_reg(sdev);
129         SCOOP_REG(sdev->base,SCOOP_GPWR) = sdev->scoop_gpwr;
130
131         return 0;
132 }
133 #else
134 #define scoop_suspend   NULL
135 #define scoop_resume    NULL
136 #endif
137
138 int __init scoop_probe(struct platform_device *pdev)
139 {
140         struct scoop_dev *devptr;
141         struct scoop_config *inf;
142         struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
143
144         if (!mem)
145                 return -EINVAL;
146
147         devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL);
148         if (!devptr)
149                 return -ENOMEM;
150
151         spin_lock_init(&devptr->scoop_lock);
152
153         inf = pdev->dev.platform_data;
154         devptr->base = ioremap(mem->start, mem->end - mem->start + 1);
155
156         if (!devptr->base) {
157                 kfree(devptr);
158                 return -ENOMEM;
159         }
160
161         platform_set_drvdata(pdev, devptr);
162
163         printk("Sharp Scoop Device found at 0x%08x -> 0x%08x\n",(unsigned int)mem->start,(unsigned int)devptr->base);
164
165         SCOOP_REG(devptr->base, SCOOP_MCR) = 0x0140;
166         reset_scoop(&pdev->dev);
167         SCOOP_REG(devptr->base, SCOOP_CPR) = 0x0000;
168         SCOOP_REG(devptr->base, SCOOP_GPCR) = inf->io_dir & 0xffff;
169         SCOOP_REG(devptr->base, SCOOP_GPWR) = inf->io_out & 0xffff;
170
171         devptr->suspend_clr = inf->suspend_clr;
172         devptr->suspend_set = inf->suspend_set;
173
174         return 0;
175 }
176
177 static int scoop_remove(struct platform_device *pdev)
178 {
179         struct scoop_dev *sdev = platform_get_drvdata(pdev);
180         if (sdev) {
181                 iounmap(sdev->base);
182                 kfree(sdev);
183                 platform_set_drvdata(pdev, NULL);
184         }
185         return 0;
186 }
187
188 static struct platform_driver scoop_driver = {
189         .probe          = scoop_probe,
190         .remove         = scoop_remove,
191         .suspend        = scoop_suspend,
192         .resume         = scoop_resume,
193         .driver         = {
194                 .name   = "sharp-scoop",
195         },
196 };
197
198 int __init scoop_init(void)
199 {
200         return platform_driver_register(&scoop_driver);
201 }
202
203 subsys_initcall(scoop_init);