From a139463010e22ed1af2e64a1229ad0a9c0ee628c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 17 May 2010 20:22:47 +0200 Subject: gta02: Add gps power management device --- arch/arm/mach-s3c2440/Makefile | 1 + arch/arm/mach-s3c2440/gta02-pm-gps.c | 222 ++++++++++++++++++++++ arch/arm/mach-s3c2440/include/mach/gta02-pm-gps.h | 1 + arch/arm/mach-s3c2440/mach-gta02.c | 16 ++ 4 files changed, 240 insertions(+) create mode 100644 arch/arm/mach-s3c2440/gta02-pm-gps.c create mode 100644 arch/arm/mach-s3c2440/include/mach/gta02-pm-gps.h diff --git a/arch/arm/mach-s3c2440/Makefile b/arch/arm/mach-s3c2440/Makefile index dc4665a6d18..050950fdee6 100644 --- a/arch/arm/mach-s3c2440/Makefile +++ b/arch/arm/mach-s3c2440/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_MACH_AT2440EVB) += mach-at2440evb.o obj-$(CONFIG_MACH_MINI2440) += mach-mini2440.o obj-$(CONFIG_MACH_NEO1973_GTA02) += mach-gta02.o \ gta02-pm-bt.o \ + gta02-pm-gps.o \ # extra machine support diff --git a/arch/arm/mach-s3c2440/gta02-pm-gps.c b/arch/arm/mach-s3c2440/gta02-pm-gps.c new file mode 100644 index 00000000000..9721f9a76e0 --- /dev/null +++ b/arch/arm/mach-s3c2440/gta02-pm-gps.c @@ -0,0 +1,222 @@ +/* + * GPS Power Management code for the Openmoko Freerunner GSM Phone + * + * (C) 2007-2009 by Openmoko Inc. + * Author: Harald Welte + * 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 + +struct gta02_pm_gps_data { +#ifdef CONFIG_PM + int keep_on_in_suspend; +#endif + int power_was_on; + struct regulator *regulator; +}; + +static struct gta02_pm_gps_data gta02_gps; + +int gta02_pm_gps_is_on(void) +{ + return gta02_gps.power_was_on; +} +EXPORT_SYMBOL_GPL(gta02_pm_gps_is_on); + +/* This is the POWERON pin */ +static void gps_pwron_set(int on) +{ + if (on) { + /* return UART pins to being UART pins */ + s3c2410_gpio_cfgpin(S3C2410_GPH(4), S3C2410_GPH4_TXD1); + /* remove pulldown now it won't be floating any more */ + s3c2410_gpio_pullup(S3C2410_GPH(5), 0); + + if (!gta02_gps.power_was_on) + regulator_enable(gta02_gps.regulator); + } else { + /* + * take care not to power unpowered GPS from UART TX + * return them to GPIO and force low + */ + s3c2410_gpio_cfgpin(S3C2410_GPH(4), S3C2410_GPIO_OUTPUT); + s3c2410_gpio_setpin(S3C2410_GPH(4), 0); + /* don't let RX from unpowered GPS float */ + s3c2410_gpio_pullup(S3C2410_GPH(5), 1); + if (gta02_gps.power_was_on) + regulator_disable(gta02_gps.regulator); + } +} + +static int gps_pwron_get(void) +{ + return regulator_is_enabled(gta02_gps.regulator); +} + +#ifdef CONFIG_PM +/* This is the flag for keeping gps ON during suspend */ +static void gps_keep_on_in_suspend_set(int on) +{ + gta02_gps.keep_on_in_suspend = on; +} + +static int gps_keep_on_in_suspend_get(void) +{ + return gta02_gps.keep_on_in_suspend; +} +#endif + +static ssize_t power_gps_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + + if (!strcmp(attr->attr.name, "power_on")) + ret = gps_pwron_get(); +#ifdef CONFIG_PM + else if (!strcmp(attr->attr.name, "keep_on_in_suspend")) + ret = gps_keep_on_in_suspend_get(); +#endif + if (ret) + return strlcpy(buf, "1\n", 3); + else + return strlcpy(buf, "0\n", 3); +} + +static ssize_t power_gps_write(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + unsigned long on = simple_strtoul(buf, NULL, 10); + + if (!strcmp(attr->attr.name, "power_on")) { + gps_pwron_set(on); + gta02_gps.power_was_on = !!on; +#ifdef CONFIG_PM + } else if (!strcmp(attr->attr.name, "keep_on_in_suspend")) { + gps_keep_on_in_suspend_set(on); +#endif + } + return count; +} + +#ifdef CONFIG_PM +static int gta02_pm_gps_suspend(struct device *dev) +{ + if (!gta02_gps.keep_on_in_suspend || + !gta02_gps.power_was_on) + gps_pwron_set(0); + else + dev_warn(dev, "GTA02: keeping gps ON " + "during suspend\n"); + return 0; +} + +static int gta02_pm_gps_resume(struct device *dev) +{ + if (!gta02_gps.keep_on_in_suspend && gta02_gps.power_was_on) + gps_pwron_set(1); + + return 0; +} + +static DEVICE_ATTR(keep_on_in_suspend, 0644, power_gps_read, power_gps_write); + +static struct dev_pm_ops gta02_gps_pm_ops = { + .suspend = gta02_pm_gps_suspend, + .resume = gta02_pm_gps_resume, +}; + +#define GTA02_GPS_PM_OPS (>a02_gps_pm_ops) + +#else +#define GTA02_GPS_PM_OPS NULL +#endif + +static DEVICE_ATTR(power_on, 0644, power_gps_read, power_gps_write); + +static struct attribute *gta02_gps_sysfs_entries[] = { + &dev_attr_power_on.attr, +#ifdef CONFIG_PM + &dev_attr_keep_on_in_suspend.attr, +#endif + NULL +}; + +static struct attribute_group gta02_gps_attr_group = { + .name = NULL, + .attrs = gta02_gps_sysfs_entries, +}; + +static int __init gta02_pm_gps_probe(struct platform_device *pdev) +{ + gta02_gps.regulator = regulator_get(&pdev->dev, "RF_3V"); + if (IS_ERR(gta02_gps.regulator)) { + dev_err(&pdev->dev, "probe failed %ld\n", + PTR_ERR(gta02_gps.regulator)); + + return PTR_ERR(gta02_gps.regulator); + } + + dev_info(&pdev->dev, "starting\n"); + + /* + * take care not to power unpowered GPS from UART TX + * return them to GPIO and force low + */ + s3c2410_gpio_cfgpin(S3C2410_GPH(4), S3C2410_GPIO_OUTPUT); + s3c2410_gpio_setpin(S3C2410_GPH(4), 0); + /* don't let RX from unpowered GPS float */ + s3c2410_gpio_pullup(S3C2410_GPH(5), 1); + + return sysfs_create_group(&pdev->dev.kobj, + >a02_gps_attr_group); +} + +static int gta02_pm_gps_remove(struct platform_device *pdev) +{ + regulator_put(gta02_gps.regulator); + sysfs_remove_group(&pdev->dev.kobj, >a02_gps_attr_group); + return 0; +} + +static struct platform_driver gta02_pm_gps_driver = { + .probe = gta02_pm_gps_probe, + .remove = gta02_pm_gps_remove, + .driver = { + .name = "gta02-pm-gps", + .pm = GTA02_GPS_PM_OPS, + }, +}; + +static int __devinit gta02_pm_gps_init(void) +{ + return platform_driver_register(>a02_pm_gps_driver); +} +module_init(gta02_pm_gps_init); + +static void gta02_pm_gps_exit(void) +{ + platform_driver_unregister(>a02_pm_gps_driver); +} +module_exit(gta02_pm_gps_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte "); diff --git a/arch/arm/mach-s3c2440/include/mach/gta02-pm-gps.h b/arch/arm/mach-s3c2440/include/mach/gta02-pm-gps.h new file mode 100644 index 00000000000..f15180ac079 --- /dev/null +++ b/arch/arm/mach-s3c2440/include/mach/gta02-pm-gps.h @@ -0,0 +1 @@ +extern int gta02_pm_gps_is_on(void); diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c index 8eae7d8599e..c256da5dcf0 100644 --- a/arch/arm/mach-s3c2440/mach-gta02.c +++ b/arch/arm/mach-s3c2440/mach-gta02.c @@ -94,6 +94,8 @@ #include #include +#include + static struct pcf50633 *gta02_pcf; /* @@ -160,6 +162,10 @@ static struct platform_device gta02_pm_bt_dev = { .name = "gta02-pm-bt", }; +static struct platform_device gta02_pm_gps_dev = { + .name = "gta02-pm-gps", +}; + #ifdef CONFIG_CHARGER_PCF50633 /* * On GTA02 the 1A charger features a 48K resistor to 0V on the ID pin. @@ -272,6 +278,13 @@ static struct regulator_consumer_supply ldo4_consumers[] = { }, }; +static struct regulator_consumer_supply ldo5_consumers[] = { + { + .dev = >a02_pm_gps_dev.dev, + .supply = "RF_3V", + }, +}; + struct pcf50633_platform_data gta02_pcf_pdata = { .resumers = { [0] = PCF50633_INT1_USBINS | @@ -384,6 +397,8 @@ struct pcf50633_platform_data gta02_pcf_pdata = { .enabled = 1, }, }, + .num_consumer_supplies = ARRAY_SIZE(ldo5_consumers), + .consumer_supplies = ldo5_consumers, }, [PCF50633_REGULATOR_LDO6] = { .constraints = { @@ -729,6 +744,7 @@ static struct platform_device *gta02_devices[] __initdata = { >a02_buttons_device, >a02_leds_device, >a02_pwm_leds_device, + >a02_pm_gps_dev, >a02_pm_bt_dev, }; -- cgit v1.2.3