[s3c24xx] bump to 2.6.30-rc6
[openwrt.git] / target / linux / s3c24xx / files-2.6.30 / arch / arm / mach-s3c2442 / gta02-pm-bt.c
1 /*
2  * Bluetooth PM code for the Openmoko Freerunner GSM Phone
3  *
4  * (C) 2007 by Openmoko Inc.
5  * Author: Harald Welte <laforge@openmoko.org>
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/module.h>
15 #include <linux/init.h>
16 #include <linux/kernel.h>
17 #include <linux/platform_device.h>
18 #include <linux/rfkill.h>
19 #include <linux/err.h>
20
21 #include <mach/hardware.h>
22 #include <asm/mach-types.h>
23 #include <linux/gta02-shadow.h>
24
25 #include <mach/gta02.h>
26 #include <linux/mfd/pcf50633/gpio.h>
27
28 #include <linux/regulator/consumer.h>
29
30 #define DRVMSG "Openmoko Freerunner Bluetooth Power Management"
31
32 struct gta02_pm_bt_data {
33         struct regulator *regulator;
34         struct rfkill *rfkill;
35         int pre_resume_state;
36 };
37
38 static ssize_t bt_read(struct device *dev, struct device_attribute *attr,
39                        char *buf)
40 {
41         int ret = 0;
42         if (!strcmp(attr->attr.name, "power_on")) {
43                 if (s3c2410_gpio_getpin(GTA02_GPIO_BT_EN))
44                         ret = 1;
45         } else if (!strcmp(attr->attr.name, "reset")) {
46                 if (s3c2410_gpio_getpin(GTA02_GPIO_BT_EN) == 0)
47                         ret = 1;
48         }
49
50         if (!ret) {
51                 return strlcpy(buf, "0\n", 3);
52         } else {
53                 return strlcpy(buf, "1\n", 3);
54         }
55 }
56
57 static void __gta02_pm_bt_toggle_radio(struct device *dev, unsigned int on)
58 {
59         struct gta02_pm_bt_data *bt_data = dev_get_drvdata(dev);
60
61         dev_info(dev, "__gta02_pm_bt_toggle_radio %d\n", on);
62
63         bt_data = dev_get_drvdata(dev);
64
65         gta02_gpb_setpin(GTA02_GPIO_BT_EN, !on);
66
67         if (on) {
68                 if (!regulator_is_enabled(bt_data->regulator))
69                         regulator_enable(bt_data->regulator);
70         } else {
71                 if (regulator_is_enabled(bt_data->regulator))
72                         regulator_disable(bt_data->regulator);
73         }
74
75         gta02_gpb_setpin(GTA02_GPIO_BT_EN, on);
76 }
77
78
79 static int bt_rfkill_toggle_radio(void *data, enum rfkill_state state)
80 {
81         struct device *dev = data;
82         unsigned long on = (state == RFKILL_STATE_ON);
83
84         __gta02_pm_bt_toggle_radio(dev, on);
85
86         return 0;
87 }
88
89 static ssize_t bt_write(struct device *dev, struct device_attribute *attr,
90                         const char *buf, size_t count)
91 {
92         unsigned long on = simple_strtoul(buf, NULL, 10);
93         struct gta02_pm_bt_data *bt_data = dev_get_drvdata(dev);
94
95         if (!strcmp(attr->attr.name, "power_on")) {
96                 enum rfkill_state state = on ? RFKILL_STATE_ON : RFKILL_STATE_OFF;
97                 bt_rfkill_toggle_radio(dev, state);
98                 bt_data->rfkill->state = state;
99
100                 __gta02_pm_bt_toggle_radio(dev, on);
101         } else if (!strcmp(attr->attr.name, "reset")) {
102                 /* reset is low-active, so we need to invert */
103                 gta02_gpb_setpin(GTA02_GPIO_BT_EN, on ? 0 : 1);
104         }
105
106         return count;
107 }
108
109 static DEVICE_ATTR(power_on, 0644, bt_read, bt_write);
110 static DEVICE_ATTR(reset, 0644, bt_read, bt_write);
111
112 #ifdef CONFIG_PM
113 static int gta02_bt_suspend(struct platform_device *pdev, pm_message_t state)
114 {
115         struct gta02_pm_bt_data *bt_data = dev_get_drvdata(&pdev->dev);
116
117         dev_dbg(&pdev->dev, DRVMSG ": suspending\n");
118
119         bt_data->pre_resume_state = s3c2410_gpio_getpin(GTA02_GPIO_BT_EN);
120         __gta02_pm_bt_toggle_radio(&pdev->dev, 0);
121
122         return 0;
123 }
124
125 static int gta02_bt_resume(struct platform_device *pdev)
126 {
127         struct gta02_pm_bt_data *bt_data = dev_get_drvdata(&pdev->dev);
128         dev_dbg(&pdev->dev, DRVMSG ": resuming\n");
129
130         __gta02_pm_bt_toggle_radio(&pdev->dev, bt_data->pre_resume_state);
131         return 0;
132 }
133 #else
134 #define gta02_bt_suspend        NULL
135 #define gta02_bt_resume         NULL
136 #endif
137
138 static struct attribute *gta02_bt_sysfs_entries[] = {
139         &dev_attr_power_on.attr,
140         &dev_attr_reset.attr,
141         NULL
142 };
143
144 static struct attribute_group gta02_bt_attr_group = {
145         .name   = NULL,
146         .attrs  = gta02_bt_sysfs_entries,
147 };
148
149 static int __init gta02_bt_probe(struct platform_device *pdev)
150 {
151         struct rfkill *rfkill;
152         struct regulator *regulator;
153         struct gta02_pm_bt_data *bt_data;
154         int ret;
155
156         dev_info(&pdev->dev, DRVMSG ": starting\n");
157
158         bt_data = kzalloc(sizeof(*bt_data), GFP_KERNEL);
159         dev_set_drvdata(&pdev->dev, bt_data);
160
161         regulator = regulator_get(&pdev->dev, "BT_3V2");
162         if (IS_ERR(regulator))
163                 return -ENODEV;
164
165         bt_data->regulator = regulator;
166
167         /* this tests the true physical state of the regulator... */
168         if (regulator_is_enabled(regulator)) {
169                 /*
170                  * but these only operate on the logical state of the
171                  * regulator... so we need to logicaly "adopt" it on
172                  * to turn it off
173                  */
174                 regulator_enable(regulator);
175                 regulator_disable(regulator);
176         }
177
178         /* we pull reset to low to make sure that the chip doesn't
179          * drain power through the reset line */
180         gta02_gpb_setpin(GTA02_GPIO_BT_EN, 0);
181
182         rfkill = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH);
183
184         rfkill->name = pdev->name;
185         rfkill->data = &pdev->dev;
186         rfkill->state = RFKILL_STATE_OFF;
187         rfkill->toggle_radio = bt_rfkill_toggle_radio;
188
189         ret = rfkill_register(rfkill);
190         if (ret) {
191                 dev_err(&pdev->dev, "Failed to register rfkill\n");
192                 return ret;
193         }
194
195         bt_data->rfkill = rfkill;
196
197         return sysfs_create_group(&pdev->dev.kobj, &gta02_bt_attr_group);
198 }
199
200 static int gta02_bt_remove(struct platform_device *pdev)
201 {
202         struct gta02_pm_bt_data *bt_data = dev_get_drvdata(&pdev->dev);
203         struct regulator *regulator;
204
205         sysfs_remove_group(&pdev->dev.kobj, &gta02_bt_attr_group);
206
207         if (bt_data->rfkill) {
208                 rfkill_unregister(bt_data->rfkill);
209                 rfkill_free(bt_data->rfkill);
210         }
211
212         if (!bt_data || !bt_data->regulator)
213                 return 0;
214
215         regulator = bt_data->regulator;
216
217         /* Make sure regulator is disabled before calling regulator_put */
218         if (regulator_is_enabled(regulator))
219                 regulator_disable(regulator);
220
221         regulator_put(regulator);
222
223         kfree(bt_data);
224
225         return 0;
226 }
227
228 static struct platform_driver gta02_bt_driver = {
229         .probe          = gta02_bt_probe,
230         .remove         = gta02_bt_remove,
231         .suspend        = gta02_bt_suspend,
232         .resume         = gta02_bt_resume,
233         .driver         = {
234                 .name           = "gta02-pm-bt",
235         },
236 };
237
238 static int __devinit gta02_bt_init(void)
239 {
240         return platform_driver_register(&gta02_bt_driver);
241 }
242
243 static void gta02_bt_exit(void)
244 {
245         platform_driver_unregister(&gta02_bt_driver);
246 }
247
248 module_init(gta02_bt_init);
249 module_exit(gta02_bt_exit);
250
251 MODULE_LICENSE("GPL");
252 MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
253 MODULE_DESCRIPTION(DRVMSG);