* [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series
@ 2010-07-17 8:18 Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 1/4] Add HP iPAQ rx1950 LEDs driver Vasily Khoruzhick
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Vasily Khoruzhick @ 2010-07-17 8:18 UTC (permalink / raw)
To: linux-arm-kernel
This patch series adds more support for iPAQ rx1950 PDA to linux:
1. LEDs driver -- it controls blue, green and red LEDs on rx1950
2. Battery driver -- adds ability to monitor and charge battery.
This driver is suitable for H1940 PDA aswell (just need to write
some machine specific callbacks and get voltage LUTs)
v2: removed ac registration from s3c_adc_battery driver, use
pda_power instead for ac support
v3: split LEDs patch to driver and machine parts,
split battery patch into battery + LED trigger parts,
now battery driver can skip gpio/callbacks usage
(if there's no gpio pin to determine that battery is
full or if charger is enabled automatically)
v4: add .blink_set callback to LEDs driver instead of
using LED_HALF value for blinking, remove LED triggers
from battery driver completely (for now)
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 1/4] Add HP iPAQ rx1950 LEDs driver
2010-07-17 8:18 [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series Vasily Khoruzhick
@ 2010-07-17 8:18 ` Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 2/4] rx1950: add LEDs device Vasily Khoruzhick
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Vasily Khoruzhick @ 2010-07-17 8:18 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
drivers/leds/Kconfig | 6 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-rx1950.c | 242 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 249 insertions(+), 0 deletions(-)
create mode 100644 drivers/leds/leds-rx1950.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 81bf25e..f986825 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -102,6 +102,12 @@ config LEDS_H1940
help
This option enables support for the LEDs on the h1940.
+config LEDS_RX1950
+ tristate "LED Support for iPAQ RX1950 device"
+ depends on MACH_RX1950
+ help
+ This option enables support for the LEDs on the rx1950.
+
config LEDS_COBALT_QUBE
tristate "LED Support for the Cobalt Qube series front LED"
depends on MIPS_COBALT
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 2493de4..faa0d5d 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_LEDS_NET5501) += leds-net5501.o
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o
obj-$(CONFIG_LEDS_H1940) += leds-h1940.o
+obj-$(CONFIG_LEDS_RX1950) += leds-rx1950.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
diff --git a/drivers/leds/leds-rx1950.c b/drivers/leds/leds-rx1950.c
new file mode 100644
index 0000000..0e9633b
--- /dev/null
+++ b/drivers/leds/leds-rx1950.c
@@ -0,0 +1,242 @@
+/*
+ * drivers/leds/leds-rx1950.c
+ *
+ * Based on leds-h1940 by Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright (c) Vasily Khoruzhick <anarsoul@gmail.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * RX1950 leds driver
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+
+#include <mach/regs-gpio.h>
+#include <mach/regs-gpioj.h>
+#include <mach/hardware.h>
+
+/*
+ * Green led.
+ */
+static void rx1950_greenled_set(struct led_classdev *led_dev,
+ enum led_brightness value)
+{
+ if (value) {
+ if (!gpio_get_value(S3C2410_GPJ(6))) {
+ gpio_direction_output(S3C2410_GPA(4), 0);
+ gpio_direction_output(S3C2410_GPA(6), 1);
+ gpio_direction_output(S3C2410_GPJ(6), 0);
+ } else {
+ gpio_direction_output(S3C2410_GPA(4), 1);
+ gpio_direction_output(S3C2410_GPA(6), 0);
+ gpio_direction_output(S3C2410_GPJ(6), 1);
+ }
+ } else {
+ gpio_direction_output(S3C2410_GPA(4), 0);
+ gpio_direction_output(S3C2410_GPA(6), 0);
+ gpio_direction_output(S3C2410_GPJ(6), 0);
+ }
+}
+
+static int rx1950_greenled_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ if (*delay_on == 0 && *delay_off == 0) {
+ *delay_on = 300;
+ *delay_off = 700;
+ }
+
+ if (*delay_on != 300 && *delay_on != 700)
+ return -EINVAL;
+
+ gpio_direction_output(S3C2410_GPA(4), 1);
+ gpio_direction_output(S3C2410_GPA(6), 0);
+ gpio_direction_output(S3C2410_GPJ(6), 1);
+
+ return 0;
+}
+
+static struct led_classdev rx1950_greenled = {
+ .name = "rx1950:green",
+ .brightness_set = rx1950_greenled_set,
+ .blink_set = rx1950_greenled_blink_set,
+ .default_trigger = "main-battery-charging-or-full",
+};
+
+/*
+ * Red led.
+ */
+static void rx1950_redled_set(struct led_classdev *led_dev,
+ enum led_brightness value)
+{
+ if (value) {
+ if (!gpio_get_value(S3C2410_GPJ(6))) {
+ gpio_direction_output(S3C2410_GPA(3), 0);
+ gpio_direction_output(S3C2410_GPA(7), 1);
+ gpio_direction_output(S3C2410_GPJ(6), 0);
+ } else {
+ gpio_direction_output(S3C2410_GPA(3), 1);
+ gpio_direction_output(S3C2410_GPA(7), 0);
+ gpio_direction_output(S3C2410_GPJ(6), 1);
+ }
+ } else {
+ gpio_direction_output(S3C2410_GPA(3), 0);
+ gpio_direction_output(S3C2410_GPA(7), 0);
+ gpio_direction_output(S3C2410_GPJ(6), 0);
+ }
+}
+
+static int rx1950_redled_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ if (*delay_on == 0 && *delay_off == 0) {
+ *delay_on = 300;
+ *delay_off = 700;
+ }
+
+ if (*delay_on != 300 && *delay_on != 700)
+ return -EINVAL;
+
+ gpio_direction_output(S3C2410_GPA(3), 1);
+ gpio_direction_output(S3C2410_GPA(7), 0);
+ gpio_direction_output(S3C2410_GPJ(6), 1);
+
+ return 0;
+}
+
+static struct led_classdev rx1950_redled = {
+ .name = "rx1950:red",
+ .brightness_set = rx1950_redled_set,
+ .blink_set = rx1950_redled_blink_set,
+ .default_trigger = "main-battery-full",
+};
+
+/*
+ * Blue led.
+ */
+static void rx1950_blueled_set(struct led_classdev *led_dev,
+ enum led_brightness value)
+{
+ if (value)
+ gpio_direction_output(S3C2410_GPA(11), 1);
+ else
+ gpio_direction_output(S3C2410_GPA(11), 0);
+}
+
+static struct led_classdev rx1950_blueled = {
+ .name = "rx1950:blue",
+ .brightness_set = rx1950_blueled_set,
+ .default_trigger = "rx1950-acx-mem",
+};
+
+static int __devinit rx1950leds_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = gpio_request(S3C2410_GPA(3), "rx1950:red_led_blink");
+ if (ret)
+ goto err_blink_red;
+
+ ret = gpio_request(S3C2410_GPA(4), "rx1950:green_led_blink");
+ if (ret)
+ goto err_blink_green;
+
+ ret = gpio_request(S3C2410_GPJ(6), "rx1950:led_blink");
+ if (ret)
+ goto err_blink;
+
+ ret = gpio_request(S3C2410_GPA(6), "rx1950:green_led");
+ if (ret)
+ goto err_green;
+ ret = led_classdev_register(&pdev->dev, &rx1950_greenled);
+ if (ret)
+ goto err_green_gpio;
+
+ ret = gpio_request(S3C2410_GPA(7), "rx1950:red_led");
+ if (ret)
+ goto err_red;
+ ret = led_classdev_register(&pdev->dev, &rx1950_redled);
+ if (ret)
+ goto err_red_gpio;
+
+ ret = gpio_request(S3C2410_GPA(11), "rx1950:blue_led");
+ if (ret)
+ goto err_blue;
+ ret = led_classdev_register(&pdev->dev, &rx1950_blueled);
+ if (ret)
+ goto err_blue_gpio;
+
+ return 0;
+
+err_blue_gpio:
+ gpio_free(S3C2410_GPA(11));
+err_blue:
+ led_classdev_unregister(&rx1950_redled);
+err_red_gpio:
+ gpio_free(S3C2410_GPA(6));
+err_red:
+ led_classdev_unregister(&rx1950_greenled);
+err_green_gpio:
+ gpio_free(S3C2410_GPA7);
+err_green:
+ gpio_free(S3C2410_GPJ(6));
+err_blink:
+ gpio_free(S3C2410_GPA(4));
+err_blink_green:
+ gpio_free(S3C2410_GPA(3));
+err_blink_red:
+ return ret;
+}
+
+static int rx1950leds_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&rx1950_greenled);
+ led_classdev_unregister(&rx1950_redled);
+ led_classdev_unregister(&rx1950_blueled);
+ gpio_free(S3C2410_GPA(11));
+ gpio_free(S3C2410_GPA(6));
+ gpio_free(S3C2410_GPA(7));
+ gpio_free(S3C2410_GPJ(6));
+ gpio_free(S3C2410_GPA(3));
+ gpio_free(S3C2410_GPA(4));
+ return 0;
+}
+
+
+static struct platform_driver rx1950leds_driver = {
+ .driver = {
+ .name = "rx1950-leds",
+ .owner = THIS_MODULE,
+ },
+ .probe = rx1950leds_probe,
+ .remove = rx1950leds_remove,
+};
+
+
+static int __init rx1950leds_init(void)
+{
+ return platform_driver_register(&rx1950leds_driver);
+}
+
+static void __exit rx1950leds_exit(void)
+{
+ platform_driver_unregister(&rx1950leds_driver);
+}
+
+module_init(rx1950leds_init);
+module_exit(rx1950leds_exit);
+
+MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
+MODULE_DESCRIPTION("LED driver for the iPAQ RX1950");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rx1950-leds");
--
1.7.1.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 2/4] rx1950: add LEDs device
2010-07-17 8:18 [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 1/4] Add HP iPAQ rx1950 LEDs driver Vasily Khoruzhick
@ 2010-07-17 8:18 ` Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 3/4] Add s3c-adc-battery driver Vasily Khoruzhick
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Vasily Khoruzhick @ 2010-07-17 8:18 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
arch/arm/mach-s3c2440/mach-rx1950.c | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-s3c2440/mach-rx1950.c b/arch/arm/mach-s3c2440/mach-rx1950.c
index 8603b57..55b806a 100644
--- a/arch/arm/mach-s3c2440/mach-rx1950.c
+++ b/arch/arm/mach-s3c2440/mach-rx1950.c
@@ -126,6 +126,11 @@ static struct s3c2410fb_display rx1950_display = {
};
+static struct platform_device rx1950_device_leds = {
+ .name = "rx1950-leds",
+ .id = -1,
+};
+
static struct s3c2410fb_mach_info rx1950_lcd_cfg = {
.displays = &rx1950_display,
.num_displays = 1,
@@ -502,6 +507,7 @@ static struct platform_device *rx1950_devices[] __initdata = {
&s3c_device_timer[1],
&rx1950_backlight,
&rx1950_device_gpiokeys,
+ &rx1950_device_leds,
};
static struct clk *rx1950_clocks[] __initdata = {
--
1.7.1.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 3/4] Add s3c-adc-battery driver
2010-07-17 8:18 [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 1/4] Add HP iPAQ rx1950 LEDs driver Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 2/4] rx1950: add LEDs device Vasily Khoruzhick
@ 2010-07-17 8:18 ` Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 4/4] rx1950: add battery device Vasily Khoruzhick
2010-07-17 8:48 ` [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series Vasily Khoruzhick
4 siblings, 0 replies; 6+ messages in thread
From: Vasily Khoruzhick @ 2010-07-17 8:18 UTC (permalink / raw)
To: linux-arm-kernel
s3c-adc-battery is driver for monitoring
and charging battery on iPAQ H1930/H1940/RX1950.
It depends on s3c-adc driver to get battery voltage and current.
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
drivers/power/Kconfig | 6 +
drivers/power/Makefile | 1 +
drivers/power/s3c_adc_battery.c | 426 +++++++++++++++++++++++++++++++++++++++
include/linux/s3c_adc_battery.h | 36 ++++
4 files changed, 469 insertions(+), 0 deletions(-)
create mode 100644 drivers/power/s3c_adc_battery.c
create mode 100644 include/linux/s3c_adc_battery.h
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 8e9ba17..645baa5 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -136,6 +136,12 @@ config BATTERY_Z2
help
Say Y to include support for the battery on the Zipit Z2.
+config BATTERY_S3C_ADC
+ tristate "Battery driver for Samsung ADC based monitoring"
+ depends on S3C_ADC
+ help
+ Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery
+
config CHARGER_PCF50633
tristate "NXP PCF50633 MBC"
depends on MFD_PCF50633
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 0005080..166bcbf 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -33,4 +33,5 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
+obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c
new file mode 100644
index 0000000..4eb05ab
--- /dev/null
+++ b/drivers/power/s3c_adc_battery.c
@@ -0,0 +1,426 @@
+/*
+ * iPAQ h1930/h1940/rx1950 battery controler driver
+ * Copyright (c) Vasily Khoruzhick
+ * Based on h1940_battery.c by Arnaud Patard
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/s3c_adc_battery.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <plat/adc.h>
+
+#define BAT_POLL_INTERVAL 10000 /* ms */
+#define JITTER_DELAY 500 /* ms */
+
+struct s3c_adc_bat {
+ struct power_supply psy;
+ struct s3c_adc_client *client;
+ struct s3c_adc_bat_pdata *pdata;
+ int volt_value;
+ int cur_value;
+ unsigned int timestamp;
+ int level;
+ int status;
+ int cable_plugged:1;
+};
+
+static struct delayed_work bat_work;
+
+static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
+{
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+}
+
+static enum power_supply_property s3c_adc_backup_bat_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+};
+
+static int s3c_adc_backup_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
+
+ if (!bat) {
+ dev_err(psy->dev, "%s: no battery infos ?!\n", __func__);
+ return -EINVAL;
+ }
+
+ if (bat->volt_value < 0 ||
+ jiffies_to_msecs(jiffies - bat->timestamp) >
+ BAT_POLL_INTERVAL) {
+ bat->volt_value = s3c_adc_read(bat->client,
+ bat->pdata->backup_volt_channel);
+ bat->volt_value *= bat->pdata->backup_volt_mult;
+ bat->timestamp = jiffies;
+ }
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = bat->volt_value;
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ val->intval = bat->pdata->backup_volt_min;
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = bat->pdata->backup_volt_max;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct s3c_adc_bat backup_bat = {
+ .psy = {
+ .name = "backup-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = s3c_adc_backup_bat_props,
+ .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props),
+ .get_property = s3c_adc_backup_bat_get_property,
+ .use_for_apm = 1,
+ },
+};
+
+static enum power_supply_property s3c_adc_main_bat_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int calc_full_volt(int volt_val, int cur_val, int impedance)
+{
+ return volt_val + cur_val * impedance / 1000;
+}
+
+static int s3c_adc_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
+
+ int new_level;
+ int full_volt;
+ const struct s3c_adc_bat_thresh *lut = bat->pdata->lut_noac;
+ unsigned int lut_size = bat->pdata->lut_noac_cnt;
+
+ if (!bat) {
+ dev_err(psy->dev, "no battery infos ?!\n");
+ return -EINVAL;
+ }
+
+ if (bat->volt_value < 0 || bat->cur_value < 0 ||
+ jiffies_to_msecs(jiffies - bat->timestamp) >
+ BAT_POLL_INTERVAL) {
+ bat->volt_value = s3c_adc_read(bat->client,
+ bat->pdata->volt_channel) * bat->pdata->volt_mult;
+ bat->cur_value = s3c_adc_read(bat->client,
+ bat->pdata->current_channel) * bat->pdata->current_mult;
+ bat->timestamp = jiffies;
+ }
+
+ new_level = 100000;
+ full_volt = calc_full_volt((bat->volt_value / 1000),
+ (bat->cur_value / 1000), bat->pdata->internal_impedance);
+
+ if (full_volt < calc_full_volt(lut->volt, lut->cur,
+ bat->pdata->internal_impedance)) {
+ lut_size--;
+ while (lut_size--) {
+ int lut_volt1;
+ int lut_volt2;
+
+ lut_volt1 = calc_full_volt(lut[0].volt, lut[0].cur,
+ bat->pdata->internal_impedance);
+ lut_volt2 = calc_full_volt(lut[1].volt, lut[1].cur,
+ bat->pdata->internal_impedance);
+ if (full_volt < lut_volt1 && full_volt >= lut_volt2) {
+ new_level = (lut[1].level +
+ (lut[0].level - lut[1].level) *
+ (full_volt - lut_volt2) /
+ (lut_volt1 - lut_volt2)) * 1000;
+ break;
+ }
+ new_level = lut[1].level * 1000;
+ lut++;
+ }
+ }
+
+ bat->level = new_level;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (bat->pdata->gpio_charge_finished < 0)
+ val->intval = bat->level == 100000 ?
+ POWER_SUPPLY_STATUS_FULL : bat->status;
+ else
+ val->intval = bat->status;
+ return 0;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = 100000;
+ return 0;
+ case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
+ val->intval = 0;
+ return 0;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = bat->level;
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = bat->volt_value;
+ return 0;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = bat->cur_value;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct s3c_adc_bat main_bat = {
+ .psy = {
+ .name = "main-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = s3c_adc_main_bat_props,
+ .num_properties = ARRAY_SIZE(s3c_adc_main_bat_props),
+ .get_property = s3c_adc_bat_get_property,
+ .external_power_changed = s3c_adc_bat_ext_power_changed,
+ .use_for_apm = 1,
+ },
+};
+
+static void s3c_adc_bat_work(struct work_struct *work)
+{
+ struct s3c_adc_bat *bat = &main_bat;
+ int is_charged;
+ int is_plugged;
+ static int was_plugged;
+
+ is_plugged = power_supply_am_i_supplied(&bat->psy);
+ bat->cable_plugged = is_plugged;
+ if (is_plugged != was_plugged) {
+ was_plugged = is_plugged;
+ if (is_plugged) {
+ if (bat->pdata->enable_charger)
+ bat->pdata->enable_charger();
+ bat->status = POWER_SUPPLY_STATUS_CHARGING;
+ } else {
+ if (bat->pdata->disable_charger)
+ bat->pdata->disable_charger();
+ bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+ } else {
+ if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) {
+ is_charged = gpio_get_value(
+ main_bat.pdata->gpio_charge_finished);
+ if (is_charged) {
+ if (bat->pdata->disable_charger)
+ bat->pdata->disable_charger();
+ bat->status = POWER_SUPPLY_STATUS_FULL;
+ } else {
+ if (bat->pdata->enable_charger)
+ bat->pdata->enable_charger();
+ bat->status = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ }
+ }
+
+ power_supply_changed(&bat->psy);
+}
+
+static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id)
+{
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+ return IRQ_HANDLED;
+}
+
+static int __init s3c_adc_bat_probe(struct platform_device *pdev)
+{
+ struct s3c_adc_client *client;
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+ int ret;
+
+ client = s3c_adc_register(pdev, NULL, NULL, 0);
+ if (IS_ERR(client)) {
+ dev_err(&pdev->dev, "cannot register adc\n");
+ return PTR_ERR(client);
+ }
+
+ platform_set_drvdata(pdev, client);
+
+ main_bat.client = client;
+ main_bat.pdata = pdata;
+ main_bat.volt_value = -1;
+ main_bat.cur_value = -1;
+ main_bat.cable_plugged = 0;
+ main_bat.status = POWER_SUPPLY_STATUS_DISCHARGING;
+
+ ret = power_supply_register(&pdev->dev, &main_bat.psy);
+ if (ret)
+ goto err_reg_main;
+ if (pdata->backup_volt_mult) {
+ backup_bat.client = client;
+ backup_bat.pdata = pdev->dev.platform_data;
+ backup_bat.volt_value = -1;
+ ret = power_supply_register(&pdev->dev, &backup_bat.psy);
+ if (ret)
+ goto err_reg_backup;
+ }
+
+ INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work);
+
+ if (pdata->gpio_charge_finished >= 0) {
+ ret = gpio_request(pdata->gpio_charge_finished, "charged");
+ if (ret)
+ goto err_gpio;
+
+ ret = request_irq(gpio_to_irq(pdata->gpio_charge_finished),
+ s3c_adc_bat_charged,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "battery charged", NULL);
+ if (ret)
+ goto err_irq;
+ }
+
+ if (pdata->init) {
+ ret = pdata->init();
+ if (ret)
+ goto err_platform;
+ }
+
+ dev_info(&pdev->dev, "successfully loaded\n");
+ device_init_wakeup(&pdev->dev, 1);
+
+ /* Schedule timer to check current status */
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+
+ return 0;
+
+err_platform:
+ if (pdata->gpio_charge_finished >= 0)
+ free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
+err_irq:
+ if (pdata->gpio_charge_finished >= 0)
+ gpio_free(pdata->gpio_charge_finished);
+err_gpio:
+ if (pdata->backup_volt_mult)
+ power_supply_unregister(&backup_bat.psy);
+err_reg_backup:
+ power_supply_unregister(&main_bat.psy);
+err_reg_main:
+ return ret;
+}
+
+static int s3c_adc_bat_remove(struct platform_device *pdev)
+{
+ struct s3c_adc_client *client = platform_get_drvdata(pdev);
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+ power_supply_unregister(&main_bat.psy);
+ if (pdata->backup_volt_mult)
+ power_supply_unregister(&backup_bat.psy);
+
+ s3c_adc_release(client);
+
+ if (pdata->gpio_charge_finished >= 0) {
+ free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
+ gpio_free(pdata->gpio_charge_finished);
+ }
+
+ cancel_delayed_work(&bat_work);
+
+ if (pdata->exit)
+ pdata->exit();
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c_adc_bat_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+ if (pdata->gpio_charge_finished >= 0) {
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(
+ gpio_to_irq(pdata->gpio_charge_finished));
+ else {
+ disable_irq(gpio_to_irq(pdata->gpio_charge_finished));
+ main_bat.pdata->disable_charger();
+ }
+ }
+
+ return 0;
+}
+
+static int s3c_adc_bat_resume(struct platform_device *pdev)
+{
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+ if (pdata->gpio_charge_finished >= 0) {
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(
+ gpio_to_irq(pdata->gpio_charge_finished));
+ else
+ enable_irq(gpio_to_irq(pdata->gpio_charge_finished));
+ }
+
+ /* Schedule timer to check current status */
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+
+ return 0;
+}
+#else
+#define s3c_adc_battery_suspend NULL
+#define s3c_adc_battery_resume NULL
+#endif
+
+static struct platform_driver s3c_adc_bat_driver = {
+ .driver = {
+ .name = "s3c-adc-battery",
+ },
+ .probe = s3c_adc_bat_probe,
+ .remove = s3c_adc_bat_remove,
+ .suspend = s3c_adc_bat_suspend,
+ .resume = s3c_adc_bat_resume,
+};
+
+
+static int __init s3c_adc_bat_init(void)
+{
+ return platform_driver_register(&s3c_adc_bat_driver);
+}
+
+static void __exit s3c_adc_bat_exit(void)
+{
+ platform_driver_unregister(&s3c_adc_bat_driver);
+}
+
+module_init(s3c_adc_bat_init);
+module_exit(s3c_adc_bat_exit);
+
+MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
+MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controler driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/s3c_adc_battery.h b/include/linux/s3c_adc_battery.h
new file mode 100644
index 0000000..dbce22f
--- /dev/null
+++ b/include/linux/s3c_adc_battery.h
@@ -0,0 +1,36 @@
+#ifndef _S3C_ADC_BATTERY_H
+#define _S3C_ADC_BATTERY_H
+
+struct s3c_adc_bat_thresh {
+ int volt; /* mV */
+ int cur; /* mA */
+ int level; /* percent */
+};
+
+struct s3c_adc_bat_pdata {
+ int (*init)(void);
+ void (*exit)(void);
+ void (*enable_charger)(void);
+ void (*disable_charger)(void);
+
+ int gpio_charge_finished;
+
+ const struct s3c_adc_bat_thresh *lut_noac;
+ unsigned int lut_noac_cnt;
+ const struct s3c_adc_bat_thresh *lut_acin;
+ unsigned int lut_acin_cnt;
+
+ const unsigned int volt_channel;
+ const unsigned int current_channel;
+ const unsigned int backup_volt_channel;
+
+ const unsigned int volt_mult;
+ const unsigned int current_mult;
+ const unsigned int backup_volt_mult;
+ const unsigned int internal_impedance;
+
+ const unsigned int backup_volt_max;
+ const unsigned int backup_volt_min;
+};
+
+#endif
--
1.7.1.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 4/4] rx1950: add battery device
2010-07-17 8:18 [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series Vasily Khoruzhick
` (2 preceding siblings ...)
2010-07-17 8:18 ` [PATCH v4 3/4] Add s3c-adc-battery driver Vasily Khoruzhick
@ 2010-07-17 8:18 ` Vasily Khoruzhick
2010-07-17 8:48 ` [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series Vasily Khoruzhick
4 siblings, 0 replies; 6+ messages in thread
From: Vasily Khoruzhick @ 2010-07-17 8:18 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
arch/arm/mach-s3c2440/Kconfig | 3 +
arch/arm/mach-s3c2440/mach-rx1950.c | 160 +++++++++++++++++++++++++++++++++++
2 files changed, 163 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig
index cd8e7de..9b6b026 100644
--- a/arch/arm/mach-s3c2440/Kconfig
+++ b/arch/arm/mach-s3c2440/Kconfig
@@ -198,6 +198,9 @@ config MACH_RX1950
select S3C_DEV_NAND
select S3C2410_IOTIMING if S3C2440_CPUFREQ
select S3C2440_XTAL_16934400
+ select S3C_ADC
+ select PDA_POWER
+ select BATTERY_S3C_ADC
help
Say Y here if you're using HP iPAQ rx1950
diff --git a/arch/arm/mach-s3c2440/mach-rx1950.c b/arch/arm/mach-s3c2440/mach-rx1950.c
index 55b806a..7342f36 100644
--- a/arch/arm/mach-s3c2440/mach-rx1950.c
+++ b/arch/arm/mach-s3c2440/mach-rx1950.c
@@ -24,8 +24,10 @@
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/sysdev.h>
+#include <linux/pda_power.h>
#include <linux/pwm_backlight.h>
#include <linux/pwm.h>
+#include <linux/s3c_adc_battery.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
@@ -131,6 +133,162 @@ static struct platform_device rx1950_device_leds = {
.id = -1,
};
+static int power_supply_init(struct device *dev)
+{
+ return gpio_request(S3C2410_GPF(2), "cable plugged");
+}
+
+static int rx1950_is_ac_online(void)
+{
+ return !gpio_get_value(S3C2410_GPF(2));
+}
+
+static void power_supply_exit(struct device *dev)
+{
+ gpio_free(S3C2410_GPF(2));
+}
+
+static char *rx1950_supplicants[] = {
+ "main-battery"
+};
+
+static struct pda_power_pdata power_supply_info = {
+ .init = power_supply_init,
+ .is_ac_online = rx1950_is_ac_online,
+ .exit = power_supply_exit,
+ .supplied_to = rx1950_supplicants,
+ .num_supplicants = ARRAY_SIZE(rx1950_supplicants),
+};
+
+static struct resource power_supply_resources[] = {
+ [0] = {
+ .name = "ac",
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE |
+ IORESOURCE_IRQ_HIGHEDGE,
+ .start = IRQ_EINT2,
+ .end = IRQ_EINT2,
+ },
+};
+
+static struct platform_device power_supply = {
+ .name = "pda-power",
+ .id = -1,
+ .dev = {
+ .platform_data =
+ &power_supply_info,
+ },
+ .resource = power_supply_resources,
+ .num_resources = ARRAY_SIZE(power_supply_resources),
+};
+
+static const struct s3c_adc_bat_thresh bat_lut_noac[] = {
+ {4100, 156, 100},
+ {4050, 156, 95},
+ {4025, 141, 90},
+ {3995, 144, 85},
+ {3957, 162, 80},
+ {3931, 147, 75},
+ {3902, 147, 70},
+ {3863, 153, 65},
+ {3838, 150, 60},
+ {3800, 153, 55},
+ {3765, 153, 50},
+ {3748, 172, 45},
+ {3740, 153, 40},
+ {3714, 175, 35},
+ {3710, 156, 30},
+ {3963, 156, 25},
+ {3672, 178, 20},
+ {3651, 178, 15},
+ {3629, 178, 10},
+ {3612, 162, 5},
+ {3605, 162, 0},
+};
+
+static const struct s3c_adc_bat_thresh bat_lut_acin[] = {
+ {4200, 0, 100},
+ {4190, 0, 99},
+ {4178, 0, 95},
+ {4110, 0, 70},
+ {4076, 0, 65},
+ {4046, 0, 60},
+ {4021, 0, 55},
+ {3999, 0, 50},
+ {3982, 0, 45},
+ {3965, 0, 40},
+ {3957, 0, 35},
+ {3948, 0, 30},
+ {3936, 0, 25},
+ {3927, 0, 20},
+ {3906, 0, 15},
+ {3880, 0, 10},
+ {3829, 0, 5},
+ {3820, 0, 0},
+};
+
+int rx1950_bat_init(void)
+{
+ int ret;
+
+ ret = gpio_request(S3C2410_GPJ(2), "rx1950-charger-enable-1");
+ if (ret)
+ goto err_gpio1;
+ ret = gpio_request(S3C2410_GPJ(3), "rx1950-charger-enable-2");
+ if (ret)
+ goto err_gpio2;
+
+ return 0;
+
+err_gpio2:
+ gpio_free(S3C2410_GPJ(2));
+err_gpio1:
+ return ret;
+}
+
+void rx1950_bat_exit(void)
+{
+ gpio_free(S3C2410_GPJ(2));
+ gpio_free(S3C2410_GPJ(3));
+}
+
+void rx1950_enable_charger(void)
+{
+ gpio_direction_output(S3C2410_GPJ(2), 1);
+ gpio_direction_output(S3C2410_GPJ(3), 1);
+}
+
+void rx1950_disable_charger(void)
+{
+ gpio_direction_output(S3C2410_GPJ(2), 0);
+ gpio_direction_output(S3C2410_GPJ(3), 0);
+}
+
+static struct s3c_adc_bat_pdata rx1950_bat_cfg = {
+ .init = rx1950_bat_init,
+ .exit = rx1950_bat_exit,
+ .enable_charger = rx1950_enable_charger,
+ .disable_charger = rx1950_disable_charger,
+ .gpio_charge_finished = S3C2410_GPF(3),
+ .lut_noac = bat_lut_noac,
+ .lut_noac_cnt = ARRAY_SIZE(bat_lut_noac),
+ .lut_acin = bat_lut_acin,
+ .lut_acin_cnt = ARRAY_SIZE(bat_lut_acin),
+ .volt_channel = 0,
+ .current_channel = 1,
+ .volt_mult = 4235,
+ .current_mult = 2900,
+ .internal_impedance = 200,
+};
+
+static struct platform_device rx1950_battery = {
+ .name = "s3c-adc-battery",
+ .id = -1,
+ .dev = {
+ .parent = &s3c_device_adc.dev,
+ .platform_data = &rx1950_bat_cfg,
+ },
+};
+
static struct s3c2410fb_mach_info rx1950_lcd_cfg = {
.displays = &rx1950_display,
.num_displays = 1,
@@ -508,6 +666,8 @@ static struct platform_device *rx1950_devices[] __initdata = {
&rx1950_backlight,
&rx1950_device_gpiokeys,
&rx1950_device_leds,
+ &power_supply,
+ &rx1950_battery,
};
static struct clk *rx1950_clocks[] __initdata = {
--
1.7.1.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series
2010-07-17 8:18 [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series Vasily Khoruzhick
` (3 preceding siblings ...)
2010-07-17 8:18 ` [PATCH v4 4/4] rx1950: add battery device Vasily Khoruzhick
@ 2010-07-17 8:48 ` Vasily Khoruzhick
4 siblings, 0 replies; 6+ messages in thread
From: Vasily Khoruzhick @ 2010-07-17 8:48 UTC (permalink / raw)
To: linux-arm-kernel
? ????????? ?? 17 ???? 2010 11:18:32 ????? Vasily Khoruzhick ???????:
> This patch series adds more support for iPAQ rx1950 PDA to linux:
> 1. LEDs driver -- it controls blue, green and red LEDs on rx1950
> 2. Battery driver -- adds ability to monitor and charge battery.
> This driver is suitable for H1940 PDA aswell (just need to write
> some machine specific callbacks and get voltage LUTs)
>
> v2: removed ac registration from s3c_adc_battery driver, use
> pda_power instead for ac support
>
> v3: split LEDs patch to driver and machine parts,
> split battery patch into battery + LED trigger parts,
> now battery driver can skip gpio/callbacks usage
> (if there's no gpio pin to determine that battery is
> full or if charger is enabled automatically)
>
> v4: add .blink_set callback to LEDs driver instead of
> using LED_HALF value for blinking, remove LED triggers
> from battery driver completely (for now)
Oops, I accidently removed lut_acin assignment, please discard this series
version.
Regards
Vasily
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20100717/fa52ce87/attachment-0001.sig>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2010-07-17 8:48 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-07-17 8:18 [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 1/4] Add HP iPAQ rx1950 LEDs driver Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 2/4] rx1950: add LEDs device Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 3/4] Add s3c-adc-battery driver Vasily Khoruzhick
2010-07-17 8:18 ` [PATCH v4 4/4] rx1950: add battery device Vasily Khoruzhick
2010-07-17 8:48 ` [PATCH v4 0/4] s3c24xx: iPAQ rx1950 series Vasily Khoruzhick
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.