diff options
| -rw-r--r-- | Documentation/devicetree/bindings/power/gpio-charger.txt | 2 | ||||
| -rw-r--r-- | drivers/power/gpio-charger.c | 76 | ||||
| -rw-r--r-- | include/linux/power/gpio-charger.h | 4 |
3 files changed, 75 insertions, 7 deletions
diff --git a/Documentation/devicetree/bindings/power/gpio-charger.txt b/Documentation/devicetree/bindings/power/gpio-charger.txt index 74b3472214f..a294ba433e9 100644 --- a/Documentation/devicetree/bindings/power/gpio-charger.txt +++ b/Documentation/devicetree/bindings/power/gpio-charger.txt @@ -11,6 +11,8 @@ Optional properties: - charger-name: Name for the chargers power_supply device. - gpio_active_low: Define this property if GPIO is active low. - supplied_to: Strings with battery names to which this charger supplies power. +- switch_name: String with the charger GPIO switch name (sys/class/switch/<switch_name>). + Switch requires a valid IRQ for the GPIO indicating charging status. Example: diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index 856de6023f9..e7d63bd80c0 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -24,6 +24,8 @@ #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/slab.h> +#include <linux/switch.h> +#include <linux/workqueue.h> #include <linux/power/gpio-charger.h> @@ -32,22 +34,40 @@ struct gpio_charger { unsigned int irq; struct power_supply charger; + + struct switch_dev *sdev; + struct work_struct work; }; +static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) +{ + return container_of(psy, struct gpio_charger, charger); +} + +static inline struct gpio_charger *work_to_gpio_charger(struct work_struct *wk) +{ + return container_of(wk, struct gpio_charger, work); +} + +static inline int gpio_state(const struct gpio_charger_platform_data *pdata) +{ + return !!gpio_get_value_cansleep(pdata->gpio) ^ + !!pdata->gpio_active_low; +} + static irqreturn_t gpio_charger_irq(int irq, void *devid) { struct power_supply *charger = devid; + struct gpio_charger *gpio_charger = psy_to_gpio_charger(charger); + + if (gpio_charger->sdev) + schedule_work(&gpio_charger->work); power_supply_changed(charger); return IRQ_HANDLED; } -static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) -{ - return container_of(psy, struct gpio_charger, charger); -} - static int gpio_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { @@ -56,8 +76,7 @@ static int gpio_charger_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - val->intval = gpio_get_value_cansleep(pdata->gpio); - val->intval ^= pdata->gpio_active_low; + val->intval = gpio_state(pdata); break; default: return -EINVAL; @@ -66,6 +85,14 @@ static int gpio_charger_get_property(struct power_supply *psy, return 0; } +static void gpio_charger_work(struct work_struct *work) +{ + struct gpio_charger *gpio_charger = work_to_gpio_charger(work); + const struct gpio_charger_platform_data *pdata = gpio_charger->pdata; + + switch_set_state(gpio_charger->sdev, gpio_state(pdata)); +} + static enum power_supply_property gpio_charger_properties[] = { POWER_SUPPLY_PROP_ONLINE, }; @@ -131,6 +158,8 @@ of_get_gpio_charger_pdata(struct device *dev) pdata->num_supplicants = count; } + of_property_read_string(np, "switch-name", &pdata->switch_name); + return pdata; } @@ -205,10 +234,40 @@ static int gpio_charger_probe(struct platform_device *pdev) gpio_charger->irq = irq; } + /* Switch needs valid irq since its state changes when irq goes off */ + if (pdata->switch_name && !gpio_charger->irq) { + dev_warn(&pdev->dev, "Must have valid irq for the switch\n"); + } else if (pdata->switch_name && gpio_charger->irq) { + gpio_charger->sdev = devm_kzalloc(&pdev->dev, + sizeof(*gpio_charger->sdev), + GFP_KERNEL); + if (!gpio_charger->sdev) { + dev_err(&pdev->dev, "Failed to alloc switch device\n"); + ret = -ENOMEM; + goto err_irq_free; + } + + gpio_charger->sdev->name = pdata->switch_name; + ret = switch_dev_register(gpio_charger->sdev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register switch device:" + " %d\n", ret); + goto err_irq_free; + } + INIT_WORK(&gpio_charger->work, gpio_charger_work); + } + platform_set_drvdata(pdev, gpio_charger); + /* Set initial state */ + if (gpio_charger->sdev) + schedule_work(&gpio_charger->work); + return 0; +err_irq_free: + if (gpio_charger->irq) + free_irq(gpio_charger->irq, &gpio_charger->charger); err_gpio_free: gpio_free(pdata->gpio); err_free: @@ -220,6 +279,9 @@ static int gpio_charger_remove(struct platform_device *pdev) struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); int i; + if (gpio_charger->sdev) + switch_dev_unregister(gpio_charger->sdev); + if (gpio_charger->irq) free_irq(gpio_charger->irq, &gpio_charger->charger); diff --git a/include/linux/power/gpio-charger.h b/include/linux/power/gpio-charger.h index de1dfe09a03..ebd476da23a 100644 --- a/include/linux/power/gpio-charger.h +++ b/include/linux/power/gpio-charger.h @@ -26,6 +26,8 @@ * @gpio_active_low: Should be set to 1 if the GPIO is active low otherwise 0 * @supplied_to: Array of battery names to which this chargers supplies power * @num_supplicants: Number of entries in the supplied_to array + * @switch_name: Name for the charger GPIO switch. Switch requires a + valid IRQ for the GPIO indicating charging status. */ struct gpio_charger_platform_data { const char *name; @@ -36,6 +38,8 @@ struct gpio_charger_platform_data { char **supplied_to; size_t num_supplicants; + + const char *switch_name; }; #endif |