From 0160817d10a97900b9ee08a8b8b2f5a148e2be0f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 19 Dec 2014 16:56:27 +0100 Subject: power: reset: augment versatile driver for integrator Augment the Versatile reset driver to also handle the core module reset sequence used on the Integrator/AP and Integrator/CP. Cc: Dmitry Eremin-Solenikov Cc: David Woodhouse Cc: Sebastian Reichel Signed-off-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/reset/arm-versatile-reboot.c | 32 +++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/arm-versatile-reboot.c b/drivers/power/reset/arm-versatile-reboot.c index 5b08bffcf1a8..adea9d0c38e0 100644 --- a/drivers/power/reset/arm-versatile-reboot.c +++ b/drivers/power/reset/arm-versatile-reboot.c @@ -15,14 +15,21 @@ #include #include +#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C +#define INTEGRATOR_HDR_LOCK_OFFSET 0x14 +#define INTEGRATOR_CM_CTRL_RESET (1 << 3) + #define REALVIEW_SYS_LOCK_OFFSET 0x20 -#define REALVIEW_SYS_LOCK_VAL 0xA05F #define REALVIEW_SYS_RESETCTL_OFFSET 0x40 +/* Magic unlocking token used on all Versatile boards */ +#define VERSATILE_LOCK_VAL 0xA05F + /* * We detect the different syscon types from the compatible strings. */ enum versatile_reboot { + INTEGRATOR_REBOOT_CM, REALVIEW_REBOOT_EB, REALVIEW_REBOOT_PB1176, REALVIEW_REBOOT_PB11MP, @@ -35,6 +42,10 @@ static struct regmap *syscon_regmap; static enum versatile_reboot versatile_reboot_type; static const struct of_device_id versatile_reboot_of_match[] = { + { + .compatible = "arm,core-module-integrator", + .data = (void *)INTEGRATOR_REBOOT_CM + }, { .compatible = "arm,realview-eb-syscon", .data = (void *)REALVIEW_REBOOT_EB, @@ -55,31 +66,46 @@ static const struct of_device_id versatile_reboot_of_match[] = { .compatible = "arm,realview-pbx-syscon", .data = (void *)REALVIEW_REBOOT_PBX, }, + {}, }; static void versatile_reboot(enum reboot_mode mode, const char *cmd) { /* Unlock the reset register */ - regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, - REALVIEW_SYS_LOCK_VAL); /* Then hit reset on the different machines */ switch (versatile_reboot_type) { + case INTEGRATOR_REBOOT_CM: + regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET, + VERSATILE_LOCK_VAL); + regmap_update_bits(syscon_regmap, + INTEGRATOR_HDR_CTRL_OFFSET, + INTEGRATOR_CM_CTRL_RESET, + INTEGRATOR_CM_CTRL_RESET); + break; case REALVIEW_REBOOT_EB: + regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + VERSATILE_LOCK_VAL); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 0x0008); break; case REALVIEW_REBOOT_PB1176: + regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + VERSATILE_LOCK_VAL); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 0x0100); break; case REALVIEW_REBOOT_PB11MP: case REALVIEW_REBOOT_PBA8: + regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + VERSATILE_LOCK_VAL); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 0x0000); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 0x0004); break; case REALVIEW_REBOOT_PBX: + regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + VERSATILE_LOCK_VAL); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 0x00f0); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, -- cgit v1.2.3 From 0a5c6a2276fe06a3ebb6a9ed26c0b8007074958e Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:34 +0100 Subject: power: reset: ltc2952: prefer devm_kzalloc over kzalloc Make use of the fact that the allocated resources can be automatically deallocated. This reduces cleanup code and chance of leaks. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 34f38a3dc3ff..6487b991ed45 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -292,7 +292,8 @@ static int ltc2952_poweroff_probe(struct platform_device *pdev) return -EBUSY; } - ltc2952_data = kzalloc(sizeof(*ltc2952_data), GFP_KERNEL); + ltc2952_data = devm_kzalloc(&pdev->dev, sizeof(*ltc2952_data), + GFP_KERNEL); if (!ltc2952_data) return -ENOMEM; @@ -300,17 +301,13 @@ static int ltc2952_poweroff_probe(struct platform_device *pdev) ret = ltc2952_poweroff_init(pdev); if (ret) - goto err; + return ret; pm_power_off = <c2952_poweroff_kill; dev_info(&pdev->dev, "probe successful\n"); return 0; - -err: - kfree(ltc2952_data); - return ret; } static int ltc2952_poweroff_remove(struct platform_device *pdev) @@ -324,8 +321,6 @@ static int ltc2952_poweroff_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) gpiod_put(ltc2952_data->gpio[i]); - - kfree(ltc2952_data); } return 0; -- cgit v1.2.3 From 07d08237d3ff08c6a53c6e8496bd255176e1d68f Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:35 +0100 Subject: power: reset: ltc2952: prefer devm_request_irq over request_irq Make use of the fact that we allocated resources can be automatically deallocated. This reduces cleanup code and chance of errors. It also removes the need for the virq member of the ltc2952_poweroff_data struct. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 6487b991ed45..0b0792a9ad56 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -72,8 +72,6 @@ struct ltc2952_poweroff_data { struct device *dev; - unsigned int virq; - /** * 0: trigger * 1: watchdog @@ -260,13 +258,11 @@ static int ltc2952_poweroff_init(struct platform_device *pdev) goto err_io; } - ltc2952_data->virq = virq; - ret = request_irq(virq, - ltc2952_poweroff_handler, - (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), - "ltc2952-poweroff", - ltc2952_data - ); + ret = devm_request_irq(&pdev->dev, virq, + ltc2952_poweroff_handler, + (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), + "ltc2952-poweroff", + ltc2952_data); if (ret) { dev_err(&pdev->dev, "cannot configure an interrupt handler\n"); @@ -316,12 +312,9 @@ static int ltc2952_poweroff_remove(struct platform_device *pdev) pm_power_off = NULL; - if (ltc2952_data) { - free_irq(ltc2952_data->virq, ltc2952_data); - + if (ltc2952_data) for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) gpiod_put(ltc2952_data->gpio[i]); - } return 0; } -- cgit v1.2.3 From f66472df697ed6341e2317d5c825f2d6916ae47f Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:36 +0100 Subject: power: reset: ltc2952: unroll gpio_desc array The three gpio's used by this driver are stored in an array of pointers. This doesn't add much besides cleanups in a loop. In fact, it makes most of the usage sites harder to read. Unroll the loop, and live with the fact that cleanups become slightly larger. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 86 +++++++++++++++------------------- 1 file changed, 38 insertions(+), 48 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 0b0792a9ad56..accadfc01270 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -72,21 +72,14 @@ struct ltc2952_poweroff_data { struct device *dev; - /** - * 0: trigger - * 1: watchdog - * 2: kill - */ - struct gpio_desc *gpio[3]; + struct gpio_desc *gpio_trigger; + struct gpio_desc *gpio_watchdog; + struct gpio_desc *gpio_kill; }; static int ltc2952_poweroff_panic; static struct ltc2952_poweroff_data *ltc2952_data; -#define POWERPATH_IO_TRIGGER 0 -#define POWERPATH_IO_WATCHDOG 1 -#define POWERPATH_IO_KILL 2 - /** * ltc2952_poweroff_timer_wde - Timer callback * Toggles the watchdog reset signal each wde_interval @@ -105,8 +98,8 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) if (ltc2952_poweroff_panic) return HRTIMER_NORESTART; - state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]); - gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state); + state = gpiod_get_value(ltc2952_data->gpio_watchdog); + gpiod_set_value(ltc2952_data->gpio_watchdog, !state); now = hrtimer_cb_get_time(timer); overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval); @@ -120,7 +113,7 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger( int ret; ret = hrtimer_start(<c2952_data->timer_wde, - ltc2952_data->wde_interval, HRTIMER_MODE_REL); + ltc2952_data->wde_interval, HRTIMER_MODE_REL); if (ret) { dev_err(ltc2952_data->dev, "unable to start the timer\n"); @@ -180,7 +173,7 @@ irq_ok: static void ltc2952_poweroff_kill(void) { - gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_KILL], 1); + gpiod_set_value(ltc2952_data->gpio_kill, 1); } static int ltc2952_poweroff_suspend(struct platform_device *pdev, @@ -196,11 +189,6 @@ static int ltc2952_poweroff_resume(struct platform_device *pdev) static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(data->gpio); i++) - data->gpio[i] = NULL; - data->wde_interval = ktime_set(0, 300L*1E6L); data->trigger_delay = ktime_set(2, 500L*1E6L); @@ -214,45 +202,46 @@ static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data) static int ltc2952_poweroff_init(struct platform_device *pdev) { int ret, virq; - unsigned int i; struct ltc2952_poweroff_data *data; - static char *name[] = { - "trigger", - "watchdog", - "kill", - NULL - }; - data = ltc2952_data; ltc2952_poweroff_default(ltc2952_data); - for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) { - ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]); + ltc2952_data->gpio_watchdog = gpiod_get(&pdev->dev, "watchdog"); + if (IS_ERR(ltc2952_data->gpio_watchdog)) { + ret = PTR_ERR(ltc2952_data->gpio_watchdog); + dev_err(&pdev->dev, "unable to claim gpio \"watchdog\"\n"); + return ret; + } - if (IS_ERR(ltc2952_data->gpio[i])) { - ret = PTR_ERR(ltc2952_data->gpio[i]); - dev_err(&pdev->dev, - "unable to claim the following gpio: %s\n", - name[i]); - goto err_io; - } + ltc2952_data->gpio_kill = gpiod_get(&pdev->dev, "kill"); + if (IS_ERR(ltc2952_data->gpio_kill)) { + ret = PTR_ERR(ltc2952_data->gpio_kill); + dev_err(&pdev->dev, "unable to claim gpio \"kill\"\n"); + goto err_kill; + } + + ltc2952_data->gpio_trigger = gpiod_get(&pdev->dev, "trigger"); + if (IS_ERR(ltc2952_data->gpio_trigger)) { + ret = PTR_ERR(ltc2952_data->gpio_trigger); + dev_err(&pdev->dev, "unable to claim gpio \"trigger\"\n"); + goto err_trigger; } ret = gpiod_direction_output( - ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], 0); + ltc2952_data->gpio_watchdog, 0); if (ret) { dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n"); goto err_io; } - ret = gpiod_direction_output(ltc2952_data->gpio[POWERPATH_IO_KILL], 0); + ret = gpiod_direction_output(ltc2952_data->gpio_kill, 0); if (ret) { dev_err(&pdev->dev, "unable to use kill-gpio as output\n"); goto err_io; } - virq = gpiod_to_irq(ltc2952_data->gpio[POWERPATH_IO_TRIGGER]); + virq = gpiod_to_irq(ltc2952_data->gpio_trigger); if (virq < 0) { dev_err(&pdev->dev, "cannot map GPIO as interrupt"); goto err_io; @@ -272,9 +261,11 @@ static int ltc2952_poweroff_init(struct platform_device *pdev) return 0; err_io: - for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) - if (ltc2952_data->gpio[i]) - gpiod_put(ltc2952_data->gpio[i]); + gpiod_put(ltc2952_data->gpio_trigger); +err_trigger: + gpiod_put(ltc2952_data->gpio_kill); +err_kill: + gpiod_put(ltc2952_data->gpio_watchdog); return ret; } @@ -308,14 +299,13 @@ static int ltc2952_poweroff_probe(struct platform_device *pdev) static int ltc2952_poweroff_remove(struct platform_device *pdev) { - unsigned int i; - pm_power_off = NULL; - if (ltc2952_data) - for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) - gpiod_put(ltc2952_data->gpio[i]); - + if (ltc2952_data) { + gpiod_put(ltc2952_data->gpio_trigger); + gpiod_put(ltc2952_data->gpio_watchdog); + gpiod_put(ltc2952_data->gpio_kill); + } return 0; } -- cgit v1.2.3 From 62113b31174a9dba99eda8a4222417dd526c7c79 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:37 +0100 Subject: power: reset: ltc2952: prefer devm_gpiod_get over gpiod_get This reduces cleanup code and chance of errors. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 44 ++++++++-------------------------- 1 file changed, 10 insertions(+), 34 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index accadfc01270..4caa9d285026 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -207,44 +207,34 @@ static int ltc2952_poweroff_init(struct platform_device *pdev) data = ltc2952_data; ltc2952_poweroff_default(ltc2952_data); - ltc2952_data->gpio_watchdog = gpiod_get(&pdev->dev, "watchdog"); + ltc2952_data->gpio_watchdog = devm_gpiod_get(&pdev->dev, "watchdog", + GPIOD_OUT_LOW); if (IS_ERR(ltc2952_data->gpio_watchdog)) { ret = PTR_ERR(ltc2952_data->gpio_watchdog); dev_err(&pdev->dev, "unable to claim gpio \"watchdog\"\n"); return ret; } - ltc2952_data->gpio_kill = gpiod_get(&pdev->dev, "kill"); + ltc2952_data->gpio_kill = devm_gpiod_get(&pdev->dev, "kill", + GPIOD_OUT_LOW); if (IS_ERR(ltc2952_data->gpio_kill)) { ret = PTR_ERR(ltc2952_data->gpio_kill); dev_err(&pdev->dev, "unable to claim gpio \"kill\"\n"); - goto err_kill; + return ret; } - ltc2952_data->gpio_trigger = gpiod_get(&pdev->dev, "trigger"); + ltc2952_data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", + GPIOD_IN); if (IS_ERR(ltc2952_data->gpio_trigger)) { ret = PTR_ERR(ltc2952_data->gpio_trigger); dev_err(&pdev->dev, "unable to claim gpio \"trigger\"\n"); - goto err_trigger; - } - - ret = gpiod_direction_output( - ltc2952_data->gpio_watchdog, 0); - if (ret) { - dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n"); - goto err_io; - } - - ret = gpiod_direction_output(ltc2952_data->gpio_kill, 0); - if (ret) { - dev_err(&pdev->dev, "unable to use kill-gpio as output\n"); - goto err_io; + return ret; } virq = gpiod_to_irq(ltc2952_data->gpio_trigger); if (virq < 0) { dev_err(&pdev->dev, "cannot map GPIO as interrupt"); - goto err_io; + return ret; } ret = devm_request_irq(&pdev->dev, virq, @@ -255,19 +245,10 @@ static int ltc2952_poweroff_init(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "cannot configure an interrupt handler\n"); - goto err_io; + return ret; } return 0; - -err_io: - gpiod_put(ltc2952_data->gpio_trigger); -err_trigger: - gpiod_put(ltc2952_data->gpio_kill); -err_kill: - gpiod_put(ltc2952_data->gpio_watchdog); - - return ret; } static int ltc2952_poweroff_probe(struct platform_device *pdev) @@ -301,11 +282,6 @@ static int ltc2952_poweroff_remove(struct platform_device *pdev) { pm_power_off = NULL; - if (ltc2952_data) { - gpiod_put(ltc2952_data->gpio_trigger); - gpiod_put(ltc2952_data->gpio_watchdog); - gpiod_put(ltc2952_data->gpio_kill); - } return 0; } -- cgit v1.2.3 From a5f67be5200787aacc7143144bdab7efe7cd268a Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:38 +0100 Subject: power: reset: ltc2952: reduce dependency on global variables Documentation/CodingStyle ch.4 mentions in a side node that global variables should only be used if you really need them. Reduce the use of the global instance of ltc2952_poweroff so we may eventually remove it entirely. While at it, rename ltc2952_poweroff_data to ltc2952_poweroff, just to save that little bit of typing. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 75 +++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 34 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 4caa9d285026..83042691cd79 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -63,7 +63,7 @@ #include #include -struct ltc2952_poweroff_data { +struct ltc2952_poweroff { struct hrtimer timer_trigger; struct hrtimer timer_wde; @@ -77,8 +77,14 @@ struct ltc2952_poweroff_data { struct gpio_desc *gpio_kill; }; +#define to_ltc2952(p, m) container_of(p, struct ltc2952_poweroff, m) + +/* + * This global variable is only needed for pm_power_off. We should + * remove it entirely once we don't need the global state anymore. + */ +static struct ltc2952_poweroff *ltc2952_data; static int ltc2952_poweroff_panic; -static struct ltc2952_poweroff_data *ltc2952_data; /** * ltc2952_poweroff_timer_wde - Timer callback @@ -94,29 +100,29 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) ktime_t now; int state; unsigned long overruns; + struct ltc2952_poweroff *data = to_ltc2952(timer, timer_wde); if (ltc2952_poweroff_panic) return HRTIMER_NORESTART; - state = gpiod_get_value(ltc2952_data->gpio_watchdog); - gpiod_set_value(ltc2952_data->gpio_watchdog, !state); + state = gpiod_get_value(data->gpio_watchdog); + gpiod_set_value(data->gpio_watchdog, !state); now = hrtimer_cb_get_time(timer); - overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval); + overruns = hrtimer_forward(timer, now, data->wde_interval); return HRTIMER_RESTART; } -static enum hrtimer_restart ltc2952_poweroff_timer_trigger( - struct hrtimer *timer) +static enum hrtimer_restart +ltc2952_poweroff_timer_trigger(struct hrtimer *timer) { - int ret; - - ret = hrtimer_start(<c2952_data->timer_wde, - ltc2952_data->wde_interval, HRTIMER_MODE_REL); + struct ltc2952_poweroff *data = to_ltc2952(timer, timer_trigger); + int ret = hrtimer_start(&data->timer_wde, + data->wde_interval, HRTIMER_MODE_REL); if (ret) { - dev_err(ltc2952_data->dev, "unable to start the timer\n"); + dev_err(data->dev, "unable to start the timer\n"); /* * The device will not toggle the watchdog reset, * thus shut down is only safe if the PowerPath controller @@ -127,7 +133,7 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger( */ } - dev_info(ltc2952_data->dev, "executing shutdown\n"); + dev_info(data->dev, "executing shutdown\n"); orderly_poweroff(true); @@ -146,7 +152,7 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger( static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id) { int ret; - struct ltc2952_poweroff_data *data = dev_id; + struct ltc2952_poweroff *data = dev_id; if (ltc2952_poweroff_panic) goto irq_ok; @@ -187,7 +193,7 @@ static int ltc2952_poweroff_resume(struct platform_device *pdev) return -ENOSYS; } -static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data) +static void ltc2952_poweroff_default(struct ltc2952_poweroff *data) { data->wde_interval = ktime_set(0, 300L*1E6L); data->trigger_delay = ktime_set(2, 500L*1E6L); @@ -202,36 +208,34 @@ static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data) static int ltc2952_poweroff_init(struct platform_device *pdev) { int ret, virq; - struct ltc2952_poweroff_data *data; + struct ltc2952_poweroff *data = platform_get_drvdata(pdev); - data = ltc2952_data; - ltc2952_poweroff_default(ltc2952_data); + ltc2952_poweroff_default(data); - ltc2952_data->gpio_watchdog = devm_gpiod_get(&pdev->dev, "watchdog", - GPIOD_OUT_LOW); - if (IS_ERR(ltc2952_data->gpio_watchdog)) { - ret = PTR_ERR(ltc2952_data->gpio_watchdog); + data->gpio_watchdog = devm_gpiod_get(&pdev->dev, "watchdog", + GPIOD_OUT_LOW); + if (IS_ERR(data->gpio_watchdog)) { + ret = PTR_ERR(data->gpio_watchdog); dev_err(&pdev->dev, "unable to claim gpio \"watchdog\"\n"); return ret; } - ltc2952_data->gpio_kill = devm_gpiod_get(&pdev->dev, "kill", - GPIOD_OUT_LOW); - if (IS_ERR(ltc2952_data->gpio_kill)) { - ret = PTR_ERR(ltc2952_data->gpio_kill); + data->gpio_kill = devm_gpiod_get(&pdev->dev, "kill", GPIOD_OUT_LOW); + if (IS_ERR(data->gpio_kill)) { + ret = PTR_ERR(data->gpio_kill); dev_err(&pdev->dev, "unable to claim gpio \"kill\"\n"); return ret; } - ltc2952_data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", - GPIOD_IN); + data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", + GPIOD_IN); if (IS_ERR(ltc2952_data->gpio_trigger)) { ret = PTR_ERR(ltc2952_data->gpio_trigger); dev_err(&pdev->dev, "unable to claim gpio \"trigger\"\n"); return ret; } - virq = gpiod_to_irq(ltc2952_data->gpio_trigger); + virq = gpiod_to_irq(data->gpio_trigger); if (virq < 0) { dev_err(&pdev->dev, "cannot map GPIO as interrupt"); return ret; @@ -241,7 +245,7 @@ static int ltc2952_poweroff_init(struct platform_device *pdev) ltc2952_poweroff_handler, (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), "ltc2952-poweroff", - ltc2952_data); + data); if (ret) { dev_err(&pdev->dev, "cannot configure an interrupt handler\n"); @@ -254,23 +258,26 @@ static int ltc2952_poweroff_init(struct platform_device *pdev) static int ltc2952_poweroff_probe(struct platform_device *pdev) { int ret; + struct ltc2952_poweroff *data; if (pm_power_off) { dev_err(&pdev->dev, "pm_power_off already registered"); return -EBUSY; } - ltc2952_data = devm_kzalloc(&pdev->dev, sizeof(*ltc2952_data), - GFP_KERNEL); - if (!ltc2952_data) + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) return -ENOMEM; - ltc2952_data->dev = &pdev->dev; + data->dev = &pdev->dev; + platform_set_drvdata(pdev, data); ret = ltc2952_poweroff_init(pdev); if (ret) return ret; + /* TODO: remove ltc2952_data */ + ltc2952_data = data; pm_power_off = <c2952_poweroff_kill; dev_info(&pdev->dev, "probe successful\n"); -- cgit v1.2.3 From 818ca4c8c7be4910f896c702645009f09813347f Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:39 +0100 Subject: power: reset: ltc2952: remove global variable poweroff_panic As per Documentation/CodingStyle ch.4, we should keep global variables to a mininum. Move the panic state into the driver data, regardless of whether panic is a system state or not. This removes the need for the custom _init and _exit functions, so replace them with a call to the module_platform_driver() macro. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 57 +++++++++++++--------------------- 1 file changed, 22 insertions(+), 35 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 83042691cd79..8c936ed555c1 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -75,6 +75,9 @@ struct ltc2952_poweroff { struct gpio_desc *gpio_trigger; struct gpio_desc *gpio_watchdog; struct gpio_desc *gpio_kill; + + bool kernel_panic; + struct notifier_block panic_notifier; }; #define to_ltc2952(p, m) container_of(p, struct ltc2952_poweroff, m) @@ -84,7 +87,6 @@ struct ltc2952_poweroff { * remove it entirely once we don't need the global state anymore. */ static struct ltc2952_poweroff *ltc2952_data; -static int ltc2952_poweroff_panic; /** * ltc2952_poweroff_timer_wde - Timer callback @@ -102,7 +104,7 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) unsigned long overruns; struct ltc2952_poweroff *data = to_ltc2952(timer, timer_wde); - if (ltc2952_poweroff_panic) + if (data->kernel_panic) return HRTIMER_NORESTART; state = gpiod_get_value(data->gpio_watchdog); @@ -154,7 +156,7 @@ static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id) int ret; struct ltc2952_poweroff *data = dev_id; - if (ltc2952_poweroff_panic) + if (data->kernel_panic) goto irq_ok; if (hrtimer_active(&data->timer_wde)) { @@ -255,6 +257,15 @@ static int ltc2952_poweroff_init(struct platform_device *pdev) return 0; } +static int ltc2952_poweroff_notify_panic(struct notifier_block *nb, + unsigned long code, void *unused) +{ + struct ltc2952_poweroff *data = to_ltc2952(nb, panic_notifier); + + data->kernel_panic = true; + return NOTIFY_DONE; +} + static int ltc2952_poweroff_probe(struct platform_device *pdev) { int ret; @@ -280,6 +291,9 @@ static int ltc2952_poweroff_probe(struct platform_device *pdev) ltc2952_data = data; pm_power_off = <c2952_poweroff_kill; + data->panic_notifier.notifier_call = ltc2952_poweroff_notify_panic; + atomic_notifier_chain_register(&panic_notifier_list, + &data->panic_notifier); dev_info(&pdev->dev, "probe successful\n"); return 0; @@ -287,8 +301,11 @@ static int ltc2952_poweroff_probe(struct platform_device *pdev) static int ltc2952_poweroff_remove(struct platform_device *pdev) { - pm_power_off = NULL; + struct ltc2952_poweroff *data = platform_get_drvdata(pdev); + pm_power_off = NULL; + atomic_notifier_chain_unregister(&panic_notifier_list, + &data->panic_notifier); return 0; } @@ -309,37 +326,7 @@ static struct platform_driver ltc2952_poweroff_driver = { .resume = ltc2952_poweroff_resume, }; -static int ltc2952_poweroff_notify_panic(struct notifier_block *nb, - unsigned long code, void *unused) -{ - ltc2952_poweroff_panic = 1; - return NOTIFY_DONE; -} - -static struct notifier_block ltc2952_poweroff_panic_nb = { - .notifier_call = ltc2952_poweroff_notify_panic, -}; - -static int __init ltc2952_poweroff_platform_init(void) -{ - ltc2952_poweroff_panic = 0; - - atomic_notifier_chain_register(&panic_notifier_list, - <c2952_poweroff_panic_nb); - - return platform_driver_register(<c2952_poweroff_driver); -} - -static void __exit ltc2952_poweroff_platform_exit(void) -{ - atomic_notifier_chain_unregister(&panic_notifier_list, - <c2952_poweroff_panic_nb); - - platform_driver_unregister(<c2952_poweroff_driver); -} - -module_init(ltc2952_poweroff_platform_init); -module_exit(ltc2952_poweroff_platform_exit); +module_platform_driver(ltc2952_poweroff_driver); MODULE_AUTHOR("René Moll "); MODULE_DESCRIPTION("LTC PowerPath power-off driver"); -- cgit v1.2.3 From 0428c40d4c448f5dc63b0f76de14fa0affa7aa2f Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:40 +0100 Subject: power: reset: ltc2952: drop empty suspend/resume functions Documentation/SubmittingDrivers suggests these be implemented even when they do nothing. On the other hand, the platform code calls these functions 'legacy'. Suspend and resume operations should go into a pm_ops structure, pointed at by the driver's pm field. This approach would lead to a lot of boiler plate, while achieving nothing. Drop the functions instead. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 8c936ed555c1..d29948762ceb 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -184,17 +184,6 @@ static void ltc2952_poweroff_kill(void) gpiod_set_value(ltc2952_data->gpio_kill, 1); } -static int ltc2952_poweroff_suspend(struct platform_device *pdev, - pm_message_t state) -{ - return -ENOSYS; -} - -static int ltc2952_poweroff_resume(struct platform_device *pdev) -{ - return -ENOSYS; -} - static void ltc2952_poweroff_default(struct ltc2952_poweroff *data) { data->wde_interval = ktime_set(0, 300L*1E6L); @@ -322,8 +311,6 @@ static struct platform_driver ltc2952_poweroff_driver = { .name = "ltc2952-poweroff", .of_match_table = of_ltc2952_poweroff_match, }, - .suspend = ltc2952_poweroff_suspend, - .resume = ltc2952_poweroff_resume, }; module_platform_driver(ltc2952_poweroff_driver); -- cgit v1.2.3 From 5c3faad29b266da015e3726cff6556df5c3cd9f5 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:41 +0100 Subject: power: reset: ltc2952: cleanup control flow in poweroff_handler ltc2952_poweroff_handler uses gotos to return from the function. Since we don't do cleanups exiting this function, just return IRQ_HANDLED on the spot and be done with it. While at it, remove the variable 'ret'. It was never used very much. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index d29948762ceb..736af3961019 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -153,29 +153,21 @@ ltc2952_poweroff_timer_trigger(struct hrtimer *timer) */ static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id) { - int ret; struct ltc2952_poweroff *data = dev_id; - if (data->kernel_panic) - goto irq_ok; - - if (hrtimer_active(&data->timer_wde)) { + if (data->kernel_panic || hrtimer_active(&data->timer_wde)) { /* shutdown is already triggered, nothing to do any more */ - goto irq_ok; + return IRQ_HANDLED; } if (!hrtimer_active(&data->timer_trigger)) { - ret = hrtimer_start(&data->timer_trigger, data->trigger_delay, - HRTIMER_MODE_REL); - - if (ret) + if (hrtimer_start(&data->timer_trigger, data->trigger_delay, + HRTIMER_MODE_REL)) dev_err(data->dev, "unable to start the wait timer\n"); } else { - ret = hrtimer_cancel(&data->timer_trigger); + hrtimer_cancel(&data->timer_trigger); /* omitting return value check, timer should have been valid */ } - -irq_ok: return IRQ_HANDLED; } -- cgit v1.2.3 From 5689b786f77308e7aa10fd736e87b270ae42026e Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:42 +0100 Subject: power: reset: ltc2952: fix C++ style function pointers The function pointers for the timers and pm_power_off are assigned with C++ style foo = &func; Let's change it instead to the more C style foo = func; Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 736af3961019..8e04b3299f9a 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -182,10 +182,10 @@ static void ltc2952_poweroff_default(struct ltc2952_poweroff *data) data->trigger_delay = ktime_set(2, 500L*1E6L); hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - data->timer_trigger.function = <c2952_poweroff_timer_trigger; + data->timer_trigger.function = ltc2952_poweroff_timer_trigger; hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - data->timer_wde.function = <c2952_poweroff_timer_wde; + data->timer_wde.function = ltc2952_poweroff_timer_wde; } static int ltc2952_poweroff_init(struct platform_device *pdev) @@ -270,7 +270,7 @@ static int ltc2952_poweroff_probe(struct platform_device *pdev) /* TODO: remove ltc2952_data */ ltc2952_data = data; - pm_power_off = <c2952_poweroff_kill; + pm_power_off = ltc2952_poweroff_kill; data->panic_notifier.notifier_call = ltc2952_poweroff_notify_panic; atomic_notifier_chain_register(&panic_notifier_list, -- cgit v1.2.3 From 2f6ea8ad738e7a9be1a675a4404606723797cc16 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:43 +0100 Subject: power: reset: ltc2952: disable timers in _remove Disable the timers when ltc2952_poweroff is removed. We don't want to risk calling functions on data that no longer exist. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 8e04b3299f9a..240b294a0980 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -285,6 +285,8 @@ static int ltc2952_poweroff_remove(struct platform_device *pdev) struct ltc2952_poweroff *data = platform_get_drvdata(pdev); pm_power_off = NULL; + hrtimer_cancel(&data->timer_trigger); + hrtimer_cancel(&data->timer_wde); atomic_notifier_chain_unregister(&panic_notifier_list, &data->panic_notifier); return 0; -- cgit v1.2.3 From c1ada2ff8045eacc11a3b894f2056aa5457b17b5 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:44 +0100 Subject: power: reset: ltc2952: check trigger value before starting timer In ltc2952_poweroff_handler it is theoretically possible that the timer fails to start on first pass (button press), but succeeds in starting on the second (button release). This will cause the button press to be misinterpreted, and will incorrectly shut down the system. Because a picture says more than a thousand words: Expected behavior: tmr: ++++++++++ btn: -----__________----- Faulty behavior: tmr: +++++ btn: -----__________----- Legend: + timer runs _ button pressed - button depressed To prevent this from happening, check the value of the gpio before starting the timer. If the button is active, we should start the timer, else we should stop it. The situation described can now still occur if the polarity of the input pin is set incorrectly, but that at least is predictable behavior and can be detected during the first tests. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 240b294a0980..b99bc251f5b4 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -160,7 +160,7 @@ static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id) return IRQ_HANDLED; } - if (!hrtimer_active(&data->timer_trigger)) { + if (gpiod_get_value(data->gpio_trigger)) { if (hrtimer_start(&data->timer_trigger, data->trigger_delay, HRTIMER_MODE_REL)) dev_err(data->dev, "unable to start the wait timer\n"); -- cgit v1.2.3 From ee18185331ded6bbcf1aa59b9a81545c1ff5eecd Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 14 Jan 2015 09:15:45 +0100 Subject: power: reset: ltc2952: make trigger input optional Currently the ltc2952 supports only one button sequence to initiate powerdown. This is not always desirable, as even prolonged button presses can happen in use. Allow ltc2952 users to pick their own power down sequence, by making the trigger input optional. Since this still means that the ltc2952 may power down the platform if the power button is pressed for about 5 seconds, we still need to make sure to start the watchdog toggle to prolong the system power for as long as we need it. This will still allow the system to control power using the kill signal. Signed-off-by: Frans Klaver Signed-off-by: Sebastian Reichel --- drivers/power/reset/ltc2952-poweroff.c | 87 +++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 32 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index b99bc251f5b4..7ef193b6f7fe 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -32,7 +32,9 @@ * - trigger (input) * A level change indicates the shut-down trigger. If it's state reverts * within the time-out defined by trigger_delay, the shut down is not - * executed. + * executed. If no pin is assigned to this input, the driver will start the + * watchdog toggle immediately. The chip will only power off the system if + * it is requested to do so through the kill line. * * - watchdog (output) * Once a shut down is triggered, the driver will toggle this signal, @@ -116,15 +118,10 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) return HRTIMER_RESTART; } -static enum hrtimer_restart -ltc2952_poweroff_timer_trigger(struct hrtimer *timer) +static void ltc2952_poweroff_start_wde(struct ltc2952_poweroff *data) { - struct ltc2952_poweroff *data = to_ltc2952(timer, timer_trigger); - int ret = hrtimer_start(&data->timer_wde, - data->wde_interval, HRTIMER_MODE_REL); - - if (ret) { - dev_err(data->dev, "unable to start the timer\n"); + if (hrtimer_start(&data->timer_wde, data->wde_interval, + HRTIMER_MODE_REL)) { /* * The device will not toggle the watchdog reset, * thus shut down is only safe if the PowerPath controller @@ -133,10 +130,17 @@ ltc2952_poweroff_timer_trigger(struct hrtimer *timer) * * Only sending a warning as the system will power-off anyway */ + dev_err(data->dev, "unable to start the timer\n"); } +} - dev_info(data->dev, "executing shutdown\n"); +static enum hrtimer_restart +ltc2952_poweroff_timer_trigger(struct hrtimer *timer) +{ + struct ltc2952_poweroff *data = to_ltc2952(timer, timer_trigger); + ltc2952_poweroff_start_wde(data); + dev_info(data->dev, "executing shutdown\n"); orderly_poweroff(true); return HRTIMER_NORESTART; @@ -190,7 +194,7 @@ static void ltc2952_poweroff_default(struct ltc2952_poweroff *data) static int ltc2952_poweroff_init(struct platform_device *pdev) { - int ret, virq; + int ret; struct ltc2952_poweroff *data = platform_get_drvdata(pdev); ltc2952_poweroff_default(data); @@ -210,29 +214,48 @@ static int ltc2952_poweroff_init(struct platform_device *pdev) return ret; } - data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", - GPIOD_IN); - if (IS_ERR(ltc2952_data->gpio_trigger)) { - ret = PTR_ERR(ltc2952_data->gpio_trigger); - dev_err(&pdev->dev, "unable to claim gpio \"trigger\"\n"); - return ret; - } - - virq = gpiod_to_irq(data->gpio_trigger); - if (virq < 0) { - dev_err(&pdev->dev, "cannot map GPIO as interrupt"); - return ret; + data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", GPIOD_IN); + if (IS_ERR(data->gpio_trigger)) { + /* + * It's not a problem if the trigger gpio isn't available, but + * it is worth a warning if its use was defined in the device + * tree. + */ + if (PTR_ERR(data->gpio_trigger) != -ENOENT) + dev_err(&pdev->dev, + "unable to claim gpio \"trigger\"\n"); + data->gpio_trigger = NULL; } - ret = devm_request_irq(&pdev->dev, virq, - ltc2952_poweroff_handler, - (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), - "ltc2952-poweroff", - data); - - if (ret) { - dev_err(&pdev->dev, "cannot configure an interrupt handler\n"); - return ret; + if (devm_request_irq(&pdev->dev, gpiod_to_irq(data->gpio_trigger), + ltc2952_poweroff_handler, + (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), + "ltc2952-poweroff", + data)) { + /* + * Some things may have happened: + * - No trigger input was defined + * - Claiming the GPIO failed + * - We could not map to an IRQ + * - We couldn't register an interrupt handler + * + * None of these really are problems, but all of them + * disqualify the push button from controlling the power. + * + * It is therefore important to note that if the ltc2952 + * detects a button press for long enough, it will still start + * its own powerdown window and cut the power on us if we don't + * start the watchdog trigger. + */ + if (data->gpio_trigger) { + dev_warn(&pdev->dev, + "unable to configure the trigger interrupt\n"); + devm_gpiod_put(&pdev->dev, data->gpio_trigger); + data->gpio_trigger = NULL; + } + dev_info(&pdev->dev, + "power down trigger input will not be used\n"); + ltc2952_poweroff_start_wde(data); } return 0; -- cgit v1.2.3 From b847dd96e659c13be9d94c3c19b51cdf0feb1c99 Mon Sep 17 00:00:00 2001 From: Beomho Seo Date: Fri, 9 Jan 2015 17:45:11 +0900 Subject: power: rt5033_battery: Add RT5033 Fuel gauge device driver This patch adds device driver of Richtek PMIC. The driver support battery fuel gauge. Fuel gauge calculates and determines the battery state of charge (SOC) according to battery open circuit voltage (OCV). Also, this driver provides battery average voltage, voltage and battery present property. Cc: Sebastian Reichel Cc: Dmitry Eremin-Solenikov Cc: David Woodhouse Signed-off-by: Beomho Seo Acked-by: Chanwoo Choi Signed-off-by: Sebastian Reichel --- drivers/power/Kconfig | 8 ++ drivers/power/Makefile | 1 + drivers/power/rt5033_battery.c | 177 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 drivers/power/rt5033_battery.c (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 0108c2af005b..da6981f92697 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -397,6 +397,14 @@ config BATTERY_GOLDFISH Say Y to enable support for the battery and AC power in the Goldfish emulator. +config BATTERY_RT5033 + tristate "RT5033 fuel gauge support" + depends on MFD_RT5033 + help + This adds support for battery fuel gauge in Richtek RT5033 PMIC. + The fuelgauge calculates and determines the battery state of charge + according to battery open circuit voltage. + source "drivers/power/reset/Kconfig" endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index dfa894273926..b83a0c749781 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o obj-$(CONFIG_BATTERY_Z2) += z2_battery.o +obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o diff --git a/drivers/power/rt5033_battery.c b/drivers/power/rt5033_battery.c new file mode 100644 index 000000000000..7b898f41c595 --- /dev/null +++ b/drivers/power/rt5033_battery.c @@ -0,0 +1,177 @@ +/* + * Fuel gauge driver for Richtek RT5033 + * + * Copyright (C) 2014 Samsung Electronics, Co., Ltd. + * Author: Beomho Seo + * + * 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 bythe Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +static int rt5033_battery_get_capacity(struct i2c_client *client) +{ + struct rt5033_battery *battery = i2c_get_clientdata(client); + u32 msb; + + regmap_read(battery->regmap, RT5033_FUEL_REG_SOC_H, &msb); + + return msb; +} + +static int rt5033_battery_get_present(struct i2c_client *client) +{ + struct rt5033_battery *battery = i2c_get_clientdata(client); + u32 val; + + regmap_read(battery->regmap, RT5033_FUEL_REG_CONFIG_L, &val); + + return (val & RT5033_FUEL_BAT_PRESENT) ? true : false; +} + +static int rt5033_battery_get_watt_prop(struct i2c_client *client, + enum power_supply_property psp) +{ + struct rt5033_battery *battery = i2c_get_clientdata(client); + unsigned int regh, regl; + int ret; + u32 msb, lsb; + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + regh = RT5033_FUEL_REG_VBAT_H; + regl = RT5033_FUEL_REG_VBAT_L; + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + regh = RT5033_FUEL_REG_AVG_VOLT_H; + regl = RT5033_FUEL_REG_AVG_VOLT_L; + break; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + regh = RT5033_FUEL_REG_OCV_H; + regl = RT5033_FUEL_REG_OCV_L; + break; + default: + return -EINVAL; + } + + regmap_read(battery->regmap, regh, &msb); + regmap_read(battery->regmap, regl, &lsb); + + ret = ((msb << 4) + (lsb >> 4)) * 1250 / 1000; + + return ret; +} + +static int rt5033_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rt5033_battery *battery = container_of(psy, + struct rt5033_battery, psy); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + val->intval = rt5033_battery_get_watt_prop(battery->client, + psp); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = rt5033_battery_get_present(battery->client); + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = rt5033_battery_get_capacity(battery->client); + break; + default: + return -EINVAL; + } + return 0; +} + +static enum power_supply_property rt5033_battery_props[] = { + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static struct regmap_config rt5033_battery_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RT5033_FUEL_REG_END, +}; + +static int rt5033_battery_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct rt5033_battery *battery; + u32 ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + return -EIO; + + battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL); + if (!battery) + return -EINVAL; + + battery->client = client; + battery->regmap = devm_regmap_init_i2c(client, + &rt5033_battery_regmap_config); + if (IS_ERR(battery->regmap)) { + dev_err(&client->dev, "Failed to initialize regmap\n"); + return -EINVAL; + } + + i2c_set_clientdata(client, battery); + + battery->psy.name = "rt5033-battery"; + battery->psy.type = POWER_SUPPLY_TYPE_BATTERY; + battery->psy.get_property = rt5033_battery_get_property; + battery->psy.properties = rt5033_battery_props; + battery->psy.num_properties = ARRAY_SIZE(rt5033_battery_props); + + ret = power_supply_register(&client->dev, &battery->psy); + if (ret) { + dev_err(&client->dev, "Failed to register power supply\n"); + return ret; + } + + return 0; +} + +static int rt5033_battery_remove(struct i2c_client *client) +{ + struct rt5033_battery *battery = i2c_get_clientdata(client); + + power_supply_unregister(&battery->psy); + + return 0; +} + +static const struct i2c_device_id rt5033_battery_id[] = { + { "rt5033-battery", }, + { } +}; +MODULE_DEVICE_TABLE(platform, rt5033_battery_id); + +static struct i2c_driver rt5033_battery_driver = { + .driver = { + .name = "rt5033-battery", + }, + .probe = rt5033_battery_probe, + .remove = rt5033_battery_remove, + .id_table = rt5033_battery_id, +}; +module_i2c_driver(rt5033_battery_driver); + +MODULE_DESCRIPTION("Richtek RT5033 fuel gauge driver"); +MODULE_AUTHOR("Beomho Seo "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From faeed51bb65ce0241052d8dc24ac331ade12e976 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 15 Jan 2015 05:00:37 +0300 Subject: power: gpio-charger: balance enable/disable_irq_wake calls enable_irq_wakeup returns 0 in case it correctly enabled the IRQ to generate the wakeup event (and thus resume should call disable_irq_wake). Currently gpio-charger driver has this logic inverted. Correct that thus correcting enable/disable_irq_wake() calls balance. Cc: stable@vger.kernel.org Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Sebastian Reichel --- drivers/power/gpio-charger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index aef74bdf7ab3..b7424c8501f1 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -229,7 +229,7 @@ static int gpio_charger_suspend(struct device *dev) if (device_may_wakeup(dev)) gpio_charger->wakeup_enabled = - enable_irq_wake(gpio_charger->irq); + !enable_irq_wake(gpio_charger->irq); return 0; } @@ -239,7 +239,7 @@ static int gpio_charger_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); - if (gpio_charger->wakeup_enabled) + if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled) disable_irq_wake(gpio_charger->irq); power_supply_changed(&gpio_charger->charger); -- cgit v1.2.3 From f1300e7f6211139da9bc1ebf685f244fbd0ee58d Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 15 Jan 2015 05:00:38 +0300 Subject: power: collie_battery: support generating wakeup event Teach collie_battery driver to communicate to the kernel that it can generate wakeup events. Handle enabling/disabling wakeup on battery full event in suspend/resume callbacks. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Sebastian Reichel --- drivers/power/collie_battery.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c index d02ae02a7590..594e4dbc2d51 100644 --- a/drivers/power/collie_battery.c +++ b/drivers/power/collie_battery.c @@ -26,6 +26,7 @@ static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ static struct work_struct bat_work; static struct ucb1x00 *ucb; +static int wakeup_enabled; struct collie_bat { int status; @@ -291,11 +292,21 @@ static int collie_bat_suspend(struct ucb1x00_dev *dev) { /* flush all pending status updates */ flush_work(&bat_work); + + if (device_may_wakeup(&dev->ucb->dev) && + collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING) + wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO)); + else + wakeup_enabled = 0; + return 0; } static int collie_bat_resume(struct ucb1x00_dev *dev) { + if (wakeup_enabled) + disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO)); + /* things may have changed while we were away */ schedule_work(&bat_work); return 0; @@ -334,10 +345,15 @@ static int collie_bat_probe(struct ucb1x00_dev *dev) collie_bat_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "main full", &collie_bat_main); - if (!ret) { - schedule_work(&bat_work); - return 0; - } + if (ret) + goto err_irq; + + device_init_wakeup(&ucb->dev, 1); + schedule_work(&bat_work); + + return 0; + +err_irq: power_supply_unregister(&collie_bat_bu.psy); err_psy_reg_bu: power_supply_unregister(&collie_bat_main.psy); -- cgit v1.2.3 From 478913fdbdfd4a781d91c993eb86838620fe7421 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 09:51:48 +0100 Subject: power: bq24190: Fix ignored supplicants The driver mismatched 'num_supplicants' with 'num_supplies' of power_supply structure. It provided list of supplicants (power_supply.supplied_to) but did not set the number of supplicants. Instead it set the num_supplies which is used when iterating over number of supplies (power_supply.supplied_from). As a result the list of supplicants was ignored by core because its size was 0. Signed-off-by: Krzysztof Kozlowski Cc: Fixes: d7bf353fd0aa ("bq24190_charger: Add support for TI BQ24190 Battery Charger") Signed-off-by: Sebastian Reichel --- drivers/power/bq24190_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c index ad3ff8fbfbbb..e4c95e1a6733 100644 --- a/drivers/power/bq24190_charger.c +++ b/drivers/power/bq24190_charger.c @@ -929,7 +929,7 @@ static void bq24190_charger_init(struct power_supply *charger) charger->properties = bq24190_charger_properties; charger->num_properties = ARRAY_SIZE(bq24190_charger_properties); charger->supplied_to = bq24190_charger_supplied_to; - charger->num_supplies = ARRAY_SIZE(bq24190_charger_supplied_to); + charger->num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to); charger->get_property = bq24190_charger_get_property; charger->set_property = bq24190_charger_set_property; charger->property_is_writeable = bq24190_charger_property_is_writeable; -- cgit v1.2.3 From e02912855f4dcb01655cac75f429035fc84fb693 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 09:50:17 +0100 Subject: power: max17042: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/max17042_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 66da691c41cf..1da6c5fbdff5 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -658,7 +658,7 @@ max17042_get_pdata(struct device *dev) } #endif -static struct regmap_config max17042_regmap_config = { +static const struct regmap_config max17042_regmap_config = { .reg_bits = 8, .val_bits = 16, .val_format_endian = REGMAP_ENDIAN_NATIVE, -- cgit v1.2.3 From 87c2d9067893cd566dba4a875aff39bc47e6ba2a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 20 Jan 2015 11:00:54 +0100 Subject: power: max77693: Add charger driver for Maxim 77693 Add new driver for Maxim 77693 switch-mode charger (part of max77693 MFD driver) providing power supply class information to userspace. The charger has +20V tolerant input. Current input can be set from 0 to 2.58 A. The charger can deliver up to 2.1 A to the battery or 3.5 A to the system (when supplying additional current from battery to system). The driver is configured through DTS (battery and system related settings) and sysfs entries (timers and top-off charging threshold). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/Kconfig | 6 + drivers/power/Makefile | 1 + drivers/power/max77693_charger.c | 758 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 765 insertions(+) create mode 100644 drivers/power/max77693_charger.c (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index da6981f92697..110d4bc03483 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -332,6 +332,12 @@ config CHARGER_MAX14577 Say Y to enable support for the battery charger control sysfs and platform data of MAX14577/77836 MUICs. +config CHARGER_MAX77693 + tristate "Maxim MAX77693 battery charger driver" + depends on MFD_MAX77693 + help + Say Y to enable support for the Maxim MAX77693 battery charger. + config CHARGER_MAX8997 tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver" depends on MFD_MAX8997 && REGULATOR_MAX8997 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b83a0c749781..31216cb7e8a1 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o +obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o diff --git a/drivers/power/max77693_charger.c b/drivers/power/max77693_charger.c new file mode 100644 index 000000000000..56cf2177aad4 --- /dev/null +++ b/drivers/power/max77693_charger.c @@ -0,0 +1,758 @@ +/* + * max77693_charger.c - Battery charger driver for the Maxim 77693 + * + * Copyright (C) 2014 Samsung Electronics + * Krzysztof Kozlowski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +static const char *max77693_charger_name = "max77693-charger"; +static const char *max77693_charger_model = "MAX77693"; +static const char *max77693_charger_manufacturer = "Maxim Integrated"; + +struct max77693_charger { + struct device *dev; + struct max77693_dev *max77693; + struct power_supply charger; + + u32 constant_volt; + u32 min_system_volt; + u32 thermal_regulation_temp; + u32 batttery_overcurrent; + u32 charge_input_threshold_volt; +}; + +static int max77693_get_charger_state(struct regmap *regmap) +{ + int state; + unsigned int data; + + if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0) + return POWER_SUPPLY_STATUS_UNKNOWN; + + data &= CHG_DETAILS_01_CHG_MASK; + data >>= CHG_DETAILS_01_CHG_SHIFT; + + switch (data) { + case MAX77693_CHARGING_PREQUALIFICATION: + case MAX77693_CHARGING_FAST_CONST_CURRENT: + case MAX77693_CHARGING_FAST_CONST_VOLTAGE: + case MAX77693_CHARGING_TOP_OFF: + /* In high temp the charging current is reduced, but still charging */ + case MAX77693_CHARGING_HIGH_TEMP: + state = POWER_SUPPLY_STATUS_CHARGING; + break; + case MAX77693_CHARGING_DONE: + state = POWER_SUPPLY_STATUS_FULL; + break; + case MAX77693_CHARGING_TIMER_EXPIRED: + case MAX77693_CHARGING_THERMISTOR_SUSPEND: + state = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case MAX77693_CHARGING_OFF: + case MAX77693_CHARGING_OVER_TEMP: + case MAX77693_CHARGING_WATCHDOG_EXPIRED: + state = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case MAX77693_CHARGING_RESERVED: + default: + state = POWER_SUPPLY_STATUS_UNKNOWN; + } + + return state; +} + +static int max77693_get_charge_type(struct regmap *regmap) +{ + int state; + unsigned int data; + + if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0) + return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + + data &= CHG_DETAILS_01_CHG_MASK; + data >>= CHG_DETAILS_01_CHG_SHIFT; + + switch (data) { + case MAX77693_CHARGING_PREQUALIFICATION: + /* + * Top-off: trickle or fast? In top-off the current varies between + * 100 and 250 mA. It is higher than prequalification current. + */ + case MAX77693_CHARGING_TOP_OFF: + state = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case MAX77693_CHARGING_FAST_CONST_CURRENT: + case MAX77693_CHARGING_FAST_CONST_VOLTAGE: + /* In high temp the charging current is reduced, but still charging */ + case MAX77693_CHARGING_HIGH_TEMP: + state = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case MAX77693_CHARGING_DONE: + case MAX77693_CHARGING_TIMER_EXPIRED: + case MAX77693_CHARGING_THERMISTOR_SUSPEND: + case MAX77693_CHARGING_OFF: + case MAX77693_CHARGING_OVER_TEMP: + case MAX77693_CHARGING_WATCHDOG_EXPIRED: + state = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + case MAX77693_CHARGING_RESERVED: + default: + state = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + + return state; +} + +/* + * Supported health statuses: + * - POWER_SUPPLY_HEALTH_DEAD + * - POWER_SUPPLY_HEALTH_GOOD + * - POWER_SUPPLY_HEALTH_OVERVOLTAGE + * - POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE + * - POWER_SUPPLY_HEALTH_UNKNOWN + * - POWER_SUPPLY_HEALTH_UNSPEC_FAILURE + */ +static int max77693_get_battery_health(struct regmap *regmap) +{ + int state; + unsigned int data; + + if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0) + return POWER_SUPPLY_HEALTH_UNKNOWN; + + data &= CHG_DETAILS_01_BAT_MASK; + data >>= CHG_DETAILS_01_BAT_SHIFT; + + switch (data) { + case MAX77693_BATTERY_NOBAT: + state = POWER_SUPPLY_HEALTH_DEAD; + break; + case MAX77693_BATTERY_PREQUALIFICATION: + case MAX77693_BATTERY_GOOD: + case MAX77693_BATTERY_LOWVOLTAGE: + state = POWER_SUPPLY_HEALTH_GOOD; + break; + case MAX77693_BATTERY_TIMER_EXPIRED: + /* + * Took longer to charge than expected, charging suspended. + * Damaged battery? + */ + state = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + break; + case MAX77693_BATTERY_OVERVOLTAGE: + state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + break; + case MAX77693_BATTERY_OVERCURRENT: + state = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + case MAX77693_BATTERY_RESERVED: + default: + state = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + + return state; +} + +static int max77693_get_present(struct regmap *regmap) +{ + unsigned int data; + + /* + * Read CHG_INT_OK register. High DETBAT bit here should be + * equal to value 0x0 in CHG_DETAILS_01/BAT field. + */ + regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); + if (data & CHG_INT_OK_DETBAT_MASK) + return 0; + return 1; +} + +static int max77693_get_online(struct regmap *regmap) +{ + unsigned int data; + + regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); + if (data & CHG_INT_OK_CHGIN_MASK) + return 1; + return 0; +} + +static enum power_supply_property max77693_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static int max77693_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max77693_charger *chg = container_of(psy, + struct max77693_charger, + charger); + struct regmap *regmap = chg->max77693->regmap; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = max77693_get_charger_state(regmap); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = max77693_get_charge_type(regmap); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = max77693_get_battery_health(regmap); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = max77693_get_present(regmap); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = max77693_get_online(regmap); + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = max77693_charger_model; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = max77693_charger_manufacturer; + break; + default: + return -EINVAL; + } + + return ret; +} + +static ssize_t device_attr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count, + int (*fn)(struct max77693_charger *, unsigned long)) +{ + struct max77693_charger *chg = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + ret = fn(chg, val); + if (ret) + return ret; + + return count; +} + +static ssize_t fast_charge_timer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct max77693_charger *chg = dev_get_drvdata(dev); + unsigned int data, val; + int ret; + + ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_01, + &data); + if (ret < 0) + return ret; + + data &= CHG_CNFG_01_FCHGTIME_MASK; + data >>= CHG_CNFG_01_FCHGTIME_SHIFT; + switch (data) { + case 0x1 ... 0x7: + /* Starting from 4 hours, step by 2 hours */ + val = 4 + (data - 1) * 2; + break; + case 0x0: + default: + val = 0; + break; + } + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static int max77693_set_fast_charge_timer(struct max77693_charger *chg, + unsigned long hours) +{ + unsigned int data; + + /* + * 0x00 - disable + * 0x01 - 4h + * 0x02 - 6h + * ... + * 0x07 - 16h + * Round down odd values. + */ + switch (hours) { + case 4 ... 16: + data = (hours - 4) / 2 + 1; + break; + case 0: + /* Disable */ + data = 0; + break; + default: + return -EINVAL; + } + data <<= CHG_CNFG_01_FCHGTIME_SHIFT; + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_01, + CHG_CNFG_01_FCHGTIME_MASK, data); +} + +static ssize_t fast_charge_timer_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return device_attr_store(dev, attr, buf, count, + max77693_set_fast_charge_timer); +} + +static ssize_t top_off_threshold_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct max77693_charger *chg = dev_get_drvdata(dev); + unsigned int data, val; + int ret; + + ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03, + &data); + if (ret < 0) + return ret; + + data &= CHG_CNFG_03_TOITH_MASK; + data >>= CHG_CNFG_03_TOITH_SHIFT; + + if (data <= 0x04) + val = 100000 + data * 25000; + else + val = data * 50000; + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static int max77693_set_top_off_threshold_current(struct max77693_charger *chg, + unsigned long uamp) +{ + unsigned int data; + + if (uamp < 100000 || uamp > 350000) + return -EINVAL; + + if (uamp <= 200000) + data = (uamp - 100000) / 25000; + else + /* (200000, 350000> */ + data = uamp / 50000; + + data <<= CHG_CNFG_03_TOITH_SHIFT; + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_03, + CHG_CNFG_03_TOITH_MASK, data); +} + +static ssize_t top_off_threshold_current_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return device_attr_store(dev, attr, buf, count, + max77693_set_top_off_threshold_current); +} + +static ssize_t top_off_timer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct max77693_charger *chg = dev_get_drvdata(dev); + unsigned int data, val; + int ret; + + ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03, + &data); + if (ret < 0) + return ret; + + data &= CHG_CNFG_03_TOTIME_MASK; + data >>= CHG_CNFG_03_TOTIME_SHIFT; + + val = data * 10; + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static int max77693_set_top_off_timer(struct max77693_charger *chg, + unsigned long minutes) +{ + unsigned int data; + + if (minutes > 70) + return -EINVAL; + + data = minutes / 10; + data <<= CHG_CNFG_03_TOTIME_SHIFT; + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_03, + CHG_CNFG_03_TOTIME_MASK, data); +} + +static ssize_t top_off_timer_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return device_attr_store(dev, attr, buf, count, + max77693_set_top_off_timer); +} + +static DEVICE_ATTR_RW(fast_charge_timer); +static DEVICE_ATTR_RW(top_off_threshold_current); +static DEVICE_ATTR_RW(top_off_timer); + +static int max77693_set_constant_volt(struct max77693_charger *chg, + unsigned int uvolt) +{ + unsigned int data; + + /* + * 0x00 - 3.650 V + * 0x01 - 3.675 V + * ... + * 0x1b - 4.325 V + * 0x1c - 4.340 V + * 0x1d - 4.350 V + * 0x1e - 4.375 V + * 0x1f - 4.400 V + */ + if (uvolt >= 3650000 && uvolt < 4340000) + data = (uvolt - 3650000) / 25000; + else if (uvolt >= 4340000 && uvolt < 4350000) + data = 0x1c; + else if (uvolt >= 4350000 && uvolt <= 4400000) + data = 0x1d + (uvolt - 4350000) / 25000; + else { + dev_err(chg->dev, "Wrong value for charging constant voltage\n"); + return -EINVAL; + } + + data <<= CHG_CNFG_04_CHGCVPRM_SHIFT; + + dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt, + data); + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_04, + CHG_CNFG_04_CHGCVPRM_MASK, data); +} + +static int max77693_set_min_system_volt(struct max77693_charger *chg, + unsigned int uvolt) +{ + unsigned int data; + + if (uvolt < 3000000 || uvolt > 3700000) { + dev_err(chg->dev, "Wrong value for minimum system regulation voltage\n"); + return -EINVAL; + } + + data = (uvolt - 3000000) / 100000; + + data <<= CHG_CNFG_04_MINVSYS_SHIFT; + + dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n", + uvolt, data); + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_04, + CHG_CNFG_04_MINVSYS_MASK, data); +} + +static int max77693_set_thermal_regulation_temp(struct max77693_charger *chg, + unsigned int cels) +{ + unsigned int data; + + switch (cels) { + case 70: + case 85: + case 100: + case 115: + data = (cels - 70) / 15; + break; + default: + dev_err(chg->dev, "Wrong value for thermal regulation loop temperature\n"); + return -EINVAL; + } + + data <<= CHG_CNFG_07_REGTEMP_SHIFT; + + dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n", + cels, data); + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_07, + CHG_CNFG_07_REGTEMP_MASK, data); +} + +static int max77693_set_batttery_overcurrent(struct max77693_charger *chg, + unsigned int uamp) +{ + unsigned int data; + + if (uamp && (uamp < 2000000 || uamp > 3500000)) { + dev_err(chg->dev, "Wrong value for battery overcurrent\n"); + return -EINVAL; + } + + if (uamp) + data = ((uamp - 2000000) / 250000) + 1; + else + data = 0; /* disable */ + + data <<= CHG_CNFG_12_B2SOVRC_SHIFT; + + dev_dbg(chg->dev, "Battery overcurrent: %u (0x%x)\n", uamp, data); + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_12, + CHG_CNFG_12_B2SOVRC_MASK, data); +} + +static int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg, + unsigned int uvolt) +{ + unsigned int data; + + switch (uvolt) { + case 4300000: + data = 0x0; + break; + case 4700000: + case 4800000: + case 4900000: + data = (uvolt - 4700000) / 100000; + default: + dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n"); + return -EINVAL; + } + + data <<= CHG_CNFG_12_VCHGINREG_SHIFT; + + dev_dbg(chg->dev, "Charge input voltage regulation threshold: %u (0x%x)\n", + uvolt, data); + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_12, + CHG_CNFG_12_VCHGINREG_MASK, data); +} + +/* + * Sets charger registers to proper and safe default values. + */ +static int max77693_reg_init(struct max77693_charger *chg) +{ + int ret; + unsigned int data; + + /* Unlock charger register protection */ + data = (0x3 << CHG_CNFG_06_CHGPROT_SHIFT); + ret = regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_06, + CHG_CNFG_06_CHGPROT_MASK, data); + if (ret) { + dev_err(chg->dev, "Error unlocking registers: %d\n", ret); + return ret; + } + + ret = max77693_set_fast_charge_timer(chg, DEFAULT_FAST_CHARGE_TIMER); + if (ret) + return ret; + + ret = max77693_set_top_off_threshold_current(chg, + DEFAULT_TOP_OFF_THRESHOLD_CURRENT); + if (ret) + return ret; + + ret = max77693_set_top_off_timer(chg, DEFAULT_TOP_OFF_TIMER); + if (ret) + return ret; + + ret = max77693_set_constant_volt(chg, chg->constant_volt); + if (ret) + return ret; + + ret = max77693_set_min_system_volt(chg, chg->min_system_volt); + if (ret) + return ret; + + ret = max77693_set_thermal_regulation_temp(chg, + chg->thermal_regulation_temp); + if (ret) + return ret; + + ret = max77693_set_batttery_overcurrent(chg, chg->batttery_overcurrent); + if (ret) + return ret; + + ret = max77693_set_charge_input_threshold_volt(chg, + chg->charge_input_threshold_volt); + if (ret) + return ret; + + return 0; +} + +#ifdef CONFIG_OF +static int max77693_dt_init(struct device *dev, struct max77693_charger *chg) +{ + struct device_node *np = dev->of_node; + + if (!np) { + dev_err(dev, "no charger OF node\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "maxim,constant-microvolt", + &chg->constant_volt)) + chg->constant_volt = DEFAULT_CONSTANT_VOLT; + + if (of_property_read_u32(np, "maxim,min-system-microvolt", + &chg->min_system_volt)) + chg->min_system_volt = DEFAULT_MIN_SYSTEM_VOLT; + + if (of_property_read_u32(np, "maxim,thermal-regulation-celsius", + &chg->thermal_regulation_temp)) + chg->thermal_regulation_temp = DEFAULT_THERMAL_REGULATION_TEMP; + + if (of_property_read_u32(np, "maxim,battery-overcurrent-microamp", + &chg->batttery_overcurrent)) + chg->batttery_overcurrent = DEFAULT_BATTERY_OVERCURRENT; + + if (of_property_read_u32(np, "maxim,charge-input-threshold-microvolt", + &chg->charge_input_threshold_volt)) + chg->charge_input_threshold_volt = + DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT; + + return 0; +} +#else /* CONFIG_OF */ +static int max77693_dt_init(struct device *dev, struct max77693_charger *chg) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int max77693_charger_probe(struct platform_device *pdev) +{ + struct max77693_charger *chg; + struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent); + int ret; + + chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + platform_set_drvdata(pdev, chg); + chg->dev = &pdev->dev; + chg->max77693 = max77693; + + ret = max77693_dt_init(&pdev->dev, chg); + if (ret) + return ret; + + ret = max77693_reg_init(chg); + if (ret) + return ret; + + chg->charger.name = max77693_charger_name; + chg->charger.type = POWER_SUPPLY_TYPE_BATTERY; + chg->charger.properties = max77693_charger_props; + chg->charger.num_properties = ARRAY_SIZE(max77693_charger_props); + chg->charger.get_property = max77693_charger_get_property; + + ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer); + if (ret) { + dev_err(&pdev->dev, "failed: create fast charge timer sysfs entry\n"); + goto err; + } + + ret = device_create_file(&pdev->dev, + &dev_attr_top_off_threshold_current); + if (ret) { + dev_err(&pdev->dev, "failed: create top off current sysfs entry\n"); + goto err; + } + + ret = device_create_file(&pdev->dev, &dev_attr_top_off_timer); + if (ret) { + dev_err(&pdev->dev, "failed: create top off timer sysfs entry\n"); + goto err; + } + + ret = power_supply_register(&pdev->dev, &chg->charger); + if (ret) { + dev_err(&pdev->dev, "failed: power supply register\n"); + goto err; + } + + return 0; + +err: + device_remove_file(&pdev->dev, &dev_attr_top_off_timer); + device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current); + device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); + + return ret; +} + +static int max77693_charger_remove(struct platform_device *pdev) +{ + struct max77693_charger *chg = platform_get_drvdata(pdev); + + device_remove_file(&pdev->dev, &dev_attr_top_off_timer); + device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current); + device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); + + power_supply_unregister(&chg->charger); + + return 0; +} + +static const struct platform_device_id max77693_charger_id[] = { + { "max77693-charger", 0, }, + { } +}; +MODULE_DEVICE_TABLE(platform, max77693_charger_id); + +static struct platform_driver max77693_charger_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "max77693-charger", + }, + .probe = max77693_charger_probe, + .remove = max77693_charger_remove, + .id_table = max77693_charger_id, +}; +module_platform_driver(max77693_charger_driver); + +MODULE_AUTHOR("Krzysztof Kozlowski "); +MODULE_DESCRIPTION("Maxim 77693 charger driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 8f961c0a53192583ac86acfb7fb6bba3fc922873 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Tue, 20 Jan 2015 17:24:02 +0000 Subject: power/reset: vexpress: Remove non-DT code Now, as all VE platforms have to be booted with DT, the code handling non-DT case can be removed. Signed-off-by: Pawel Moll Signed-off-by: Sebastian Reichel --- drivers/power/reset/vexpress-poweroff.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c index 9dfc9cee3232..be12d9b92957 100644 --- a/drivers/power/reset/vexpress-poweroff.c +++ b/drivers/power/reset/vexpress-poweroff.c @@ -111,23 +111,20 @@ static int _vexpress_register_restart_handler(struct device *dev) static int vexpress_reset_probe(struct platform_device *pdev) { - enum vexpress_reset_func func; const struct of_device_id *match = of_match_device(vexpress_reset_of_match, &pdev->dev); struct regmap *regmap; int ret = 0; - if (match) - func = (enum vexpress_reset_func)match->data; - else - func = pdev->id_entry->driver_data; + if (!match) + return -EINVAL; regmap = devm_regmap_init_vexpress_config(&pdev->dev); if (IS_ERR(regmap)) return PTR_ERR(regmap); dev_set_drvdata(&pdev->dev, regmap); - switch (func) { + switch ((enum vexpress_reset_func)match->data) { case FUNC_SHUTDOWN: vexpress_power_off_device = &pdev->dev; pm_power_off = vexpress_power_off; @@ -144,20 +141,12 @@ static int vexpress_reset_probe(struct platform_device *pdev) return ret; } -static const struct platform_device_id vexpress_reset_id_table[] = { - { .name = "vexpress-reset", .driver_data = FUNC_RESET, }, - { .name = "vexpress-shutdown", .driver_data = FUNC_SHUTDOWN, }, - { .name = "vexpress-reboot", .driver_data = FUNC_REBOOT, }, - {} -}; - static struct platform_driver vexpress_reset_driver = { .probe = vexpress_reset_probe, .driver = { .name = "vexpress-reset", .of_match_table = vexpress_reset_of_match, }, - .id_table = vexpress_reset_id_table, }; static int __init vexpress_reset_init(void) -- cgit v1.2.3 From 5b40b3e6f1326b159f77509aa4b58e9f4c713ac0 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Wed, 21 Jan 2015 12:11:34 +0800 Subject: power: max77693: fix simple_return.cocci warnings drivers/power/max77693_charger.c:615:1-4: WARNING: end returns can be simpified Simplify a trivial if-return sequence. Possibly combine with a preceding function call. Generated by: scripts/coccinelle/misc/simple_return.cocci CC: Krzysztof Kozlowski Signed-off-by: Fengguang Wu Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/max77693_charger.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max77693_charger.c b/drivers/power/max77693_charger.c index 56cf2177aad4..841047cb9ff1 100644 --- a/drivers/power/max77693_charger.c +++ b/drivers/power/max77693_charger.c @@ -612,12 +612,8 @@ static int max77693_reg_init(struct max77693_charger *chg) if (ret) return ret; - ret = max77693_set_charge_input_threshold_volt(chg, + return max77693_set_charge_input_threshold_volt(chg, chg->charge_input_threshold_volt); - if (ret) - return ret; - - return 0; } #ifdef CONFIG_OF -- cgit v1.2.3 From a7c45b6c28ee65b148efc72c7b7afcd22a51e50b Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Wed, 21 Jan 2015 12:11:34 +0800 Subject: power: max77693: fix platform_no_drv_owner.cocci warnings drivers/power/max77693_charger.c:747:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Krzysztof Kozlowski Signed-off-by: Fengguang Wu Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/max77693_charger.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/max77693_charger.c b/drivers/power/max77693_charger.c index 841047cb9ff1..b042970fdeaf 100644 --- a/drivers/power/max77693_charger.c +++ b/drivers/power/max77693_charger.c @@ -740,7 +740,6 @@ MODULE_DEVICE_TABLE(platform, max77693_charger_id); static struct platform_driver max77693_charger_driver = { .driver = { - .owner = THIS_MODULE, .name = "max77693-charger", }, .probe = max77693_charger_probe, -- cgit v1.2.3 From 85cdf36e11557dc367c1361e4b7bb2c4619cae91 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Thu, 1 Jan 2015 18:04:52 +0100 Subject: power: ab8500_fg.c: Remove unused function Remove the function ab8500_fg_reinit() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Acked-by: Arnd Bergmann Signed-off-by: Sebastian Reichel --- drivers/power/ab8500_fg.c | 14 -------------- include/linux/mfd/abx500/ab8500-bm.h | 1 - 2 files changed, 15 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 69b80bcaa9e7..c908658aa31a 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2435,20 +2435,6 @@ static void ab8500_fg_reinit_work(struct work_struct *work) } } -/** - * ab8500_fg_reinit() - forces FG algorithm to reinitialize with current values - * - * This function can be used to force the FG algorithm to recalculate a new - * voltage based battery capacity. - */ -void ab8500_fg_reinit(void) -{ - struct ab8500_fg *di = ab8500_fg_get(); - /* User won't be notified if a null pointer returned. */ - if (di != NULL) - queue_delayed_work(di->fg_wq, &di->fg_reinit_work, 0); -} - /* Exposure to the sysfs interface */ struct ab8500_fg_sysfs_entry { diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h index cc892a8d8d6e..12a5b396921e 100644 --- a/include/linux/mfd/abx500/ab8500-bm.h +++ b/include/linux/mfd/abx500/ab8500-bm.h @@ -461,7 +461,6 @@ struct ab8500_fg; #ifdef CONFIG_AB8500_BM extern struct abx500_bm_data ab8500_bm_data; -void ab8500_fg_reinit(void); void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA); struct ab8500_btemp *ab8500_btemp_get(void); int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp); -- cgit v1.2.3 From fa0f8d670085745711da1fde727996d587a3a0d4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 4 Dec 2014 16:33:21 +0100 Subject: power: reset: Add reset driver for R-Mobile platforms Add a reset driver for Renesas R-Mobile and SH-Mobile SoCs. It registers a restart handler to trigger a soft power-on reset through the R-Mobile System Controller. The priority of this restart handler is 192, to allow a watchdog driver to use priority 128. Note that we do not use syscon-reboot, as the HPB (Peripheral Bus Bridge) semaphore should be acquired on systems where both the ARM and SH core are in use. The driver can be extended later to support this, when needed. Signed-off-by: Geert Uytterhoeven Reviewed-by: Guenter Roeck Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 6 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/rmobile-reset.c | 91 +++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 drivers/power/reset/rmobile-reset.c (limited to 'drivers/power') diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 028e76504519..d743145e0001 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -159,5 +159,11 @@ config POWER_RESET_SYSCON help Reboot support for generic SYSCON mapped register reset. +config POWER_RESET_RMOBILE + tristate "Renesas R-Mobile reset driver" + depends on ARCH_RMOBILE || COMPILE_TEST + help + Reboot support for Renesas R-Mobile and SH-Mobile SoCs. + endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 1d4804d6b323..1631652009ac 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o +obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o diff --git a/drivers/power/reset/rmobile-reset.c b/drivers/power/reset/rmobile-reset.c new file mode 100644 index 000000000000..e6569df76941 --- /dev/null +++ b/drivers/power/reset/rmobile-reset.c @@ -0,0 +1,91 @@ +/* + * Renesas R-Mobile Reset Driver + * + * Copyright (C) 2014 Glider bvba + * + * 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 +#include +#include +#include +#include +#include +#include + +/* SYSC Register Bank 2 */ +#define RESCNT2 0x20 /* Reset Control Register 2 */ + +/* Reset Control Register 2 */ +#define RESCNT2_PRES 0x80000000 /* Soft power-on reset */ + +static void __iomem *sysc_base2; + +static int rmobile_reset_handler(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + pr_debug("%s %lu\n", __func__, mode); + + /* Let's assume we have acquired the HPB semaphore */ + writel(RESCNT2_PRES, sysc_base2 + RESCNT2); + + return NOTIFY_DONE; +} + +static struct notifier_block rmobile_reset_nb = { + .notifier_call = rmobile_reset_handler, + .priority = 192, +}; + +static int rmobile_reset_probe(struct platform_device *pdev) +{ + int error; + + sysc_base2 = of_iomap(pdev->dev.of_node, 1); + if (!sysc_base2) + return -ENODEV; + + error = register_restart_handler(&rmobile_reset_nb); + if (error) { + dev_err(&pdev->dev, + "cannot register restart handler (err=%d)\n", error); + goto fail_unmap; + } + + return 0; + +fail_unmap: + iounmap(sysc_base2); + return error; +} + +static int rmobile_reset_remove(struct platform_device *pdev) +{ + unregister_restart_handler(&rmobile_reset_nb); + iounmap(sysc_base2); + return 0; +} + +static const struct of_device_id rmobile_reset_of_match[] = { + { .compatible = "renesas,sysc-rmobile", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rmobile_reset_of_match); + +static struct platform_driver rmobile_reset_driver = { + .probe = rmobile_reset_probe, + .remove = rmobile_reset_remove, + .driver = { + .name = "rmobile_reset", + .of_match_table = rmobile_reset_of_match, + }, +}; + +module_platform_driver(rmobile_reset_driver); + +MODULE_DESCRIPTION("Renesas R-Mobile Reset Driver"); +MODULE_AUTHOR("Geert Uytterhoeven "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 31f50e48e3e4ea9d503285a389d6a1b5349d66c0 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Thu, 4 Dec 2014 14:56:04 +0000 Subject: power: bq24190_charger: suppress build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes following build warning: In file included from include/linux/printk.h:261:0, from include/linux/kernel.h:13, from include/linux/list.h:8, from include/linux/module.h:9, from drivers/power/bq24190_charger.c:11: drivers/power/bq24190_charger.c: In function ‘bq24190_irq_handler_thread’: include/linux/dynamic_debug.h:86:20: warning: ‘ss_reg’ may be used uninitialized in this function [-Wmaybe-uninitialized] __dynamic_dev_dbg(&descriptor, dev, fmt, \ ^ drivers/power/bq24190_charger.c:1211:5: note: ‘ss_reg’ was declared here u8 ss_reg, f_reg; ^ In file included from include/linux/printk.h:261:0, from include/linux/kernel.h:13, from include/linux/list.h:8, from include/linux/module.h:9, from drivers/power/bq24190_charger.c:11: include/linux/dynamic_debug.h:86:20: warning: ‘f_reg’ may be used uninitialized in this function [-Wmaybe-uninitialized] __dynamic_dev_dbg(&descriptor, dev, fmt, \ ^ drivers/power/bq24190_charger.c:1211:13: note: ‘f_reg’ was declared here u8 ss_reg, f_reg; Signed-off-by: Lad, Prabhakar Signed-off-by: Sebastian Reichel --- drivers/power/bq24190_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c index e4c95e1a6733..d0e8236a6404 100644 --- a/drivers/power/bq24190_charger.c +++ b/drivers/power/bq24190_charger.c @@ -1208,7 +1208,7 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) { struct bq24190_dev_info *bdi = data; bool alert_userspace = false; - u8 ss_reg, f_reg; + u8 ss_reg = 0, f_reg = 0; int ret; pm_runtime_get_sync(bdi->dev); -- cgit v1.2.3 From 19fb8b2df4fa0cae1656ca75b75f4e142f2a7829 Mon Sep 17 00:00:00 2001 From: Karol Wrona Date: Fri, 28 Nov 2014 14:23:02 +0100 Subject: power: max14577: Remove SYSFS dependency from Kconfig This is only a small clean-up. The driver can be built without SYSFS as there exist stubs for used functions. Signed-off-by: Karol Wrona Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 110d4bc03483..e2569a538501 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -327,7 +327,6 @@ config CHARGER_MANAGER config CHARGER_MAX14577 tristate "Maxim MAX14577/77836 battery charger driver" depends on MFD_MAX14577 - depends on SYSFS help Say Y to enable support for the battery charger control sysfs and platform data of MAX14577/77836 MUICs. -- cgit v1.2.3 From 9dbf5a28642bb2db57fb5150252e133e19acd33a Mon Sep 17 00:00:00 2001 From: Eric Bénard Date: Wed, 12 Nov 2014 23:04:08 +0100 Subject: bq27x00_battery: fix register offset for bq27425 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SOC is at 0x1C so we must add 0x4 as stated in the comment to read the right value. - DCAP is at 0x3c so we also must use a value with the right offset to get the correct design capacity. Actually testing on a bq27410 which has the same register map as bq27425 (but adds new registers). Signed-off-by: Eric Bénard Signed-off-by: Sebastian Reichel --- drivers/power/bq27x00_battery.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index a78ac201828e..baae2151cb9a 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -76,7 +76,8 @@ /* bq27425 register addresses are same as bq27x00 addresses minus 4 */ #define BQ27425_REG_OFFSET 0x04 -#define BQ27425_REG_SOC 0x18 /* Register address plus offset */ +#define BQ27425_REG_SOC (0x1C + BQ27425_REG_OFFSET) +#define BQ27425_REG_DCAP (0x3C + BQ27425_REG_OFFSET) #define BQ27000_RS 20 /* Resistor sense */ #define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000) @@ -282,9 +283,12 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) { int ilmd; - if (bq27xxx_is_chip_version_higher(di)) - ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); - else + if (bq27xxx_is_chip_version_higher(di)) { + if (di->chip == BQ27425) + ilmd = bq27x00_read(di, BQ27425_REG_DCAP, false); + else + ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); + } else ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); if (ilmd < 0) { -- cgit v1.2.3 From 90f04a28fbadbc179ee6325fd4ee7d5beb27bcf0 Mon Sep 17 00:00:00 2001 From: Puthikorn Voravootivat Date: Tue, 21 Oct 2014 18:15:37 -0700 Subject: bq27x00_battery: Call power_supply_changed only when capacity changed In current driver, power_supply_changed() is called whenever any of the battery attribute changed. This causes kernel to increases the '/sys/power/wakeup_count' and make suspend not working correctly. This patch change this behavior to call power_supply_changed() only when the battery capacity changed. Signed-off-by: Puthikorn Voravootivat Reviewed-by: David Riley Reviewed-by: Benson Leung Signed-off-by: Sebastian Reichel --- drivers/power/bq27x00_battery.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index baae2151cb9a..b72ba7c1bd69 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -497,10 +497,11 @@ static void bq27x00_update(struct bq27x00_device_info *di) di->charge_design_full = bq27x00_battery_read_ilmd(di); } - if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) { - di->cache = cache; + if (di->cache.capacity != cache.capacity) power_supply_changed(&di->bat); - } + + if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) + di->cache = cache; di->last_update = jiffies; } -- cgit v1.2.3 From a538cf04ef67861a208075a6d57d0f045822e1d6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 10 Oct 2014 17:41:17 -0700 Subject: power/reset: at91-poweroff: Fix error handling and other compiler warnings at91_poweroff_get_wakeup_mode can return a negative error code and should therefore not return an enum type. Similar, its result should not be assigned to an enum type. Otherwise, the returned value is never negative, resulting in a compiler warning and a missed error condition, which in turn results in writing bad values into a chip register. Also fix other compiler warnings which can be easily avoided. drivers/power/reset/at91-poweroff.c:74:24: warning: type qualifiers ignored on function return type drivers/power/reset/at91-poweroff.c:74:24: warning: no previous prototype for 'at91_poweroff_get_wakeup_mode' drivers/power/reset/at91-poweroff.c:83:16: warning: comparison between signed and unsigned integer expressions drivers/power/reset/at91-poweroff.c:97:2: warning: comparison of unsigned expression < 0 is always false Cc: Maxime Ripard Cc: Nicolas Ferre Signed-off-by: Guenter Roeck Acked-by: Maxime Ripard Signed-off-by: Sebastian Reichel --- drivers/power/reset/at91-poweroff.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index c61000333bb9..4b72ea51c364 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -71,10 +71,11 @@ static void at91_poweroff(void) writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR); } -const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np) +static int at91_poweroff_get_wakeup_mode(struct device_node *np) { const char *pm; - int err, i; + unsigned int i; + int err; err = of_property_read_string(np, "atmel,wakeup-mode", &pm); if (err < 0) @@ -90,7 +91,7 @@ const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np) static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - enum wakeup_type wakeup_mode; + int wakeup_mode; u32 mode = 0, tmp; wakeup_mode = at91_poweroff_get_wakeup_mode(np); -- cgit v1.2.3 From c1155c64e603378dccfc21ee0612cf60dd11725b Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Fri, 19 Dec 2014 17:55:13 +0900 Subject: power: charger-manager: Use alarmtimer for battery monitoring in suspend. To guerantee proper charing and managing batteries even in suspend, charger-manager has used rtc device with rtc framework interface. However, it is better to use alarmtimer for cleaner and more appropriate operation. This patch makes driver to use alarmtimer for polling work in suspend and removes all deprecated codes related with using rtc interface. Signed-off-by: Jonghwa Lee Signed-off-by: Sebastian Reichel --- drivers/power/Kconfig | 2 +- drivers/power/charger-manager.c | 289 ++++++++++------------------------ include/linux/power/charger-manager.h | 32 +--- 3 files changed, 84 insertions(+), 239 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index e2569a538501..a79f16afb588 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -315,7 +315,7 @@ config CHARGER_GPIO config CHARGER_MANAGER bool "Battery charger manager for multiple chargers" - depends on REGULATOR && RTC_CLASS + depends on REGULATOR select EXTCON help Say Y to enable charger-manager support, which allows multiple diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 649052e1f2d9..14b0d85318eb 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -69,16 +69,10 @@ static LIST_HEAD(cm_list); static DEFINE_MUTEX(cm_list_mtx); /* About in-suspend (suspend-again) monitoring */ -static struct rtc_device *rtc_dev; -/* - * Backup RTC alarm - * Save the wakeup alarm before entering suspend-to-RAM - */ -static struct rtc_wkalrm rtc_wkalarm_save; -/* Backup RTC alarm time in terms of seconds since 01-01-1970 00:00:00 */ -static unsigned long rtc_wkalarm_save_time; +static struct alarm *cm_timer; + static bool cm_suspended; -static bool cm_rtc_set; +static bool cm_timer_set; static unsigned long cm_suspend_duration_ms; /* About normal (not suspended) monitoring */ @@ -87,9 +81,6 @@ static unsigned long next_polling; /* Next appointed polling time */ static struct workqueue_struct *cm_wq; /* init at driver add */ static struct delayed_work cm_monitor_work; /* init at driver add */ -/* Global charger-manager description */ -static struct charger_global_desc *g_desc; /* init with setup_charger_manager */ - /** * is_batt_present - See if the battery presents in place. * @cm: the Charger Manager representing the battery. @@ -1047,10 +1038,13 @@ static bool cm_setup_timer(void) { struct charger_manager *cm; unsigned int wakeup_ms = UINT_MAX; - bool ret = false; + int timer_req = 0; - mutex_lock(&cm_list_mtx); + if (time_after(next_polling, jiffies)) + CM_MIN_VALID(wakeup_ms, + jiffies_to_msecs(next_polling - jiffies)); + mutex_lock(&cm_list_mtx); list_for_each_entry(cm, &cm_list, entry) { unsigned int fbchk_ms = 0; @@ -1070,162 +1064,38 @@ static bool cm_setup_timer(void) /* Skip if polling is not required for this CM */ if (!is_polling_required(cm) && !cm->emergency_stop) continue; + timer_req++; if (cm->desc->polling_interval_ms == 0) continue; CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms); } - mutex_unlock(&cm_list_mtx); - if (wakeup_ms < UINT_MAX && wakeup_ms > 0) { - pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); - if (rtc_dev) { - struct rtc_wkalrm tmp; - unsigned long time, now; - unsigned long add = DIV_ROUND_UP(wakeup_ms, 1000); - - /* - * Set alarm with the polling interval (wakeup_ms) - * except when rtc_wkalarm_save comes first. - * However, the alarm time should be NOW + - * CM_RTC_SMALL or later. - */ - tmp.enabled = 1; - rtc_read_time(rtc_dev, &tmp.time); - rtc_tm_to_time(&tmp.time, &now); - if (add < CM_RTC_SMALL) - add = CM_RTC_SMALL; - time = now + add; + if (timer_req && cm_timer) { + ktime_t now, add; - ret = true; + /* + * Set alarm with the polling interval (wakeup_ms) + * The alarm time should be NOW + CM_RTC_SMALL or later. + */ + if (wakeup_ms == UINT_MAX || + wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC) + wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC; - if (rtc_wkalarm_save.enabled && - rtc_wkalarm_save_time && - rtc_wkalarm_save_time < time) { - if (rtc_wkalarm_save_time < now + CM_RTC_SMALL) - time = now + CM_RTC_SMALL; - else - time = rtc_wkalarm_save_time; + pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); - /* The timer is not appointed by CM */ - ret = false; - } + now = ktime_get_boottime(); + add = ktime_set(wakeup_ms / MSEC_PER_SEC, + (wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC); + alarm_start(cm_timer, ktime_add(now, add)); - pr_info("Waking up after %lu secs\n", time - now); + cm_suspend_duration_ms = wakeup_ms; - rtc_time_to_tm(time, &tmp.time); - rtc_set_alarm(rtc_dev, &tmp); - cm_suspend_duration_ms += wakeup_ms; - return ret; - } + return true; } - - if (rtc_dev) - rtc_set_alarm(rtc_dev, &rtc_wkalarm_save); return false; } -static void _cm_fbchk_in_suspend(struct charger_manager *cm) -{ - unsigned long jiffy_now = jiffies; - - if (!cm->fullbatt_vchk_jiffies_at) - return; - - if (g_desc && g_desc->assume_timer_stops_in_suspend) - jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms); - - /* Execute now if it's going to be executed not too long after */ - jiffy_now += CM_JIFFIES_SMALL; - - if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at)) - fullbatt_vchk(&cm->fullbatt_vchk_work.work); -} - -/** - * cm_suspend_again - Determine whether suspend again or not - * - * Returns true if the system should be suspended again - * Returns false if the system should be woken up - */ -bool cm_suspend_again(void) -{ - struct charger_manager *cm; - bool ret = false; - - if (!g_desc || !g_desc->rtc_only_wakeup || !g_desc->rtc_only_wakeup() || - !cm_rtc_set) - return false; - - if (cm_monitor()) - goto out; - - ret = true; - mutex_lock(&cm_list_mtx); - list_for_each_entry(cm, &cm_list, entry) { - _cm_fbchk_in_suspend(cm); - - if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) || - cm->status_save_batt != is_batt_present(cm)) { - ret = false; - break; - } - } - mutex_unlock(&cm_list_mtx); - - cm_rtc_set = cm_setup_timer(); -out: - /* It's about the time when the non-CM appointed timer goes off */ - if (rtc_wkalarm_save.enabled) { - unsigned long now; - struct rtc_time tmp; - - rtc_read_time(rtc_dev, &tmp); - rtc_tm_to_time(&tmp, &now); - - if (rtc_wkalarm_save_time && - now + CM_RTC_SMALL >= rtc_wkalarm_save_time) - return false; - } - return ret; -} -EXPORT_SYMBOL_GPL(cm_suspend_again); - -/** - * setup_charger_manager - initialize charger_global_desc data - * @gd: pointer to instance of charger_global_desc - */ -int setup_charger_manager(struct charger_global_desc *gd) -{ - if (!gd) - return -EINVAL; - - if (rtc_dev) - rtc_class_close(rtc_dev); - rtc_dev = NULL; - g_desc = NULL; - - if (!gd->rtc_only_wakeup) { - pr_err("The callback rtc_only_wakeup is not given\n"); - return -EINVAL; - } - - if (gd->rtc_name) { - rtc_dev = rtc_class_open(gd->rtc_name); - if (IS_ERR_OR_NULL(rtc_dev)) { - rtc_dev = NULL; - /* Retry at probe. RTC may be not registered yet */ - } - } else { - pr_warn("No wakeup timer is given for charger manager. " - "In-suspend monitoring won't work.\n"); - } - - g_desc = gd; - return 0; -} -EXPORT_SYMBOL_GPL(setup_charger_manager); - /** * charger_extcon_work - enable/diable charger according to the state * of charger cable @@ -1719,6 +1589,12 @@ static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev) return dev_get_platdata(&pdev->dev); } +static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now) +{ + cm_timer_set = false; + return ALARMTIMER_NORESTART; +} + static int charger_manager_probe(struct platform_device *pdev) { struct charger_desc *desc = cm_get_drv_data(pdev); @@ -1728,16 +1604,6 @@ static int charger_manager_probe(struct platform_device *pdev) union power_supply_propval val; struct power_supply *fuel_gauge; - if (g_desc && !rtc_dev && g_desc->rtc_name) { - rtc_dev = rtc_class_open(g_desc->rtc_name); - if (IS_ERR_OR_NULL(rtc_dev)) { - rtc_dev = NULL; - dev_err(&pdev->dev, "Cannot get RTC %s\n", - g_desc->rtc_name); - return -ENODEV; - } - } - if (IS_ERR(desc)) { dev_err(&pdev->dev, "No platform data (desc) found\n"); return -ENODEV; @@ -1752,6 +1618,12 @@ static int charger_manager_probe(struct platform_device *pdev) cm->dev = &pdev->dev; cm->desc = desc; + /* Initialize alarm timer */ + if (alarmtimer_get_rtcdev()) { + cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL); + alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func); + } + /* * The following two do not need to be errors. * Users may intentionally ignore those two features. @@ -1993,38 +1865,41 @@ static int cm_suspend_noirq(struct device *dev) return ret; } +static bool cm_need_to_awake(void) +{ + struct charger_manager *cm; + + if (cm_timer) + return false; + + mutex_lock(&cm_list_mtx); + list_for_each_entry(cm, &cm_list, entry) { + if (is_charging(cm)) { + mutex_unlock(&cm_list_mtx); + return true; + } + } + mutex_unlock(&cm_list_mtx); + + return false; +} + static int cm_suspend_prepare(struct device *dev) { struct charger_manager *cm = dev_get_drvdata(dev); - if (!cm_suspended) { - if (rtc_dev) { - struct rtc_time tmp; - unsigned long now; - - rtc_read_alarm(rtc_dev, &rtc_wkalarm_save); - rtc_read_time(rtc_dev, &tmp); + if (cm_need_to_awake()) + return -EBUSY; - if (rtc_wkalarm_save.enabled) { - rtc_tm_to_time(&rtc_wkalarm_save.time, - &rtc_wkalarm_save_time); - rtc_tm_to_time(&tmp, &now); - if (now > rtc_wkalarm_save_time) - rtc_wkalarm_save_time = 0; - } else { - rtc_wkalarm_save_time = 0; - } - } + if (!cm_suspended) cm_suspended = true; - } - cancel_delayed_work(&cm->fullbatt_vchk_work); - cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm); - cm->status_save_batt = is_batt_present(cm); + cm_timer_set = cm_setup_timer(); - if (!cm_rtc_set) { - cm_suspend_duration_ms = 0; - cm_rtc_set = cm_setup_timer(); + if (cm_timer_set) { + cancel_work_sync(&setup_polling); + cancel_delayed_work_sync(&cm_monitor_work); + cancel_delayed_work(&cm->fullbatt_vchk_work); } return 0; @@ -2034,18 +1909,21 @@ static void cm_suspend_complete(struct device *dev) { struct charger_manager *cm = dev_get_drvdata(dev); - if (cm_suspended) { - if (rtc_dev) { - struct rtc_wkalrm tmp; - - rtc_read_alarm(rtc_dev, &tmp); - rtc_wkalarm_save.pending = tmp.pending; - rtc_set_alarm(rtc_dev, &rtc_wkalarm_save); - } + if (cm_suspended) cm_suspended = false; - cm_rtc_set = false; + + if (cm_timer_set) { + ktime_t remain; + + alarm_cancel(cm_timer); + cm_timer_set = false; + remain = alarm_expires_remaining(cm_timer); + cm_suspend_duration_ms -= ktime_to_ms(remain); + schedule_work(&setup_polling); } + _cm_monitor(cm); + /* Re-enqueue delayed work (fullbatt_vchk_work) */ if (cm->fullbatt_vchk_jiffies_at) { unsigned long delay = 0; @@ -2060,21 +1938,18 @@ static void cm_suspend_complete(struct device *dev) } /* - * Account for cm_suspend_duration_ms if - * assume_timer_stops_in_suspend is active + * Account for cm_suspend_duration_ms with assuming that + * timer stops in suspend. */ - if (g_desc && g_desc->assume_timer_stops_in_suspend) { - if (delay > cm_suspend_duration_ms) - delay -= cm_suspend_duration_ms; - else - delay = 0; - } + if (delay > cm_suspend_duration_ms) + delay -= cm_suspend_duration_ms; + else + delay = 0; queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, msecs_to_jiffies(delay)); } device_set_wakeup_capable(cm->dev, false); - uevent_notify(cm, NULL); } static const struct dev_pm_ops charger_manager_pm = { diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h index e97fc656a058..416ebeb6ee1e 100644 --- a/include/linux/power/charger-manager.h +++ b/include/linux/power/charger-manager.h @@ -17,6 +17,7 @@ #include #include +#include enum data_source { CM_BATTERY_PRESENT, @@ -44,29 +45,6 @@ enum cm_event_types { CM_EVENT_OTHERS, }; -/** - * struct charger_global_desc - * @rtc_name: the name of RTC used to wake up the system from suspend. - * @rtc_only_wakeup: - * If the system is woken up by waekup-sources other than the RTC or - * callbacks, Charger Manager should recognize with - * rtc_only_wakeup() returning false. - * If the RTC given to CM is the only wakeup reason, - * rtc_only_wakeup should return true. - * @assume_timer_stops_in_suspend: - * Assume that the jiffy timer stops in suspend-to-RAM. - * When enabled, CM does not rely on jiffies value in - * suspend_again and assumes that jiffies value does not - * change during suspend. - */ -struct charger_global_desc { - char *rtc_name; - - bool (*rtc_only_wakeup)(void); - - bool assume_timer_stops_in_suspend; -}; - /** * struct charger_cable * @extcon_name: the name of extcon device. @@ -266,22 +244,14 @@ struct charger_manager { char psy_name_buf[PSY_NAME_MAX + 1]; struct power_supply charger_psy; - bool status_save_ext_pwr_inserted; - bool status_save_batt; - u64 charging_start_time; u64 charging_end_time; }; #ifdef CONFIG_CHARGER_MANAGER -extern int setup_charger_manager(struct charger_global_desc *gd); -extern bool cm_suspend_again(void); extern void cm_notify_event(struct power_supply *psy, enum cm_event_types type, char *msg); #else -static inline int setup_charger_manager(struct charger_global_desc *gd) -{ return 0; } -static inline bool cm_suspend_again(void) { return false; } static inline void cm_notify_event(struct power_supply *psy, enum cm_event_types type, char *msg) { } #endif -- cgit v1.2.3 From 4f5fd640469839532cb2bd558f878539548f2861 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 25 Nov 2014 16:49:46 -0800 Subject: power/reset: brcmstb: Make the driver buildable on MIPS Now that the driver doesn't use any ARM-specific headers, it is safe to build on MIPS or with COMPILE_TEST. Signed-off-by: Kevin Cernekee Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index d743145e0001..bf5acb3edd85 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -39,14 +39,13 @@ config POWER_RESET_AXXIA Say Y if you have an Axxia family SoC. config POWER_RESET_BRCMSTB - bool "Broadcom STB reset driver" if COMPILE_TEST - depends on ARM + bool "Broadcom STB reset driver" + depends on ARM || MIPS || COMPILE_TEST default ARCH_BRCMSTB help - This driver provides restart support for ARM-based Broadcom STB - boards. + This driver provides restart support for Broadcom STB boards. - Say Y here if you have an ARM-based Broadcom STB board and you wish + Say Y here if you have a Broadcom STB board and you wish to have restart support. config POWER_RESET_GPIO -- cgit v1.2.3 From bb1de7f61f1834af81a1665e6450f47dd18a0dd5 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 25 Nov 2014 16:49:47 -0800 Subject: power/reset: brcmstb: Use the DT "compatible" string to indicate bit positions Some of the older chips used different bits to arm and trigger the reset. Add the infrastructure needed to specify this through the "compatible" string. Signed-off-by: Kevin Cernekee Signed-off-by: Sebastian Reichel --- drivers/power/reset/brcmstb-reboot.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/brcmstb-reboot.c b/drivers/power/reset/brcmstb-reboot.c index 100606f9b3dc..af5aedf39261 100644 --- a/drivers/power/reset/brcmstb-reboot.c +++ b/drivers/power/reset/brcmstb-reboot.c @@ -11,6 +11,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -34,13 +35,20 @@ static struct regmap *regmap; static u32 rst_src_en; static u32 sw_mstr_rst; +struct reset_reg_mask { + u32 rst_src_en_mask; + u32 sw_mstr_rst_mask; +}; + +static const struct reset_reg_mask *reset_masks; + static int brcmstb_restart_handler(struct notifier_block *this, unsigned long mode, void *cmd) { int rc; u32 tmp; - rc = regmap_write(regmap, rst_src_en, 1); + rc = regmap_write(regmap, rst_src_en, reset_masks->rst_src_en_mask); if (rc) { pr_err("failed to write rst_src_en (%d)\n", rc); return NOTIFY_DONE; @@ -52,7 +60,7 @@ static int brcmstb_restart_handler(struct notifier_block *this, return NOTIFY_DONE; } - rc = regmap_write(regmap, sw_mstr_rst, 1); + rc = regmap_write(regmap, sw_mstr_rst, reset_masks->sw_mstr_rst_mask); if (rc) { pr_err("failed to write sw_mstr_rst (%d)\n", rc); return NOTIFY_DONE; @@ -75,10 +83,28 @@ static struct notifier_block brcmstb_restart_nb = { .priority = 128, }; +static const struct reset_reg_mask reset_bits_40nm = { + .rst_src_en_mask = BIT(0), + .sw_mstr_rst_mask = BIT(0), +}; + +static const struct of_device_id of_match[] = { + { .compatible = "brcm,brcmstb-reboot", .data = &reset_bits_40nm }, + {}, +}; + static int brcmstb_reboot_probe(struct platform_device *pdev) { int rc; struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id; + + of_id = of_match_node(of_match, np); + if (!of_id) { + pr_err("failed to look up compatible string\n"); + return -EINVAL; + } + reset_masks = of_id->data; regmap = syscon_regmap_lookup_by_phandle(np, "syscon"); if (IS_ERR(regmap)) { @@ -108,11 +134,6 @@ static int brcmstb_reboot_probe(struct platform_device *pdev) return rc; } -static const struct of_device_id of_match[] = { - { .compatible = "brcm,brcmstb-reboot", }, - {}, -}; - static struct platform_driver brcmstb_reboot_driver = { .probe = brcmstb_reboot_probe, .driver = { -- cgit v1.2.3 From 79969f6aafcb4c5d02fc9b33afc58446e4e9dbac Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 25 Nov 2014 16:49:48 -0800 Subject: power/reset: brcmstb: Add support for old 65nm chips The register bit fields are a little different, so add an entry and a compatible string to accommodate them. Signed-off-by: Kevin Cernekee Signed-off-by: Sebastian Reichel --- Documentation/devicetree/bindings/arm/brcm-brcmstb.txt | 4 +++- drivers/power/reset/brcmstb-reboot.c | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/Documentation/devicetree/bindings/arm/brcm-brcmstb.txt b/Documentation/devicetree/bindings/arm/brcm-brcmstb.txt index 3c436cc4f35d..430608ec09f0 100644 --- a/Documentation/devicetree/bindings/arm/brcm-brcmstb.txt +++ b/Documentation/devicetree/bindings/arm/brcm-brcmstb.txt @@ -79,7 +79,9 @@ reboot Required properties - compatible - The string property "brcm,brcmstb-reboot". + The string property "brcm,brcmstb-reboot" for 40nm/28nm chips with + the new SYS_CTRL interface, or "brcm,bcm7038-reboot" for 65nm + chips with the old SUN_TOP_CTRL interface. - syscon A phandle / integer array that points to the syscon node which describes diff --git a/drivers/power/reset/brcmstb-reboot.c b/drivers/power/reset/brcmstb-reboot.c index af5aedf39261..884b53c483c0 100644 --- a/drivers/power/reset/brcmstb-reboot.c +++ b/drivers/power/reset/brcmstb-reboot.c @@ -88,8 +88,14 @@ static const struct reset_reg_mask reset_bits_40nm = { .sw_mstr_rst_mask = BIT(0), }; +static const struct reset_reg_mask reset_bits_65nm = { + .rst_src_en_mask = BIT(3), + .sw_mstr_rst_mask = BIT(31), +}; + static const struct of_device_id of_match[] = { { .compatible = "brcm,brcmstb-reboot", .data = &reset_bits_40nm }, + { .compatible = "brcm,bcm7038-reboot", .data = &reset_bits_65nm }, {}, }; -- cgit v1.2.3 From 085bc24d1553aae0d8a2b8e5b7c80c7862d0212c Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Tue, 28 Oct 2014 08:05:12 +0100 Subject: Add LTC2941/LTC2943 Battery Gauge Driver Both the LTC2941 and LTC2943 measure battery capacity. The LTC2943 is compatible with the LTC2941, it adds voltage and temperature monitoring, and uses a slightly different conversion formula for the charge counter. To avoid confusion with e.g. the LTC2945, the driver is called LTC2941 instead of LTC294X. v2: Fix units of measurement: uV, uA and centidegrees. v3: Correctly set configuration register. Allow negative values for the sense resistor. v4: Run checkpatch.pl and fix all errors and warnings. v5: Prefix "lltc," to devicetree properties. Signed-off-by: Mike Looijmans [ removed .owner field ] Signed-off-by: Sebastian Reichel --- drivers/power/Kconfig | 8 + drivers/power/Makefile | 1 + drivers/power/ltc2941-battery-gauge.c | 547 ++++++++++++++++++++++++++++++++++ 3 files changed, 556 insertions(+) create mode 100644 drivers/power/ltc2941-battery-gauge.c (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index a79f16afb588..27b751b995fb 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -388,6 +388,14 @@ config CHARGER_TPS65090 Say Y here to enable support for battery charging with TPS65090 PMIC chips. +config BATTERY_GAUGE_LTC2941 + tristate "LTC2941/LTC2943 Battery Gauge Driver" + depends on I2C + help + Say Y here to include support for LTC2941 and LTC2943 Battery + Gauge IC. The driver reports the charge count continuously, and + measures the voltage and temperature every 10 seconds. + config AB8500_BM bool "AB8500 Battery Management Driver" depends on AB8500_CORE && AB8500_GPADC diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 31216cb7e8a1..36f9e0d10111 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o +obj-$(CONFIG_BATTERY_GAUGE_LTC2941) += ltc2941-battery-gauge.o obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o diff --git a/drivers/power/ltc2941-battery-gauge.c b/drivers/power/ltc2941-battery-gauge.c new file mode 100644 index 000000000000..e31c927a6d16 --- /dev/null +++ b/drivers/power/ltc2941-battery-gauge.c @@ -0,0 +1,547 @@ +/* + * I2C client/driver for the Linear Technology LTC2941 and LTC2943 + * Battery Gas Gauge IC + * + * Copyright (C) 2014 Topic Embedded Systems + * + * Author: Auryn Verwegen + * Author: Mike Looijmans + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I16_MSB(x) ((x >> 8) & 0xFF) +#define I16_LSB(x) (x & 0xFF) + +#define LTC294X_WORK_DELAY 10 /* Update delay in seconds */ + +#define LTC294X_MAX_VALUE 0xFFFF +#define LTC294X_MID_SUPPLY 0x7FFF + +#define LTC2941_MAX_PRESCALER_EXP 7 +#define LTC2943_MAX_PRESCALER_EXP 6 + +enum ltc294x_reg { + LTC294X_REG_STATUS = 0x00, + LTC294X_REG_CONTROL = 0x01, + LTC294X_REG_ACC_CHARGE_MSB = 0x02, + LTC294X_REG_ACC_CHARGE_LSB = 0x03, + LTC294X_REG_THRESH_HIGH_MSB = 0x04, + LTC294X_REG_THRESH_HIGH_LSB = 0x05, + LTC294X_REG_THRESH_LOW_MSB = 0x06, + LTC294X_REG_THRESH_LOW_LSB = 0x07, + LTC294X_REG_VOLTAGE_MSB = 0x08, + LTC294X_REG_VOLTAGE_LSB = 0x09, + LTC294X_REG_CURRENT_MSB = 0x0E, + LTC294X_REG_CURRENT_LSB = 0x0F, + LTC294X_REG_TEMPERATURE_MSB = 0x14, + LTC294X_REG_TEMPERATURE_LSB = 0x15, +}; + +#define LTC2943_REG_CONTROL_MODE_MASK (BIT(7) | BIT(6)) +#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7) +#define LTC294X_REG_CONTROL_PRESCALER_MASK (BIT(5) | BIT(4) | BIT(3)) +#define LTC294X_REG_CONTROL_SHUTDOWN_MASK (BIT(0)) +#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \ + ((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK) +#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0 + +#define LTC2941_NUM_REGS 0x08 +#define LTC2943_NUM_REGS 0x18 + +struct ltc294x_info { + struct i2c_client *client; /* I2C Client pointer */ + struct power_supply supply; /* Supply pointer */ + struct delayed_work work; /* Work scheduler */ + int num_regs; /* Number of registers (chip type) */ + int id; /* Identifier of ltc294x chip */ + int charge; /* Last charge register content */ + int r_sense; /* mOhm */ + int Qlsb; /* nAh */ +}; + +static DEFINE_IDR(ltc294x_id); +static DEFINE_MUTEX(ltc294x_lock); + +static inline int convert_bin_to_uAh( + const struct ltc294x_info *info, int Q) +{ + return ((Q * (info->Qlsb / 10))) / 100; +} + +static inline int convert_uAh_to_bin( + const struct ltc294x_info *info, int uAh) +{ + int Q; + + Q = (uAh * 100) / (info->Qlsb/10); + return (Q < LTC294X_MAX_VALUE) ? Q : LTC294X_MAX_VALUE; +} + +static int ltc294x_read_regs(struct i2c_client *client, + enum ltc294x_reg reg, u8 *buf, int num_regs) +{ + int ret; + struct i2c_msg msgs[2] = { }; + u8 reg_start = reg; + + msgs[0].addr = client->addr; + msgs[0].len = 1; + msgs[0].buf = ®_start; + + msgs[1].addr = client->addr; + msgs[1].len = num_regs; + msgs[1].buf = buf; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, &msgs[0], 2); + if (ret < 0) { + dev_err(&client->dev, "ltc2941 read_reg failed!\n"); + return ret; + } + + dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n", + __func__, reg, num_regs, *buf); + + return 0; +} + +static int ltc294x_write_regs(struct i2c_client *client, + enum ltc294x_reg reg, const u8 *buf, int num_regs) +{ + int ret; + u8 reg_start = reg; + + ret = i2c_smbus_write_i2c_block_data(client, reg_start, num_regs, buf); + if (ret < 0) { + dev_err(&client->dev, "ltc2941 write_reg failed!\n"); + return ret; + } + + dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n", + __func__, reg, num_regs, *buf); + + return 0; +} + +static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp) +{ + int ret; + u8 value; + u8 control; + + /* Read status and control registers */ + ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1); + if (ret < 0) { + dev_err(&info->client->dev, + "Could not read registers from device\n"); + goto error_exit; + } + + control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) | + LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED; + /* Put the 2943 into "monitor" mode, so it measures every 10 sec */ + if (info->num_regs == LTC2943_NUM_REGS) + control |= LTC2943_REG_CONTROL_MODE_SCAN; + + if (value != control) { + ret = ltc294x_write_regs(info->client, + LTC294X_REG_CONTROL, &control, 1); + if (ret < 0) { + dev_err(&info->client->dev, + "Could not write register\n"); + goto error_exit; + } + } + + return 0; + +error_exit: + return ret; +} + +static int ltc294x_read_charge_register(const struct ltc294x_info *info) +{ + int ret; + u8 datar[2]; + + ret = ltc294x_read_regs(info->client, + LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2); + if (ret < 0) + return ret; + return (datar[0] << 8) + datar[1]; +} + +static int ltc294x_get_charge_now(const struct ltc294x_info *info, int *val) +{ + int value = ltc294x_read_charge_register(info); + + if (value < 0) + return value; + /* When r_sense < 0, this counts up when the battery discharges */ + if (info->Qlsb < 0) + value -= 0xFFFF; + *val = convert_bin_to_uAh(info, value); + return 0; +} + +static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val) +{ + int ret; + u8 dataw[2]; + u8 ctrl_reg; + s32 value; + + value = convert_uAh_to_bin(info, val); + /* Direction depends on how sense+/- were connected */ + if (info->Qlsb < 0) + value += 0xFFFF; + if ((value < 0) || (value > 0xFFFF)) /* input validation */ + return -EINVAL; + + /* Read control register */ + ret = ltc294x_read_regs(info->client, + LTC294X_REG_CONTROL, &ctrl_reg, 1); + if (ret < 0) + return ret; + /* Disable analog section */ + ctrl_reg |= LTC294X_REG_CONTROL_SHUTDOWN_MASK; + ret = ltc294x_write_regs(info->client, + LTC294X_REG_CONTROL, &ctrl_reg, 1); + if (ret < 0) + return ret; + /* Set new charge value */ + dataw[0] = I16_MSB(value); + dataw[1] = I16_LSB(value); + ret = ltc294x_write_regs(info->client, + LTC294X_REG_ACC_CHARGE_MSB, &dataw[0], 2); + if (ret < 0) + goto error_exit; + /* Enable analog section */ +error_exit: + ctrl_reg &= ~LTC294X_REG_CONTROL_SHUTDOWN_MASK; + ret = ltc294x_write_regs(info->client, + LTC294X_REG_CONTROL, &ctrl_reg, 1); + + return ret < 0 ? ret : 0; +} + +static int ltc294x_get_charge_counter( + const struct ltc294x_info *info, int *val) +{ + int value = ltc294x_read_charge_register(info); + + if (value < 0) + return value; + value -= LTC294X_MID_SUPPLY; + *val = convert_bin_to_uAh(info, value); + return 0; +} + +static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val) +{ + int ret; + u8 datar[2]; + u32 value; + + ret = ltc294x_read_regs(info->client, + LTC294X_REG_VOLTAGE_MSB, &datar[0], 2); + value = (datar[0] << 8) | datar[1]; + *val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */ + return ret; +} + +static int ltc294x_get_current(const struct ltc294x_info *info, int *val) +{ + int ret; + u8 datar[2]; + s32 value; + + ret = ltc294x_read_regs(info->client, + LTC294X_REG_CURRENT_MSB, &datar[0], 2); + value = (datar[0] << 8) | datar[1]; + value -= 0x7FFF; + /* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm, + * the formula below keeps everything in s32 range while preserving + * enough digits */ + *val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */ + return ret; +} + +static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val) +{ + int ret; + u8 datar[2]; + u32 value; + + ret = ltc294x_read_regs(info->client, + LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2); + value = (datar[0] << 8) | datar[1]; + /* Full-scale is 510 Kelvin, convert to centidegrees */ + *val = (((51000 * value) / 0xFFFF) - 27215); + return ret; +} + +static int ltc294x_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct ltc294x_info *info = + container_of(psy, struct ltc294x_info, supply); + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_NOW: + return ltc294x_get_charge_now(info, &val->intval); + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + return ltc294x_get_charge_counter(info, &val->intval); + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + return ltc294x_get_voltage(info, &val->intval); + case POWER_SUPPLY_PROP_CURRENT_NOW: + return ltc294x_get_current(info, &val->intval); + case POWER_SUPPLY_PROP_TEMP: + return ltc294x_get_temperature(info, &val->intval); + default: + return -EINVAL; + } +} + +static int ltc294x_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct ltc294x_info *info = + container_of(psy, struct ltc294x_info, supply); + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_NOW: + return ltc294x_set_charge_now(info, val->intval); + default: + return -EPERM; + } +} + +static int ltc294x_property_is_writeable( + struct power_supply *psy, enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_NOW: + return 1; + default: + return 0; + } +} + +static void ltc294x_update(struct ltc294x_info *info) +{ + int charge = ltc294x_read_charge_register(info); + + if (charge != info->charge) { + info->charge = charge; + power_supply_changed(&info->supply); + } +} + +static void ltc294x_work(struct work_struct *work) +{ + struct ltc294x_info *info; + + info = container_of(work, struct ltc294x_info, work.work); + ltc294x_update(info); + schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ); +} + +static enum power_supply_property ltc294x_properties[] = { + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_TEMP, +}; + +static int ltc294x_i2c_remove(struct i2c_client *client) +{ + struct ltc294x_info *info = i2c_get_clientdata(client); + + cancel_delayed_work(&info->work); + power_supply_unregister(&info->supply); + kfree(info->supply.name); + mutex_lock(<c294x_lock); + idr_remove(<c294x_id, info->id); + mutex_unlock(<c294x_lock); + return 0; +} + +static int ltc294x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ltc294x_info *info; + int ret; + int num; + u32 prescaler_exp; + s32 r_sense; + struct device_node *np; + + mutex_lock(<c294x_lock); + ret = idr_alloc(<c294x_id, client, 0, 0, GFP_KERNEL); + mutex_unlock(<c294x_lock); + if (ret < 0) + goto fail_id; + + num = ret; + + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + ret = -ENOMEM; + goto fail_info; + } + + i2c_set_clientdata(client, info); + + info->num_regs = id->driver_data; + info->supply.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num); + if (!info->supply.name) { + ret = -ENOMEM; + goto fail_name; + } + + np = of_node_get(client->dev.of_node); + + /* r_sense can be negative, when sense+ is connected to the battery + * instead of the sense-. This results in reversed measurements. */ + ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense); + if (ret < 0) { + dev_err(&client->dev, + "Could not find lltc,resistor-sense in devicetree\n"); + goto fail_name; + } + info->r_sense = r_sense; + + ret = of_property_read_u32(np, "lltc,prescaler-exponent", + &prescaler_exp); + if (ret < 0) { + dev_warn(&client->dev, + "lltc,prescaler-exponent not in devicetree\n"); + prescaler_exp = LTC2941_MAX_PRESCALER_EXP; + } + + if (info->num_regs == LTC2943_NUM_REGS) { + if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP) + prescaler_exp = LTC2943_MAX_PRESCALER_EXP; + info->Qlsb = ((340 * 50000) / r_sense) / + (4096 / (1 << (2*prescaler_exp))); + } else { + if (prescaler_exp > LTC2941_MAX_PRESCALER_EXP) + prescaler_exp = LTC2941_MAX_PRESCALER_EXP; + info->Qlsb = ((58 * 50000) / r_sense) / + (128 / (1 << prescaler_exp)); + } + + info->client = client; + info->id = num; + info->supply.type = POWER_SUPPLY_TYPE_BATTERY; + info->supply.properties = ltc294x_properties; + if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB) + info->supply.num_properties = + ARRAY_SIZE(ltc294x_properties); + else if (info->num_regs >= LTC294X_REG_CURRENT_LSB) + info->supply.num_properties = + ARRAY_SIZE(ltc294x_properties) - 1; + else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB) + info->supply.num_properties = + ARRAY_SIZE(ltc294x_properties) - 2; + else + info->supply.num_properties = + ARRAY_SIZE(ltc294x_properties) - 3; + info->supply.get_property = ltc294x_get_property; + info->supply.set_property = ltc294x_set_property; + info->supply.property_is_writeable = ltc294x_property_is_writeable; + info->supply.external_power_changed = NULL; + + INIT_DELAYED_WORK(&info->work, ltc294x_work); + + ret = ltc294x_reset(info, prescaler_exp); + if (ret < 0) { + dev_err(&client->dev, "Communication with chip failed\n"); + goto fail_comm; + } + + ret = power_supply_register(&client->dev, &info->supply); + if (ret) { + dev_err(&client->dev, "failed to register ltc2941\n"); + goto fail_register; + } else { + schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ); + } + + return 0; + +fail_register: + kfree(info->supply.name); +fail_comm: +fail_name: +fail_info: + mutex_lock(<c294x_lock); + idr_remove(<c294x_id, num); + mutex_unlock(<c294x_lock); +fail_id: + return ret; +} + +#ifdef CONFIG_PM_SLEEP + +static int ltc294x_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc294x_info *info = i2c_get_clientdata(client); + + cancel_delayed_work(&info->work); + return 0; +} + +static int ltc294x_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc294x_info *info = i2c_get_clientdata(client); + + schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ); + return 0; +} + +static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume); +#define LTC294X_PM_OPS (<c294x_pm_ops) + +#else +#define LTC294X_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + + +static const struct i2c_device_id ltc294x_i2c_id[] = { + {"ltc2941", LTC2941_NUM_REGS}, + {"ltc2943", LTC2943_NUM_REGS}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id); + +static struct i2c_driver ltc294x_driver = { + .driver = { + .name = "LTC2941", + .pm = LTC294X_PM_OPS, + }, + .probe = ltc294x_i2c_probe, + .remove = ltc294x_i2c_remove, + .id_table = ltc294x_i2c_id, +}; +module_i2c_driver(ltc294x_driver); + +MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems"); +MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products"); +MODULE_DESCRIPTION("LTC2941/LTC2943 Battery Gas Gauge IC driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From a30067bb541f834b22c9cfa2dd99ebe3da709115 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 23 Jan 2015 11:38:00 +0100 Subject: power: test_power: Use enum as index for array of supplies Replace hard-coded numbers for indices of power supply array with enum. This improves a little the readability as one does not have to guess which power supply is associated with number. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/test_power.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c index 0152f35dca5c..f26b1fa00fe1 100644 --- a/drivers/power/test_power.c +++ b/drivers/power/test_power.c @@ -21,6 +21,13 @@ #include #include +enum test_power_id { + TEST_AC, + TEST_BATTERY, + TEST_USB, + TEST_POWER_NUM, +}; + static int ac_online = 1; static int usb_online = 1; static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING; @@ -147,7 +154,7 @@ static char *test_power_ac_supplied_to[] = { }; static struct power_supply test_power_supplies[] = { - { + [TEST_AC] = { .name = "test_ac", .type = POWER_SUPPLY_TYPE_MAINS, .supplied_to = test_power_ac_supplied_to, @@ -155,13 +162,15 @@ static struct power_supply test_power_supplies[] = { .properties = test_power_ac_props, .num_properties = ARRAY_SIZE(test_power_ac_props), .get_property = test_power_get_ac_property, - }, { + }, + [TEST_BATTERY] = { .name = "test_battery", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = test_power_battery_props, .num_properties = ARRAY_SIZE(test_power_battery_props), .get_property = test_power_get_battery_property, - }, { + }, + [TEST_USB] = { .name = "test_usb", .type = POWER_SUPPLY_TYPE_USB, .supplied_to = test_power_ac_supplied_to, @@ -178,6 +187,8 @@ static int __init test_power_init(void) int i; int ret; + BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_supplies)); + for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) { ret = power_supply_register(NULL, &test_power_supplies[i]); if (ret) { @@ -309,7 +320,7 @@ static inline void signal_power_supply_changed(struct power_supply *psy) static int param_set_ac_online(const char *key, const struct kernel_param *kp) { ac_online = map_get_value(map_ac_online, key, ac_online); - signal_power_supply_changed(&test_power_supplies[0]); + signal_power_supply_changed(&test_power_supplies[TEST_AC]); return 0; } @@ -322,7 +333,7 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp) static int param_set_usb_online(const char *key, const struct kernel_param *kp) { usb_online = map_get_value(map_ac_online, key, usb_online); - signal_power_supply_changed(&test_power_supplies[2]); + signal_power_supply_changed(&test_power_supplies[TEST_USB]); return 0; } @@ -336,7 +347,7 @@ static int param_set_battery_status(const char *key, const struct kernel_param *kp) { battery_status = map_get_value(map_status, key, battery_status); - signal_power_supply_changed(&test_power_supplies[1]); + signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); return 0; } @@ -350,7 +361,7 @@ static int param_set_battery_health(const char *key, const struct kernel_param *kp) { battery_health = map_get_value(map_health, key, battery_health); - signal_power_supply_changed(&test_power_supplies[1]); + signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); return 0; } @@ -364,7 +375,7 @@ static int param_set_battery_present(const char *key, const struct kernel_param *kp) { battery_present = map_get_value(map_present, key, battery_present); - signal_power_supply_changed(&test_power_supplies[0]); + signal_power_supply_changed(&test_power_supplies[TEST_AC]); return 0; } @@ -380,7 +391,7 @@ static int param_set_battery_technology(const char *key, { battery_technology = map_get_value(map_technology, key, battery_technology); - signal_power_supply_changed(&test_power_supplies[1]); + signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); return 0; } @@ -401,7 +412,7 @@ static int param_set_battery_capacity(const char *key, return -EINVAL; battery_capacity = tmp; - signal_power_supply_changed(&test_power_supplies[1]); + signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); return 0; } @@ -416,7 +427,7 @@ static int param_set_battery_voltage(const char *key, return -EINVAL; battery_voltage = tmp; - signal_power_supply_changed(&test_power_supplies[1]); + signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); return 0; } -- cgit v1.2.3 From 25a5b57dd76fbcbcb4107aa95400862cb924470d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 25 Jan 2015 12:30:40 -0800 Subject: power/reset: arm-versatile: Register with kernel restart handler Register with kernel restart handler instead of setting arm_pm_restart directly. Select high priority since the restart handler is instantiated through devicetree, indicating that it should be used if configured. Signed-off-by: Guenter Roeck Signed-off-by: Sebastian Reichel --- drivers/power/reset/arm-versatile-reboot.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/arm-versatile-reboot.c b/drivers/power/reset/arm-versatile-reboot.c index adea9d0c38e0..b208073c887d 100644 --- a/drivers/power/reset/arm-versatile-reboot.c +++ b/drivers/power/reset/arm-versatile-reboot.c @@ -13,7 +13,6 @@ #include #include #include -#include #define INTEGRATOR_HDR_CTRL_OFFSET 0x0C #define INTEGRATOR_HDR_LOCK_OFFSET 0x14 @@ -69,7 +68,8 @@ static const struct of_device_id versatile_reboot_of_match[] = { {}, }; -static void versatile_reboot(enum reboot_mode mode, const char *cmd) +static int versatile_reboot(struct notifier_block *this, unsigned long mode, + void *cmd) { /* Unlock the reset register */ /* Then hit reset on the different machines */ @@ -113,12 +113,20 @@ static void versatile_reboot(enum reboot_mode mode, const char *cmd) break; } dsb(); + + return NOTIFY_DONE; } +static struct notifier_block versatile_reboot_nb = { + .notifier_call = versatile_reboot, + .priority = 192, +}; + static int __init versatile_reboot_probe(void) { const struct of_device_id *reboot_id; struct device_node *np; + int err; np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match, &reboot_id); @@ -130,7 +138,10 @@ static int __init versatile_reboot_probe(void) if (IS_ERR(syscon_regmap)) return PTR_ERR(syscon_regmap); - arm_pm_restart = versatile_reboot; + err = register_restart_handler(&versatile_reboot_nb); + if (err) + return err; + pr_info("versatile reboot driver registered\n"); return 0; } -- cgit v1.2.3 From 481ff6ff967fefdbfb967f7251e40c22a312d244 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 25 Jan 2015 12:30:41 -0800 Subject: power/reset: at91: Register with kernel restart handler Register with kernel restart handler instead of setting arm_pm_restart directly. Register with high priority since the driver unconditionally overwrites other restart handlers if instantiated. Signed-off-by: Guenter Roeck Signed-off-by: Sebastian Reichel --- drivers/power/reset/at91-reset.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 69a75d99ae92..13584e24736a 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -17,8 +17,6 @@ #include #include -#include - #include #include @@ -54,7 +52,8 @@ static void __iomem *at91_ramc_base[2], *at91_rstc_base; * reset register it can be left driving the data bus and * killing the chance of a subsequent boot from NAND */ -static void at91sam9260_restart(enum reboot_mode mode, const char *cmd) +static int at91sam9260_restart(struct notifier_block *this, unsigned long mode, + void *cmd) { asm volatile( /* Align to cache lines */ @@ -76,9 +75,12 @@ static void at91sam9260_restart(enum reboot_mode mode, const char *cmd) "r" (1), "r" (AT91_SDRAMC_LPCB_POWER_DOWN), "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)); + + return NOTIFY_DONE; } -static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd) +static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode, + void *cmd) { asm volatile( /* @@ -117,6 +119,8 @@ static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd) "r" (AT91_DDRSDRC_LPCB_POWER_DOWN), "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST) : "r0"); + + return NOTIFY_DONE; } static void __init at91_reset_status(struct platform_device *pdev) @@ -161,6 +165,10 @@ static struct of_device_id at91_reset_of_match[] = { { /* sentinel */ } }; +static struct notifier_block at91_restart_nb = { + .priority = 192, +}; + static int at91_reset_of_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -183,9 +191,8 @@ static int at91_reset_of_probe(struct platform_device *pdev) } match = of_match_node(at91_reset_of_match, pdev->dev.of_node); - arm_pm_restart = match->data; - - return 0; + at91_restart_nb.notifier_call = match->data; + return register_restart_handler(&at91_restart_nb); } static int at91_reset_platform_probe(struct platform_device *pdev) @@ -212,10 +219,11 @@ static int at91_reset_platform_probe(struct platform_device *pdev) } match = platform_get_device_id(pdev); - arm_pm_restart = (void (*)(enum reboot_mode, const char*)) - match->driver_data; + at91_restart_nb.notifier_call = + (int (*)(struct notifier_block *, + unsigned long, void *)) match->driver_data; - return 0; + return register_restart_handler(&at91_restart_nb); } static int at91_reset_probe(struct platform_device *pdev) -- cgit v1.2.3 From b2b3a8b934e649d0f0659584f2186602dbb391b5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 25 Jan 2015 12:30:42 -0800 Subject: power/reset: Remove sun6i reboot driver sun6i restart is now handled by its watchdog driver directly, so this driver is no longer needed. Signed-off-by: Guenter Roeck Acked-by: Maxime Ripard Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 6 --- drivers/power/reset/Makefile | 1 - drivers/power/reset/sun6i-reboot.c | 85 -------------------------------------- 3 files changed, 92 deletions(-) delete mode 100644 drivers/power/reset/sun6i-reboot.c (limited to 'drivers/power') diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index bf5acb3edd85..4cb5744cd259 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -109,12 +109,6 @@ config POWER_RESET_RESTART Instead they restart, and u-boot holds the SoC until the user presses a key. u-boot then boots into Linux. -config POWER_RESET_SUN6I - bool "Allwinner A31 SoC reset driver" - depends on ARCH_SUNXI - help - Reboot support for the Allwinner A31 SoCs. - config POWER_RESET_ST bool "ST restart power-off driver" depends on ARCH_STI diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 1631652009ac..11de15bae52e 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o -obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o diff --git a/drivers/power/reset/sun6i-reboot.c b/drivers/power/reset/sun6i-reboot.c deleted file mode 100644 index af2cd7ff2fe8..000000000000 --- a/drivers/power/reset/sun6i-reboot.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Allwinner A31 SoCs reset code - * - * Copyright (C) 2012-2014 Maxime Ripard - * - * Maxime Ripard - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#define SUN6I_WATCHDOG1_IRQ_REG 0x00 -#define SUN6I_WATCHDOG1_CTRL_REG 0x10 -#define SUN6I_WATCHDOG1_CTRL_RESTART BIT(0) -#define SUN6I_WATCHDOG1_CONFIG_REG 0x14 -#define SUN6I_WATCHDOG1_CONFIG_RESTART BIT(0) -#define SUN6I_WATCHDOG1_CONFIG_IRQ BIT(1) -#define SUN6I_WATCHDOG1_MODE_REG 0x18 -#define SUN6I_WATCHDOG1_MODE_ENABLE BIT(0) - -static void __iomem *wdt_base; - -static void sun6i_wdt_restart(enum reboot_mode mode, const char *cmd) -{ - if (!wdt_base) - return; - - /* Disable interrupts */ - writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG); - - /* We want to disable the IRQ and just reset the whole system */ - writel(SUN6I_WATCHDOG1_CONFIG_RESTART, - wdt_base + SUN6I_WATCHDOG1_CONFIG_REG); - - /* Enable timer. The default and lowest interval value is 0.5s */ - writel(SUN6I_WATCHDOG1_MODE_ENABLE, - wdt_base + SUN6I_WATCHDOG1_MODE_REG); - - /* Restart the watchdog. */ - writel(SUN6I_WATCHDOG1_CTRL_RESTART, - wdt_base + SUN6I_WATCHDOG1_CTRL_REG); - - while (1) { - mdelay(5); - writel(SUN6I_WATCHDOG1_MODE_ENABLE, - wdt_base + SUN6I_WATCHDOG1_MODE_REG); - } -} - -static int sun6i_reboot_probe(struct platform_device *pdev) -{ - wdt_base = of_iomap(pdev->dev.of_node, 0); - if (!wdt_base) { - WARN(1, "failed to map watchdog base address"); - return -ENODEV; - } - - arm_pm_restart = sun6i_wdt_restart; - - return 0; -} - -static struct of_device_id sun6i_reboot_of_match[] = { - { .compatible = "allwinner,sun6i-a31-wdt" }, - {} -}; - -static struct platform_driver sun6i_reboot_driver = { - .probe = sun6i_reboot_probe, - .driver = { - .name = "sun6i-reboot", - .of_match_table = sun6i_reboot_of_match, - }, -}; -module_platform_driver(sun6i_reboot_driver); -- cgit v1.2.3 From 453fe4f11c2ac18b654db13daeaa71409ef142e0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 25 Jan 2015 12:30:43 -0800 Subject: power/reset: st-poweroff: Register with kernel restart handler Register with kernel restart handler instead of setting arm_pm_restart directly. Select high priority since the restart handler is instantiated through devicetree, indicating that it should be used if configured. Signed-off-by: Guenter Roeck Signed-off-by: Sebastian Reichel --- drivers/power/reset/st-poweroff.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/st-poweroff.c b/drivers/power/reset/st-poweroff.c index a0acf25ee2a2..27383de9caa8 100644 --- a/drivers/power/reset/st-poweroff.c +++ b/drivers/power/reset/st-poweroff.c @@ -15,10 +15,9 @@ #include #include #include +#include #include -#include - struct reset_syscfg { struct regmap *regmap; /* syscfg used for reset */ @@ -75,7 +74,8 @@ static struct reset_syscfg stid127_reset = { static struct reset_syscfg *st_restart_syscfg; -static void st_restart(enum reboot_mode reboot_mode, const char *cmd) +static int st_restart(struct notifier_block *this, unsigned long mode, + void *cmd) { /* reset syscfg updated */ regmap_update_bits(st_restart_syscfg->regmap, @@ -88,8 +88,15 @@ static void st_restart(enum reboot_mode reboot_mode, const char *cmd) st_restart_syscfg->offset_rst_msk, st_restart_syscfg->mask_rst_msk, 0); + + return NOTIFY_DONE; } +static struct notifier_block st_restart_nb = { + .notifier_call = st_restart, + .priority = 192, +}; + static struct of_device_id st_reset_of_match[] = { { .compatible = "st,stih415-restart", @@ -126,9 +133,7 @@ static int st_reset_probe(struct platform_device *pdev) return PTR_ERR(st_restart_syscfg->regmap); } - arm_pm_restart = st_restart; - - return 0; + return register_restart_handler(&st_restart_nb); } static struct platform_driver st_reset_driver = { -- cgit v1.2.3 From 7fa650bc0d2c124d28d6c532de7a7d739348953d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 25 Jan 2015 12:30:44 -0800 Subject: power/reset: st-poweroff: Fix misleading Kconfig description The st-poweroff driver does not really power off the system but resets it, so Kconfig should not claim that the driver would handle power-off. Signed-off-by: Guenter Roeck Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 4cb5744cd259..bb81e2a88ff3 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -110,10 +110,10 @@ config POWER_RESET_RESTART user presses a key. u-boot then boots into Linux. config POWER_RESET_ST - bool "ST restart power-off driver" + bool "ST restart driver" depends on ARCH_STI help - Power off and reset support for STMicroelectronics boards. + Reset support for STMicroelectronics boards. config POWER_RESET_VERSATILE bool "ARM Versatile family reboot driver" -- cgit v1.2.3 From 7bae8f04818cb388ca8652a55daa4132b469295e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 25 Jan 2015 12:30:45 -0800 Subject: power/reset: restart-poweroff: Remove arm dependencies This driver is now arm specific anymore, so there is no need to include an arm specific include file. Also drop unnecessary depencency on ARM from Kconfig. Signed-off-by: Guenter Roeck Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 1 - drivers/power/reset/restart-poweroff.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index bb81e2a88ff3..27f6646731b0 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -103,7 +103,6 @@ config POWER_RESET_QNAP config POWER_RESET_RESTART bool "Restart power-off driver" - depends on ARM help Some boards don't actually have the ability to power off. Instead they restart, and u-boot holds the SoC until the diff --git a/drivers/power/reset/restart-poweroff.c b/drivers/power/reset/restart-poweroff.c index f46f2c2e4648..41b22c4d5236 100644 --- a/drivers/power/reset/restart-poweroff.c +++ b/drivers/power/reset/restart-poweroff.c @@ -16,7 +16,6 @@ #include #include #include -#include static void restart_poweroff_do_poweroff(void) { -- cgit v1.2.3 From 24727b45b484e8937dcde53fa8d1aa70ac30ec0c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 27 Jan 2015 16:51:54 +0100 Subject: power_supply: 88pm860x: Fix leaked power supply on probe fail Driver forgot to unregister power supply if request_threaded_irq() failed in probe(). In such case the memory associated with power supply leaked. Signed-off-by: Krzysztof Kozlowski Fixes: a830d28b48bf ("power_supply: Enable battery-charger for 88pm860x") Cc: Signed-off-by: Sebastian Reichel --- drivers/power/88pm860x_charger.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power') diff --git a/drivers/power/88pm860x_charger.c b/drivers/power/88pm860x_charger.c index 650930e4fa79..734ec4afa14d 100644 --- a/drivers/power/88pm860x_charger.c +++ b/drivers/power/88pm860x_charger.c @@ -711,6 +711,7 @@ static int pm860x_charger_probe(struct platform_device *pdev) return 0; out_irq: + power_supply_unregister(&info->usb); while (--i >= 0) free_irq(info->irq[i], info); out: -- cgit v1.2.3