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

Archive Download this file



interactive