tracking-2.6.27-rc2-fix-fiq.patch
[kernel.git] / drivers / power / gta02_hdq.c
1 /*
2  * HDQ driver for the FIC Neo1973 GTA02 GSM phone
3  *
4  * (C) 2006-2007 by Openmoko, Inc.
5  * Author: Andy Green <andy@openmoko.com>
6  * All rights reserved.
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/kernel.h>
15 #include <linux/init.h>
16 #include <linux/delay.h>
17 #include <linux/platform_device.h>
18 #include <mach/hardware.h>
19 #include <asm/mach-types.h>
20 #include <asm/arch/gta02.h>
21 #include <asm/arch/fiq_ipc_gta02.h>
22
23
24
25 #define HDQ_READ 0
26 #define HDQ_WRITE 0x80
27
28
29 int gta02hdq_initialized(void)
30 {
31         return fiq_ipc.hdq_probed;
32 }
33 EXPORT_SYMBOL_GPL(gta02hdq_initialized);
34
35 int gta02hdq_read(int address)
36 {
37         int count_sleeps = 5;
38         int ret = -ETIME;
39
40         mutex_lock(&fiq_ipc.hdq_lock);
41
42         fiq_ipc.hdq_ads = address | HDQ_READ;
43         fiq_ipc.hdq_request_ctr++;
44         fiq_kick();
45         /*
46          * FIQ takes care of it while we block our calling process
47          * But we're not spinning -- other processes run normally while
48          * we wait for the result
49          */
50         while (count_sleeps--) {
51                 msleep(10); /* valid transaction always completes in < 10ms */
52
53                 if (fiq_ipc.hdq_request_ctr != fiq_ipc.hdq_transaction_ctr)
54                         continue;
55
56                 if (fiq_ipc.hdq_error)
57                         goto done; /* didn't see a response in good time */
58
59                 ret = fiq_ipc.hdq_rx_data;
60                 goto done;
61         }
62         ret = -EINVAL;
63
64 done:
65         mutex_unlock(&fiq_ipc.hdq_lock);
66         return ret;
67 }
68 EXPORT_SYMBOL_GPL(gta02hdq_read);
69
70 int gta02hdq_write(int address, u8 data)
71 {
72         int count_sleeps = 5;
73         int ret = -ETIME;
74
75         mutex_lock(&fiq_ipc.hdq_lock);
76
77         fiq_ipc.hdq_ads = address | HDQ_WRITE;
78         fiq_ipc.hdq_tx_data = data;
79         fiq_ipc.hdq_request_ctr++;
80         fiq_kick();
81         /*
82          * FIQ takes care of it while we block our calling process
83          * But we're not spinning -- other processes run normally while
84          * we wait for the result
85          */
86         while (count_sleeps--) {
87                 msleep(10); /* valid transaction always completes in < 10ms */
88
89                 if (fiq_ipc.hdq_request_ctr != fiq_ipc.hdq_transaction_ctr)
90                         continue; /* something bad with FIQ */
91
92                 if (fiq_ipc.hdq_error)
93                         goto done; /* didn't see a response in good time */
94         }
95         ret = -EINVAL;
96
97 done:
98         mutex_unlock(&fiq_ipc.hdq_lock);
99         return ret;
100 }
101 EXPORT_SYMBOL_GPL(gta02hdq_write);
102
103 /* sysfs */
104
105 static ssize_t hdq_sysfs_dump(struct device *dev, struct device_attribute *attr,
106                          char *buf)
107 {
108         int n;
109         int v;
110         u8 u8a[128]; /* whole address space for HDQ */
111         char *end = buf;
112
113         /* the dump does not take care about 16 bit regs, because at this
114          * bus level we don't know about the chip details
115          */
116         for (n = 0; n < sizeof(u8a); n++) {
117                 v = gta02hdq_read(n);
118                 if (v < 0)
119                         goto bail;
120                 u8a[n] = v;
121         }
122
123         for (n = 0; n < sizeof(u8a); n += 16) {
124                 hex_dump_to_buffer(u8a + n, sizeof(u8a), 16, 1, end, 4096, 0);
125                 end += strlen(end);
126                 *end++ = '\n';
127                 *end = '\0';
128         }
129         return (end - buf);
130
131 bail:
132         return sprintf(buf, "ERROR %d\n", v);
133 }
134
135 /* you write by <address> <data>, eg, "34 128" */
136
137 #define atoi(str) simple_strtoul(((str != NULL) ? str : ""), NULL, 0)
138
139 static ssize_t hdq_sysfs_write(struct device *dev,
140                                struct device_attribute *attr,
141                                const char *buf, size_t count)
142 {
143         const char *end = buf + count;
144         int address = atoi(buf);
145
146         while ((buf != end) && (*buf != ' '))
147                 buf++;
148         if (buf >= end)
149                 return 0;
150         while ((buf < end) && (*buf == ' '))
151                 buf++;
152         if (buf >= end)
153                 return 0;
154
155         gta02hdq_write(address, atoi(buf));
156
157         return count;
158 }
159
160 static DEVICE_ATTR(dump, 0400, hdq_sysfs_dump, NULL);
161 static DEVICE_ATTR(write, 0600, NULL, hdq_sysfs_write);
162
163 static struct attribute *gta02hdq_sysfs_entries[] = {
164         &dev_attr_dump.attr,
165         &dev_attr_write.attr,
166         NULL
167 };
168
169 static struct attribute_group gta02hdq_attr_group = {
170         .name   = "hdq",
171         .attrs  = gta02hdq_sysfs_entries,
172 };
173
174
175 #ifdef CONFIG_PM
176 static int gta02hdq_suspend(struct platform_device *pdev, pm_message_t state)
177 {
178         /* after 18s of this, the battery monitor will also go to sleep */
179         s3c2410_gpio_setpin(fiq_ipc.hdq_gpio_pin, 0);
180         s3c2410_gpio_cfgpin(fiq_ipc.hdq_gpio_pin, S3C2410_GPIO_OUTPUT);
181         return 0;
182 }
183
184 static int gta02hdq_resume(struct platform_device *pdev)
185 {
186         s3c2410_gpio_setpin(fiq_ipc.hdq_gpio_pin, 1);
187         s3c2410_gpio_cfgpin(fiq_ipc.hdq_gpio_pin, S3C2410_GPIO_OUTPUT);
188         return 0;
189 }
190 #endif
191
192 static int __init gta02hdq_probe(struct platform_device *pdev)
193 {
194         struct resource *r = platform_get_resource(pdev, 0, 0);
195
196         if (!machine_is_neo1973_gta02())
197                 return -EIO;
198
199         if (!r)
200                 return -EINVAL;
201
202         if (!fiq_ready) {
203                 printk(KERN_ERR "hdq probe fails on fiq not ready\n");
204                 return -EINVAL;
205         }
206
207         platform_set_drvdata(pdev, NULL);
208
209         mutex_init(&fiq_ipc.hdq_lock);
210
211         /* set our HDQ comms pin from the platform data */
212         fiq_ipc.hdq_gpio_pin = r->start;
213
214         s3c2410_gpio_setpin(fiq_ipc.hdq_gpio_pin, 1);
215         s3c2410_gpio_cfgpin(fiq_ipc.hdq_gpio_pin, S3C2410_GPIO_OUTPUT);
216
217         fiq_ipc.hdq_probed = 1; /* we are ready to do stuff now */
218
219         return sysfs_create_group(&pdev->dev.kobj, &gta02hdq_attr_group);
220 }
221
222 static int gta02hdq_remove(struct platform_device *pdev)
223 {
224         sysfs_remove_group(&pdev->dev.kobj, &gta02hdq_attr_group);
225         return 0;
226 }
227
228 static struct platform_driver gta02hdq_driver = {
229         .probe          = gta02hdq_probe,
230         .remove         = gta02hdq_remove,
231 #ifdef CONFIG_PM
232         .suspend        = gta02hdq_suspend,
233         .resume         = gta02hdq_resume,
234 #endif
235         .driver         = {
236                 .name           = "gta02-hdq",
237         },
238 };
239
240 static int __init gta02hdq_init(void)
241 {
242         return platform_driver_register(&gta02hdq_driver);
243 }
244
245 static void __exit gta02hdq_exit(void)
246 {
247         platform_driver_unregister(&gta02hdq_driver);
248 }
249
250 module_init(gta02hdq_init);
251 module_exit(gta02hdq_exit);
252
253 MODULE_AUTHOR("Andy Green <andy@openmoko.com>");
254 MODULE_DESCRIPTION("GTA02 HDQ driver");
255 MODULE_LICENSE("GPL");