From c5868175457f1c3d7b68fab2dcc962fa1d7bb4e7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 17 May 2010 20:27:00 +0200 Subject: gta02: Add wlan power management device --- arch/arm/mach-s3c2440/Makefile | 1 + arch/arm/mach-s3c2440/gta02-pm-wlan.c | 201 +++++++++++++++++++++ arch/arm/mach-s3c2440/include/mach/gta02-pm-wlan.h | 10 + arch/arm/mach-s3c2440/mach-gta02.c | 6 + 4 files changed, 218 insertions(+) create mode 100644 arch/arm/mach-s3c2440/gta02-pm-wlan.c create mode 100644 arch/arm/mach-s3c2440/include/mach/gta02-pm-wlan.h diff --git a/arch/arm/mach-s3c2440/Makefile b/arch/arm/mach-s3c2440/Makefile index 2111be7632e..dc29bee1039 100644 --- a/arch/arm/mach-s3c2440/Makefile +++ b/arch/arm/mach-s3c2440/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_MACH_NEO1973_GTA02) += mach-gta02.o \ gta02-pm-bt.o \ gta02-pm-gps.o \ gta02-pm-gsm.o \ + gta02-pm-wlan.o \ # extra machine support diff --git a/arch/arm/mach-s3c2440/gta02-pm-wlan.c b/arch/arm/mach-s3c2440/gta02-pm-wlan.c new file mode 100644 index 00000000000..c6ae6f3ced8 --- /dev/null +++ b/arch/arm/mach-s3c2440/gta02-pm-wlan.c @@ -0,0 +1,201 @@ +/* + * GTA02 WLAN power management + * + * (C) 2008, 2009 by Openmoko Inc. + * Author: Andy Green + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +/* ----- Module hardware reset ("power") ----------------------------------- */ + + +void gta02_wlan_reset(int assert_reset) +{ + if (assert_reset) { + s3c2410_gpio_setpin(GTA02_GPIO_nWLAN_RESET, 0); + msleep(200); /* probably excessive but we don't have specs */ + } else { + s3c2410_gpio_setpin(GTA02_GPIO_nWLAN_RESET, 1); + } +} + +/* ----- rfkill ------------------------------------------------------------ */ + +/* + * S3C MCI handles suspend/resume through device removal/insertion. In order to + * preserve rfkill state, as required in clause 7 of section 3.1 in rfkill.txt, + * we therefore need to maintain rfkill state outside the driver. + * + * This platform driver is as good a place as any other. + */ + +static int (*gta02_wlan_rfkill_cb)(void *user, int on); +static void *gta02_wlan_rfkill_user; +static DEFINE_MUTEX(gta02_wlan_rfkill_lock); +static int gta02_wlan_rfkill_on; + +/* + * gta02_wlan_query_rfkill_lock is used to obtain the rfkill state before the + * driver is ready to process rfkill callbacks. To prevent the state from + * changing until the driver has completed its initialization, we grab and hold + * the rfkill lock. + * + * A call to gta02_wlan_query_rfkill_lock must be followed by either + * - a call to gta02_wlan_set_rfkill_cb, to complete the setup, or + * - a call to gta02_wlan_query_rfkill_unlock to abort the setup process. + */ + +int gta02_wlan_query_rfkill_lock(void) +{ + mutex_lock(>a02_wlan_rfkill_lock); + return gta02_wlan_rfkill_on; +} +EXPORT_SYMBOL_GPL(gta02_wlan_query_rfkill_lock); + +void gta02_wlan_query_rfkill_unlock(void) +{ + mutex_unlock(>a02_wlan_rfkill_lock); +} +EXPORT_SYMBOL_GPL(gta02_wlan_query_rfkill_unlock); + +void gta02_wlan_set_rfkill_cb(int (*cb)(void *user, int on), void *user) +{ + BUG_ON(!mutex_is_locked(>a02_wlan_rfkill_lock)); + BUG_ON(gta02_wlan_rfkill_cb); + gta02_wlan_rfkill_cb = cb; + gta02_wlan_rfkill_user = user; + mutex_unlock(>a02_wlan_rfkill_lock); +} +EXPORT_SYMBOL_GPL(gta02_wlan_set_rfkill_cb); + +void gta02_wlan_clear_rfkill_cb(void) +{ + mutex_lock(>a02_wlan_rfkill_lock); + BUG_ON(!gta02_wlan_rfkill_cb); + gta02_wlan_rfkill_cb = NULL; + mutex_unlock(>a02_wlan_rfkill_lock); +} +EXPORT_SYMBOL_GPL(gta02_wlan_clear_rfkill_cb); + +static int gta02_wlan_set_radio_block(void *data, bool blocked) +{ + struct device *dev = data; + int res = 0; + + dev_dbg(dev, "gta02_wlan_toggle_radio: blocked %d (%p)\n", + blocked, gta02_wlan_rfkill_cb); + mutex_lock(>a02_wlan_rfkill_lock); + if (gta02_wlan_rfkill_cb) + res = gta02_wlan_rfkill_cb(gta02_wlan_rfkill_user, !blocked); + if (!res) + gta02_wlan_rfkill_on = !blocked; + mutex_unlock(>a02_wlan_rfkill_lock); + return res; +} + +static const struct rfkill_ops gta02_wlan_rfkill_ops = { + .set_block = gta02_wlan_set_radio_block, +}; + +/* ----- Initialization/removal -------------------------------------------- */ + + +static int __init gta02_wlan_probe(struct platform_device *pdev) +{ + /* default-on for now */ + const int default_state = 1; + struct rfkill *rfkill; + int ret; + + dev_info(&pdev->dev, "starting\n"); + + s3c2410_gpio_cfgpin(GTA02_GPIO_nWLAN_RESET, S3C2410_GPIO_OUTPUT); + gta02_wlan_reset(1); + gta02_wlan_reset(0); + + rfkill = rfkill_alloc("ar6000", &pdev->dev, RFKILL_TYPE_WLAN, + >a02_wlan_rfkill_ops, &pdev->dev); + + + if (!rfkill) { + dev_err(&pdev->dev, "Failed to allocate rfkill\n"); + return -ENOMEM; + } + + rfkill_init_sw_state(rfkill, default_state); + /* + * If the WLAN driver somehow managed to get activated before we're + * ready, the driver is now in an unknown state, which isn't something + * we're prepared to handle. This can't happen, so just fail hard. + */ + BUG_ON(gta02_wlan_rfkill_cb); + gta02_wlan_rfkill_on = default_state; + + ret = rfkill_register(rfkill); + if (ret) { + rfkill_destroy(rfkill); + dev_err(&pdev->dev, "Failed to register rfkill\n"); + return ret; + } + + dev_set_drvdata(&pdev->dev, rfkill); + + return 0; +} + +static int gta02_wlan_remove(struct platform_device *pdev) +{ + struct rfkill *rfkill = dev_get_drvdata(&pdev->dev); + + rfkill_destroy(rfkill); + + return 0; +} + +static struct platform_driver gta02_wlan_driver = { + .probe = gta02_wlan_probe, + .remove = gta02_wlan_remove, + .driver = { + .name = "gta02-pm-wlan", + }, +}; + +static int __devinit gta02_wlan_init(void) +{ + return platform_driver_register(>a02_wlan_driver); +} +module_init(gta02_wlan_init); + +static void gta02_wlan_exit(void) +{ + platform_driver_unregister(>a02_wlan_driver); +} +module_exit(gta02_wlan_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andy Green "); +MODULE_DESCRIPTION("Openmoko GTA02 WLAN power management"); diff --git a/arch/arm/mach-s3c2440/include/mach/gta02-pm-wlan.h b/arch/arm/mach-s3c2440/include/mach/gta02-pm-wlan.h new file mode 100644 index 00000000000..48369789709 --- /dev/null +++ b/arch/arm/mach-s3c2440/include/mach/gta02-pm-wlan.h @@ -0,0 +1,10 @@ +#ifndef __MACH_GTA02_PM_WLAN_H +#define __MACH_GTA02_PM_WLAN_H + +void gta02_wlan_reset(int assert_reset); +int gta02_wlan_query_rfkill_lock(void); +void gta02_wlan_query_rfkill_unlock(void); +void gta02_wlan_set_rfkill_cb(int (*cb)(void *user, int on), void *user); +void gta02_wlan_clear_rfkill_cb(void); + +#endif /* __MACH_GTA02_PM_WLAN_H */ diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c index fb90cca6223..c778ef0bee5 100644 --- a/arch/arm/mach-s3c2440/mach-gta02.c +++ b/arch/arm/mach-s3c2440/mach-gta02.c @@ -96,6 +96,7 @@ #include #include +#include static struct pcf50633 *gta02_pcf; @@ -171,6 +172,10 @@ static struct platform_device gta02_pm_gsm_dev = { .name = "gta02-pm-gsm", }; +static struct platform_device gta02_pm_wlan_dev = { + .name = "gta02-pm-wlan", +}; + static struct regulator_consumer_supply gsm_supply_consumer = { .dev = >a02_pm_gsm_dev.dev, .supply = "GSM", @@ -784,6 +789,7 @@ static struct platform_device *gta02_devices[] __initdata = { >a02_pwm_leds_device, >a02_pm_gps_dev, >a02_pm_bt_dev, + >a02_pm_wlan_dev, }; /* These guys DO need to be children of PMU. */ -- cgit v1.2.3