summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/power/gpio-charger.txt2
-rw-r--r--drivers/power/gpio-charger.c76
-rw-r--r--include/linux/power/gpio-charger.h4
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