All of lore.kernel.org
 help / color / mirror / Atom feed
From: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
To: Michael Haas
	<michael.haas-cl+VPiYnx/1AfugRpC6u6w@public.gmane.org>,
	"wens-jdAy2FN1RRM@public.gmane.org"
	<wens-jdAy2FN1RRM@public.gmane.org>,
	"maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org"
	<maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>,
	"sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org"
	<sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
	"dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org"
	<dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
	"dwmw2-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org"
	<dwmw2-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
Cc: "robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org"
	<robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
	"mark.rutland-5wv7dgnIgG8@public.gmane.org"
	<mark.rutland-5wv7dgnIgG8@public.gmane.org>,
	"linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org"
	<linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>,
	"devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org"
	<devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>,
	"linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org"
	<linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org>,
	"linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org"
	<linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>,
	"linux-pm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org"
	<linux-pm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>,
	"linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org"
	<linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org>
Subject: Re: [PATCH 2/4] power: add axp20x-battery driver
Date: Tue, 05 Jul 2016 16:33:31 +0800	[thread overview]
Message-ID: <4101467707611@web7h.yandex.ru> (raw)
In-Reply-To: <577B44FD.6040404-cl+VPiYnx/1AfugRpC6u6w@public.gmane.org>



05.07.2016, 13:26, "Michael Haas" <michael.haas-cl+VPiYnx/1AfugRpC6u6w@public.gmane.org>:
> Hi,
>
> nice work! Is this in any way related to Bruno Prémonts driver for the
> axp20x?
>
> I've got a reworked version of that lying around, but it's not quite
> ready for submission. Do you know what pieces are missing in your driver
> for axp20x support - as opposed to axp22x, which is already working?
>
> Michael
>
> On 01.07.2016 11:29, Icenowy Zheng wrote:
>>  This driver is the battery power supply driver of axp20x PMIC. Currently
>>  it supports only AXP22x variants.
>>
>>  Signed-off-by: Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
>>  ---
>>    drivers/mfd/axp20x.c | 14 +++
>>    drivers/power/Makefile | 1 +
>>    drivers/power/axp20x_battery.c | 254 +++++++++++++++++++++++++++++++++++++++++
>>    3 files changed, 269 insertions(+)
>>    create mode 100644 drivers/power/axp20x_battery.c
>>
>>  diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
>>  index cee5288..064e5015 100644
>>  --- a/drivers/mfd/axp20x.c
>>  +++ b/drivers/mfd/axp20x.c
>>  @@ -166,6 +166,15 @@ static struct resource axp22x_usb_power_supply_resources[] = {
>>            DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
>>    };
>>
>>  +static struct resource axp22x_battery_resources[] = {
>>  + DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_BATT_PLUGIN, "BATT_PLUGIN"),
>>  + DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_BATT_REMOVAL, "BATT_REMOVAL"),
>>  + DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_BATT_ENT_ACT_MODE, "BATT_ENT_ACT_MODE"),
>>  + DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_BATT_EXIT_ACT_MODE, "BATT_EXIT_ACT_MODE"),
>>  + DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_CHARG, "CHARG"),
>>  + DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_CHARG_DONE, "CHARG_DONE"),
>>  +};
>>  +
>>    static struct resource axp22x_pek_resources[] = {
>>            {
>>                    .name = "PEK_DBR",
>>  @@ -538,6 +547,11 @@ static struct mfd_cell axp22x_cells[] = {
>>                    .of_compatible = "x-powers,axp202-usb-power-supply",
>>                    .num_resources = ARRAY_SIZE(axp22x_usb_power_supply_resources),
>>                    .resources = axp22x_usb_power_supply_resources,
>>  + }, {
>>  + .name = "axp20x-battery",
>>  + .of_compatible = "x-powers,axp202-battery",
>>  + .num_resources = ARRAY_SIZE(axp22x_battery_resources),
>>  + .resources = axp22x_battery_resources,
>>            },
>>    };
>>
>>  diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>>  index e46b75d..1452d23 100644
>>  --- a/drivers/power/Makefile
>>  +++ b/drivers/power/Makefile
>>  @@ -10,6 +10,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_AXP20X_POWER) += axp20x_battery.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_battery.c b/drivers/power/axp20x_battery.c
>>  new file mode 100644
>>  index 0000000..8fac2cd
>>  --- /dev/null
>>  +++ b/drivers/power/axp20x_battery.c
>>  @@ -0,0 +1,254 @@
>>  +/*
>>  + * AXP20x PMIC battery status driver
>>  + *
>>  + * Copyright (C) 2016 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
>>  + * Copyright (C) 2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>  + * 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-battery"
>>  +
>>  +#define AXP20X_PWR_STATUS_ACIN_USED BIT(6)
>>  +#define AXP20X_PWR_STATUS_VBUS_USED BIT(4)
>>  +#define AXP20X_PWR_STATUS_BAT_DIRECTION BIT(2)
>>  +
>>  +#define AXP20X_OP_MODE_CHARGING BIT(6)
>>  +#define AXP20X_OP_MODE_BATTERY_PRESENT BIT(5)
>>  +#define AXP20X_OP_MODE_BATTERY_ACTIVE BIT(3)
>>  +
>>  +#define AXP20X_CAPACITY_CORRECT BIT(7)
>>  +
>>  +struct axp20x_battery {
>>  + struct axp20x_dev *axp20x;
>>  + struct regmap *regmap;
>>  + struct power_supply *supply;
>>  +};
>>  +
>>  +static irqreturn_t axp20x_battery_irq(int irq, void *devid)
>>  +{
>>  + struct axp20x_battery *power = devid;
>>  +
>>  + power_supply_changed(power->supply);
>>  +
>>  + return IRQ_HANDLED;
>>  +}
>>  +
>>  +static int axp20x_battery_get_property(struct power_supply *psy,
>>  + enum power_supply_property psp, union power_supply_propval *val)
>>  +{
>>  + struct axp20x_battery *power = power_supply_get_drvdata(psy);
>>  + unsigned int input, op_mode, capacity, dh, dl;
>>  + int ret, ext;
>>  +
>>  + /* All the properties below need the input-status reg value */
>>  + ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input);
>>  + if (ret)
>>  + return ret;
>>  +
>>  + ret = regmap_read(power->regmap, AXP20X_PWR_OP_MODE, &op_mode);
>>  + if (ret)
>>  + return ret;
>>  +
>>  + switch (psp) {
>>  + case POWER_SUPPLY_PROP_HEALTH:
>>  + if (!(op_mode & AXP20X_OP_MODE_BATTERY_PRESENT)) {
>>  + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
>>  + break;
>>  + }
>>  +
>>  + val->intval = POWER_SUPPLY_HEALTH_GOOD;
>>  +
>>  + if (op_mode & AXP20X_OP_MODE_BATTERY_ACTIVE) {
>>  + /* AXP20X is now trying to re-activate the battery */
>>  + val->intval = POWER_SUPPLY_HEALTH_DEAD;
>>  + break;
>>  + }
>>  + break;
>>  + case POWER_SUPPLY_PROP_PRESENT:
>>  + val->intval = !!(op_mode & AXP20X_OP_MODE_BATTERY_PRESENT);
>>  + break;
>>  + case POWER_SUPPLY_PROP_CAPACITY:
>>  + ret = regmap_read(power->regmap, AXP20X_FG_RES, &capacity);
>>  + if (ret)
>>  + return ret;
>>  + if (capacity & AXP20X_CAPACITY_CORRECT)
>>  + val->intval = capacity & (~AXP20X_CAPACITY_CORRECT);
>>  + else
>>  + return -EIO;
>>  + /* from axp_capchange function of Allwinner 3.4 driver */
>>  + if (val->intval == 127)
>>  + val->intval = 100;
>>  + break;
>>  + case POWER_SUPPLY_PROP_STATUS:
>>  + if (!(op_mode & AXP20X_OP_MODE_BATTERY_PRESENT)) {
>>  + val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
>>  + break;
>>  + }
>>  +
>>  + ret = regmap_read(power->regmap, AXP20X_FG_RES, &capacity);
>>  + if (ret)
>>  + return ret;
>>  +
>>  + ext = (input & AXP20X_PWR_STATUS_ACIN_USED) ||
>>  + (input & AXP20X_PWR_STATUS_VBUS_USED);
>>  +
>>  + if (op_mode & AXP20X_OP_MODE_CHARGING)
>>  + val->intval = POWER_SUPPLY_STATUS_CHARGING;
>>  + else if (!(input & AXP20X_PWR_STATUS_BAT_DIRECTION) && !ext)
>>  + val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
>>  + else if (capacity == (100 | AXP20X_CAPACITY_CORRECT) ||
>>  + capacity == (127 | AXP20X_CAPACITY_CORRECT))
>>  + val->intval = POWER_SUPPLY_STATUS_FULL;
>>  + else
>>  + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
>>  + break;
>>  + case POWER_SUPPLY_PROP_CURRENT_NOW:
>>  + ret = regmap_read(power->regmap, AXP20X_BATT_DISCHRG_I_H, &dh);
>>  + if (ret)
>>  + return ret;
>>  + ret = regmap_read(power->regmap, AXP20X_BATT_DISCHRG_I_L, &dl);
>>  + if (ret)
>>  + return ret;
>>  + /* it's a 12 bit integer, high 8-bit is stored in dh */
>>  + val->intval = dh << 4 | dl >> 4;
>>  + break;
>>  + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
>>  + ret = regmap_read(power->regmap, AXP20X_BATT_V_H, &dh);
>>  + if (ret)
>>  + return ret;
>>  + ret = regmap_read(power->regmap, AXP20X_BATT_V_L, &dl);
>>  + if (ret)
>>  + return ret;
>>  + /* it's a 12 bit integer, high 8-bit is stored in dh */
>>  + val->intval = dh << 4 | dl >> 4;
>>  + /* The formula below is from axp22_vbat_to_mV function
>>  + * of Allwinner 3.4 kernel.
>>  + */
>>  + val->intval = val->intval * 1100 / 1000;
>>  + break;
>>  + default:
>>  + return -EINVAL;
>>  + }
>>  +
>>  + return 0;
>>  +}
>>  +
>>  +static enum power_supply_property axp22x_battery_properties[] = {
>>  + POWER_SUPPLY_PROP_CAPACITY,
>>  + POWER_SUPPLY_PROP_HEALTH,
>>  + POWER_SUPPLY_PROP_PRESENT,
>>  + POWER_SUPPLY_PROP_STATUS,
>>  + POWER_SUPPLY_PROP_CURRENT_NOW,
>>  + POWER_SUPPLY_PROP_VOLTAGE_NOW,
>>  +};
>>  +
>>  +static const struct power_supply_desc axp22x_battery_desc = {
>>  + .name = "axp20x-battery",
>>  + .type = POWER_SUPPLY_TYPE_BATTERY,
>>  + .properties = axp22x_battery_properties,
>>  + .num_properties = ARRAY_SIZE(axp22x_battery_properties),
>>  + .get_property = axp20x_battery_get_property,
>>  +};
>>  +
>>  +static int axp20x_battery_probe(struct platform_device *pdev)
>>  +{
>>  + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
>>  + struct power_supply_config psy_cfg = {};
>>  + struct axp20x_battery *power;
>>  + static const char * const axp22x_irq_names[] = {
>>  + "BATT_PLUGIN", "BATT_REMOVAL", "BATT_ENT_ACT_MODE",
>>  + "BATT_EXIT_ACT_MODE", "CHARG", "CHARG_DONE", NULL };
>>  + static const char * const *irq_names;
>>  + const struct power_supply_desc *battery_desc;
>>  + int i, irq, ret;
>>  +
>>  + if (!of_device_is_available(pdev->dev.of_node))
>>  + return -ENODEV;
>>  +
>>  + if (!axp20x) {
>>  + dev_err(&pdev->dev, "Parent drvdata not set\n");
>>  + return -EINVAL;
>>  + }
>>  +
>>  + power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
>>  + if (!power)
>>  + return -ENOMEM;
>>  +
>>  + power->axp20x = axp20x;
>>  + power->regmap = axp20x->regmap;
>>  +
>>  + switch (power->axp20x->variant) {
>>  + case AXP221_ID:
>>  + case AXP223_ID:
>>  + battery_desc = &axp22x_battery_desc;
>>  + irq_names = axp22x_irq_names;
>>  + break;
>>  + default:
>>  + dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
>>  + axp20x->variant);
>>  + return -EINVAL;
>>  + }
>>  +
>>  + psy_cfg.of_node = pdev->dev.of_node;
>>  + psy_cfg.drv_data = power;
>>  +
>>  + power->supply = devm_power_supply_register(&pdev->dev, battery_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; irq_names[i]; 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);
>>  + ret = devm_request_any_context_irq(&pdev->dev, irq,
>>  + axp20x_battery_irq, 0, DRVNAME, power);
>>  + if (ret < 0)
>>  + dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
>>  + irq_names[i], ret);
>>  + }
>>  +
>>  + return 0;
>>  +}
>>  +
>>  +static const struct of_device_id axp20x_battery_match[] = {
>>  + { .compatible = "x-powers,axp202-battery" },
>>  + { }
>>  +};
>>  +MODULE_DEVICE_TABLE(of, axp20x_battery_match);
>>  +
>>  +static struct platform_driver axp20x_battery_driver = {
>>  + .probe = axp20x_battery_probe,
>>  + .driver = {
>>  + .name = DRVNAME,
>>  + .of_match_table = axp20x_battery_match,
>>  + },
>>  +};
>>  +
>>  +module_platform_driver(axp20x_battery_driver);
>>  +
>>  +MODULE_AUTHOR("Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>");
>>  +MODULE_DESCRIPTION("AXP20x PMIC battery status driver");
>>  +MODULE_LICENSE("GPL");

Therotically, it can run on axp20x. However, I have currently no test device. (Still waiting for my C.H.I.P.)
So I can only promise it to run on axp22x.

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

WARNING: multiple messages have this Message-ID (diff)
From: icenowy@aosc.xyz (Icenowy Zheng)
To: linux-arm-kernel@lists.infradead.org
Subject: [linux-sunxi] [PATCH 2/4] power: add axp20x-battery driver
Date: Tue, 05 Jul 2016 16:33:31 +0800	[thread overview]
Message-ID: <4101467707611@web7h.yandex.ru> (raw)
In-Reply-To: <577B44FD.6040404@mailbox.org>



05.07.2016, 13:26, "Michael Haas" <michael.haas@mailbox.org>:
> Hi,
>
> nice work! Is this in any way related to Bruno Pr?monts driver for the
> axp20x?
>
> I've got a reworked version of that lying around, but it's not quite
> ready for submission. Do you know what pieces are missing in your driver
> for axp20x support - as opposed to axp22x, which is already working?
>
> Michael
>
> On 01.07.2016 11:29, Icenowy Zheng wrote:
>> ?This driver is the battery power supply driver of axp20x PMIC. Currently
>> ?it supports only AXP22x variants.
>>
>> ?Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
>> ?---
>> ???drivers/mfd/axp20x.c | 14 +++
>> ???drivers/power/Makefile | 1 +
>> ???drivers/power/axp20x_battery.c | 254 +++++++++++++++++++++++++++++++++++++++++
>> ???3 files changed, 269 insertions(+)
>> ???create mode 100644 drivers/power/axp20x_battery.c
>>
>> ?diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
>> ?index cee5288..064e5015 100644
>> ?--- a/drivers/mfd/axp20x.c
>> ?+++ b/drivers/mfd/axp20x.c
>> ?@@ -166,6 +166,15 @@ static struct resource axp22x_usb_power_supply_resources[] = {
>> ???????????DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
>> ???};
>>
>> ?+static struct resource axp22x_battery_resources[] = {
>> ?+ DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_BATT_PLUGIN, "BATT_PLUGIN"),
>> ?+ DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_BATT_REMOVAL, "BATT_REMOVAL"),
>> ?+ DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_BATT_ENT_ACT_MODE, "BATT_ENT_ACT_MODE"),
>> ?+ DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_BATT_EXIT_ACT_MODE, "BATT_EXIT_ACT_MODE"),
>> ?+ DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_CHARG, "CHARG"),
>> ?+ DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_CHARG_DONE, "CHARG_DONE"),
>> ?+};
>> ?+
>> ???static struct resource axp22x_pek_resources[] = {
>> ???????????{
>> ???????????????????.name = "PEK_DBR",
>> ?@@ -538,6 +547,11 @@ static struct mfd_cell axp22x_cells[] = {
>> ???????????????????.of_compatible = "x-powers,axp202-usb-power-supply",
>> ???????????????????.num_resources = ARRAY_SIZE(axp22x_usb_power_supply_resources),
>> ???????????????????.resources = axp22x_usb_power_supply_resources,
>> ?+ }, {
>> ?+ .name = "axp20x-battery",
>> ?+ .of_compatible = "x-powers,axp202-battery",
>> ?+ .num_resources = ARRAY_SIZE(axp22x_battery_resources),
>> ?+ .resources = axp22x_battery_resources,
>> ???????????},
>> ???};
>>
>> ?diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> ?index e46b75d..1452d23 100644
>> ?--- a/drivers/power/Makefile
>> ?+++ b/drivers/power/Makefile
>> ?@@ -10,6 +10,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_AXP20X_POWER) += axp20x_battery.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_battery.c b/drivers/power/axp20x_battery.c
>> ?new file mode 100644
>> ?index 0000000..8fac2cd
>> ?--- /dev/null
>> ?+++ b/drivers/power/axp20x_battery.c
>> ?@@ -0,0 +1,254 @@
>> ?+/*
>> ?+ * AXP20x PMIC battery status driver
>> ?+ *
>> ?+ * Copyright (C) 2016 Icenowy Zheng <icenowy@aosc.xyz>
>> ?+ * 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-battery"
>> ?+
>> ?+#define AXP20X_PWR_STATUS_ACIN_USED BIT(6)
>> ?+#define AXP20X_PWR_STATUS_VBUS_USED BIT(4)
>> ?+#define AXP20X_PWR_STATUS_BAT_DIRECTION BIT(2)
>> ?+
>> ?+#define AXP20X_OP_MODE_CHARGING BIT(6)
>> ?+#define AXP20X_OP_MODE_BATTERY_PRESENT BIT(5)
>> ?+#define AXP20X_OP_MODE_BATTERY_ACTIVE BIT(3)
>> ?+
>> ?+#define AXP20X_CAPACITY_CORRECT BIT(7)
>> ?+
>> ?+struct axp20x_battery {
>> ?+ struct axp20x_dev *axp20x;
>> ?+ struct regmap *regmap;
>> ?+ struct power_supply *supply;
>> ?+};
>> ?+
>> ?+static irqreturn_t axp20x_battery_irq(int irq, void *devid)
>> ?+{
>> ?+ struct axp20x_battery *power = devid;
>> ?+
>> ?+ power_supply_changed(power->supply);
>> ?+
>> ?+ return IRQ_HANDLED;
>> ?+}
>> ?+
>> ?+static int axp20x_battery_get_property(struct power_supply *psy,
>> ?+ enum power_supply_property psp, union power_supply_propval *val)
>> ?+{
>> ?+ struct axp20x_battery *power = power_supply_get_drvdata(psy);
>> ?+ unsigned int input, op_mode, capacity, dh, dl;
>> ?+ int ret, ext;
>> ?+
>> ?+ /* All the properties below need the input-status reg value */
>> ?+ ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input);
>> ?+ if (ret)
>> ?+ return ret;
>> ?+
>> ?+ ret = regmap_read(power->regmap, AXP20X_PWR_OP_MODE, &op_mode);
>> ?+ if (ret)
>> ?+ return ret;
>> ?+
>> ?+ switch (psp) {
>> ?+ case POWER_SUPPLY_PROP_HEALTH:
>> ?+ if (!(op_mode & AXP20X_OP_MODE_BATTERY_PRESENT)) {
>> ?+ val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
>> ?+ break;
>> ?+ }
>> ?+
>> ?+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
>> ?+
>> ?+ if (op_mode & AXP20X_OP_MODE_BATTERY_ACTIVE) {
>> ?+ /* AXP20X is now trying to re-activate the battery */
>> ?+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
>> ?+ break;
>> ?+ }
>> ?+ break;
>> ?+ case POWER_SUPPLY_PROP_PRESENT:
>> ?+ val->intval = !!(op_mode & AXP20X_OP_MODE_BATTERY_PRESENT);
>> ?+ break;
>> ?+ case POWER_SUPPLY_PROP_CAPACITY:
>> ?+ ret = regmap_read(power->regmap, AXP20X_FG_RES, &capacity);
>> ?+ if (ret)
>> ?+ return ret;
>> ?+ if (capacity & AXP20X_CAPACITY_CORRECT)
>> ?+ val->intval = capacity & (~AXP20X_CAPACITY_CORRECT);
>> ?+ else
>> ?+ return -EIO;
>> ?+ /* from axp_capchange function of Allwinner 3.4 driver */
>> ?+ if (val->intval == 127)
>> ?+ val->intval = 100;
>> ?+ break;
>> ?+ case POWER_SUPPLY_PROP_STATUS:
>> ?+ if (!(op_mode & AXP20X_OP_MODE_BATTERY_PRESENT)) {
>> ?+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
>> ?+ break;
>> ?+ }
>> ?+
>> ?+ ret = regmap_read(power->regmap, AXP20X_FG_RES, &capacity);
>> ?+ if (ret)
>> ?+ return ret;
>> ?+
>> ?+ ext = (input & AXP20X_PWR_STATUS_ACIN_USED) ||
>> ?+ (input & AXP20X_PWR_STATUS_VBUS_USED);
>> ?+
>> ?+ if (op_mode & AXP20X_OP_MODE_CHARGING)
>> ?+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
>> ?+ else if (!(input & AXP20X_PWR_STATUS_BAT_DIRECTION) && !ext)
>> ?+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
>> ?+ else if (capacity == (100 | AXP20X_CAPACITY_CORRECT) ||
>> ?+ capacity == (127 | AXP20X_CAPACITY_CORRECT))
>> ?+ val->intval = POWER_SUPPLY_STATUS_FULL;
>> ?+ else
>> ?+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
>> ?+ break;
>> ?+ case POWER_SUPPLY_PROP_CURRENT_NOW:
>> ?+ ret = regmap_read(power->regmap, AXP20X_BATT_DISCHRG_I_H, &dh);
>> ?+ if (ret)
>> ?+ return ret;
>> ?+ ret = regmap_read(power->regmap, AXP20X_BATT_DISCHRG_I_L, &dl);
>> ?+ if (ret)
>> ?+ return ret;
>> ?+ /* it's a 12 bit integer, high 8-bit is stored in dh */
>> ?+ val->intval = dh << 4 | dl >> 4;
>> ?+ break;
>> ?+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
>> ?+ ret = regmap_read(power->regmap, AXP20X_BATT_V_H, &dh);
>> ?+ if (ret)
>> ?+ return ret;
>> ?+ ret = regmap_read(power->regmap, AXP20X_BATT_V_L, &dl);
>> ?+ if (ret)
>> ?+ return ret;
>> ?+ /* it's a 12 bit integer, high 8-bit is stored in dh */
>> ?+ val->intval = dh << 4 | dl >> 4;
>> ?+ /* The formula below is from axp22_vbat_to_mV function
>> ?+ * of Allwinner 3.4 kernel.
>> ?+ */
>> ?+ val->intval = val->intval * 1100 / 1000;
>> ?+ break;
>> ?+ default:
>> ?+ return -EINVAL;
>> ?+ }
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+
>> ?+static enum power_supply_property axp22x_battery_properties[] = {
>> ?+ POWER_SUPPLY_PROP_CAPACITY,
>> ?+ POWER_SUPPLY_PROP_HEALTH,
>> ?+ POWER_SUPPLY_PROP_PRESENT,
>> ?+ POWER_SUPPLY_PROP_STATUS,
>> ?+ POWER_SUPPLY_PROP_CURRENT_NOW,
>> ?+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
>> ?+};
>> ?+
>> ?+static const struct power_supply_desc axp22x_battery_desc = {
>> ?+ .name = "axp20x-battery",
>> ?+ .type = POWER_SUPPLY_TYPE_BATTERY,
>> ?+ .properties = axp22x_battery_properties,
>> ?+ .num_properties = ARRAY_SIZE(axp22x_battery_properties),
>> ?+ .get_property = axp20x_battery_get_property,
>> ?+};
>> ?+
>> ?+static int axp20x_battery_probe(struct platform_device *pdev)
>> ?+{
>> ?+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
>> ?+ struct power_supply_config psy_cfg = {};
>> ?+ struct axp20x_battery *power;
>> ?+ static const char * const axp22x_irq_names[] = {
>> ?+ "BATT_PLUGIN", "BATT_REMOVAL", "BATT_ENT_ACT_MODE",
>> ?+ "BATT_EXIT_ACT_MODE", "CHARG", "CHARG_DONE", NULL };
>> ?+ static const char * const *irq_names;
>> ?+ const struct power_supply_desc *battery_desc;
>> ?+ int i, irq, ret;
>> ?+
>> ?+ if (!of_device_is_available(pdev->dev.of_node))
>> ?+ return -ENODEV;
>> ?+
>> ?+ if (!axp20x) {
>> ?+ dev_err(&pdev->dev, "Parent drvdata not set\n");
>> ?+ return -EINVAL;
>> ?+ }
>> ?+
>> ?+ power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
>> ?+ if (!power)
>> ?+ return -ENOMEM;
>> ?+
>> ?+ power->axp20x = axp20x;
>> ?+ power->regmap = axp20x->regmap;
>> ?+
>> ?+ switch (power->axp20x->variant) {
>> ?+ case AXP221_ID:
>> ?+ case AXP223_ID:
>> ?+ battery_desc = &axp22x_battery_desc;
>> ?+ irq_names = axp22x_irq_names;
>> ?+ break;
>> ?+ default:
>> ?+ dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
>> ?+ axp20x->variant);
>> ?+ return -EINVAL;
>> ?+ }
>> ?+
>> ?+ psy_cfg.of_node = pdev->dev.of_node;
>> ?+ psy_cfg.drv_data = power;
>> ?+
>> ?+ power->supply = devm_power_supply_register(&pdev->dev, battery_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; irq_names[i]; 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);
>> ?+ ret = devm_request_any_context_irq(&pdev->dev, irq,
>> ?+ axp20x_battery_irq, 0, DRVNAME, power);
>> ?+ if (ret < 0)
>> ?+ dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
>> ?+ irq_names[i], ret);
>> ?+ }
>> ?+
>> ?+ return 0;
>> ?+}
>> ?+
>> ?+static const struct of_device_id axp20x_battery_match[] = {
>> ?+ { .compatible = "x-powers,axp202-battery" },
>> ?+ { }
>> ?+};
>> ?+MODULE_DEVICE_TABLE(of, axp20x_battery_match);
>> ?+
>> ?+static struct platform_driver axp20x_battery_driver = {
>> ?+ .probe = axp20x_battery_probe,
>> ?+ .driver = {
>> ?+ .name = DRVNAME,
>> ?+ .of_match_table = axp20x_battery_match,
>> ?+ },
>> ?+};
>> ?+
>> ?+module_platform_driver(axp20x_battery_driver);
>> ?+
>> ?+MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
>> ?+MODULE_DESCRIPTION("AXP20x PMIC battery status driver");
>> ?+MODULE_LICENSE("GPL");

Therotically, it can run on axp20x. However, I have currently no test device. (Still waiting for my C.H.I.P.)
So I can only promise it to run on axp22x.

  parent reply	other threads:[~2016-07-05  8:33 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-07-01  9:29 [PATCH 1/4] mfd: axp20x: add adc volatile ranges for axp22x Icenowy Zheng
2016-07-01  9:29 ` Icenowy Zheng
     [not found] ` <20160701092926.32005-1-icenowy-ymACFijhrKM@public.gmane.org>
2016-07-01  9:29   ` [PATCH 2/4] power: add axp20x-battery driver Icenowy Zheng
2016-07-01  9:29     ` Icenowy Zheng
     [not found]     ` <20160701092926.32005-2-icenowy-ymACFijhrKM@public.gmane.org>
2016-07-05  5:26       ` Michael Haas
2016-07-05  5:26         ` [linux-sunxi] " Michael Haas
2016-07-05  5:26         ` Michael Haas
     [not found]         ` <577B44FD.6040404-cl+VPiYnx/1AfugRpC6u6w@public.gmane.org>
2016-07-05  8:33           ` Icenowy Zheng [this message]
2016-07-05  8:33             ` Icenowy Zheng
     [not found]             ` <4101467707611-tvSLaYVQeptuio3avFS2gg@public.gmane.org>
2016-07-05  9:25               ` maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
2016-07-05  9:25                 ` [linux-sunxi] " maxime.ripard
2016-07-05  9:25                 ` maxime.ripard at free-electrons.com
2016-07-05 10:09                 ` Icenowy Zheng
2016-07-05 10:09                   ` [linux-sunxi] " Icenowy Zheng
2016-07-05 12:45                 ` Bruno Prémont
2016-07-05 12:45                   ` [linux-sunxi] " Bruno Prémont
2016-07-05 12:45                   ` Bruno Prémont
2016-07-06  4:34               ` Michael Haas
2016-07-06  4:34                 ` [linux-sunxi] " Michael Haas
2016-07-06  4:34                 ` Michael Haas
     [not found]                 ` <577C8A6D.8000409-bdq14YP6qtSV9CzYT+GlPGD2FQJk+8+b@public.gmane.org>
2016-07-06  7:48                   ` Icenowy Zheng
2016-07-06  7:48                     ` [linux-sunxi] " Icenowy Zheng
2016-07-05  8:47           ` Icenowy Zheng
2016-07-05  8:47             ` [linux-sunxi] " Icenowy Zheng
     [not found]             ` <112861467708458-tvSLaYVQeptuio3avFS2gg@public.gmane.org>
2016-07-05 12:43               ` Bruno Prémont
2016-07-05 12:43                 ` [linux-sunxi] " Bruno Prémont
2016-07-05 12:43                 ` Bruno Prémont
2016-07-01  9:29   ` [PATCH 3/4] ARM: sun8i: add axp20x-battery node for axp22x Icenowy Zheng
2016-07-01  9:29     ` Icenowy Zheng
2016-07-01  9:29   ` [PATCH 4/4] ARM: sun8i: enable axp22x-battery on A23/33 Q8 tablets Icenowy Zheng
2016-07-01  9:29     ` Icenowy Zheng
2016-08-05  7:46   ` [PATCH 1/4] mfd: axp20x: add adc volatile ranges for axp22x Lee Jones
2016-08-05  7:46     ` Lee Jones
2016-08-05  7:46     ` Lee Jones

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=4101467707611@web7h.yandex.ru \
    --to=icenowy-ymacfijhrkm@public.gmane.org \
    --cc=dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
    --cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=dwmw2-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org \
    --cc=linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org \
    --cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-pm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org \
    --cc=mark.rutland-5wv7dgnIgG8@public.gmane.org \
    --cc=maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org \
    --cc=michael.haas-cl+VPiYnx/1AfugRpC6u6w@public.gmane.org \
    --cc=robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
    --cc=sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
    --cc=wens-jdAy2FN1RRM@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: link
Be 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.