From: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> To: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>, Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>, Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>, David Woodhouse <dwmw2-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>, Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org>, Felipe Balbi <balbi-l0cyMroinI0@public.gmane.org>, Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> Cc: "Bruno Prémont" <bonbons-ud5FBsm0p/xEiooADzr8i9i2O/JbrIOy@public.gmane.org>, linux-pm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org, devicetree <devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>, linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org, "Hans de Goede" <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> Subject: [PATCH 4/8] power: Add an axp20x-usb-power driver Date: Tue, 9 Jun 2015 23:37:57 +0200 [thread overview] Message-ID: <1433885881-19809-5-git-send-email-hdegoede@redhat.com> (raw) In-Reply-To: <1433885881-19809-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> This adds a driver for the usb power_supply bits of the axp20x PMICs. I initially started writing my own driver, before coming aware of Bruno Prémont's excellent earlier RFC with a driver for this. My driver was lacking CURRENT_MAX and VOLTAGE_MIN support Bruno's drvier has, so I've copied the code for those from his driver. Note that the AC-power-supply and battery charger bits will need separate drivers. Each one needs its own devictree child-node so that other devicetree nodes can reference the right power-supply, and thus each one will get its own mfd-cell / platform_device and platform-driver. Cc: Bruno Prémont <bonbons-ud5FBsm0p/xEiooADzr8i9i2O/JbrIOy@public.gmane.org> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> --- .../bindings/power_supply/axp20x_usb_power.txt | 34 +++ drivers/power/Kconfig | 7 + drivers/power/Makefile | 1 + drivers/power/axp20x_usb_power.c | 241 +++++++++++++++++++++ include/linux/mfd/axp20x.h | 24 ++ 5 files changed, 307 insertions(+) create mode 100644 Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt create mode 100644 drivers/power/axp20x_usb_power.c diff --git a/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt b/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt new file mode 100644 index 0000000..a84d801 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt @@ -0,0 +1,34 @@ +AXP20x USB power supply + +Required Properties: +-compatible: "x-powers,axp202-usb-power-supply" + +This node is a subnode of the axp20x PMIC. + +Example: + +axp209: pmic@34 { + compatible = "x-powers,axp209"; + reg = <0x34>; + interrupt-parent = <&nmi_intc>; + interrupts = <0 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <1>; + + regulators { + x-powers,dcdc-freq = <1500>; + + vdd_cpu: dcdc2 { + regulator-always-on; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1450000>; + regulator-name = "vdd-cpu"; + }; + + ... + }; + + usb_power_supply: usb_power_supply { + compatible = "x-powers,axp202-usb-power-supply"; + }; +}; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 4091fb0..1fee60c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -439,6 +439,13 @@ config BATTERY_RT5033 The fuelgauge calculates and determines the battery state of charge according to battery open circuit voltage. +config AXP20X_POWER + tristate "AXP20x power supply driver" + depends on MFD_AXP20X + help + This driver provides support for the power supply features of + AXP20x PMIC. + source "drivers/power/reset/Kconfig" endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b7b0181..ae0f27d 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o obj-$(CONFIG_PDA_POWER) += pda_power.o obj-$(CONFIG_APM_POWER) += apm_power.o +obj-$(CONFIG_AXP20X_POWER) += axp20x_usb_power.o obj-$(CONFIG_MAX8925_POWER) += max8925_power.o obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o obj-$(CONFIG_WM831X_POWER) += wm831x_power.o diff --git a/drivers/power/axp20x_usb_power.c b/drivers/power/axp20x_usb_power.c new file mode 100644 index 0000000..4be4c8b --- /dev/null +++ b/drivers/power/axp20x_usb_power.c @@ -0,0 +1,241 @@ +/* + * AXP20x PMIC USB power supply status driver + * + * Copyright (C) 2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> + * Copyright (C) 2014 Bruno Prémont <bonbons-ud5FBsm0p/xEiooADzr8i9i2O/JbrIOy@public.gmane.org> + * + * 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. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/axp20x.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define DRVNAME "axp20x-usb-power-supply" + +#define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5) +#define AXP20X_PWR_STATUS_VBUS_USED BIT(4) + +#define AXP20X_USB_STATUS_VBUS_VALID BIT(2) + +#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000) +#define AXP20X_VBUS_CLIMIT_MASK 3 +#define AXP20X_VBUC_CLIMIT_900mA 0 +#define AXP20X_VBUC_CLIMIT_500mA 1 +#define AXP20X_VBUC_CLIMIT_100mA 2 +#define AXP20X_VBUC_CLIMIT_NONE 3 + +#define AXP20X_ADC_EN1_VBUS_CURR BIT(2) +#define AXP20X_ADC_EN1_VBUS_VOLT BIT(3) + +#define AXP20X_VBUS_MON_VBUS_VALID BIT(3) + +struct axp20x_usb_power { + struct regmap *regmap; + struct power_supply *supply; +}; + +static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) +{ + struct axp20x_usb_power *power = devid; + + power_supply_changed(power->supply); + + return IRQ_HANDLED; +} + +static int axp20x_usb_power_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + struct axp20x_usb_power *power = power_supply_get_drvdata(psy); + unsigned int input, v; + int r; + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + r = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); + if (r) + return r; + + val->intval = AXP20X_VBUS_VHOLD_uV(v); + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + r = axp20x_read_16bit(power->regmap, AXP20X_VBUS_V_ADC_H, 12); + if (r < 0) + return r; + + val->intval = r * 1700; /* 1 step = 1.7 mV */ + return 0; + case POWER_SUPPLY_PROP_CURRENT_MAX: + r = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); + if (r) + return r; + + switch (v & AXP20X_VBUS_CLIMIT_MASK) { + case AXP20X_VBUC_CLIMIT_100mA: + val->intval = 100000; + break; + case AXP20X_VBUC_CLIMIT_500mA: + val->intval = 500000; + break; + case AXP20X_VBUC_CLIMIT_900mA: + val->intval = 900000; + break; + case AXP20X_VBUC_CLIMIT_NONE: + val->intval = -1; + break; + } + return 0; + case POWER_SUPPLY_PROP_CURRENT_NOW: + r = axp20x_read_16bit(power->regmap, AXP20X_VBUS_I_ADC_H, 12); + if (r < 0) + return r; + + val->intval = r * 375; /* 1 step = 0.375 mA */ + return 0; + default: + break; + } + + /* All the properties below need the input-status reg value */ + r = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input); + if (r) + return r; + + switch (psp) { + case POWER_SUPPLY_PROP_HEALTH: + if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) { + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + + r = regmap_read(power->regmap, AXP20X_USB_OTG_STATUS, &v); + if (r) + return r; + + if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) { + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + } + + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED); + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property axp20x_usb_power_properties[] = { + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static const struct power_supply_desc axp20x_usb_power_desc = { + .name = "axp20x-usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = axp20x_usb_power_properties, + .num_properties = ARRAY_SIZE(axp20x_usb_power_properties), + .get_property = axp20x_usb_power_get_property, +}; + +static int axp20x_usb_power_probe(struct platform_device *pdev) +{ + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct power_supply_config psy_cfg = {}; + struct axp20x_usb_power *power; + const char *irq_names[] = { "VBUS_PLUGIN", "VBUS_REMOVAL", + "VBUS_VALID", "VBUS_NOT_VALID" }; + int i, irq, r; + + if (!of_device_is_available(pdev->dev.of_node)) + return -ENODEV; + + power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); + if (!power) + return -ENOMEM; + + power->regmap = axp20x->regmap; + + /* Enable vbus valid checking */ + r = regmap_update_bits(power->regmap, AXP20X_VBUS_MON, + AXP20X_VBUS_MON_VBUS_VALID, AXP20X_VBUS_MON_VBUS_VALID); + if (r) + return r; + + /* Enable vbus voltage and current measurement */ + r = regmap_update_bits(power->regmap, AXP20X_ADC_EN1, + AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT, + AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT); + if (r) + return r; + + psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.drv_data = power; + + power->supply = devm_power_supply_register(&pdev->dev, + &axp20x_usb_power_desc, &psy_cfg); + if (IS_ERR(power->supply)) + return PTR_ERR(power->supply); + + /* Request irqs after registering, as irqs may trigger immediately */ + for (i = 0; i < ARRAY_SIZE(irq_names); i++) { + irq = platform_get_irq_byname(pdev, irq_names[i]); + if (irq < 0) { + dev_warn(&pdev->dev, "No IRQ for %s: %d\n", + irq_names[i], irq); + continue; + } + irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); + r = devm_request_any_context_irq(&pdev->dev, irq, + axp20x_usb_power_irq, 0, DRVNAME, power); + if (r < 0) + dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n", + irq_names[i], r); + } + + return 0; +} + +static const struct of_device_id axp20x_usb_power_match[] = { + { .compatible = "x-powers,axp202-usb-power-supply" }, + { } +}; +MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); + +static struct platform_driver axp20x_usb_power_driver = { + .probe = axp20x_usb_power_probe, + .driver = { + .name = DRVNAME, + .of_match_table = axp20x_usb_power_match, + }, +}; + +module_platform_driver(axp20x_usb_power_driver); + +MODULE_AUTHOR("Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>"); +MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index f4290ae..d7adb2e 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -11,6 +11,8 @@ #ifndef __LINUX_MFD_AXP20X_H #define __LINUX_MFD_AXP20X_H +#include <linux/regmap.h> + enum { AXP202_ID = 0, AXP209_ID, @@ -366,4 +368,26 @@ struct axp20x_fg_pdata { int thermistor_curve[MAX_THERM_CURVE_SIZE][2]; }; +/* generic helper function for reading 9-16 bit wide regs */ +static inline int axp20x_read_16bit(struct regmap *regmap, + unsigned int reg, unsigned int width) +{ + unsigned int v, raw; + int r; + + r = regmap_read(regmap, reg, &v); + if (r) + return r; + + raw = v << (width - 8); + + r = regmap_read(regmap, reg + 1, &v); + if (r) + return r; + + raw |= v; + + return raw; +} + #endif /* __LINUX_MFD_AXP20X_H */ -- 2.3.6 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html
WARNING: multiple messages have this Message-ID (diff)
From: hdegoede@redhat.com (Hans de Goede) To: linux-arm-kernel@lists.infradead.org Subject: [PATCH 4/8] power: Add an axp20x-usb-power driver Date: Tue, 9 Jun 2015 23:37:57 +0200 [thread overview] Message-ID: <1433885881-19809-5-git-send-email-hdegoede@redhat.com> (raw) In-Reply-To: <1433885881-19809-1-git-send-email-hdegoede@redhat.com> This adds a driver for the usb power_supply bits of the axp20x PMICs. I initially started writing my own driver, before coming aware of Bruno Pr?mont's excellent earlier RFC with a driver for this. My driver was lacking CURRENT_MAX and VOLTAGE_MIN support Bruno's drvier has, so I've copied the code for those from his driver. Note that the AC-power-supply and battery charger bits will need separate drivers. Each one needs its own devictree child-node so that other devicetree nodes can reference the right power-supply, and thus each one will get its own mfd-cell / platform_device and platform-driver. Cc: Bruno Pr?mont <bonbons@linux-vserver.org> Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- .../bindings/power_supply/axp20x_usb_power.txt | 34 +++ drivers/power/Kconfig | 7 + drivers/power/Makefile | 1 + drivers/power/axp20x_usb_power.c | 241 +++++++++++++++++++++ include/linux/mfd/axp20x.h | 24 ++ 5 files changed, 307 insertions(+) create mode 100644 Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt create mode 100644 drivers/power/axp20x_usb_power.c diff --git a/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt b/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt new file mode 100644 index 0000000..a84d801 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt @@ -0,0 +1,34 @@ +AXP20x USB power supply + +Required Properties: +-compatible: "x-powers,axp202-usb-power-supply" + +This node is a subnode of the axp20x PMIC. + +Example: + +axp209: pmic at 34 { + compatible = "x-powers,axp209"; + reg = <0x34>; + interrupt-parent = <&nmi_intc>; + interrupts = <0 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <1>; + + regulators { + x-powers,dcdc-freq = <1500>; + + vdd_cpu: dcdc2 { + regulator-always-on; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1450000>; + regulator-name = "vdd-cpu"; + }; + + ... + }; + + usb_power_supply: usb_power_supply { + compatible = "x-powers,axp202-usb-power-supply"; + }; +}; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 4091fb0..1fee60c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -439,6 +439,13 @@ config BATTERY_RT5033 The fuelgauge calculates and determines the battery state of charge according to battery open circuit voltage. +config AXP20X_POWER + tristate "AXP20x power supply driver" + depends on MFD_AXP20X + help + This driver provides support for the power supply features of + AXP20x PMIC. + source "drivers/power/reset/Kconfig" endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b7b0181..ae0f27d 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o obj-$(CONFIG_PDA_POWER) += pda_power.o obj-$(CONFIG_APM_POWER) += apm_power.o +obj-$(CONFIG_AXP20X_POWER) += axp20x_usb_power.o obj-$(CONFIG_MAX8925_POWER) += max8925_power.o obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o obj-$(CONFIG_WM831X_POWER) += wm831x_power.o diff --git a/drivers/power/axp20x_usb_power.c b/drivers/power/axp20x_usb_power.c new file mode 100644 index 0000000..4be4c8b --- /dev/null +++ b/drivers/power/axp20x_usb_power.c @@ -0,0 +1,241 @@ +/* + * AXP20x PMIC USB power supply status driver + * + * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> + * Copyright (C) 2014 Bruno Pr?mont <bonbons@linux-vserver.org> + * + * 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. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/axp20x.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define DRVNAME "axp20x-usb-power-supply" + +#define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5) +#define AXP20X_PWR_STATUS_VBUS_USED BIT(4) + +#define AXP20X_USB_STATUS_VBUS_VALID BIT(2) + +#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000) +#define AXP20X_VBUS_CLIMIT_MASK 3 +#define AXP20X_VBUC_CLIMIT_900mA 0 +#define AXP20X_VBUC_CLIMIT_500mA 1 +#define AXP20X_VBUC_CLIMIT_100mA 2 +#define AXP20X_VBUC_CLIMIT_NONE 3 + +#define AXP20X_ADC_EN1_VBUS_CURR BIT(2) +#define AXP20X_ADC_EN1_VBUS_VOLT BIT(3) + +#define AXP20X_VBUS_MON_VBUS_VALID BIT(3) + +struct axp20x_usb_power { + struct regmap *regmap; + struct power_supply *supply; +}; + +static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) +{ + struct axp20x_usb_power *power = devid; + + power_supply_changed(power->supply); + + return IRQ_HANDLED; +} + +static int axp20x_usb_power_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + struct axp20x_usb_power *power = power_supply_get_drvdata(psy); + unsigned int input, v; + int r; + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + r = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); + if (r) + return r; + + val->intval = AXP20X_VBUS_VHOLD_uV(v); + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + r = axp20x_read_16bit(power->regmap, AXP20X_VBUS_V_ADC_H, 12); + if (r < 0) + return r; + + val->intval = r * 1700; /* 1 step = 1.7 mV */ + return 0; + case POWER_SUPPLY_PROP_CURRENT_MAX: + r = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); + if (r) + return r; + + switch (v & AXP20X_VBUS_CLIMIT_MASK) { + case AXP20X_VBUC_CLIMIT_100mA: + val->intval = 100000; + break; + case AXP20X_VBUC_CLIMIT_500mA: + val->intval = 500000; + break; + case AXP20X_VBUC_CLIMIT_900mA: + val->intval = 900000; + break; + case AXP20X_VBUC_CLIMIT_NONE: + val->intval = -1; + break; + } + return 0; + case POWER_SUPPLY_PROP_CURRENT_NOW: + r = axp20x_read_16bit(power->regmap, AXP20X_VBUS_I_ADC_H, 12); + if (r < 0) + return r; + + val->intval = r * 375; /* 1 step = 0.375 mA */ + return 0; + default: + break; + } + + /* All the properties below need the input-status reg value */ + r = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input); + if (r) + return r; + + switch (psp) { + case POWER_SUPPLY_PROP_HEALTH: + if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) { + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + + r = regmap_read(power->regmap, AXP20X_USB_OTG_STATUS, &v); + if (r) + return r; + + if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) { + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + } + + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED); + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property axp20x_usb_power_properties[] = { + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static const struct power_supply_desc axp20x_usb_power_desc = { + .name = "axp20x-usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = axp20x_usb_power_properties, + .num_properties = ARRAY_SIZE(axp20x_usb_power_properties), + .get_property = axp20x_usb_power_get_property, +}; + +static int axp20x_usb_power_probe(struct platform_device *pdev) +{ + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct power_supply_config psy_cfg = {}; + struct axp20x_usb_power *power; + const char *irq_names[] = { "VBUS_PLUGIN", "VBUS_REMOVAL", + "VBUS_VALID", "VBUS_NOT_VALID" }; + int i, irq, r; + + if (!of_device_is_available(pdev->dev.of_node)) + return -ENODEV; + + power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); + if (!power) + return -ENOMEM; + + power->regmap = axp20x->regmap; + + /* Enable vbus valid checking */ + r = regmap_update_bits(power->regmap, AXP20X_VBUS_MON, + AXP20X_VBUS_MON_VBUS_VALID, AXP20X_VBUS_MON_VBUS_VALID); + if (r) + return r; + + /* Enable vbus voltage and current measurement */ + r = regmap_update_bits(power->regmap, AXP20X_ADC_EN1, + AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT, + AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT); + if (r) + return r; + + psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.drv_data = power; + + power->supply = devm_power_supply_register(&pdev->dev, + &axp20x_usb_power_desc, &psy_cfg); + if (IS_ERR(power->supply)) + return PTR_ERR(power->supply); + + /* Request irqs after registering, as irqs may trigger immediately */ + for (i = 0; i < ARRAY_SIZE(irq_names); i++) { + irq = platform_get_irq_byname(pdev, irq_names[i]); + if (irq < 0) { + dev_warn(&pdev->dev, "No IRQ for %s: %d\n", + irq_names[i], irq); + continue; + } + irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); + r = devm_request_any_context_irq(&pdev->dev, irq, + axp20x_usb_power_irq, 0, DRVNAME, power); + if (r < 0) + dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n", + irq_names[i], r); + } + + return 0; +} + +static const struct of_device_id axp20x_usb_power_match[] = { + { .compatible = "x-powers,axp202-usb-power-supply" }, + { } +}; +MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); + +static struct platform_driver axp20x_usb_power_driver = { + .probe = axp20x_usb_power_probe, + .driver = { + .name = DRVNAME, + .of_match_table = axp20x_usb_power_match, + }, +}; + +module_platform_driver(axp20x_usb_power_driver); + +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index f4290ae..d7adb2e 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -11,6 +11,8 @@ #ifndef __LINUX_MFD_AXP20X_H #define __LINUX_MFD_AXP20X_H +#include <linux/regmap.h> + enum { AXP202_ID = 0, AXP209_ID, @@ -366,4 +368,26 @@ struct axp20x_fg_pdata { int thermistor_curve[MAX_THERM_CURVE_SIZE][2]; }; +/* generic helper function for reading 9-16 bit wide regs */ +static inline int axp20x_read_16bit(struct regmap *regmap, + unsigned int reg, unsigned int width) +{ + unsigned int v, raw; + int r; + + r = regmap_read(regmap, reg, &v); + if (r) + return r; + + raw = v << (width - 8); + + r = regmap_read(regmap, reg + 1, &v); + if (r) + return r; + + raw |= v; + + return raw; +} + #endif /* __LINUX_MFD_AXP20X_H */ -- 2.3.6
next prev parent reply other threads:[~2015-06-09 21:37 UTC|newest] Thread overview: 62+ messages / expand[flat|nested] mbox.gz Atom feed top 2015-06-09 21:37 [PATCH 0/8] mfd/power/phy: Add support for otg vbus detection via axp pmic Hans de Goede 2015-06-09 21:37 ` Hans de Goede 2015-06-09 21:37 ` [PATCH 1/8] mfd: axp20x: Add missing registers, and mark more registers volatile Hans de Goede 2015-06-09 21:37 ` Hans de Goede [not found] ` <1433885881-19809-2-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> 2015-06-10 7:36 ` Lee Jones 2015-06-10 7:36 ` Lee Jones 2015-06-09 21:37 ` [PATCH 3/8] power: Add devm_power_supply_get_by_phandle() helper function Hans de Goede 2015-06-09 21:37 ` Hans de Goede 2015-06-10 14:49 ` Sebastian Reichel 2015-06-10 14:49 ` Sebastian Reichel 2015-06-09 21:38 ` [PATCH 8/8] ARM: dts: sun7i: Enable USB DRC on Bananapi Hans de Goede 2015-06-09 21:38 ` Hans de Goede [not found] ` <1433885881-19809-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> 2015-06-09 21:37 ` [PATCH 2/8] mfd: axp20x: Add a cell for the usb power_supply part of the axp20x PMICs Hans de Goede 2015-06-09 21:37 ` Hans de Goede [not found] ` <1433885881-19809-3-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> 2015-06-10 1:19 ` Chen-Yu Tsai 2015-06-10 1:19 ` [linux-sunxi] " Chen-Yu Tsai [not found] ` <CAGb2v652W+i6L1k5-CnHtXZXdJjFswkcyFGromhWSYaXtitW_w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 2015-06-10 7:57 ` Hans de Goede 2015-06-10 7:57 ` [linux-sunxi] " Hans de Goede [not found] ` <5577EDD9.8020900-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> 2015-06-13 13:50 ` Maxime Ripard 2015-06-13 13:50 ` [linux-sunxi] " Maxime Ripard 2015-06-24 12:18 ` Michal Suchanek 2015-06-24 12:18 ` [linux-sunxi] " Michal Suchanek 2015-06-10 7:35 ` Lee Jones 2015-06-10 7:35 ` Lee Jones 2015-06-10 7:36 ` Lee Jones 2015-06-10 7:36 ` Lee Jones 2015-06-09 21:37 ` Hans de Goede [this message] 2015-06-09 21:37 ` [PATCH 4/8] power: Add an axp20x-usb-power driver Hans de Goede 2015-06-10 7:29 ` Lee Jones 2015-06-10 7:29 ` Lee Jones 2015-06-10 9:22 ` Hans de Goede 2015-06-10 9:22 ` Hans de Goede [not found] ` <557801B8.5040804-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> 2015-06-10 9:34 ` Lee Jones 2015-06-10 9:34 ` Lee Jones 2015-06-09 21:37 ` [PATCH 5/8] phy-sun4i-usb: Add support for monitoring vbus via a power-supply Hans de Goede 2015-06-09 21:37 ` Hans de Goede 2015-06-09 21:37 ` [PATCH 6/8] ARM: dts: axp209: Add usb_power_supply child node to the ax209 node Hans de Goede 2015-06-09 21:37 ` Hans de Goede [not found] ` <1433885881-19809-7-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> 2015-07-31 5:31 ` Chen-Yu Tsai 2015-07-31 5:31 ` [linux-sunxi] " Chen-Yu Tsai [not found] ` <CAGb2v66X79dK84hG8NVTho-kPPMNcKXQspvT2pCM8Zbmnme53A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 2015-07-31 5:51 ` Bruno Prémont 2015-07-31 5:51 ` [linux-sunxi] " Bruno Prémont 2015-07-31 6:14 ` Chen-Yu Tsai 2015-07-31 6:14 ` Chen-Yu Tsai [not found] ` <CAGb2v67nM7pauyjyhBdfgX7p_Rud6TEsNqphB+gx4ROekVqtbw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 2015-07-31 6:35 ` Bruno Prémont 2015-07-31 6:35 ` [linux-sunxi] " Bruno Prémont 2015-07-31 7:57 ` Maxime Ripard 2015-07-31 7:57 ` Maxime Ripard [not found] ` <20150731083541.5f2c683a-I2t2yFIzmohO7ya8xxV06g@public.gmane.org> 2015-07-31 8:31 ` Hans de Goede 2015-07-31 8:31 ` Hans de Goede [not found] ` <55BB3262.5000403-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> 2015-07-31 9:00 ` Chen-Yu Tsai 2015-07-31 9:00 ` [linux-sunxi] " Chen-Yu Tsai [not found] ` <CAGb2v66+CZe0v=CuYOLfnNwkX70kwPOP28Nya2Y8xw8yrg4-8Q-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 2015-07-31 9:11 ` Hans de Goede 2015-07-31 9:11 ` Hans de Goede [not found] ` <55BB3BCB.6080608-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> 2015-07-31 10:06 ` Chen-Yu Tsai 2015-07-31 10:06 ` [linux-sunxi] " Chen-Yu Tsai 2015-07-31 9:14 ` Hans de Goede 2015-07-31 9:14 ` Hans de Goede 2015-06-09 21:38 ` [PATCH 7/8] ARM: dts: sun7i: Add regulator configuration to the bananapi dts file Hans de Goede 2015-06-09 21:38 ` Hans de Goede 2015-06-10 6:15 ` [PATCH 0/8] mfd/power/phy: Add support for otg vbus detection via axp pmic Priit Laes 2015-06-10 6:15 ` [linux-sunxi] " Priit Laes
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1433885881-19809-5-git-send-email-hdegoede@redhat.com \ --to=hdegoede-h+wxahxf7alqt0dzr+alfa@public.gmane.org \ --cc=balbi-l0cyMroinI0@public.gmane.org \ --cc=bonbons-ud5FBsm0p/xEiooADzr8i9i2O/JbrIOy@public.gmane.org \ --cc=dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \ --cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \ --cc=dwmw2-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org \ --cc=kishon-l0cyMroinI0@public.gmane.org \ --cc=lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \ --cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \ --cc=linux-pm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \ --cc=linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org \ --cc=maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org \ --cc=sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.