diff options
Diffstat (limited to 'target/linux/s3c24xx/patches/0010-gta01-power_control.patch.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches/0010-gta01-power_control.patch.patch | 976 |
1 files changed, 976 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches/0010-gta01-power_control.patch.patch b/target/linux/s3c24xx/patches/0010-gta01-power_control.patch.patch new file mode 100755 index 0000000000..76d645f345 --- /dev/null +++ b/target/linux/s3c24xx/patches/0010-gta01-power_control.patch.patch @@ -0,0 +1,976 @@ +From 0739383d85822b93831803abb207794c16eedd79 Mon Sep 17 00:00:00 2001 +From: mokopatches <mokopatches@openmoko.org> +Date: Wed, 16 Jul 2008 14:44:48 +0100 +Subject: [PATCH] gta01-power_control.patch + [PATCH] Neo1973 GPS / GSM / Bluetooth power control via sysfs + +Signed-off-by: Harald Welte <laforge@openmoko.org> +--- + arch/arm/plat-s3c24xx/Makefile | 1 + + arch/arm/plat-s3c24xx/neo1973_pm_bt.c | 152 +++++++++ + arch/arm/plat-s3c24xx/neo1973_pm_gps.c | 560 ++++++++++++++++++++++++++++++++ + arch/arm/plat-s3c24xx/neo1973_pm_gsm.c | 217 ++++++++++++ + 4 files changed, 930 insertions(+), 0 deletions(-) + create mode 100644 arch/arm/plat-s3c24xx/neo1973_pm_bt.c + create mode 100644 arch/arm/plat-s3c24xx/neo1973_pm_gps.c + create mode 100644 arch/arm/plat-s3c24xx/neo1973_pm_gsm.c + +diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile +index 131d202..6f43aca 100644 +--- a/arch/arm/plat-s3c24xx/Makefile ++++ b/arch/arm/plat-s3c24xx/Makefile +@@ -29,3 +29,4 @@ obj-$(CONFIG_PM) += pm.o + obj-$(CONFIG_PM) += sleep.o + obj-$(CONFIG_S3C2410_DMA) += dma.o + obj-$(CONFIG_MACH_SMDK) += common-smdk.o ++obj-$(CONFIG_MACH_NEO1973) += neo1973_pm_gsm.o neo1973_pm_gps.o neo1973_pm_bt.o +diff --git a/arch/arm/plat-s3c24xx/neo1973_pm_bt.c b/arch/arm/plat-s3c24xx/neo1973_pm_bt.c +new file mode 100644 +index 0000000..b1af441 +--- /dev/null ++++ b/arch/arm/plat-s3c24xx/neo1973_pm_bt.c +@@ -0,0 +1,152 @@ ++/* ++ * Bluetooth PM code for the FIC Neo1973 GSM Phone ++ * ++ * (C) 2007 by OpenMoko Inc. ++ * Author: Harald Welte <laforge@openmoko.org> ++ * 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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++ ++#include <linux/pcf50606.h> ++ ++#include <asm/hardware.h> ++#include <asm/arch/gta01.h> ++ ++#define DRVMSG "FIC Neo1973 Bluetooth Power Management" ++ ++static ssize_t bt_read(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ if (!strcmp(attr->attr.name, "power_on")) { ++ if (pcf50606_onoff_get(pcf50606_global, ++ PCF50606_REGULATOR_D1REG) && ++ pcf50606_voltage_get(pcf50606_global, ++ PCF50606_REGULATOR_D1REG) == 3100) ++ goto out_1; ++ } else if (!strcmp(attr->attr.name, "reset")) { ++ if (s3c2410_gpio_getpin(GTA01_GPIO_BT_EN) == 0) ++ goto out_1; ++ } ++ ++ return strlcpy(buf, "0\n", 3); ++out_1: ++ return strlcpy(buf, "1\n", 3); ++} ++ ++static ssize_t bt_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")) { ++ /* if we are powering up, assert reset, then power, then ++ * release reset */ ++ if (on) { ++ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, 0); ++ pcf50606_voltage_set(pcf50606_global, ++ PCF50606_REGULATOR_D1REG, ++ 3100); ++ } ++ pcf50606_onoff_set(pcf50606_global, ++ PCF50606_REGULATOR_D1REG, on); ++ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, on); ++ } else if (!strcmp(attr->attr.name, "reset")) { ++ /* reset is low-active, so we need to invert */ ++ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, on ? 0 : 1); ++ } ++ ++ return count; ++} ++ ++static DEVICE_ATTR(power_on, 0644, bt_read, bt_write); ++static DEVICE_ATTR(reset, 0644, bt_read, bt_write); ++ ++#ifdef CONFIG_PM ++static int gta01_bt_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ dev_dbg(&pdev->dev, DRVMSG ": suspending\n"); ++ /* FIXME: The PMU should save the PMU status, and the GPIO code should ++ * preserve the GPIO level, so there shouldn't be anything left to do ++ * for us, should there? */ ++ ++ return 0; ++} ++ ++static int gta01_bt_resume(struct platform_device *pdev) ++{ ++ dev_dbg(&pdev->dev, DRVMSG ": resuming\n"); ++ ++ return 0; ++} ++#else ++#define gta01_bt_suspend NULL ++#define gta01_bt_resume NULL ++#endif ++ ++static struct attribute *gta01_bt_sysfs_entries[] = { ++ &dev_attr_power_on.attr, ++ &dev_attr_reset.attr, ++ NULL ++}; ++ ++static struct attribute_group gta01_bt_attr_group = { ++ .name = NULL, ++ .attrs = gta01_bt_sysfs_entries, ++}; ++ ++static int __init gta01_bt_probe(struct platform_device *pdev) ++{ ++ dev_info(&pdev->dev, DRVMSG ": starting\n"); ++ ++ /* we make sure that the voltage is off */ ++ pcf50606_onoff_set(pcf50606_global, ++ PCF50606_REGULATOR_D1REG, 0); ++ /* we pull reset to low to make sure that the chip doesn't ++ * drain power through the reset line */ ++ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, 0); ++ ++ return sysfs_create_group(&pdev->dev.kobj, >a01_bt_attr_group); ++} ++ ++static int gta01_bt_remove(struct platform_device *pdev) ++{ ++ sysfs_remove_group(&pdev->dev.kobj, >a01_bt_attr_group); ++ ++ return 0; ++} ++ ++static struct platform_driver gta01_bt_driver = { ++ .probe = gta01_bt_probe, ++ .remove = gta01_bt_remove, ++ .suspend = gta01_bt_suspend, ++ .resume = gta01_bt_resume, ++ .driver = { ++ .name = "neo1973-pm-bt", ++ }, ++}; ++ ++static int __devinit gta01_bt_init(void) ++{ ++ return platform_driver_register(>a01_bt_driver); ++} ++ ++static void gta01_bt_exit(void) ++{ ++ platform_driver_unregister(>a01_bt_driver); ++} ++ ++module_init(gta01_bt_init); ++module_exit(gta01_bt_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>"); ++MODULE_DESCRIPTION(DRVMSG); +diff --git a/arch/arm/plat-s3c24xx/neo1973_pm_gps.c b/arch/arm/plat-s3c24xx/neo1973_pm_gps.c +new file mode 100644 +index 0000000..f8cf719 +--- /dev/null ++++ b/arch/arm/plat-s3c24xx/neo1973_pm_gps.c +@@ -0,0 +1,560 @@ ++/* ++ * GPS Power Management code for the FIC Neo1973 GSM Phone ++ * ++ * (C) 2007 by OpenMoko Inc. ++ * Author: Harald Welte <laforge@openmoko.org> ++ * 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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++ ++#include <linux/pcf50606.h> ++ ++#include <asm/hardware.h> ++#include <asm/arch/gta01.h> ++ ++/* This is the 2.8V supply for the RTC crystal, the mail clock crystal and ++ * the input to VDD_RF */ ++static void gps_power_2v8_set(int on) ++{ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ case GTA01v4_SYSTEM_REV: ++ if (on) ++ pcf50606_voltage_set(pcf50606_global, ++ PCF50606_REGULATOR_IOREG, 2800); ++ pcf50606_onoff_set(pcf50606_global, ++ PCF50606_REGULATOR_IOREG, on); ++ break; ++ case GTA01Bv2_SYSTEM_REV: ++ s3c2410_gpio_setpin(GTA01_GPIO_GPS_EN_2V8, on); ++ break; ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ break; ++ } ++} ++ ++static int gps_power_2v8_get(void) ++{ ++ int ret = 0; ++ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ case GTA01v4_SYSTEM_REV: ++ if (pcf50606_onoff_get(pcf50606_global, ++ PCF50606_REGULATOR_IOREG) && ++ pcf50606_voltage_get(pcf50606_global, ++ PCF50606_REGULATOR_IOREG) == 2800) ++ ret = 1; ++ break; ++ case GTA01Bv2_SYSTEM_REV: ++ if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_EN_2V8)) ++ ret = 1; ++ break; ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ break; ++ } ++ ++ return ret; ++} ++ ++/* This is the 3V supply (AVDD) for the external RF frontend (LNA bias) */ ++static void gps_power_3v_set(int on) ++{ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ case GTA01v4_SYSTEM_REV: ++ if (on) ++ pcf50606_voltage_set(pcf50606_global, ++ PCF50606_REGULATOR_D1REG, 3000); ++ pcf50606_onoff_set(pcf50606_global, ++ PCF50606_REGULATOR_D1REG, on); ++ break; ++ case GTA01Bv2_SYSTEM_REV: ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ s3c2410_gpio_setpin(GTA01_GPIO_GPS_EN_3V, on); ++ break; ++ } ++} ++ ++static int gps_power_3v_get(void) ++{ ++ int ret = 0; ++ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ case GTA01v4_SYSTEM_REV: ++ if (pcf50606_onoff_get(pcf50606_global, ++ PCF50606_REGULATOR_D1REG) && ++ pcf50606_voltage_get(pcf50606_global, ++ PCF50606_REGULATOR_D1REG) == 3000) ++ ret = 1; ++ break; ++ case GTA01Bv2_SYSTEM_REV: ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_EN_3V)) ++ ret = 1; ++ break; ++ } ++ ++ return ret; ++} ++ ++/* This is the 3.3V supply for VDD_IO and VDD_LPREG input */ ++static void gps_power_3v3_set(int on) ++{ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ case GTA01v4_SYSTEM_REV: ++ case GTA01Bv2_SYSTEM_REV: ++ if (on) ++ pcf50606_voltage_set(pcf50606_global, ++ PCF50606_REGULATOR_DCD, 3300); ++ pcf50606_onoff_set(pcf50606_global, ++ PCF50606_REGULATOR_DCD, on); ++ break; ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ s3c2410_gpio_setpin(GTA01_GPIO_GPS_EN_3V3, on); ++ break; ++ } ++} ++ ++static int gps_power_3v3_get(void) ++{ ++ int ret = 0; ++ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ case GTA01v4_SYSTEM_REV: ++ case GTA01Bv2_SYSTEM_REV: ++ if (pcf50606_onoff_get(pcf50606_global, ++ PCF50606_REGULATOR_DCD) && ++ pcf50606_voltage_get(pcf50606_global, ++ PCF50606_REGULATOR_DCD) == 3300) ++ ret = 1; ++ break; ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_EN_3V3)) ++ ret = 1; ++ break; ++ } ++ ++ return ret; ++} ++ ++/* This is the 2.5V supply for VDD_PLLREG and VDD_COREREG input */ ++static void gps_power_2v5_set(int on) ++{ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ /* This is CORE_1V8 and cannot be disabled */ ++ break; ++ case GTA01v4_SYSTEM_REV: ++ case GTA01Bv2_SYSTEM_REV: ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ if (on) ++ pcf50606_voltage_set(pcf50606_global, ++ PCF50606_REGULATOR_D2REG, 2500); ++ pcf50606_onoff_set(pcf50606_global, ++ PCF50606_REGULATOR_D2REG, on); ++ break; ++ } ++} ++ ++static int gps_power_2v5_get(void) ++{ ++ int ret = 0; ++ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ /* This is CORE_1V8 and cannot be disabled */ ++ ret = 1; ++ break; ++ case GTA01v4_SYSTEM_REV: ++ case GTA01Bv2_SYSTEM_REV: ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ if (pcf50606_onoff_get(pcf50606_global, ++ PCF50606_REGULATOR_D2REG) && ++ pcf50606_voltage_get(pcf50606_global, ++ PCF50606_REGULATOR_D2REG) == 2500) ++ ret = 1; ++ break; ++ } ++ ++ return ret; ++} ++ ++/* This is the 1.5V supply for VDD_CORE */ ++static void gps_power_1v5_set(int on) ++{ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ case GTA01v4_SYSTEM_REV: ++ case GTA01Bv2_SYSTEM_REV: ++ /* This is switched via 2v5 */ ++ break; ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ if (on) ++ pcf50606_voltage_set(pcf50606_global, ++ PCF50606_REGULATOR_DCD, 1500); ++ pcf50606_onoff_set(pcf50606_global, ++ PCF50606_REGULATOR_DCD, on); ++ break; ++ } ++} ++ ++static int gps_power_1v5_get(void) ++{ ++ int ret = 0; ++ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ case GTA01v4_SYSTEM_REV: ++ case GTA01Bv2_SYSTEM_REV: ++ /* This is switched via 2v5 */ ++ ret = 1; ++ break; ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ if (pcf50606_onoff_get(pcf50606_global, ++ PCF50606_REGULATOR_DCD) && ++ pcf50606_voltage_get(pcf50606_global, ++ PCF50606_REGULATOR_DCD) == 1500) ++ ret = 1; ++ break; ++ } ++ ++ return ret; ++} ++ ++/* This is the POWERON pin */ ++static void gps_pwron_set(int on) ++{ ++ s3c2410_gpio_setpin(GTA01_GPIO_GPS_PWRON, on); ++} ++ ++static int gps_pwron_get(void) ++{ ++ if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_PWRON)) ++ return 1; ++ else ++ return 0; ++} ++ ++/* This is the nRESET pin */ ++static void gps_rst_set(int on) ++{ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ pcf50606_gpo0_set(pcf50606_global, on); ++ break; ++ case GTA01v4_SYSTEM_REV: ++ case GTA01Bv2_SYSTEM_REV: ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ s3c2410_gpio_setpin(GTA01_GPIO_GPS_RESET, on); ++ break; ++ } ++} ++ ++static int gps_rst_get(void) ++{ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ if (pcf50606_gpo0_get(pcf50606_global)) ++ return 1; ++ break; ++ case GTA01v4_SYSTEM_REV: ++ case GTA01Bv2_SYSTEM_REV: ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_RESET)) ++ return 1; ++ break; ++ } ++ ++ return 0; ++} ++ ++static ssize_t power_gps_read(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int ret = 0; ++ ++ if (!strcmp(attr->attr.name, "power_tcxo_2v8")) { ++ ret = gps_power_2v8_get(); ++ } else if (!strcmp(attr->attr.name, "power_avdd_3v")) { ++ ret = gps_power_3v_get(); ++ } else if (!strcmp(attr->attr.name, "pwron")) { ++ ret = gps_pwron_get(); ++ } else if (!strcmp(attr->attr.name, "reset")) { ++ ret = gps_rst_get(); ++ } else if (!strcmp(attr->attr.name, "power_lp_io_3v3")) { ++ ret = gps_power_3v3_get(); ++ } else if (!strcmp(attr->attr.name, "power_pll_core_2v5")) { ++ ret = gps_power_2v5_get(); ++ } else if (!strcmp(attr->attr.name, "power_core_1v5") || ++ !strcmp(attr->attr.name, "power_vdd_core_1v5")) { ++ ret = gps_power_1v5_get(); ++ } ++ ++ 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_tcxo_2v8")) { ++ gps_power_2v8_set(on); ++ } else if (!strcmp(attr->attr.name, "power_avdd_3v")) { ++ gps_power_3v_set(on); ++ } else if (!strcmp(attr->attr.name, "pwron")) { ++ gps_pwron_set(on); ++ } else if (!strcmp(attr->attr.name, "reset")) { ++ gps_rst_set(on); ++ } else if (!strcmp(attr->attr.name, "power_lp_io_3v3")) { ++ gps_power_3v3_set(on); ++ } else if (!strcmp(attr->attr.name, "power_pll_core_2v5")) { ++ gps_power_2v5_set(on); ++ } else if (!strcmp(attr->attr.name, "power_core_1v5") || ++ !strcmp(attr->attr.name, "power_vdd_core_1v5")) { ++ gps_power_1v5_set(on); ++ } ++ ++ return count; ++} ++ ++static void gps_power_sequence_up(void) ++{ ++ /* According to PMB2520 Data Sheet, Rev. 2006-06-05, ++ * Chapter 4.2.2 */ ++ ++ /* nRESET must be asserted low */ ++ gps_rst_set(0); ++ ++ /* POWERON must be de-asserted (low) */ ++ gps_pwron_set(0); ++ ++ /* Apply VDD_IO and VDD_LPREG_IN */ ++ gps_power_3v3_set(1); ++ ++ /* VDD_COREREG_IN, VDD_PLLREG_IN */ ++ gps_power_1v5_set(1); ++ gps_power_2v5_set(1); ++ ++ /* and VDD_RF may be applied */ ++ gps_power_2v8_set(1); ++ ++ /* We need to enable AVDD, since in GTA01Bv3 it is ++ * shared with RFREG_IN */ ++ gps_power_3v_set(1); ++ ++ msleep(3); /* Is 3ms enough? */ ++ ++ /* De-asert nRESET */ ++ gps_rst_set(1); ++ ++ /* Switch power on */ ++ gps_pwron_set(1); ++ ++} ++ ++static void gps_power_sequence_down(void) ++{ ++ /* According to PMB2520 Data Sheet, Rev. 2006-06-05, ++ * Chapter 4.2.3.1 */ ++ gps_pwron_set(0); ++ ++ /* Don't disable AVDD before PWRON is cleared, since ++ * in GTA01Bv3, AVDD and RFREG_IN are shared */ ++ gps_power_3v_set(0); ++ ++ /* Remove VDD_COREREG_IN, VDD_PLLREG_IN and VDD_REFREG_IN */ ++ gps_power_1v5_set(0); ++ gps_power_2v5_set(0); ++ gps_power_2v8_set(0); ++ ++ /* Remove VDD_LPREG_IN and VDD_IO */ ++ gps_power_3v3_set(0); ++} ++ ++ ++static ssize_t power_sequence_read(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ return strlcpy(buf, "power_up power_down\n", PAGE_SIZE); ++} ++ ++static ssize_t power_sequence_write(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ dev_dbg(dev, "wrote: '%s'\n", buf); ++ ++ if (!strncmp(buf, "power_up", 8)) ++ gps_power_sequence_up(); ++ else if (!strncmp(buf, "power_down", 10)) ++ gps_power_sequence_down(); ++ else ++ return -EINVAL; ++ ++ return count; ++} ++ ++static DEVICE_ATTR(power_tcxo_2v8, 0644, power_gps_read, power_gps_write); ++static DEVICE_ATTR(power_avdd_3v, 0644, power_gps_read, power_gps_write); ++static DEVICE_ATTR(pwron, 0644, power_gps_read, power_gps_write); ++static DEVICE_ATTR(reset, 0644, power_gps_read, power_gps_write); ++static DEVICE_ATTR(power_lp_io_3v3, 0644, power_gps_read, power_gps_write); ++static DEVICE_ATTR(power_pll_core_2v5, 0644, power_gps_read, power_gps_write); ++static DEVICE_ATTR(power_core_1v5, 0644, power_gps_read, power_gps_write); ++static DEVICE_ATTR(power_vdd_core_1v5, 0644, power_gps_read, power_gps_write); ++static DEVICE_ATTR(power_sequence, 0644, power_sequence_read, ++ power_sequence_write); ++ ++#ifdef CONFIG_PM ++static int gta01_pm_gps_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ /* FIXME */ ++ gps_power_sequence_down(); ++ ++ return 0; ++} ++ ++static int gta01_pm_gps_resume(struct platform_device *pdev) ++{ ++ /* FIXME */ ++ gps_power_sequence_up(); ++ ++ return 0; ++} ++#else ++#define gta01_pm_gps_suspend NULL ++#define gta01_pm_gps_resume NULL ++#endif ++ ++static struct attribute *gta01_gps_sysfs_entries[] = { ++ &dev_attr_power_avdd_3v.attr, ++ &dev_attr_pwron.attr, ++ &dev_attr_reset.attr, ++ &dev_attr_power_lp_io_3v3.attr, ++ &dev_attr_power_pll_core_2v5.attr, ++ &dev_attr_power_sequence.attr, ++ NULL, /* power_core_1v5 */ ++ NULL, /* power_vdd_core_1v5 */ ++ NULL /* terminating entry */ ++}; ++ ++static struct attribute_group gta01_gps_attr_group = { ++ .name = NULL, ++ .attrs = gta01_gps_sysfs_entries, ++}; ++ ++static int __init gta01_pm_gps_probe(struct platform_device *pdev) ++{ ++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_PWRON, S3C2410_GPIO_OUTPUT); ++ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ break; ++ case GTA01v4_SYSTEM_REV: ++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_RESET, S3C2410_GPIO_OUTPUT); ++ break; ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_EN_3V3, S3C2410_GPIO_OUTPUT); ++ /* fallthrough */ ++ case GTA01Bv2_SYSTEM_REV: ++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_EN_2V8, S3C2410_GPIO_OUTPUT); ++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_EN_3V, S3C2410_GPIO_OUTPUT); ++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_RESET, S3C2410_GPIO_OUTPUT); ++ break; ++ default: ++ dev_warn(&pdev->dev, "Unknown GTA01 Revision 0x%x, " ++ "AGPS PM features not available!!!\n", ++ system_rev); ++ return -1; ++ break; ++ } ++ ++ gps_power_sequence_down(); ++ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ case GTA01v4_SYSTEM_REV: ++ case GTA01Bv2_SYSTEM_REV: ++ gta01_gps_sysfs_entries[ARRAY_SIZE(gta01_gps_sysfs_entries)-3] = ++ &dev_attr_power_tcxo_2v8.attr; ++ break; ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ gta01_gps_sysfs_entries[ARRAY_SIZE(gta01_gps_sysfs_entries)-3] = ++ &dev_attr_power_core_1v5.attr; ++ gta01_gps_sysfs_entries[ARRAY_SIZE(gta01_gps_sysfs_entries)-2] = ++ &dev_attr_power_vdd_core_1v5.attr; ++ break; ++ } ++ ++ return sysfs_create_group(&pdev->dev.kobj, >a01_gps_attr_group); ++} ++ ++static int gta01_pm_gps_remove(struct platform_device *pdev) ++{ ++ gps_power_sequence_down(); ++ sysfs_remove_group(&pdev->dev.kobj, >a01_gps_attr_group); ++ ++ return 0; ++} ++ ++static struct platform_driver gta01_pm_gps_driver = { ++ .probe = gta01_pm_gps_probe, ++ .remove = gta01_pm_gps_remove, ++ .suspend = gta01_pm_gps_suspend, ++ .resume = gta01_pm_gps_resume, ++ .driver = { ++ .name = "neo1973-pm-gps", ++ }, ++}; ++ ++static int __devinit gta01_pm_gps_init(void) ++{ ++ return platform_driver_register(>a01_pm_gps_driver); ++} ++ ++static void gta01_pm_gps_exit(void) ++{ ++ platform_driver_unregister(>a01_pm_gps_driver); ++} ++ ++module_init(gta01_pm_gps_init); ++module_exit(gta01_pm_gps_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>"); ++MODULE_DESCRIPTION("FIC Neo1973 GPS Power Management"); +diff --git a/arch/arm/plat-s3c24xx/neo1973_pm_gsm.c b/arch/arm/plat-s3c24xx/neo1973_pm_gsm.c +new file mode 100644 +index 0000000..a1615f8 +--- /dev/null ++++ b/arch/arm/plat-s3c24xx/neo1973_pm_gsm.c +@@ -0,0 +1,217 @@ ++/* ++ * GSM Management code for the FIC Neo1973 GSM Phone ++ * ++ * (C) 2007 by OpenMoko Inc. ++ * Author: Harald Welte <laforge@openmoko.org> ++ * 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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include <linux/console.h> ++#include <linux/errno.h> ++ ++#include <asm/hardware.h> ++#include <asm/arch/gta01.h> ++ ++struct gta01pm_priv { ++ int gpio_ngsm_en; ++ struct console *con; ++}; ++ ++static struct gta01pm_priv gta01_gsm; ++ ++static struct console *find_s3c24xx_console(void) ++{ ++ struct console *con; ++ ++ acquire_console_sem(); ++ ++ for (con = console_drivers; con; con = con->next) { ++ if (!strcmp(con->name, "ttySAC")) ++ break; ++ } ++ ++ release_console_sem(); ++ ++ return con; ++} ++ ++static ssize_t gsm_read(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ if (!strcmp(attr->attr.name, "power_on")) { ++ if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_ON)) ++ goto out_1; ++ } else if (!strcmp(attr->attr.name, "reset")) { ++ if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_RST)) ++ goto out_1; ++ } else if (!strcmp(attr->attr.name, "download")) { ++ if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_DNLOAD)) ++ goto out_1; ++ } ++ ++ return strlcpy(buf, "0\n", 3); ++out_1: ++ return strlcpy(buf, "1\n", 3); ++} ++ ++static ssize_t gsm_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")) { ++ if (on) { ++ dev_info(dev, "powering up GSM, thus disconnecting " ++ "serial console\n"); ++ ++ if (gta01_gsm.con) ++ console_stop(gta01_gsm.con); ++ ++ if (gta01_gsm.gpio_ngsm_en) ++ s3c2410_gpio_setpin(gta01_gsm.gpio_ngsm_en, 0); ++ ++ s3c2410_gpio_setpin(GTA01_GPIO_MODEM_ON, 1); ++ } else { ++ s3c2410_gpio_setpin(GTA01_GPIO_MODEM_ON, 0); ++ ++ if (gta01_gsm.gpio_ngsm_en) ++ s3c2410_gpio_setpin(gta01_gsm.gpio_ngsm_en, 1); ++ ++ if (gta01_gsm.con) ++ console_start(gta01_gsm.con); ++ ++ dev_info(dev, "powered down GSM, thus enabling " ++ "serial console\n"); ++ } ++ } else if (!strcmp(attr->attr.name, "reset")) { ++ s3c2410_gpio_setpin(GTA01_GPIO_MODEM_RST, on); ++ } else if (!strcmp(attr->attr.name, "download")) { ++ s3c2410_gpio_setpin(GTA01_GPIO_MODEM_DNLOAD, on); ++ } ++ ++ return count; ++} ++ ++static DEVICE_ATTR(power_on, 0644, gsm_read, gsm_write); ++static DEVICE_ATTR(reset, 0644, gsm_read, gsm_write); ++static DEVICE_ATTR(download, 0644, gsm_read, gsm_write); ++ ++#ifdef CONFIG_PM ++static int gta01_gsm_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ /* GPIO state is saved/restored by S3C2410 core GPIO driver, so we ++ * don't need to do anything here */ ++ ++ return 0; ++} ++ ++static int gta01_gsm_resume(struct platform_device *pdev) ++{ ++ /* GPIO state is saved/restored by S3C2410 core GPIO driver, so we ++ * don't need to do anything here */ ++ ++ /* Make sure that the kernel console on the serial port is still ++ * disabled. FIXME: resume ordering race with serial driver! */ ++ if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_ON) && gta01_gsm.con) ++ console_stop(gta01_gsm.con); ++ ++ return 0; ++} ++#else ++#define gta01_gsm_suspend NULL ++#define gta01_gsm_resume NULL ++#endif ++ ++static struct attribute *gta01_gsm_sysfs_entries[] = { ++ &dev_attr_power_on.attr, ++ &dev_attr_reset.attr, ++ NULL, ++ NULL ++}; ++ ++static struct attribute_group gta01_gsm_attr_group = { ++ .name = NULL, ++ .attrs = gta01_gsm_sysfs_entries, ++}; ++ ++static int __init gta01_gsm_probe(struct platform_device *pdev) ++{ ++ switch (system_rev) { ++ case GTA01v3_SYSTEM_REV: ++ gta01_gsm.gpio_ngsm_en = GTA01v3_GPIO_nGSM_EN; ++ break; ++ case GTA01v4_SYSTEM_REV: ++ gta01_gsm.gpio_ngsm_en = 0; ++ break; ++ case GTA01Bv2_SYSTEM_REV: ++ case GTA01Bv3_SYSTEM_REV: ++ case GTA01Bv4_SYSTEM_REV: ++ gta01_gsm.gpio_ngsm_en = GTA01Bv2_GPIO_nGSM_EN; ++ s3c2410_gpio_setpin(GTA01v3_GPIO_nGSM_EN, 0); ++ break; ++ default: ++ dev_warn(&pdev->dev, "Unknown GTA01 Revision 0x%x, " ++ "some PM features not available!!!\n", ++ system_rev); ++ break; ++ } ++ ++ switch (system_rev) { ++ case GTA01v4_SYSTEM_REV: ++ case GTA01Bv2_SYSTEM_REV: ++ gta01_gsm_sysfs_entries[ARRAY_SIZE(gta01_gsm_sysfs_entries)-2] = ++ &dev_attr_download.attr; ++ break; ++ default: ++ break; ++ } ++ ++ gta01_gsm.con = find_s3c24xx_console(); ++ if (!gta01_gsm.con) ++ dev_warn(&pdev->dev, "cannot find S3C24xx console driver\n"); ++ ++ return sysfs_create_group(&pdev->dev.kobj, >a01_gsm_attr_group); ++} ++ ++static int gta01_gsm_remove(struct platform_device *pdev) ++{ ++ sysfs_remove_group(&pdev->dev.kobj, >a01_gsm_attr_group); ++ ++ return 0; ++} ++ ++static struct platform_driver gta01_gsm_driver = { ++ .probe = gta01_gsm_probe, ++ .remove = gta01_gsm_remove, ++ .suspend = gta01_gsm_suspend, ++ .resume = gta01_gsm_resume, ++ .driver = { ++ .name = "neo1973-pm-gsm", ++ }, ++}; ++ ++static int __devinit gta01_gsm_init(void) ++{ ++ return platform_driver_register(>a01_gsm_driver); ++} ++ ++static void gta01_gsm_exit(void) ++{ ++ platform_driver_unregister(>a01_gsm_driver); ++} ++ ++module_init(gta01_gsm_init); ++module_exit(gta01_gsm_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>"); ++MODULE_DESCRIPTION("FIC Neo1973 GSM Power Management"); +-- +1.5.6.3 + |