Linux LED subsystem development
 help / color / mirror / Atom feed
From: Junhao Xie <bigfoot@classfun.cn>
To: devicetree@vger.kernel.org, linux-hwmon@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org,
	linux-pm@vger.kernel.org, linux-rtc@vger.kernel.org,
	linux-watchdog@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-rockchip@lists.infradead.org
Cc: Jean Delvare <jdelvare@suse.com>,
	Guenter Roeck <linux@roeck-us.net>, Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>, Pavel Machek <pavel@ucw.cz>,
	Lee Jones <lee@kernel.org>, Sebastian Reichel <sre@kernel.org>,
	Alexandre Belloni <alexandre.belloni@bootlin.com>,
	Wim Van Sebroeck <wim@linux-watchdog.org>,
	Heiko Stuebner <heiko@sntech.de>, Chukun Pan <amadeus@jmu.edu.cn>,
	Junhao Xie <bigfoot@classfun.cn>
Subject: [PATCH 4/9] power: supply: photonicat-supply: Add Photonicat PMU battery and charger
Date: Fri,  6 Sep 2024 17:36:25 +0800	[thread overview]
Message-ID: <20240906093630.2428329-5-bigfoot@classfun.cn> (raw)
In-Reply-To: <20240906093630.2428329-1-bigfoot@classfun.cn>

Photonicat PMU supports battery and charger power supply.
The MCU only provides voltage meter.

Signed-off-by: Junhao Xie <bigfoot@classfun.cn>
---
 drivers/power/supply/Kconfig             |  12 ++
 drivers/power/supply/Makefile            |   1 +
 drivers/power/supply/photonicat-supply.c | 250 +++++++++++++++++++++++
 3 files changed, 263 insertions(+)
 create mode 100644 drivers/power/supply/photonicat-supply.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index bcfa63fb9f1e..4d2fcf568810 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -455,6 +455,18 @@ config CHARGER_PCF50633
 	help
 	  Say Y to include support for NXP PCF50633 Main Battery Charger.
 
+config PHOTONICAT_POWER
+	tristate "Photonicat PMU power supply driver"
+	depends on MFD_PHOTONICAT_PMU
+	help
+	  Photonicat PMU supports battery and charger power supply.
+	  The MCU only provides voltage meter.
+
+	  Say Y here to enable support for Photonicat PMU power supply.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called photonicat-supply.
+
 config BATTERY_RX51
 	tristate "Nokia RX-51 (N900) battery driver"
 	depends on TWL4030_MADC
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 8dcb41545317..81ada9bb6438 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_CHARGER_RT9471)	+= rt9471.o
 obj-$(CONFIG_BATTERY_TWL4030_MADC)	+= twl4030_madc_battery.o
 obj-$(CONFIG_CHARGER_88PM860X)	+= 88pm860x_charger.o
 obj-$(CONFIG_CHARGER_PCF50633)	+= pcf50633-charger.o
+obj-$(CONFIG_PHOTONICAT_POWER)	+= photonicat-supply.o
 obj-$(CONFIG_BATTERY_RX51)	+= rx51_battery.o
 obj-$(CONFIG_AB8500_BM)		+= ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o
 obj-$(CONFIG_CHARGER_CPCAP)	+= cpcap-charger.o
diff --git a/drivers/power/supply/photonicat-supply.c b/drivers/power/supply/photonicat-supply.c
new file mode 100644
index 000000000000..e6861a3904fe
--- /dev/null
+++ b/drivers/power/supply/photonicat-supply.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 Junhao Xie <bigfoot@classfun.cn>
+ */
+
+#include <linux/module.h>
+#include <linux/mfd/photonicat-pmu.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+enum pcat_supply_type {
+	PCAT_SUPPLY_BATTERY,
+	PCAT_SUPPLY_CHARGER,
+};
+
+struct pcat_supply {
+	struct device *dev;
+	struct pcat_pmu *pmu;
+	struct notifier_block nb;
+	struct power_supply *psy;
+	struct power_supply_desc desc;
+	struct power_supply_battery_info *bat_info;
+	enum pcat_supply_type type;
+	u16 supply_microvolt;
+	struct completion initial_report;
+};
+
+static int pcat_pmu_get_capacity(struct pcat_supply *supply)
+{
+	int uv;
+
+	if (supply->type != PCAT_SUPPLY_BATTERY)
+		return 0;
+	uv = supply->supply_microvolt * 1000;
+
+	return power_supply_batinfo_ocv2cap(supply->bat_info, uv, 20);
+}
+
+static int pcat_pmu_get_energy(struct pcat_supply *supply)
+{
+	int capacity;
+
+	if (supply->type != PCAT_SUPPLY_BATTERY)
+		return 0;
+	capacity = pcat_pmu_get_capacity(supply);
+	if (capacity < 0)
+		return 0;
+
+	return supply->bat_info->energy_full_design_uwh / 100 * capacity;
+}
+
+static int pcat_pmu_get_status(struct pcat_supply *supply)
+{
+	if (supply->type != PCAT_SUPPLY_BATTERY)
+		return 0;
+
+	if (pcat_pmu_get_capacity(supply) < 100) {
+		if (power_supply_am_i_supplied(supply->psy))
+			return POWER_SUPPLY_STATUS_CHARGING;
+		else
+			return POWER_SUPPLY_STATUS_DISCHARGING;
+	}
+
+	return POWER_SUPPLY_STATUS_FULL;
+}
+
+static int pcat_pmu_get_supply_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					union power_supply_propval *val)
+{
+	struct pcat_supply *supply = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = pcat_pmu_get_capacity(supply);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = supply->supply_microvolt > 1000;
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_FULL:
+		val->intval = supply->bat_info->energy_full_design_uwh;
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_NOW:
+		val->intval = pcat_pmu_get_energy(supply);
+		break;
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = pcat_pmu_get_status(supply);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		val->intval = supply->bat_info->voltage_max_design_uv;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		val->intval = supply->bat_info->voltage_min_design_uv;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = supply->supply_microvolt * 1000;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static enum power_supply_property pcat_charger_props[] = {
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property pcat_battery_props[] = {
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_ENERGY_FULL,
+	POWER_SUPPLY_PROP_ENERGY_NOW,
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static int pcat_supply_notify(struct notifier_block *nb, unsigned long action,
+			      void *data)
+{
+	struct pcat_supply *supply = container_of(nb, struct pcat_supply, nb);
+	struct pcat_data_cmd_status *status = pcat_data_get_data(data);
+
+	if (action != PCAT_CMD_STATUS_REPORT)
+		return NOTIFY_DONE;
+
+	switch (supply->type) {
+	case PCAT_SUPPLY_BATTERY:
+		supply->supply_microvolt = status->battery_microvolt;
+		break;
+	case PCAT_SUPPLY_CHARGER:
+		supply->supply_microvolt = status->charger_microvolt;
+		break;
+	}
+
+	complete(&supply->initial_report);
+
+	return NOTIFY_DONE;
+}
+
+static int pcat_supply_probe(struct platform_device *pdev)
+{
+	int ret;
+	const char *label;
+	const char *supply_type;
+	struct device *dev = &pdev->dev;
+	struct pcat_supply *supply;
+	struct power_supply_config psy_cfg = {};
+
+	supply = devm_kzalloc(dev, sizeof(*supply), GFP_KERNEL);
+	if (!supply)
+		return -ENOMEM;
+
+	supply->dev = dev;
+	supply->pmu = dev_get_drvdata(dev->parent);
+	supply->nb.notifier_call = pcat_supply_notify;
+	init_completion(&supply->initial_report);
+	psy_cfg.drv_data = supply;
+	psy_cfg.of_node = dev->of_node;
+	platform_set_drvdata(pdev, supply);
+
+	ret = of_property_read_string(dev->of_node, "type", &supply_type);
+	if (ret)
+		return dev_err_probe(dev, ret, "No supply type property\n");
+
+	ret = of_property_read_string(dev->of_node, "label", &label);
+	if (ret)
+		return dev_err_probe(dev, ret, "No label property\n");
+
+	if (!strcmp(supply_type, "battery")) {
+		supply->type = PCAT_SUPPLY_BATTERY;
+		supply->desc.type = POWER_SUPPLY_TYPE_BATTERY;
+		supply->desc.properties = pcat_battery_props;
+		supply->desc.num_properties = ARRAY_SIZE(pcat_battery_props);
+	} else if (!strcmp(supply_type, "charger")) {
+		supply->type = PCAT_SUPPLY_CHARGER;
+		supply->desc.type = POWER_SUPPLY_TYPE_MAINS;
+		supply->desc.properties = pcat_charger_props;
+		supply->desc.num_properties = ARRAY_SIZE(pcat_charger_props);
+	} else
+		return dev_err_probe(dev, -EINVAL, "Unknown supply type %s\n",
+				     supply_type);
+
+	ret = pcat_pmu_register_notify(supply->pmu, &supply->nb);
+	if (ret)
+		return ret;
+
+	if (!wait_for_completion_timeout(&supply->initial_report,
+					 msecs_to_jiffies(3000))) {
+		ret = dev_err_probe(dev, -ETIMEDOUT,
+				    "timeout waiting for initial report\n");
+		goto error;
+	}
+
+	dev_info(dev, "Voltage: %u mV\n", supply->supply_microvolt);
+
+	supply->desc.name = label;
+	supply->desc.get_property = pcat_pmu_get_supply_property;
+
+	supply->psy = devm_power_supply_register(dev, &supply->desc, &psy_cfg);
+	if (IS_ERR(supply->psy)) {
+		ret = PTR_ERR(supply->psy);
+		dev_err_probe(dev, ret, "Failed to register supply\n");
+		goto error;
+	}
+
+	if (supply->type == PCAT_SUPPLY_BATTERY) {
+		ret = power_supply_get_battery_info(supply->psy,
+						    &supply->bat_info);
+		if (ret) {
+			dev_err_probe(dev, ret, "Unable to get battery info\n");
+			goto error;
+		}
+	}
+
+	return 0;
+error:
+	pcat_pmu_unregister_notify(supply->pmu, &supply->nb);
+	return ret;
+}
+
+static void pcat_supply_remove(struct platform_device *pdev)
+{
+	struct pcat_supply *supply = platform_get_drvdata(pdev);
+
+	pcat_pmu_unregister_notify(supply->pmu, &supply->nb);
+}
+
+static const struct of_device_id pcat_supply_dt_ids[] = {
+	{ .compatible = "ariaboard,photonicat-pmu-supply", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pcat_supply_dt_ids);
+
+static struct platform_driver pcat_supply_driver = {
+	.driver = {
+		.name = "photonicat-supply",
+		.of_match_table = pcat_supply_dt_ids,
+	},
+	.probe = pcat_supply_probe,
+	.remove = pcat_supply_remove,
+};
+module_platform_driver(pcat_supply_driver);
+
+MODULE_AUTHOR("Junhao Xie <bigfoot@classfun.cn>");
+MODULE_DESCRIPTION("Photonicat PMU Power Supply");
+MODULE_LICENSE("GPL");
-- 
2.46.0


  parent reply	other threads:[~2024-09-06  9:38 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-09-06  9:36 [PATCH 0/9] Introduce Photonicat power management MCU driver Junhao Xie
2024-09-06  9:36 ` [PATCH 1/9] mfd: Add driver for Photonicat power management MCU Junhao Xie
2024-09-06  9:43   ` Krzysztof Kozlowski
2024-09-06 10:40     ` Junhao Xie
2024-09-07  8:10   ` Markus Elfring
2024-09-07 14:46     ` Junhao Xie
2024-09-07  8:44   ` Markus Elfring
2024-09-07 14:33     ` Junhao Xie
2024-09-08  8:14       ` Krzysztof Kozlowski
2024-09-12  7:55         ` Lee Jones
2024-09-06  9:36 ` [PATCH 2/9] power: reset: add Photonicat PMU poweroff driver Junhao Xie
2024-09-06  9:44   ` Krzysztof Kozlowski
2024-09-06 10:05     ` Junhao Xie
2024-09-06  9:36 ` [PATCH 3/9] watchdog: Add Photonicat PMU watchdog driver Junhao Xie
2024-09-06 11:52   ` Guenter Roeck
2024-09-06 13:41     ` Junhao Xie
2024-09-06  9:36 ` Junhao Xie [this message]
2024-09-06  9:36 ` [PATCH 5/9] rtc: Add Photonicat PMU real-time clock Junhao Xie
2024-09-06  9:36 ` [PATCH 6/9] hwmon: Add support for Photonicat PMU board temperature sensor Junhao Xie
2024-09-06 11:41   ` Guenter Roeck
2024-09-06 13:49     ` Junhao Xie
2024-09-06 14:33       ` Guenter Roeck
2024-09-06  9:36 ` [PATCH 7/9] leds: add Photonicat PMU LED driver Junhao Xie
2024-10-02 15:35   ` Lee Jones
2024-11-08  3:48     ` Junhao Xie
2024-09-06  9:36 ` [PATCH 8/9] dt-bindings: Add documentation for Photonicat PMU Junhao Xie
2024-09-06  9:51   ` Krzysztof Kozlowski
2024-09-07 14:27     ` Junhao Xie
2024-09-08  8:13       ` Krzysztof Kozlowski
2024-09-06  9:36 ` [PATCH 9/9] arm64: dts: rockchip: add Photonicat PMU support for Ariaboard Photonicat Junhao Xie
2024-09-06  9:53   ` Krzysztof Kozlowski
2024-09-06 13:56     ` Junhao Xie
2024-09-06  9:45 ` [PATCH 0/9] Introduce Photonicat power management MCU driver Krzysztof Kozlowski
2024-09-06 10:20   ` Junhao Xie
2024-09-08  9:30 ` Chukun Pan
2024-09-11  6:23   ` Junhao Xie

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=20240906093630.2428329-5-bigfoot@classfun.cn \
    --to=bigfoot@classfun.cn \
    --cc=alexandre.belloni@bootlin.com \
    --cc=amadeus@jmu.edu.cn \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=heiko@sntech.de \
    --cc=jdelvare@suse.com \
    --cc=krzk+dt@kernel.org \
    --cc=lee@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-hwmon@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=linux-rtc@vger.kernel.org \
    --cc=linux-watchdog@vger.kernel.org \
    --cc=linux@roeck-us.net \
    --cc=pavel@ucw.cz \
    --cc=robh@kernel.org \
    --cc=sre@kernel.org \
    --cc=wim@linux-watchdog.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox