From 03e997d263d1d02dd7293e8467a9c01a7a3b81d1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 24 Sep 2009 19:11:52 +0200 Subject: ARM: gta02: Add button support The gta02 has two buttons which are connected to gpio pins and thus can get supported by using the generic gpio-keys driver. Signed-off-by: Lars-Peter Clausen --- arch/arm/mach-s3c2442/mach-gta02.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index 0fb385bd9cd..0f0c61912e6 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -58,6 +58,8 @@ #include #include +#include + #include #include #include @@ -550,6 +552,36 @@ static struct s3c2410_hcd_info gta02_usb_info = { }, }; +/* Buttons */ +static struct gpio_keys_button gta02_buttons[] = { + { + .gpio = GTA02_GPIO_AUX_KEY, + .code = KEY_PHONE, + .desc = "Aux", + .type = EV_KEY, + .debounce_interval = 100, + }, + { + .gpio = GTA02_GPIO_HOLD_KEY, + .code = KEY_PAUSE, + .desc = "Hold", + .type = EV_KEY, + .debounce_interval = 100, + }, +}; + +static struct gpio_keys_platform_data gta02_buttons_pdata = { + .buttons = gta02_buttons, + .nbuttons = ARRAY_SIZE(gta02_buttons), +}; + +static struct platform_device gta02_buttons_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = >a02_buttons_pdata, + }, +}; static void __init gta02_map_io(void) { @@ -571,6 +603,7 @@ static struct platform_device *gta02_devices[] __initdata = { &s3c24xx_pwm_device, &s3c_device_iis, &s3c_device_i2c0, + >a02_buttons_device, }; /* These guys DO need to be children of PMU. */ -- cgit v1.2.3 From 423ca7950557104e1565df3efcb6f632002dd35f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 24 Sep 2009 19:20:02 +0200 Subject: ARM: gta02: Add LED support The gta02 has three leds which are connected though gpio pins and thus can get supported by using the generic leds-gpio driver. Signed-off-by: Lars-Peter Clausen --- arch/arm/mach-s3c2442/mach-gta02.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index 0f0c61912e6..f2eeba97bb8 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -60,6 +60,8 @@ #include +#include + #include #include #include @@ -583,6 +585,35 @@ static struct platform_device gta02_buttons_device = { }, }; +/* LEDs */ +static struct gpio_led gta02_gpio_leds[] = { + { + .name = "gta02:orange:power", + .gpio = GTA02_GPIO_PWR_LED1, + }, + { + .name = "gta02:blue:power", + .gpio = GTA02_GPIO_PWR_LED2, + }, + { + .name = "gta02:red:aux", + .gpio = GTA02_GPIO_AUX_LED, + }, +}; + +static struct gpio_led_platform_data gta02_gpio_leds_pdata = { + .leds = gta02_gpio_leds, + .num_leds = ARRAY_SIZE(gta02_gpio_leds), +}; + +static struct platform_device gta02_leds_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = >a02_gpio_leds_pdata, + }, +}; + static void __init gta02_map_io(void) { s3c24xx_init_io(gta02_iodesc, ARRAY_SIZE(gta02_iodesc)); @@ -604,6 +635,7 @@ static struct platform_device *gta02_devices[] __initdata = { &s3c_device_iis, &s3c_device_i2c0, >a02_buttons_device, + >a02_leds_device, }; /* These guys DO need to be children of PMU. */ -- cgit v1.2.3 From 4924a969d8dde0e15e67dcc1fb6e2b7df6cb90f5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 24 Sep 2009 19:30:01 +0200 Subject: ARM: gta02: Add gpio bank B quirk for hardware revision 5 and earlier On hardware revision 5 and earlier the leds found on the gta02 are missing a resistor and reading their gpio pin status will always return 0. So we have to shadow the led states in software. This is done by "hijacking" the gpio accessor functions for bank B. Signed-off-by: Lars-Peter Clausen --- arch/arm/mach-s3c2442/mach-gta02.c | 64 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index f2eeba97bb8..85ca92a69bb 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -89,6 +89,7 @@ #include #include #include +#include #include static struct pcf50633 *gta02_pcf; @@ -607,8 +608,8 @@ static struct gpio_led_platform_data gta02_gpio_leds_pdata = { }; static struct platform_device gta02_leds_device = { - .name = "leds-gpio", - .id = -1, + .name = "leds-gpio", + .id = -1, .dev = { .platform_data = >a02_gpio_leds_pdata, }, @@ -674,11 +675,70 @@ static void gta02_poweroff(void) pcf50633_reg_set_bit_mask(gta02_pcf, PCF50633_REG_OOCSHDWN, 1, 1); } +/* On hardware rev 5 and earlier the leds are missing a resistor and reading + * from their gpio pins will always return 0, so we have to shadow the + * led states software */ +static unsigned long gpb_shadow; +extern struct s3c_gpio_chip s3c24xx_gpios[]; + +static void gta02_gpb_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + void __iomem *base = S3C24XX_GPIO_BASE(S3C2410_GPB(0)); + unsigned long flags; + unsigned long dat; + + local_irq_save(flags); + + dat = __raw_readl(base + 0x04) | gpb_shadow; + dat &= ~(1 << offset); + gpb_shadow &= ~(1 << offset); + if (value) { + dat |= 1 << offset; + switch (offset) { + case 0 ... 2: + gpb_shadow |= 1 << offset; + break; + default: + break; + } + } + __raw_writel(dat, base + 0x04); + + local_irq_restore(flags); +} + +static int gta02_gpb_get(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *base = S3C24XX_GPIO_BASE(S3C2410_GPB(0)); + unsigned long val; + + val = __raw_readl(base + 0x04) | gpb_shadow; + val >>= offset; + val &= 1; + + return val; +} + +static void gta02_hijack_gpb(void) +{ +/* Uncomment this, once support for S3C_SYSTEM_REV_ATAG has been merged + * upstream. + if (S3C_SYSTEM_REV_ATAG > GTA02v5_SYSTEM_REV) + return; +*/ + + s3c24xx_gpios[1].chip.set = gta02_gpb_set; + s3c24xx_gpios[1].chip.get = gta02_gpb_get; +} + static void __init gta02_machine_init(void) { /* Set the panic callback to make AUX LED blink at ~5Hz. */ panic_blink = gta02_panic_blink; + gta02_hijack_gpb(); + s3c_pm_init(); #ifdef CONFIG_CHARGER_PCF50633 -- cgit v1.2.3 From aa20bf0814698e26ec6a850a5c215446a7087d22 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 4 Oct 2009 20:54:02 +0200 Subject: Add power management devices. --- arch/arm/mach-s3c2442/Makefile | 7 +- arch/arm/mach-s3c2442/gta02-pm-bt.c | 257 ++++++++++++++++++ arch/arm/mach-s3c2442/gta02-pm-gps.c | 241 +++++++++++++++++ arch/arm/mach-s3c2442/gta02-pm-gsm.c | 300 +++++++++++++++++++++ arch/arm/mach-s3c2442/gta02-pm-wlan.c | 201 ++++++++++++++ arch/arm/mach-s3c2442/include/mach/gta02-pm-gps.h | 1 + arch/arm/mach-s3c2442/include/mach/gta02-pm-wlan.h | 10 + arch/arm/mach-s3c2442/include/mach/gta02.h | 2 + arch/arm/mach-s3c2442/mach-gta02.c | 26 +- 9 files changed, 1042 insertions(+), 3 deletions(-) create mode 100644 arch/arm/mach-s3c2442/gta02-pm-bt.c create mode 100644 arch/arm/mach-s3c2442/gta02-pm-gps.c create mode 100644 arch/arm/mach-s3c2442/gta02-pm-gsm.c create mode 100644 arch/arm/mach-s3c2442/gta02-pm-wlan.c create mode 100644 arch/arm/mach-s3c2442/include/mach/gta02-pm-gps.h create mode 100644 arch/arm/mach-s3c2442/include/mach/gta02-pm-wlan.h diff --git a/arch/arm/mach-s3c2442/Makefile b/arch/arm/mach-s3c2442/Makefile index 2a19113a576..ebe0579c537 100644 --- a/arch/arm/mach-s3c2442/Makefile +++ b/arch/arm/mach-s3c2442/Makefile @@ -12,7 +12,10 @@ obj- := obj-$(CONFIG_CPU_S3C2442) += s3c2442.o obj-$(CONFIG_CPU_S3C2442) += clock.o -obj-$(CONFIG_MACH_NEO1973_GTA02) += mach-gta02.o - +obj-$(CONFIG_MACH_NEO1973_GTA02) += mach-gta02.o \ + gta02-pm-bt.o \ + gta02-pm-gps.o \ + gta02-pm-gsm.o \ + gta02-pm-wlan.o # Machine support diff --git a/arch/arm/mach-s3c2442/gta02-pm-bt.c b/arch/arm/mach-s3c2442/gta02-pm-bt.c new file mode 100644 index 00000000000..76671ce96d0 --- /dev/null +++ b/arch/arm/mach-s3c2442/gta02-pm-bt.c @@ -0,0 +1,257 @@ +/* + * Bluetooth PM code for the Openmoko Freerunner GSM Phone + * + * (C) 2007 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 + +#include +#include + +#include + +#define DRVMSG "Openmoko Freerunner Bluetooth Power Management" + +struct gta02_pm_bt_data { + struct regulator *regulator; + struct rfkill *rfkill; + int pre_resume_state; +}; + +static ssize_t bt_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = 0; + if (!strcmp(attr->attr.name, "power_on")) { + if (s3c2410_gpio_getpin(GTA02_GPIO_BT_EN)) + ret = 1; + } else if (!strcmp(attr->attr.name, "reset")) { + if (s3c2410_gpio_getpin(GTA02_GPIO_BT_EN) == 0) + ret = 1; + } + + if (!ret) { + return strlcpy(buf, "0\n", 3); + } else { + return strlcpy(buf, "1\n", 3); + } +} + +static void __gta02_pm_bt_toggle_radio(struct device *dev, unsigned int on) +{ + struct gta02_pm_bt_data *bt_data = dev_get_drvdata(dev); + + dev_info(dev, "__gta02_pm_bt_toggle_radio %d\n", on); + + bt_data = dev_get_drvdata(dev); + + s3c2410_gpio_setpin(GTA02_GPIO_BT_EN, !on); + + if (on) { + if (!regulator_is_enabled(bt_data->regulator)) + regulator_enable(bt_data->regulator); + } else { + if (regulator_is_enabled(bt_data->regulator)) + regulator_disable(bt_data->regulator); + } + + s3c2410_gpio_setpin(GTA02_GPIO_BT_EN, on); +} + + +static int bt_rfkill_set_block(void *data, bool blocked) +{ + struct device *dev = data; + + __gta02_pm_bt_toggle_radio(dev, !blocked); + + return 0; +} + +static const struct rfkill_ops gta02_bt_rfkill_ops = { + .set_block = bt_rfkill_set_block, +}; + + +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); + struct gta02_pm_bt_data *bt_data = dev_get_drvdata(dev); + + if (!strcmp(attr->attr.name, "power_on")) { + rfkill_set_sw_state(bt_data->rfkill, on ? 1 : 0); + + __gta02_pm_bt_toggle_radio(dev, on); + } else if (!strcmp(attr->attr.name, "reset")) { + /* reset is low-active, so we need to invert */ + s3c2410_gpio_setpin(GTA02_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 gta02_bt_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct gta02_pm_bt_data *bt_data = dev_get_drvdata(&pdev->dev); + + dev_dbg(&pdev->dev, DRVMSG ": suspending\n"); + + bt_data->pre_resume_state = s3c2410_gpio_getpin(GTA02_GPIO_BT_EN); + __gta02_pm_bt_toggle_radio(&pdev->dev, 0); + + return 0; +} + +static int gta02_bt_resume(struct platform_device *pdev) +{ + struct gta02_pm_bt_data *bt_data = dev_get_drvdata(&pdev->dev); + dev_dbg(&pdev->dev, DRVMSG ": resuming\n"); + + __gta02_pm_bt_toggle_radio(&pdev->dev, bt_data->pre_resume_state); + return 0; +} +#else +#define gta02_bt_suspend NULL +#define gta02_bt_resume NULL +#endif + +static struct attribute *gta02_bt_sysfs_entries[] = { + &dev_attr_power_on.attr, + &dev_attr_reset.attr, + NULL +}; + +static struct attribute_group gta02_bt_attr_group = { + .name = NULL, + .attrs = gta02_bt_sysfs_entries, +}; + +static int __init gta02_bt_probe(struct platform_device *pdev) +{ + struct rfkill *rfkill; + struct regulator *regulator; + struct gta02_pm_bt_data *bt_data; + int ret; + + dev_info(&pdev->dev, DRVMSG ": starting\n"); + + bt_data = kzalloc(sizeof(*bt_data), GFP_KERNEL); + dev_set_drvdata(&pdev->dev, bt_data); + + regulator = regulator_get(&pdev->dev, "BT_3V2"); + if (IS_ERR(regulator)) + return -ENODEV; + + bt_data->regulator = regulator; + + /* this tests the true physical state of the regulator... */ + if (regulator_is_enabled(regulator)) { + /* + * but these only operate on the logical state of the + * regulator... so we need to logicaly "adopt" it on + * to turn it off + */ + regulator_enable(regulator); + regulator_disable(regulator); + } + + /* we pull reset to low to make sure that the chip doesn't + * drain power through the reset line */ + s3c2410_gpio_setpin(GTA02_GPIO_BT_EN, 0); + + rfkill = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, + >a02_bt_rfkill_ops, &pdev->dev); + + if (!rfkill) { + dev_err(&pdev->dev, "Failed to allocate rfkill\n"); + return -ENOMEM; + } + + rfkill_init_sw_state(rfkill, 0); + + ret = rfkill_register(rfkill); + if (ret) { + rfkill_destroy(rfkill); + dev_err(&pdev->dev, "Failed to register rfkill\n"); + return ret; + } + + bt_data->rfkill = rfkill; + + return sysfs_create_group(&pdev->dev.kobj, >a02_bt_attr_group); +} + +static int gta02_bt_remove(struct platform_device *pdev) +{ + struct gta02_pm_bt_data *bt_data = dev_get_drvdata(&pdev->dev); + struct regulator *regulator; + + sysfs_remove_group(&pdev->dev.kobj, >a02_bt_attr_group); + + if (bt_data->rfkill) { + rfkill_destroy(bt_data->rfkill); + } + + if (!bt_data || !bt_data->regulator) + return 0; + + regulator = bt_data->regulator; + + /* Make sure regulator is disabled before calling regulator_put */ + if (regulator_is_enabled(regulator)) + regulator_disable(regulator); + + regulator_put(regulator); + + kfree(bt_data); + + return 0; +} + +static struct platform_driver gta02_bt_driver = { + .probe = gta02_bt_probe, + .remove = gta02_bt_remove, + .suspend = gta02_bt_suspend, + .resume = gta02_bt_resume, + .driver = { + .name = "gta02-pm-bt", + }, +}; + +static int __devinit gta02_bt_init(void) +{ + return platform_driver_register(>a02_bt_driver); +} +module_init(gta02_bt_init); + +static void gta02_bt_exit(void) +{ + platform_driver_unregister(>a02_bt_driver); +} +module_exit(gta02_bt_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION(DRVMSG); diff --git a/arch/arm/mach-s3c2442/gta02-pm-gps.c b/arch/arm/mach-s3c2442/gta02-pm-gps.c new file mode 100644 index 00000000000..02be4cec851 --- /dev/null +++ b/arch/arm/mach-s3c2442/gta02-pm-gps.c @@ -0,0 +1,241 @@ +/* + * 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 +#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") || + !strcmp(attr->attr.name, "pwron")) { + 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") || + !strcmp(attr->attr.name, "pwron")) { + 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 platform_device *pdev, + pm_message_t state) +{ + if (!gta02_gps.keep_on_in_suspend || + !gta02_gps.power_was_on) + gps_pwron_set(0); + else + dev_warn(&pdev->dev, "GTA02: keeping gps ON " + "during suspend\n"); + return 0; +} + +static int gta02_pm_gps_resume(struct platform_device *pdev) +{ + 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); +#else +#define gta02_pm_gps_suspend NULL +#define gta02_pm_gps_resume 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"); + + /* + * Here we should call the code that handles the set GPS power + * off action. But, the regulator API does not allow us to + * reassert regulator state, and when we read the regulator API + * logical state, it can differ from the actual state, So + * a workaround for this is to just set the regulator off in the + * PMU directly. Because that's different from normal flow, we + * have to reproduce other things from the OFF action here too. + */ + + /* + * u-boot enables LDO5 (GPS), which doesn't make sense and + * causes confusion. We therefore disable the regulator here. + */ + pcf50633_reg_write(gta02_pcf, PCF50633_REG_LDO5ENA, 0); + + /* + * 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, + .suspend = gta02_pm_gps_suspend, + .resume = gta02_pm_gps_resume, + .driver = { + .name = "gta02-pm-gps", + }, +}; + +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-s3c2442/gta02-pm-gsm.c b/arch/arm/mach-s3c2442/gta02-pm-gsm.c new file mode 100644 index 00000000000..bef8468dafe --- /dev/null +++ b/arch/arm/mach-s3c2442/gta02-pm-gsm.c @@ -0,0 +1,300 @@ +/* + * GSM Management code for the Openmoko Freerunner GSM Phone + * + * (C) 2007 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 +#include + +#include + +#include +#include +#include +#include + +/* FIXME: Add interrupt handler counting irqs or something */ +static int gta_gsm_interrupts; + +extern void s3c24xx_serial_console_set_silence(int); + +struct gta02pm_priv { + int gpio_ndl_gsm; + struct console *con; +}; + +static struct gta02pm_priv gta02_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 (pcf50633_gpio_get(gta02_pcf, PCF50633_GPIO2)) + goto out_1; + } else if (!strcmp(attr->attr.name, "download")) { + if (!s3c2410_gpio_getpin(GTA02_GPIO_nDL_GSM)) + goto out_1; + } else if (!strcmp(attr->attr.name, "flowcontrolled")) { + if (s3c2410_gpio_getcfg(S3C2410_GPH(1)) == S3C2410_GPIO_OUTPUT) + goto out_1; + } + + return strlcpy(buf, "0\n", 3); +out_1: + return strlcpy(buf, "1\n", 3); +} + +static void gsm_on_off(struct device *dev, int on) +{ + if (!on) { + s3c2410_gpio_cfgpin(S3C2410_GPH(1), S3C2410_GPIO_INPUT); + s3c2410_gpio_cfgpin(S3C2410_GPH(2), S3C2410_GPIO_INPUT); + + pcf50633_gpio_set(gta02_pcf, PCF50633_GPIO2, 0); + + if (gta02_gsm.con) { + s3c24xx_serial_console_set_silence(0); + console_start(gta02_gsm.con); + + dev_dbg(dev, "powered down gta02 GSM, enabling " + "serial console\n"); + } + + return; + } + + if (gta02_gsm.con) { + dev_dbg(dev, "powering up GSM, thus " + "disconnecting serial console\n"); + + console_stop(gta02_gsm.con); + s3c24xx_serial_console_set_silence(1); + } + + /* allow UART to talk to GSM side now we will power it */ + s3c2410_gpio_cfgpin(S3C2410_GPH(1), S3C2410_GPH1_nRTS0); + s3c2410_gpio_cfgpin(S3C2410_GPH(2), S3C2410_GPH2_TXD0); + + pcf50633_gpio_set(gta02_pcf, PCF50633_GPIO2, 7); + + msleep(100); + + s3c2410_gpio_setpin(GTA02_GPIO_MODEM_ON, 1); + msleep(500); + s3c2410_gpio_setpin(GTA02_GPIO_MODEM_ON, 0); + + /* + * workaround for calypso firmware moko10 and earlier, + * without this it will leave IRQ line high after + * booting + */ + s3c2410_gpio_setpin(S3C2410_GPH(1), 1); + s3c2410_gpio_cfgpin(S3C2410_GPH(1), S3C2410_GPIO_OUTPUT); + msleep(1000); + s3c2410_gpio_cfgpin(S3C2410_GPH(1), S3C2410_GPH1_nRTS0); + +} + +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")) { + gsm_on_off(dev, on); + + return count; + } + + if (!strcmp(attr->attr.name, "download")) { + /* + * the keyboard / buttons driver requests and enables + * the JACK_INSERT IRQ. We have to take care about + * not enabling and disabling the IRQ when it was + * already in that state or we get "unblanaced IRQ" + * kernel warnings and stack dumps. So we use the + * copy of the ndl_gsm state to figure out if we should + * enable or disable the jack interrupt + */ + if (on) { + if (gta02_gsm.gpio_ndl_gsm) + disable_irq(gpio_to_irq( + GTA02_GPIO_JACK_INSERT)); + } else { + if (!gta02_gsm.gpio_ndl_gsm) + enable_irq(gpio_to_irq( + GTA02_GPIO_JACK_INSERT)); + } + + gta02_gsm.gpio_ndl_gsm = !on; + s3c2410_gpio_setpin(GTA02_GPIO_nDL_GSM, !on); + + return count; + } + + if (!strcmp(attr->attr.name, "flowcontrolled")) { + if (on) { + gta_gsm_interrupts = 0; + s3c2410_gpio_setpin(S3C2410_GPH(1), 1); + s3c2410_gpio_cfgpin(S3C2410_GPH(1), S3C2410_GPIO_OUTPUT); + } else + s3c2410_gpio_cfgpin(S3C2410_GPH(1), S3C2410_GPH1_nRTS0); + } + + 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); +static DEVICE_ATTR(flowcontrolled, 0644, gsm_read, gsm_write); + +#ifdef CONFIG_PM + +static int gta02_gsm_resume(struct platform_device *pdev); +static int gta02_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 much here. */ + + /* If flowcontrol asserted, abort if GSM already interrupted */ + if (s3c2410_gpio_getcfg(S3C2410_GPH(1)) == S3C2410_GPIO_OUTPUT) { + if (gta_gsm_interrupts) + goto busy; + } + + /* disable DL GSM to prevent jack_insert becoming 'floating' */ + s3c2410_gpio_setpin(GTA02_GPIO_nDL_GSM, 1); + return 0; + +busy: + return -EBUSY; +} + +static int +gta02_gsm_suspend_late(struct platform_device *pdev, pm_message_t state) +{ + /* Last chance: abort if GSM already interrupted */ + if (s3c2410_gpio_getcfg(S3C2410_GPH(1)) == S3C2410_GPIO_OUTPUT) { + if (gta_gsm_interrupts) + return -EBUSY; + } + return 0; +} + +static int gta02_gsm_resume(struct platform_device *pdev) +{ + /* GPIO state is saved/restored by S3C2410 core GPIO driver, so we + * don't need to do much here. */ + + /* Make sure that the kernel console on the serial port is still + * disabled. FIXME: resume ordering race with serial driver! */ + if (gta02_gsm.con && s3c2410_gpio_getpin(GTA02_GPIO_MODEM_ON)) + console_stop(gta02_gsm.con); + + s3c2410_gpio_setpin(GTA02_GPIO_nDL_GSM, gta02_gsm.gpio_ndl_gsm); + + return 0; +} +#else +#define gta02_gsm_suspend NULL +#define gta02_gsm_suspend_late NULL +#define gta02_gsm_resume NULL +#endif /* CONFIG_PM */ + +static struct attribute *gta02_gsm_sysfs_entries[] = { + &dev_attr_power_on.attr, + &dev_attr_reset.attr, + &dev_attr_download.attr, + &dev_attr_flowcontrolled.attr, + NULL +}; + +static struct attribute_group gta02_gsm_attr_group = { + .name = NULL, + .attrs = gta02_gsm_sysfs_entries, +}; + +static int __init gta02_gsm_probe(struct platform_device *pdev) +{ + gta02_gsm.con = find_s3c24xx_console(); + if (!gta02_gsm.con) + dev_warn(&pdev->dev, + "cannot find S3C24xx console driver\n"); + + /* note that download initially disabled, and enforce that */ + gta02_gsm.gpio_ndl_gsm = 1; + s3c2410_gpio_setpin(GTA02_GPIO_nDL_GSM, 1); + + /* GSM is to be initially off (at boot, or if this module inserted) */ + gsm_on_off(&pdev->dev, 0); + + return sysfs_create_group(&pdev->dev.kobj, >a02_gsm_attr_group); +} + +static int gta02_gsm_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, >a02_gsm_attr_group); + + return 0; +} + +static struct platform_driver gta02_gsm_driver = { + .probe = gta02_gsm_probe, + .remove = gta02_gsm_remove, + .suspend = gta02_gsm_suspend, + .suspend_late = gta02_gsm_suspend_late, + .resume = gta02_gsm_resume, + .driver = { + .name = "gta02-pm-gsm", + }, +}; + +static int __devinit gta02_gsm_init(void) +{ + return platform_driver_register(>a02_gsm_driver); +} +module_init(gta02_gsm_init); + +static void gta02_gsm_exit(void) +{ + platform_driver_unregister(>a02_gsm_driver); +} +module_exit(gta02_gsm_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("Openmoko Freerunner GSM Power Management"); diff --git a/arch/arm/mach-s3c2442/gta02-pm-wlan.c b/arch/arm/mach-s3c2442/gta02-pm-wlan.c new file mode 100644 index 00000000000..6e0226600b1 --- /dev/null +++ b/arch/arm/mach-s3c2442/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-s3c2442/include/mach/gta02-pm-gps.h b/arch/arm/mach-s3c2442/include/mach/gta02-pm-gps.h new file mode 100644 index 00000000000..f15180ac079 --- /dev/null +++ b/arch/arm/mach-s3c2442/include/mach/gta02-pm-gps.h @@ -0,0 +1 @@ +extern int gta02_pm_gps_is_on(void); diff --git a/arch/arm/mach-s3c2442/include/mach/gta02-pm-wlan.h b/arch/arm/mach-s3c2442/include/mach/gta02-pm-wlan.h new file mode 100644 index 00000000000..48369789709 --- /dev/null +++ b/arch/arm/mach-s3c2442/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-s3c2442/include/mach/gta02.h b/arch/arm/mach-s3c2442/include/mach/gta02.h index 953331d8d56..69bc62de693 100644 --- a/arch/arm/mach-s3c2442/include/mach/gta02.h +++ b/arch/arm/mach-s3c2442/include/mach/gta02.h @@ -81,4 +81,6 @@ int gta02_get_pcb_revision(void); +extern struct pcf50633 *gta02_pcf; + #endif /* _GTA02_H */ diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index 85ca92a69bb..3b99459f4fe 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -58,6 +58,7 @@ #include #include +#include #include #include @@ -92,7 +93,10 @@ #include #include -static struct pcf50633 *gta02_pcf; +#include +#include + +struct pcf50633 *gta02_pcf; /* * This gets called every 1ms when we paniced. @@ -615,6 +619,22 @@ static struct platform_device gta02_leds_device = { }, }; +static struct platform_device gta02_pm_gps_dev = { + .name = "gta02-pm-gps", +}; + +static struct platform_device gta02_pm_bt_dev = { + .name = "gta02-pm-bt", +}; + +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 void __init gta02_map_io(void) { s3c24xx_init_io(gta02_iodesc, ARRAY_SIZE(gta02_iodesc)); @@ -637,6 +657,10 @@ static struct platform_device *gta02_devices[] __initdata = { &s3c_device_i2c0, >a02_buttons_device, >a02_leds_device, + >a02_pm_gps_dev, + >a02_pm_bt_dev, + >a02_pm_gsm_dev, + >a02_pm_wlan_dev, }; /* These guys DO need to be children of PMU. */ -- cgit v1.2.3 From cc3b40ba5089b23562afbe78f7c3e79117e6fd37 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 5 Oct 2009 14:38:03 +0200 Subject: Select usb host device. --- arch/arm/mach-s3c2442/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-s3c2442/Kconfig b/arch/arm/mach-s3c2442/Kconfig index 103e913f225..26c14b1b835 100644 --- a/arch/arm/mach-s3c2442/Kconfig +++ b/arch/arm/mach-s3c2442/Kconfig @@ -33,6 +33,7 @@ config MACH_NEO1973_GTA02 select POWER_SUPPLY select MACH_NEO1973 select S3C2410_PWM + select S3C_DEV_USB_HOST help Say Y here if you are using the Openmoko GTA02 / Freerunner GSM Phone -- cgit v1.2.3 From c920589f55e7a322278e0529ce07231a10eaaaea Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 5 Oct 2009 22:35:30 +0200 Subject: gta02: Include mach/regs-gpioj.h --- arch/arm/mach-s3c2442/include/mach/gta02.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-s3c2442/include/mach/gta02.h b/arch/arm/mach-s3c2442/include/mach/gta02.h index 69bc62de693..11624c0b926 100644 --- a/arch/arm/mach-s3c2442/include/mach/gta02.h +++ b/arch/arm/mach-s3c2442/include/mach/gta02.h @@ -2,6 +2,7 @@ #define _GTA02_H #include +#include /* Different hardware revisions, passed in ATAG_REVISION by u-boot */ #define GTA02v1_SYSTEM_REV 0x00000310 -- cgit v1.2.3 From bc94658fe9c361303245cff7f9798e9453b3baf0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 9 Oct 2009 00:40:13 +0200 Subject: gta02: request usb pullup pin before using it. --- arch/arm/mach-s3c2442/mach-gta02.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index 3b99459f4fe..b20a912b991 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -465,11 +465,11 @@ static void gta02_udc_command(enum s3c2410_udc_cmd_e cmd) switch (cmd) { case S3C2410_UDC_P_ENABLE: pr_debug("%s S3C2410_UDC_P_ENABLE\n", __func__); - gpio_direction_output(GTA02_GPIO_USB_PULLUP, 1); + gpio_set_value(GTA02_GPIO_USB_PULLUP, 1); break; case S3C2410_UDC_P_DISABLE: pr_debug("%s S3C2410_UDC_P_DISABLE\n", __func__); - gpio_direction_output(GTA02_GPIO_USB_PULLUP, 0); + gpio_set_value(GTA02_GPIO_USB_PULLUP, 0); break; case S3C2410_UDC_P_RESET: pr_debug("%s S3C2410_UDC_P_RESET\n", __func__); @@ -756,6 +756,19 @@ static void gta02_hijack_gpb(void) s3c24xx_gpios[1].chip.get = gta02_gpb_get; } +static void gta02_request_gpios(void) +{ + int ret; + ret = gpio_request(GTA02_GPIO_USB_PULLUP, "USB pullup"); + if (ret) { + printk(KERN_ERR "Failed to request USB pullup gpio pin: %d\n", ret); + } else { + ret = gpio_direction_output(GTA02_GPIO_USB_PULLUP, 0); + if (ret) + printk(KERN_ERR "Failed to set USB pullup gpio direction: %d\n", ret); + } +} + static void __init gta02_machine_init(void) { /* Set the panic callback to make AUX LED blink at ~5Hz. */ @@ -763,6 +776,8 @@ static void __init gta02_machine_init(void) gta02_hijack_gpb(); + gta02_request_gpios(); + s3c_pm_init(); #ifdef CONFIG_CHARGER_PCF50633 -- cgit v1.2.3 From 9aa3a682096fec8e726d7178e2d585b6a395f7cb Mon Sep 17 00:00:00 2001 From: Holger Freyther Date: Wed, 19 Nov 2008 17:10:51 +0000 Subject: gta02: Disable hardware ECC unless we get instructed to enable it Early verions off uboot used for the gta02 flashed the nand with ecc information incompatible to s3c2442 hardware ecc. Disable hardware error correction by default, unless the bootloader explicitly enables it. Signed-off-by: Lars-Peter Clausen --- arch/arm/mach-s3c2442/mach-gta02.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index b20a912b991..d9ee75f2afe 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -457,6 +457,7 @@ static struct s3c2410_platform_nand gta02_nand_info = { .twrph1 = 15, .nr_sets = ARRAY_SIZE(gta02_nand_sets), .sets = gta02_nand_sets, + .software_ecc = 1, }; @@ -756,6 +757,18 @@ static void gta02_hijack_gpb(void) s3c24xx_gpios[1].chip.get = gta02_gpb_get; } +/* + * Allow the bootloader to enable hw ecc + * hardware_ecc=1|0 + */ +static int __init hardware_ecc_setup(char *str) +{ + if (str && str[0] == '1') + gta02_nand_info.software_ecc = 0; + return 1; +} +__setup("hardware_ecc=", hardware_ecc_setup); + static void gta02_request_gpios(void) { int ret; -- cgit v1.2.3 From 55bf93ae65386da5ada691a490c7565fa76f2283 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Nov 2009 02:17:45 +0100 Subject: gta02: Use hw pwm for power leds and vibrator. --- arch/arm/mach-s3c2442/mach-gta02.c | 66 +++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index d9ee75f2afe..bdd34917e5b 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -62,6 +62,7 @@ #include #include +#include #include #include @@ -415,12 +416,6 @@ static struct platform_device gta02_nor_flash = { .num_resources = 1, }; - -struct platform_device s3c24xx_pwm_device = { - .name = "s3c24xx_pwm", - .num_resources = 0, -}; - static struct i2c_board_info gta02_i2c_devs[] __initdata = { { I2C_BOARD_INFO("pcf50633", 0x73), @@ -593,14 +588,6 @@ static struct platform_device gta02_buttons_device = { /* LEDs */ static struct gpio_led gta02_gpio_leds[] = { - { - .name = "gta02:orange:power", - .gpio = GTA02_GPIO_PWR_LED1, - }, - { - .name = "gta02:blue:power", - .gpio = GTA02_GPIO_PWR_LED2, - }, { .name = "gta02:red:aux", .gpio = GTA02_GPIO_AUX_LED, @@ -620,6 +607,41 @@ static struct platform_device gta02_leds_device = { }, }; +static struct led_pwm gta02_pwm_leds[] = { + { + .name = "gta02:orange:power", + .max_brightness = 0xff, + .pwm_period_ns = 1000000, + .pwm_id = 0, + }, + { + .name = "gta02:blue:power", + .max_brightness = 0xff, + .pwm_period_ns = 1000000, + .pwm_id = 1, + }, + { + .name = "gta02::vibrator", + .max_brightness = 0x3f, + .pwm_period_ns = 60000000, + .pwm_id = 3, + } +}; + +static struct led_pwm_platform_data gta02_pwm_leds_pdata = { + .num_leds = ARRAY_SIZE(gta02_pwm_leds), + .leds = gta02_pwm_leds, +}; + +static struct platform_device gta02_pwm_leds_device = { + .name = "leds_pwm", + .id = -1, + .dev = { + .platform_data = >a02_pwm_leds_pdata, + } +}; + + static struct platform_device gta02_pm_gps_dev = { .name = "gta02-pm-gps", }; @@ -653,11 +675,14 @@ static struct platform_device *gta02_devices[] __initdata = { &s3c_device_usbgadget, &s3c_device_nand, >a02_nor_flash, - &s3c24xx_pwm_device, + &s3c_device_timer[0], + &s3c_device_timer[1], + &s3c_device_timer[3], &s3c_device_iis, &s3c_device_i2c0, >a02_buttons_device, >a02_leds_device, + >a02_pwm_leds_device, >a02_pm_gps_dev, >a02_pm_bt_dev, >a02_pm_gsm_dev, @@ -779,7 +804,14 @@ static void gta02_request_gpios(void) ret = gpio_direction_output(GTA02_GPIO_USB_PULLUP, 0); if (ret) printk(KERN_ERR "Failed to set USB pullup gpio direction: %d\n", ret); - } + } +} + +static void gta02_enable_pwm_pins(void) +{ + s3c2410_gpio_cfgpin(GTA02_GPIO_PWR_LED1, S3C2410_GPB0_TOUT0); + s3c2410_gpio_cfgpin(GTA02_GPIO_PWR_LED2, S3C2410_GPB1_TOUT1); + s3c2410_gpio_cfgpin(GTA02_GPIO_VIBRATOR_ON, S3C2410_GPB3_TOUT3); } static void __init gta02_machine_init(void) @@ -806,6 +838,8 @@ static void __init gta02_machine_init(void) i2c_register_board_info(0, gta02_i2c_devs, ARRAY_SIZE(gta02_i2c_devs)); platform_add_devices(gta02_devices, ARRAY_SIZE(gta02_devices)); + gta02_enable_pwm_pins(); + pm_power_off = gta02_poweroff; } -- cgit v1.2.3 From f1929d0afdb9423115674520752fcd75bbdb4407 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 11 Dec 2009 10:37:20 +0100 Subject: gta02: Select s3c_device_nand --- arch/arm/mach-s3c2442/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-s3c2442/Kconfig b/arch/arm/mach-s3c2442/Kconfig index 26c14b1b835..2e33904759c 100644 --- a/arch/arm/mach-s3c2442/Kconfig +++ b/arch/arm/mach-s3c2442/Kconfig @@ -34,6 +34,7 @@ config MACH_NEO1973_GTA02 select MACH_NEO1973 select S3C2410_PWM select S3C_DEV_USB_HOST + select S3C_DEV_NAND help Say Y here if you are using the Openmoko GTA02 / Freerunner GSM Phone -- cgit v1.2.3 From dc90a4e8ecb13b51487c6a3e5a4f9866bf5641ad Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 15 Dec 2009 18:22:18 +0100 Subject: Use dev_pm_ops --- arch/arm/mach-s3c2442/gta02-pm-gps.c | 21 +++++++++++++-------- arch/arm/mach-s3c2442/gta02-pm-gsm.c | 27 +++++++++++++++------------ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/arch/arm/mach-s3c2442/gta02-pm-gps.c b/arch/arm/mach-s3c2442/gta02-pm-gps.c index 02be4cec851..2b71b7e4811 100644 --- a/arch/arm/mach-s3c2442/gta02-pm-gps.c +++ b/arch/arm/mach-s3c2442/gta02-pm-gps.c @@ -126,19 +126,18 @@ static ssize_t power_gps_write(struct device *dev, } #ifdef CONFIG_PM -static int gta02_pm_gps_suspend(struct platform_device *pdev, - pm_message_t state) +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(&pdev->dev, "GTA02: keeping gps ON " + dev_warn(dev, "GTA02: keeping gps ON " "during suspend\n"); return 0; } -static int gta02_pm_gps_resume(struct platform_device *pdev) +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); @@ -147,9 +146,16 @@ static int gta02_pm_gps_resume(struct platform_device *pdev) } 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_pm_gps_suspend NULL -#define gta02_pm_gps_resume NULL +#define GTA02_GPS_PM_OPS NULL #endif static DEVICE_ATTR(power_on, 0644, power_gps_read, power_gps_write); @@ -218,10 +224,9 @@ static int gta02_pm_gps_remove(struct platform_device *pdev) static struct platform_driver gta02_pm_gps_driver = { .probe = gta02_pm_gps_probe, .remove = gta02_pm_gps_remove, - .suspend = gta02_pm_gps_suspend, - .resume = gta02_pm_gps_resume, .driver = { .name = "gta02-pm-gps", + .pm = GTA02_GPS_PM_OPS, }, }; diff --git a/arch/arm/mach-s3c2442/gta02-pm-gsm.c b/arch/arm/mach-s3c2442/gta02-pm-gsm.c index bef8468dafe..4b6cad806fe 100644 --- a/arch/arm/mach-s3c2442/gta02-pm-gsm.c +++ b/arch/arm/mach-s3c2442/gta02-pm-gsm.c @@ -184,8 +184,7 @@ static DEVICE_ATTR(flowcontrolled, 0644, gsm_read, gsm_write); #ifdef CONFIG_PM -static int gta02_gsm_resume(struct platform_device *pdev); -static int gta02_gsm_suspend(struct platform_device *pdev, pm_message_t state) +static int gta02_gsm_suspend(struct device *dev) { /* GPIO state is saved/restored by S3C2410 core GPIO driver, so we * don't need to do much here. */ @@ -204,8 +203,7 @@ busy: return -EBUSY; } -static int -gta02_gsm_suspend_late(struct platform_device *pdev, pm_message_t state) +static int gta02_gsm_suspend_late(struct device *dev) { /* Last chance: abort if GSM already interrupted */ if (s3c2410_gpio_getcfg(S3C2410_GPH(1)) == S3C2410_GPIO_OUTPUT) { @@ -215,7 +213,7 @@ gta02_gsm_suspend_late(struct platform_device *pdev, pm_message_t state) return 0; } -static int gta02_gsm_resume(struct platform_device *pdev) +static int gta02_gsm_resume(struct device *dev) { /* GPIO state is saved/restored by S3C2410 core GPIO driver, so we * don't need to do much here. */ @@ -229,10 +227,17 @@ static int gta02_gsm_resume(struct platform_device *pdev) return 0; } + +static struct dev_pm_ops gta02_gsm_pm_ops = { + .suspend = gta02_gsm_suspend, + .suspend_noirq = gta02_gsm_suspend_late, + .resume = gta02_gsm_resume, +}; + +#define GTA02_GSM_PM_OPS (>a02_gsm_pm_ops) + #else -#define gta02_gsm_suspend NULL -#define gta02_gsm_suspend_late NULL -#define gta02_gsm_resume NULL +#define GTA02_GSM_PM_OPS NULL #endif /* CONFIG_PM */ static struct attribute *gta02_gsm_sysfs_entries[] = { @@ -275,11 +280,9 @@ static int gta02_gsm_remove(struct platform_device *pdev) static struct platform_driver gta02_gsm_driver = { .probe = gta02_gsm_probe, .remove = gta02_gsm_remove, - .suspend = gta02_gsm_suspend, - .suspend_late = gta02_gsm_suspend_late, - .resume = gta02_gsm_resume, .driver = { - .name = "gta02-pm-gsm", + .name = "gta02-pm-gsm", + .pm = GTA02_GSM_PM_OPS, }, }; -- cgit v1.2.3 From a08aba8248820f067d73d1479721453f9a82c591 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 15 Dec 2009 18:47:22 +0100 Subject: gta02: Specify valid_ops_mask for regulator consumers. --- arch/arm/mach-s3c2442/mach-gta02.c | 482 ++++++++++++++++++++++++++++++++----- 1 file changed, 417 insertions(+), 65 deletions(-) diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index bdd34917e5b..ff801779075 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -49,7 +50,6 @@ #include #include -#include #include #include @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -97,6 +98,17 @@ #include #include +#include +#include +#include + +#include + +#include +#include + +#include + struct pcf50633 *gta02_pcf; /* @@ -159,6 +171,140 @@ static struct s3c2410_uartcfg gta02_uartcfgs[] = { }, }; +/* + * we crank down SD Card clock dynamically when GPS is powered + */ + +static int gta02_glamo_mci_use_slow(void) +{ + return gta02_pm_gps_is_on(); +} + +static void gta02_glamo_external_reset(int level) +{ + s3c2410_gpio_setpin(GTA02_GPIO_3D_RESET, level); + s3c2410_gpio_cfgpin(GTA02_GPIO_3D_RESET, S3C2410_GPIO_OUTPUT); +} + +struct spi_gpio_platform_data spigpio_platform_data = { + .sck = GTA02_GPIO_GLAMO(10), + .mosi = GTA02_GPIO_GLAMO(11), + .miso = GTA02_GPIO_GLAMO(5), + .num_chipselect = 1, +}; + +static struct platform_device spigpio_device = { + .name = "spi_gpio", + .id = 2, + .dev = { + .platform_data = &spigpio_platform_data, + }, +}; + +static void gta02_glamo_registered(struct device *dev) +{ + spigpio_device.dev.parent = dev; + platform_device_register(&spigpio_device); +} + +static struct fb_videomode gta02_glamo_modes[] = { + { + .name = "480x640", + .xres = 480, + .yres = 640, + .pixclock = 40816, + .left_margin = 8, + .right_margin = 16, + .upper_margin = 2, + .lower_margin = 16, + .hsync_len = 8, + .vsync_len = 2, + .vmode = FB_VMODE_NONINTERLACED, + }, { + .name = "240x320", + .xres = 240, + .yres = 320, + .pixclock = 40816, + .left_margin = 8, + .right_margin = 16, + .upper_margin = 2, + .lower_margin = 16, + .hsync_len = 8, + .vsync_len = 2, + .vmode = FB_VMODE_NONINTERLACED, + } +}; + + +static struct glamo_fb_platform_data gta02_glamo_fb_pdata = { + .width = 43, + .height = 58, + + .num_modes = ARRAY_SIZE(gta02_glamo_modes), + .modes = gta02_glamo_modes, +}; + +static struct glamo_mmc_platform_data gta02_glamo_mmc_pdata = { + .glamo_mmc_use_slow = gta02_glamo_mci_use_slow, +}; + +static struct glamo_gpio_platform_data gta02_glamo_gpio_pdata = { + .base = GTA02_GPIO_GLAMO_BASE, + .registered = gta02_glamo_registered, +}; + +static struct glamo_platform_data gta02_glamo_pdata = { + .fb_data = >a02_glamo_fb_pdata, + .mmc_data = >a02_glamo_mmc_pdata, + .gpio_data = >a02_glamo_gpio_pdata, + + .osci_clock_rate = 32768, + + .glamo_external_reset = gta02_glamo_external_reset, +}; + +static struct resource gta02_glamo_resources[] = { + [0] = { + .start = S3C2410_CS1, + .end = S3C2410_CS1 + 0x1000000 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = GTA02_IRQ_3D, + .end = GTA02_IRQ_3D, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = S3C2410_BOARD_IRQ_START, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device gta02_glamo_dev = { + .name = "glamo3362", + .num_resources = ARRAY_SIZE(gta02_glamo_resources), + .resource = gta02_glamo_resources, + .dev = { + .platform_data = >a02_glamo_pdata, + }, +}; + +static struct platform_device gta02_pm_gps_dev = { + .name = "gta02-pm-gps", +}; + +static struct platform_device gta02_pm_bt_dev = { + .name = "gta02-pm-bt", +}; + +static struct platform_device gta02_pm_gsm_dev = { + .name = "gta02-pm-gsm", +}; + +static struct platform_device gta02_pm_wlan_dev = { + .name = "gta02-pm-wlan", +}; + #ifdef CONFIG_CHARGER_PCF50633 /* * On GTA02 the 1A charger features a 48K resistor to 0V on the ID pin. @@ -171,6 +317,20 @@ static struct s3c2410_uartcfg gta02_uartcfgs[] = { #define ADC_NOM_CHG_DETECT_1A 6 #define ADC_NOM_CHG_DETECT_USB 43 +static int gta02_get_charger_online_status(void) +{ + struct pcf50633 *pcf = gta02_pcf; + + return pcf50633_mbc_get_status(pcf) & PCF50633_MBC_USB_ONLINE; +} + +static int gta02_get_charger_active_status(void) +{ + struct pcf50633 *pcf = gta02_pcf; + + return pcf50633_mbc_get_status(pcf) & PCF50633_MBC_USB_ACTIVE; +} + static void gta02_configure_pmu_for_charger(struct pcf50633 *pcf, void *unused, int res) { @@ -247,6 +407,8 @@ static void gta02_udc_vbus_draw(unsigned int ma) #else /* !CONFIG_CHARGER_PCF50633 */ #define gta02_pmu_event_callback NULL #define gta02_udc_vbus_draw NULL +#define gta02_get_charger_online_status NULL +#define gta02_get_charger_active_status NULL #endif /* @@ -264,6 +426,38 @@ static char *gta02_batteries[] = { "battery", }; +static struct regulator_consumer_supply ldo4_consumers[] = { + { + .dev = >a02_pm_bt_dev.dev, + .supply = "BT_3V2", + }, +}; + +static struct regulator_consumer_supply ldo5_consumers[] = { + { + .dev = >a02_pm_gps_dev.dev, + .supply = "RF_3V", + }, +}; + +static struct regulator_consumer_supply hcldo_consumers[] = { + { + .dev = >a02_glamo_dev.dev, + .supply = "SD_3V3", + }, +}; + +static struct regulator_consumer_supply ldo6_consumers[] = { + REGULATOR_SUPPLY("VDC", "spi2.0"), + REGULATOR_SUPPLY("VDDIO", "spi2.0"), +}; + +static struct pcf50633_bl_platform_data gta02_backlight_data = { + .default_brightness = 0x3f, + .default_brightness_limit = 0, + .ramp_time = 3, +}; + struct pcf50633_platform_data gta02_pcf_pdata = { .resumers = { [0] = PCF50633_INT1_USBINS | @@ -278,12 +472,18 @@ struct pcf50633_platform_data gta02_pcf_pdata = { .batteries = gta02_batteries, .num_batteries = ARRAY_SIZE(gta02_batteries), + + .chg_ref_current_ma = 1000, + + .backlight_data = >a02_backlight_data, + .reg_init_data = { [PCF50633_REGULATOR_AUTO] = { .constraints = { .min_uV = 3300000, .max_uV = 3300000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, .always_on = 1, .apply_uV = 1, .state_mem = { @@ -296,6 +496,7 @@ struct pcf50633_platform_data gta02_pcf_pdata = { .min_uV = 1300000, .max_uV = 1600000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, .always_on = 1, .apply_uV = 1, }, @@ -317,15 +518,19 @@ struct pcf50633_platform_data gta02_pcf_pdata = { .min_uV = 2000000, .max_uV = 3300000, .valid_modes_mask = REGULATOR_MODE_NORMAL, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_VOLTAGE, .always_on = 1, }, + .num_consumer_supplies = ARRAY_SIZE(hcldo_consumers), + .consumer_supplies = hcldo_consumers, }, [PCF50633_REGULATOR_LDO1] = { .constraints = { .min_uV = 3300000, .max_uV = 3300000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, .apply_uV = 1, .state_mem = { .enabled = 0, @@ -337,6 +542,7 @@ struct pcf50633_platform_data gta02_pcf_pdata = { .min_uV = 3300000, .max_uV = 3300000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, .apply_uV = 1, }, }, @@ -345,6 +551,7 @@ struct pcf50633_platform_data gta02_pcf_pdata = { .min_uV = 3000000, .max_uV = 3000000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, .apply_uV = 1, }, }, @@ -353,32 +560,42 @@ struct pcf50633_platform_data gta02_pcf_pdata = { .min_uV = 3200000, .max_uV = 3200000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, .apply_uV = 1, }, + .num_consumer_supplies = ARRAY_SIZE(ldo4_consumers), + .consumer_supplies = ldo4_consumers, }, [PCF50633_REGULATOR_LDO5] = { .constraints = { .min_uV = 3000000, .max_uV = 3000000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, .apply_uV = 1, .state_mem = { .enabled = 1, }, }, + .num_consumer_supplies = ARRAY_SIZE(ldo5_consumers), + .consumer_supplies = ldo5_consumers, }, [PCF50633_REGULATOR_LDO6] = { .constraints = { .min_uV = 3000000, .max_uV = 3000000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, + .num_consumer_supplies = ARRAY_SIZE(ldo6_consumers), + .consumer_supplies = ldo6_consumers, }, [PCF50633_REGULATOR_MEMLDO] = { .constraints = { .min_uV = 1800000, .max_uV = 1800000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, .state_mem = { .enabled = 1, }, @@ -480,78 +697,202 @@ static struct s3c2410_udc_mach_info gta02_udc_cfg = { }; +/* USB */ +static struct s3c2410_hcd_info gta02_usb_info = { + .port[0] = { + .flags = S3C_HCDFLG_USED, + }, + .port[1] = { + .flags = 0, + }, +}; - -static void gta02_bl_set_intensity(int intensity) +/* JBT6k74 display controller */ +static void gta02_jbt6k74_probe_completed(struct device *dev) { - struct pcf50633 *pcf = gta02_pcf; - int old_intensity = pcf50633_reg_read(pcf, PCF50633_REG_LEDOUT); + pcf50633_bl_set_brightness_limit(gta02_pcf, 0x3f); +} + +const struct jbt6k74_platform_data jbt6k74_pdata = { + .probe_completed = gta02_jbt6k74_probe_completed, + .gpio_reset = GTA02_GPIO_GLAMO(4), +}; - /* We map 8-bit intensity to 6-bit intensity in hardware. */ - intensity >>= 2; +static struct spi_board_info gta02_spi_board_info[] = { + { + .modalias = "jbt6k74", + .platform_data = &jbt6k74_pdata, + .controller_data = (void*)GTA02_GPIO_GLAMO(12), + /* irq */ + .max_speed_hz = 100 * 1000, + .bus_num = 2, + .chip_select = 0 + }, +}; - /* - * This can happen during, eg, print of panic on blanked console, - * but we can't service i2c without interrupts active, so abort. - */ - if (in_atomic()) { - printk(KERN_ERR "gta02_bl_set_intensity called while atomic\n"); - return; - } +/* BQ27000 Battery */ - old_intensity = pcf50633_reg_read(pcf, PCF50633_REG_LEDOUT); - if (intensity == old_intensity) - return; +struct bq27000_platform_data bq27000_pdata = { + .name = "battery", + .rsense_mohms = 20, + .hdq_read = hdq_read, + .hdq_write = hdq_write, + .hdq_initialized = hdq_initialized, + .get_charger_online_status = gta02_get_charger_online_status, + .get_charger_active_status = gta02_get_charger_active_status +}; + +struct platform_device bq27000_battery_device = { + .name = "bq27000-battery", + .dev = { + .platform_data = &bq27000_pdata, + }, +}; - /* We can't do this anywhere else. */ - pcf50633_reg_write(pcf, PCF50633_REG_LEDDIM, 5); +/* Platform battery */ - if (!(pcf50633_reg_read(pcf, PCF50633_REG_LEDENA) & 3)) - old_intensity = 0; +/* Capacity of a typical BL-5C dumb battery */ +#define GTA02_BAT_CHARGE_FULL 850000 - /* - * The PCF50633 cannot handle LEDOUT = 0 (datasheet p60) - * if seen, you have to re-enable the LED unit. - */ - if (!intensity || !old_intensity) - pcf50633_reg_write(pcf, PCF50633_REG_LEDENA, 0); +static int gta02_bat_voltscale(int volt) +{ + /* This table is suggested by SpeedEvil based on analysis of + * experimental data */ + static const int lut[][2] = { + { 4120, 100 }, + { 3900, 60 }, + { 3740, 25 }, + { 3600, 5 }, + { 3000, 0 } }; + int i, res = 0; + + if (volt > lut[0][0]) + res = lut[0][1]; + else + for (i = 0; lut[i][1]; i++) { + if (volt <= lut[i][0] && volt >= lut[i+1][0]) { + res = lut[i][1] - (lut[i][0]-volt)* + (lut[i][1]-lut[i+1][1])/ + (lut[i][0]-lut[i+1][0]); + break; + } + } + return res; +} + +static int gta02_bat_get_voltage(void) +{ + struct pcf50633 *pcf = gta02_pcf; + u16 adc, mv = 0; + adc = pcf50633_adc_sync_read(pcf, + PCF50633_ADCC1_MUX_BATSNS_RES, + PCF50633_ADCC1_AVERAGE_16); + /* The formula from DS is for divide-by-two mode, current driver uses + divide-by-three */ + mv = (adc * 6000) / 1023; + return mv * 1000; +} - /* Illegal to set LEDOUT to 0. */ - if (!intensity) - pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_LEDOUT, 0x3f, 2); +static int gta02_bat_get_present(void) +{ + /* There is no reliable way to tell if it is present or not */ + return 1; +} + +static int gta02_bat_get_status(void) +{ +#ifdef CONFIG_CHARGER_PCF50633 + if (gta02_get_charger_active_status()) + return POWER_SUPPLY_STATUS_CHARGING; else - pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_LEDOUT, 0x3f, - intensity); + return POWER_SUPPLY_STATUS_DISCHARGING; +#else + return POWER_SUPPLY_STATUS_UNKNOWN; +#endif +} - if (intensity) - pcf50633_reg_write(pcf, PCF50633_REG_LEDENA, 2); +static int gta02_bat_get_capacity(void) +{ + return gta02_bat_voltscale(gta02_bat_get_voltage()/1000); +} +static int gta02_bat_get_charge_full(void) +{ + return GTA02_BAT_CHARGE_FULL; } -static struct generic_bl_info gta02_bl_info = { - .name = "gta02-bl", - .max_intensity = 0xff, - .default_intensity = 0xff, - .set_bl_intensity = gta02_bl_set_intensity, +static int gta02_bat_get_charge_now(void) +{ + return gta02_bat_get_capacity() * gta02_bat_get_charge_full() / 100; +} + +static enum power_supply_property gta02_platform_bat_properties[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, +}; + +int (*gta02_platform_bat_get_property[])(void) = { + gta02_bat_get_present, + gta02_bat_get_status, + gta02_bat_get_voltage, + gta02_bat_get_capacity, + gta02_bat_get_charge_full, + gta02_bat_get_charge_now, +}; + +static struct platform_bat_platform_data gta02_platform_bat_pdata = { + .name = "battery", + .properties = gta02_platform_bat_properties, + .num_properties = ARRAY_SIZE(gta02_platform_bat_properties), + .get_property = gta02_platform_bat_get_property, + .is_present = gta02_bat_get_present, }; -static struct platform_device gta02_bl_dev = { - .name = "generic-bl", - .id = 1, +struct platform_device gta02_platform_bat = { + .name = "platform_battery", + .id = -1, .dev = { - .platform_data = >a02_bl_info, - }, + .platform_data = >a02_platform_bat_pdata, + } }; +/* HDQ */ + +static void gta02_hdq_attach_child_devices(struct device *parent_device) +{ + bq27000_battery_device.dev.parent = parent_device; + platform_device_register(&bq27000_battery_device); +} +static void gta02_hdq_gpio_direction_out(void) +{ + s3c2410_gpio_cfgpin(GTA02v5_GPIO_HDQ, S3C2410_GPIO_OUTPUT); +} -/* USB */ -static struct s3c2410_hcd_info gta02_usb_info = { - .port[0] = { - .flags = S3C_HCDFLG_USED, - }, - .port[1] = { - .flags = 0, +static void gta02_hdq_gpio_direction_in(void) +{ + s3c2410_gpio_cfgpin(GTA02v5_GPIO_HDQ, S3C2410_GPIO_INPUT); +} + +static void gta02_hdq_gpio_set_value(int val) +{ + + s3c2410_gpio_setpin(GTA02v5_GPIO_HDQ, val); +} + +static int gta02_hdq_gpio_get_value(void) +{ + return s3c2410_gpio_getpin(GTA02v5_GPIO_HDQ); +} + +static struct resource gta02_hdq_resources[] = { + [0] = { + .start = GTA02v5_GPIO_HDQ, + .end = GTA02v5_GPIO_HDQ, }, }; @@ -641,21 +982,27 @@ static struct platform_device gta02_pwm_leds_device = { } }; +struct hdq_platform_data gta02_hdq_platform_data = { + .attach_child_devices = gta02_hdq_attach_child_devices, + .gpio_dir_out = gta02_hdq_gpio_direction_out, + .gpio_dir_in = gta02_hdq_gpio_direction_in, + .gpio_set = gta02_hdq_gpio_set_value, + .gpio_get = gta02_hdq_gpio_get_value, -static struct platform_device gta02_pm_gps_dev = { - .name = "gta02-pm-gps", -}; + .enable_fiq = gta02_fiq_enable, + .disable_fiq = gta02_fiq_disable, + .kick_fiq = gta02_fiq_kick, -static struct platform_device gta02_pm_bt_dev = { - .name = "gta02-pm-bt", -}; - -static struct platform_device gta02_pm_gsm_dev = { - .name = "gta02-pm-gsm", }; -static struct platform_device gta02_pm_wlan_dev = { - .name = "gta02-pm-wlan", +struct platform_device gta02_hdq_device = { + .name = "hdq", + .num_resources = 1, + .resource = gta02_hdq_resources, + .dev = { + .platform_data = >a02_hdq_platform_data, + .parent = &s3c_device_timer[0].dev, + }, }; static void __init gta02_map_io(void) @@ -687,12 +1034,15 @@ static struct platform_device *gta02_devices[] __initdata = { >a02_pm_bt_dev, >a02_pm_gsm_dev, >a02_pm_wlan_dev, + &s3c_device_adc, }; /* These guys DO need to be children of PMU. */ static struct platform_device *gta02_devices_pmu_children[] = { - >a02_bl_dev, + >a02_glamo_dev, + &s3c_device_timer[2], + >a02_hdq_device, }; @@ -836,6 +1186,8 @@ static void __init gta02_machine_init(void) s3c_i2c0_set_platdata(NULL); i2c_register_board_info(0, gta02_i2c_devs, ARRAY_SIZE(gta02_i2c_devs)); + spi_register_board_info(gta02_spi_board_info, + ARRAY_SIZE(gta02_spi_board_info)); platform_add_devices(gta02_devices, ARRAY_SIZE(gta02_devices)); gta02_enable_pwm_pins(); -- cgit v1.2.3