diff mbox

[v2,4/6] power: supply: gpio-charger: generic gpio algo

Message ID 20180305180631.GE5533@lenoch (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Ladislav Michl March 5, 2018, 6:06 p.m. UTC
Just a proof of concept...

Signed-off-by: Ladislav Michl <ladis@linux-mips.org>
---
 Changelog:
 - v2: Rebased on top of another round of cleanup patches

 drivers/power/supply/gpio-charger.c | 243 ++++++++++++++++++++++++++++--------
 1 file changed, 193 insertions(+), 50 deletions(-)
diff mbox

Patch

diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c
index cfa7b5a4573d..08e0c7fa6833 100644
--- a/drivers/power/supply/gpio-charger.c
+++ b/drivers/power/supply/gpio-charger.c
@@ -23,19 +23,60 @@ 
 #include <linux/power_supply.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/gpio/consumer.h>
 
 #include <linux/power/gpio-charger.h>
 
-struct gpio_charger {
-	unsigned int irq;
+struct gpio_charger_pin_map {
+	int val;
+	int res;
+};
+
+#define _VA_NARGS(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
+#define VA_NARGS(...)	_VA_NARGS(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1) / 2
+#define MAP(mask, ...)	mask, VA_NARGS(__VA_ARGS__), \
+			(struct gpio_charger_pin_map[VA_NARGS(__VA_ARGS__)]) \
+			{__VA_ARGS__}
+
+struct gpio_charger_mapping {
+	enum power_supply_property psp;
+	int defres;
+	int mask;
+	int nmaps;
+	struct gpio_charger_pin_map *map;
+};
+
+#define FLAG_PIN_OPTIONAL	0x01
+#define MAPPING(map)		ARRAY_SIZE(map), map
+
+struct gpio_charger_config {
+	int nmaps;
+	struct gpio_charger_mapping *mapping;
+	struct {
+		char *label;
+		int flags;
+	} gpios[];
+};
+
+struct gpio_charger_pin {
+	struct gpio_desc *gpiod;
+	int irq;
 	bool wakeup_enabled;
+};
 
+struct gpio_charger {
 	struct power_supply *charger;
 	struct power_supply_desc charger_desc;
-	struct gpio_desc *gpiod;
+
+	int nmaps;
+	struct gpio_charger_mapping *mapping;
+
+	int npins;
+	struct gpio_charger_pin pin[];
 };
 
+
 static irqreturn_t gpio_charger_irq(int irq, void *devid)
 {
 	struct power_supply *charger = devid;
@@ -50,17 +91,56 @@  static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy)
 	return power_supply_get_drvdata(psy);
 }
 
+static inline int gpio_charger_pins_available(struct gpio_charger *charger,
+					struct gpio_charger_mapping *mapping)
+{
+	int i;
+
+	for (i = 0; i < charger->npins; i++)
+		if (!charger->pin[i].gpiod && ((1 << i) & mapping->mask))
+			return 0;
+	return 1;
+}
+
 static int gpio_charger_get_property(struct power_supply *psy,
 		enum power_supply_property psp, union power_supply_propval *val)
 {
+	int i, v, res;
+	struct gpio_charger_pin *pin;
+	struct gpio_charger_mapping *mapping = NULL;
 	struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
 
-	switch (psp) {
-	case POWER_SUPPLY_PROP_ONLINE:
-		val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod);
-		break;
-	default:
+	for (i = 0; i < gpio_charger->nmaps; i++) {
+		if (gpio_charger->mapping[i].psp == psp) {
+			mapping = &gpio_charger->mapping[i];
+			break;
+		}
+	}
+
+	if (!mapping)
 		return -EINVAL;
+
+	if (gpio_charger_pins_available(gpio_charger, mapping)) {
+		res = 0;
+		for (i = 0; i < gpio_charger->npins; i++) {
+			pin = &gpio_charger->pin[i];
+			if (((1 << i) & mapping->mask) == 0)
+				continue;
+			v = gpiod_get_value_cansleep(pin->gpiod);
+			if (v < 0)
+				return v;
+			res |= v << i;
+		}
+
+		val->intval = mapping->defres;
+		for (i = 0; i < mapping->nmaps; i++) {
+			if (mapping->map[i].val == res) {
+				val->intval = mapping->map[i].res;
+				break;
+			}
+		}
+	} else {
+			val->intval = mapping->defres;
 	}
 
 	return 0;
@@ -94,40 +174,35 @@  static enum power_supply_type gpio_charger_get_type(struct device *dev)
 	return POWER_SUPPLY_TYPE_UNKNOWN;
 }
 
-static enum power_supply_property gpio_charger_properties[] = {
+static struct gpio_charger_mapping simple_mapping = {
 	POWER_SUPPLY_PROP_ONLINE,
+	0,
+	MAP(0x01, { 0x01, 1 }),
 };
 
-static int gpio_charger_probe(struct platform_device *pdev)
+static int gpio_charger_simple_init(struct device *dev,
+			struct gpio_charger *gpio_charger,
+			const struct gpio_charger_platform_data *pdata)
 {
-	struct device *dev = &pdev->dev;
-	const struct gpio_charger_platform_data *pdata = dev->platform_data;
-	struct power_supply_config psy_cfg = {};
-	struct gpio_charger *gpio_charger;
+	int err;
+	struct gpio_desc *gpiod;
 	struct power_supply_desc *charger_desc;
 	unsigned long flags;
-	int irq, ret;
-
-	if (!pdata && !dev->of_node) {
-		dev_err(dev, "No platform data\n");
-		return -ENOENT;
-	}
-
-	gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL);
-	if (!gpio_charger)
-		return -ENOMEM;
+	static enum power_supply_property properties[] = {
+		POWER_SUPPLY_PROP_ONLINE,
+	};
 
 	/*
 	 * This will fetch a GPIO descriptor from device tree, ACPI or
 	 * boardfile descriptor tables. It's good to try this first.
 	 */
-	gpio_charger->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
+	gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
 
 	/*
 	 * If this fails and we're not using device tree, try the
 	 * legacy platform data method.
 	 */
-	if (IS_ERR(gpio_charger->gpiod) && !dev->of_node) {
+	if (IS_ERR(gpiod) && !dev->of_node && pdata) {
 		/* Non-DT: use legacy GPIO numbers */
 		if (!gpio_is_valid(pdata->gpio)) {
 			dev_err(dev, "Invalid gpio pin in pdata\n");
@@ -136,25 +211,77 @@  static int gpio_charger_probe(struct platform_device *pdev)
 		flags = GPIOF_IN;
 		if (pdata->gpio_active_low)
 			flags |= GPIOF_ACTIVE_LOW;
-		ret = devm_gpio_request_one(dev, pdata->gpio, flags,
+		err = devm_gpio_request_one(dev, pdata->gpio, flags,
 					    dev_name(dev));
-		if (ret) {
-			dev_err(dev, "Failed to request gpio pin: %d\n", ret);
-			return ret;
+		if (err) {
+			dev_err(dev, "Failed to request gpio pin: %d\n", err);
+			return err;
 		}
 		/* Then convert this to gpiod for now */
-		gpio_charger->gpiod = gpio_to_desc(pdata->gpio);
-	} else if (IS_ERR(gpio_charger->gpiod)) {
+		gpiod = gpio_to_desc(pdata->gpio);
+	} else if (IS_ERR(gpiod)) {
 		/* Just try again if this happens */
-		if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER)
-			return -EPROBE_DEFER;
-		dev_err(dev, "error getting GPIO descriptor\n");
-		return PTR_ERR(gpio_charger->gpiod);
+		if (PTR_ERR(gpiod) != -EPROBE_DEFER)
+			dev_err(dev, "error getting GPIO descriptor\n");
+		return PTR_ERR(gpiod);
+	}
+
+	gpio_charger->nmaps = 1;
+	gpio_charger->mapping = &simple_mapping;
+	gpio_charger->npins = 1;
+	gpio_charger->pin[0].gpiod = gpiod;
+
+	charger_desc = &gpio_charger->charger_desc;
+	charger_desc->properties = properties;
+	charger_desc->num_properties = ARRAY_SIZE(properties);
+
+	return 0;
+}
+
+static int gpio_charger_cfg_init(struct device *dev,
+			struct gpio_charger *gpio_charger,
+			struct gpio_charger_config *cfg)
+{
+	return -EINVAL;
+}
+
+static int gpio_charger_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct gpio_charger_platform_data *pdata = dev->platform_data;
+	struct power_supply_config psy_cfg = {};
+	struct gpio_charger_pin *pin;
+	struct gpio_charger *gpio_charger;
+	struct power_supply_desc *charger_desc;
+	struct gpio_charger_config *cfg;
+	int i, ret;
+
+	if (!pdata && !dev->of_node) {
+		dev_err(dev, "No platform data\n");
+		return -ENOENT;
 	}
 
+	i = 1;
+	cfg = (struct gpio_charger_config *)of_device_get_match_data(dev);
+	if (cfg)
+		while (cfg->gpios[i].label)
+			i++;
+
+	gpio_charger = devm_kzalloc(dev, sizeof(struct gpio_charger) +
+					i * sizeof(struct gpio_charger_pin),
+					GFP_KERNEL);
+	if (!gpio_charger)
+		return -ENOMEM;
+
+	if (cfg)
+		ret = gpio_charger_cfg_init(dev, gpio_charger, cfg);
+	else
+		ret = gpio_charger_simple_init(dev, gpio_charger, pdata);
+
+	if (ret)
+		return ret;
+
 	charger_desc = &gpio_charger->charger_desc;
-	charger_desc->properties = gpio_charger_properties;
-	charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties);
 	charger_desc->get_property = gpio_charger_get_property;
 
 	psy_cfg.of_node = dev->of_node;
@@ -181,15 +308,17 @@  static int gpio_charger_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	irq = gpiod_to_irq(gpio_charger->gpiod);
-	if (irq > 0) {
-		ret = devm_request_any_context_irq(dev, irq, gpio_charger_irq,
+	for (i = 0; i < gpio_charger->npins; i++) {
+		pin = &gpio_charger->pin[i];
+		pin->irq = gpiod_to_irq(pin->gpiod);
+		if (pin->irq < 0)
+			continue;
+		ret = devm_request_any_context_irq(dev, pin->irq,
+				gpio_charger_irq,
 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 				dev_name(dev), gpio_charger->charger);
 		if (ret < 0)
 			dev_warn(dev, "Failed to request irq: %d\n", ret);
-		else
-			gpio_charger->irq = irq;
 	}
 
 	platform_set_drvdata(pdev, gpio_charger);
@@ -202,22 +331,36 @@  static int gpio_charger_probe(struct platform_device *pdev)
 #ifdef CONFIG_PM_SLEEP
 static int gpio_charger_suspend(struct device *dev)
 {
+	int i;
+	struct gpio_charger_pin *pin;
 	struct gpio_charger *gpio_charger = dev_get_drvdata(dev);
 
-	if (device_may_wakeup(dev))
-		gpio_charger->wakeup_enabled =
-			!enable_irq_wake(gpio_charger->irq);
+	if (device_may_wakeup(dev)) {
+		for (i = 0; i < gpio_charger->npins; i++) {
+			pin = &gpio_charger->pin[i];
+			if (pin->irq < 0)
+				continue;
+			pin->wakeup_enabled = !enable_irq_wake(pin->irq);
+		}
+	}
 
 	return 0;
 }
 
 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);
+	int i;
+	struct gpio_charger_pin *pin;
+	struct gpio_charger *gpio_charger = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev)) {
+		for (i = 0; i < gpio_charger->npins; i++) {
+			pin = &gpio_charger->pin[i];
+			if (pin->wakeup_enabled)
+				disable_irq_wake(pin->irq);
+		}
+	}
 
-	if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled)
-		disable_irq_wake(gpio_charger->irq);
 	power_supply_changed(gpio_charger->charger);
 
 	return 0;