Root/target/linux/s3c24xx/files-2.6.30/arch/arm/mach-s3c2442/gta02-pm-wlan.c

1/*
2 * GTA02 WLAN power management
3 *
4 * (C) 2008, 2009 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/module.h>
15#include <linux/init.h>
16#include <linux/kernel.h>
17#include <linux/mutex.h>
18#include <linux/platform_device.h>
19
20#include <mach/hardware.h>
21#include <asm/mach-types.h>
22
23#include <mach/gta02.h>
24#include <mach/gta02-pm-wlan.h>
25#include <mach/regs-gpio.h>
26#include <mach/regs-gpioj.h>
27
28#include <linux/delay.h>
29#include <linux/rfkill.h>
30
31
32/* ----- Module hardware reset ("power") ----------------------------------- */
33
34
35void gta02_wlan_reset(int assert_reset)
36{
37    if (assert_reset) {
38        s3c2410_gpio_setpin(GTA02_GPIO_nWLAN_RESET, 0);
39        msleep(200); /* probably excessive but we don't have specs */
40    } else {
41        s3c2410_gpio_setpin(GTA02_GPIO_nWLAN_RESET, 1);
42    }
43}
44
45/* ----- rfkill ------------------------------------------------------------ */
46
47/*
48 * S3C MCI handles suspend/resume through device removal/insertion. In order to
49 * preserve rfkill state, as required in clause 7 of section 3.1 in rfkill.txt,
50 * we therefore need to maintain rfkill state outside the driver.
51 *
52 * This platform driver is as good a place as any other.
53 */
54
55static int (*gta02_wlan_rfkill_cb)(void *user, int on);
56static void *gta02_wlan_rfkill_user;
57static DEFINE_MUTEX(gta02_wlan_rfkill_lock);
58static int gta02_wlan_rfkill_on;
59
60/*
61 * gta02_wlan_query_rfkill_lock is used to obtain the rfkill state before the
62 * driver is ready to process rfkill callbacks. To prevent the state from
63 * changing until the driver has completed its initialization, we grab and hold
64 * the rfkill lock.
65 *
66 * A call to gta02_wlan_query_rfkill_lock must be followed by either
67 * - a call to gta02_wlan_set_rfkill_cb, to complete the setup, or
68 * - a call to gta02_wlan_query_rfkill_unlock to abort the setup process.
69 */
70
71int gta02_wlan_query_rfkill_lock(void)
72{
73    mutex_lock(&gta02_wlan_rfkill_lock);
74    return gta02_wlan_rfkill_on;
75}
76EXPORT_SYMBOL_GPL(gta02_wlan_query_rfkill_lock);
77
78void gta02_wlan_query_rfkill_unlock(void)
79{
80    mutex_unlock(&gta02_wlan_rfkill_lock);
81}
82EXPORT_SYMBOL_GPL(gta02_wlan_query_rfkill_unlock);
83
84void gta02_wlan_set_rfkill_cb(int (*cb)(void *user, int on), void *user)
85{
86    BUG_ON(!mutex_is_locked(&gta02_wlan_rfkill_lock));
87    BUG_ON(gta02_wlan_rfkill_cb);
88    gta02_wlan_rfkill_cb = cb;
89    gta02_wlan_rfkill_user = user;
90    mutex_unlock(&gta02_wlan_rfkill_lock);
91}
92EXPORT_SYMBOL_GPL(gta02_wlan_set_rfkill_cb);
93
94void gta02_wlan_clear_rfkill_cb(void)
95{
96    mutex_lock(&gta02_wlan_rfkill_lock);
97    BUG_ON(!gta02_wlan_rfkill_cb);
98    gta02_wlan_rfkill_cb = NULL;
99    mutex_unlock(&gta02_wlan_rfkill_lock);
100}
101EXPORT_SYMBOL_GPL(gta02_wlan_clear_rfkill_cb);
102
103static int gta02_wlan_toggle_radio(void *data, enum rfkill_state state)
104{
105    struct device *dev = data;
106    int on = state == RFKILL_STATE_UNBLOCKED;
107    int res = 0;
108
109    dev_dbg(dev, "gta02_wlan_toggle_radio: state %d (%p)\n",
110        state, gta02_wlan_rfkill_cb);
111    mutex_lock(&gta02_wlan_rfkill_lock);
112    if (gta02_wlan_rfkill_cb)
113        res = gta02_wlan_rfkill_cb(gta02_wlan_rfkill_user, on);
114    if (!res)
115        gta02_wlan_rfkill_on = on;
116    mutex_unlock(&gta02_wlan_rfkill_lock);
117    return res;
118}
119
120
121/* ----- Initialization/removal -------------------------------------------- */
122
123
124static int __init gta02_wlan_probe(struct platform_device *pdev)
125{
126    /* default-on for now */
127    const int default_state = 1;
128    struct rfkill *rfkill;
129    int error;
130
131    dev_info(&pdev->dev, "starting\n");
132
133    s3c2410_gpio_cfgpin(GTA02_GPIO_nWLAN_RESET, S3C2410_GPIO_OUTPUT);
134    gta02_wlan_reset(1);
135    gta02_wlan_reset(0);
136
137    rfkill = rfkill_allocate(&pdev->dev, RFKILL_TYPE_WLAN);
138    rfkill->name = "ar6000";
139    rfkill->data = &pdev->dev;
140    rfkill->state = default_state ? RFKILL_STATE_ON : RFKILL_STATE_OFF;
141    /*
142     * If the WLAN driver somehow managed to get activated before we're
143     * ready, the driver is now in an unknown state, which isn't something
144     * we're prepared to handle. This can't happen, so just fail hard.
145     */
146    BUG_ON(gta02_wlan_rfkill_cb);
147    gta02_wlan_rfkill_on = default_state;
148    rfkill->toggle_radio = gta02_wlan_toggle_radio;
149
150    error = rfkill_register(rfkill);
151    if (error) {
152        rfkill_free(rfkill);
153        return error;
154    }
155
156    dev_set_drvdata(&pdev->dev, rfkill);
157
158    return 0;
159}
160
161static int gta02_wlan_remove(struct platform_device *pdev)
162{
163    struct rfkill *rfkill = dev_get_drvdata(&pdev->dev);
164
165    rfkill_unregister(rfkill);
166    rfkill_free(rfkill);
167
168    return 0;
169}
170
171static struct platform_driver gta02_wlan_driver = {
172    .probe = gta02_wlan_probe,
173    .remove = gta02_wlan_remove,
174    .driver = {
175        .name = "gta02-pm-wlan",
176    },
177};
178
179static int __devinit gta02_wlan_init(void)
180{
181    return platform_driver_register(&gta02_wlan_driver);
182}
183
184static void gta02_wlan_exit(void)
185{
186    platform_driver_unregister(&gta02_wlan_driver);
187}
188
189module_init(gta02_wlan_init);
190module_exit(gta02_wlan_exit);
191
192MODULE_LICENSE("GPL");
193MODULE_AUTHOR("Andy Green <andy@openmoko.com>");
194MODULE_DESCRIPTION("Openmoko GTA02 WLAN power management");
195

Archive Download this file



interactive