This patchset does the following:
- Add CURRENT_MAX and INPUT_CURRENT_MAX power supply properties to
expose the "fast charge current" (maximum current from charger to
battery) and "CHGIN input current limit" (maximum current from
external supply to charger).
- Add functions for toggling charging and OTG modes.
- Add an extcon-based handler that enables charging or OTG depending
on the cable type plugged in. The extcon device to use for cable
detection can be specified in the device tree, and is entirely
optional.
The extcon listener implementation is inspired by the rt5033 charger
driver (commit 8242336dc8a8 ("power: supply: rt5033_charger: Add cable
detection and USB OTG supply")).
Signed-off-by: Artur Weber <[email protected]>
---
This started off as a cleanup of "[PATCH 0/3] max77693: USB event listener
for charger"[1] (and its later iterations[2]), but I ended up replacing 99%
of the code and adding a few more additions on top.
This patchset was made for two reasons:
- I noticed that charging on my Galaxy Tab 3 8.0 seems to "work", but is
so slow that it starts to discharge during normal usage. After some
investigation I found that under the downstream kernel, the charging
limit registers (two of them! see patch descriptions) are changed to a
higher value when plugged into a charger, and adjusted based on the cable
type.
- I was investigating what was needed to get OTG support running, and
found that some OTG mode bits had to be set in the charger when an OTG
cable is detected, and that required me to modify the charging driver.
That's why the patchset includes *both* charging detection and OTG support.
== Kernel-side charging management ==
The original patchset this was meant to supersede was denied because,
among other reasons, upstream did not agree to having the charging be
controlled this way in the charger driver, suggesting that either
user-space or the charger-manager driver should do it[3][4].
charger-manager is not used in any devices, plus the already mentioned
patchset comments suggest that it's not the correct way to do things
("DT should be used for concrete hardware, not glue drivers")[4].
There are also some drivers that already do this sort of kernel-side charger
toggling/management: other than the aforementioned RT5033 driver which this
new patchset is based off[5], there's also the MAX8997 charger[6] (the patch
for which was merged just a year after the MAX77693 discussion[7]!) and the
AXP288 driver[8]. Still, that's just 3 out of many more drivers...
For this reason, I'm sending this first version of the patchset as an RFC
for further discussion. I've done some thinking about what a potential
implementation allowing for controlling charging from userspace might look
like, and here's what I came up with:
- Allow for setting the charging input current by setting the INPUT_CURRENT_
MAX property of the power supply. (Leave current from charger to battery
set by the device tree in the kernel, since it's arguably more dangerous.)
- Allow for setting the CHARGER regulator on/off through some sysfs property.
How this should be done is tough to specify; there seems to be a few
ways to do it (e.g. setting the STATUS property[9]), we could use a custom
sysfs property.
- Add a custom sysfs property to disable kernel-side charging.
About that last one - I'm convinced at least *some* level of kernel-side
charging control should exist, and that's for a practical reason:
A reccuring complaint[10] is devices that are low on battery and plugged
into a charger shutting down before they can fully boot. If we set up
charging during boot, the charger driver can handle charging until a
userspace daemon appears and takes over.
Of course, this is all up for discussion - comments appreciated ;)
== CHARGER regulator ==
I think it's worth also analysing the role that the CHARGER regulator plays
here. At the moment, the CHARGER regulator (managed in drivers/regulator/
max77693-regulator.c) handles:
* the CHGIN input limit as the regulator current limit;
* charger enable/disable as the regulator enable/disable.
A comment in the regulator driver suggests that this should in fact manage
the *fast charge* current, not the CHGIN input current, and that "this
should be fixed"[11]. But now we're managing the fast charge current in
the charger driver... and, if the regulator driver ever was to be "fixed"
this way, it would break the charger driver's ability to set input current.
We could potentially drop the CHARGER regulator and manage these registers
within the charger driver, but since it's not causing us any real problems
at the moment (and the "fix" scenario mentioned earlier is very unlikely),
I decided to keep it and use it to handle the two functions mentioned above.
(Although one of the reasons for the superseded patchset being denied was
that "[this] power supply driver should not manage regulators"[3]...)
---
Special thanks to Wolfgang Wiedmeyer whose original patchset[1] served as
the base for this one, and to Henrik Grimler for helping me out with
figuring out how the input current/fast charge current registers are
used.
[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
[3] https://lore.kernel.org/all/20160927081344.GC4394@kozik-lap/
[4] https://lore.kernel.org/all/[email protected]/
[5] https://github.com/torvalds/linux/blob/v6.9/drivers/power/supply/rt5033_charger.c
[6] https://github.com/torvalds/linux/blob/v6.9/drivers/power/supply/max8997_charger.c#L98-L141
[7] https://github.com/torvalds/linux/commit/f384989e88d4484fc9a9e31b0cf0a36e6f172136
[8] https://github.com/torvalds/linux/blob/v6.9/drivers/power/supply/axp288_charger.c#L617-L673
[9] https://github.com/torvalds/linux/blob/v6.9/drivers/power/supply/rt9471.c#L370-L371
[10] https://gitlab.com/postmarketOS/pmaports/-/issues/2594
[11] https://github.com/torvalds/linux/blob/v6.9/drivers/regulator/max77693-regulator.c#L45-L54
---
Artur Weber (10):
dt-bindings: power: supply: max77693: Add fast charge current property
dt-bindings: power: supply: max77693: Add maxim,usb-connector property
mfd: max77693: Add defines for OTG control
power: supply: max77693: Expose INPUT_CURRENT_LIMIT and CURRENT_MAX
power: supply: max77693: Set charge current limits during init
power: supply: max77693: Add USB extcon detection for enabling charging
power: supply: max77693: Add support for detecting and enabling OTG
power: supply: max77693: Set up charge/input current according to cable type
ARM: dts: samsung: exynos4212-tab3: Set fast charge current for MAX77693
ARM: dts: samsung: exynos4212-tab3: Add USB connector node
Wolfgang Wiedmeyer (1):
mfd: max77693: Add defines for charger current control
.../bindings/power/supply/maxim,max77693.yaml | 13 +
arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi | 14 +
drivers/power/supply/Kconfig | 1 +
drivers/power/supply/max77693_charger.c | 291 +++++++++++++++++++++
include/linux/mfd/max77693-private.h | 9 +
5 files changed, 328 insertions(+)
---
base-commit: a38297e3fb012ddfa7ce0321a7e5a8daeb1872b6
change-id: 20240525-max77693-charger-extcon-9ebb7bad83ce
Best regards,
--
Artur Weber <[email protected]>
These three bits are used to enable OTG control in the charger
driver. Add them in preparation for an updated driver.
Signed-off-by: Artur Weber <[email protected]>
---
include/linux/mfd/max77693-private.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h
index 4570646e2f33..4448b024255d 100644
--- a/include/linux/mfd/max77693-private.h
+++ b/include/linux/mfd/max77693-private.h
@@ -209,7 +209,10 @@ enum max77693_charger_battery_state {
/* MAX77693 CHG_CNFG_00 register */
#define CHG_CNFG_00_CHG_MASK 0x1
+#define CHG_CNFG_00_OTG_MASK 0x2
#define CHG_CNFG_00_BUCK_MASK 0x4
+#define CHG_CNFG_00_BOOST_MASK 0x8
+#define CHG_CNFG_00_DIS_MUIC_CTRL_MASK 0x20
/* MAX77693_CHG_REG_CHG_CNFG_01 register */
#define CHG_CNFG_01_FCHGTIME_SHIFT 0
--
2.45.1
Add the maxim,fast-charge-current-microamp property, used to specify the
current limit to use for fast charge (when plugged into a wall charger).
Signed-off-by: Artur Weber <[email protected]>
---
Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml
index f5fd53debbc8..4f80cc5418f5 100644
--- a/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml
+++ b/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml
@@ -56,6 +56,13 @@ properties:
maximum: 3500000
default: 3500000
+ maxim,fast-charge-current-microamp:
+ description:
+ Current to use for fast charge in uA.
+ minimum: 60000
+ maximum: 2580000
+ default: 500000
+
maxim,charge-input-threshold-microvolt:
description: |
Threshold voltage in uV for triggering input voltage regulation loop.
--
2.45.1
From: Wolfgang Wiedmeyer <[email protected]>
This prepares for an updated regulator and charger driver. The defines
are needed to set the maximum input current and the fast charge
current.
Signed-off-by: Wolfgang Wiedmeyer <[email protected]>
[[email protected]: small fix]
Signed-off-by: Denis 'GNUtoo' Carikli <[email protected]>
[Artur: swap hardcoded ILIM values for DEFAULT_FAST_CHARGE_CURRENT]
Signed-off-by: Artur Weber <[email protected]>
---
include/linux/mfd/max77693-private.h | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h
index 54444ff2a5de..4570646e2f33 100644
--- a/include/linux/mfd/max77693-private.h
+++ b/include/linux/mfd/max77693-private.h
@@ -145,6 +145,8 @@ enum max77693_pmic_reg {
#define DEFAULT_THERMAL_REGULATION_TEMP 100
/* microamps */
#define DEFAULT_BATTERY_OVERCURRENT 3500000
+/* microamps */
+#define DEFAULT_FAST_CHARGE_CURRENT 500000
/* microvolts */
#define DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT 4300000
@@ -217,6 +219,10 @@ enum max77693_charger_battery_state {
#define CHG_CNFG_01_CHGRSTRT_MASK (0x3 << CHG_CNFG_01_CHGRSTRT_SHIFT)
#define CHG_CNFG_01_PQEN_MAKS BIT(CHG_CNFG_01_PQEN_SHIFT)
+/* MAX77693_CHG_REG_CHG_CNFG_02 register */
+#define CHG_CNFG_02_CC_SHIFT 0
+#define CHG_CNFG_02_CC_MASK 0x3F
+
/* MAX77693_CHG_REG_CHG_CNFG_03 register */
#define CHG_CNFG_03_TOITH_SHIFT 0
#define CHG_CNFG_03_TOTIME_SHIFT 3
--
2.45.1
Allow for specifying a USB connector to use for charger type/OTG cable
detection.
The way this is done is inspired by the rt5033-charger implementation.
Signed-off-by: Artur Weber <[email protected]>
---
Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml
index 4f80cc5418f5..e5b29a8aed56 100644
--- a/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml
+++ b/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml
@@ -71,6 +71,12 @@ properties:
enum: [4300000, 4700000, 4800000, 4900000]
default: 4300000
+ maxim,usb-connector:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ USB connector to use for charger type/OTG cable detection. Phandle
+ to a USB connector according to usb-connector.yaml.
+
required:
- compatible
--
2.45.1
There are two charger current limit registers:
- Fast charge current limit (which controls current going from the
charger to the battery);
- CHGIN input current limit (which controls current going into the
charger through the cable, and is managed by the CHARGER regulator).
Add functions for setting both of the values, and set them to a
safe default value of 500mA at initialization.
The default value for the fast charge current limit can be modified
by setting the maxim,fast-charge-current-microamp DT property; the
CHGIN input current limit will be set up later in the charger detection
mechanism.
Signed-off-by: Artur Weber <[email protected]>
---
drivers/power/supply/max77693_charger.c | 45 +++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
index 894c35b750b3..d59b1524b0a4 100644
--- a/drivers/power/supply/max77693_charger.c
+++ b/drivers/power/supply/max77693_charger.c
@@ -28,6 +28,7 @@ struct max77693_charger {
u32 min_system_volt;
u32 thermal_regulation_temp;
u32 batttery_overcurrent;
+ u32 fast_charge_current;
u32 charge_input_threshold_volt;
};
@@ -591,6 +592,35 @@ static int max77693_set_batttery_overcurrent(struct max77693_charger *chg,
CHG_CNFG_12_B2SOVRC_MASK, data);
}
+static int max77693_set_input_current_limit(struct max77693_charger *chg,
+ unsigned int uamp)
+{
+ dev_dbg(chg->dev, "CHGIN input current limit: %u\n", uamp);
+
+ return regulator_set_current_limit(chg->regu, (int)uamp, (int)uamp);
+}
+
+static int max77693_set_fast_charge_current(struct max77693_charger *chg,
+ unsigned int uamp)
+{
+ unsigned int data;
+
+ data = (uamp / 1000) * 10 / 333; /* 0.1A/3 steps */
+
+ if (data > CHG_CNFG_02_CC_MASK) {
+ dev_err(chg->dev, "Wrong value for fast charge current\n");
+ return -EINVAL;
+ }
+
+ data <<= CHG_CNFG_02_CC_SHIFT;
+
+ dev_dbg(chg->dev, "Fast charge current: %u (0x%x)\n", uamp, data);
+
+ return regmap_update_bits(chg->max77693->regmap,
+ MAX77693_CHG_REG_CHG_CNFG_02,
+ CHG_CNFG_02_CC_MASK, data);
+}
+
static int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg,
unsigned int uvolt)
{
@@ -668,6 +698,15 @@ static int max77693_reg_init(struct max77693_charger *chg)
if (ret)
return ret;
+ ret = max77693_set_fast_charge_current(chg, chg->fast_charge_current);
+ if (ret)
+ return ret;
+
+ ret = max77693_set_input_current_limit(chg,
+ DEFAULT_FAST_CHARGE_CURRENT);
+ if (ret)
+ return ret;
+
return max77693_set_charge_input_threshold_volt(chg,
chg->charge_input_threshold_volt);
}
@@ -703,11 +742,17 @@ static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
chg->charge_input_threshold_volt =
DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT;
+ if (of_property_read_u32(np, "maxim,fast-charge-current-microamp",
+ &chg->fast_charge_current))
+ chg->fast_charge_current = DEFAULT_FAST_CHARGE_CURRENT;
+
return 0;
}
#else /* CONFIG_OF */
static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
{
+ chg->fast_charge_current = DEFAULT_FAST_CHARGE_CURRENT;
+
return 0;
}
#endif /* CONFIG_OF */
--
2.45.1
There are two charger current limit registers:
- Fast charge current limit (which controls current going from the
charger to the battery);
- CHGIN input current limit (which controls current going into the
charger through the cable, and is managed by the CHARGER regulator).
Add the necessary functions to retrieve the CHGIN input limit (from CHARGER
regulator) and maximum fast charge current values, and expose them as power
supply properties.
Signed-off-by: Artur Weber <[email protected]>
---
drivers/power/supply/max77693_charger.c | 52 +++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
index 2001e12c9f7d..894c35b750b3 100644
--- a/drivers/power/supply/max77693_charger.c
+++ b/drivers/power/supply/max77693_charger.c
@@ -9,6 +9,7 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/mfd/max77693.h>
#include <linux/mfd/max77693-common.h>
#include <linux/mfd/max77693-private.h>
@@ -21,6 +22,7 @@ struct max77693_charger {
struct device *dev;
struct max77693_dev *max77693;
struct power_supply *charger;
+ struct regulator *regu;
u32 constant_volt;
u32 min_system_volt;
@@ -197,12 +199,51 @@ static int max77693_get_online(struct regmap *regmap, int *val)
return 0;
}
+/*
+ * There are *two* current limit registers:
+ * - CHGIN limit, which limits the input current from the external charger;
+ * - Fast charge current limit, which limits the current going to the battery.
+ */
+
+static int max77693_get_input_current_limit(struct max77693_charger *chg,
+ int *val)
+{
+ int ret;
+
+ ret = regulator_get_current_limit(chg->regu);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+static int max77693_get_fast_charge_current(struct regmap *regmap, int *val)
+{
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_CNFG_02, &data);
+ if (ret < 0)
+ return ret;
+
+ data &= CHG_CNFG_02_CC_MASK;
+ data >>= CHG_CNFG_02_CC_SHIFT;
+
+ *val = (data * 333 / 10) * 1000; /* 3 steps/0.1A */
+
+ return 0;
+}
+
static enum power_supply_property max77693_charger_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
};
@@ -231,6 +272,12 @@ static int max77693_charger_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_ONLINE:
ret = max77693_get_online(regmap, &val->intval);
break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = max77693_get_input_current_limit(chg, &val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ ret = max77693_get_fast_charge_current(regmap, &val->intval);
+ break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = max77693_charger_model;
break;
@@ -680,6 +727,11 @@ static int max77693_charger_probe(struct platform_device *pdev)
chg->dev = &pdev->dev;
chg->max77693 = max77693;
+ chg->regu = devm_regulator_get(chg->dev, "CHARGER");
+ if (IS_ERR(chg->regu))
+ return dev_err_probe(&pdev->dev, PTR_ERR(chg->regu),
+ "failed to get charger regulator\n");
+
ret = max77693_dt_init(&pdev->dev, chg);
if (ret)
return ret;
--
2.45.1
Add a device tree property, "maxim,usb-connector", that can be used to
specify a USB connector to use to detect whether a charging cable has
been plugged in/out, and enable/disable charging accordingly.
To accommodate this, also add an internal pointer to the CHARGER regulator,
which is used to enable/disable charging and set the input current limit
(which will be done in subsequent commits).
The extcon listener/worker implementation is inspired by the rt5033_charger
driver.
Signed-off-by: Artur Weber <[email protected]>
---
drivers/power/supply/Kconfig | 1 +
drivers/power/supply/max77693_charger.c | 125 ++++++++++++++++++++++++++++++++
2 files changed, 126 insertions(+)
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 3e31375491d5..e4aeff9e7ad1 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -549,6 +549,7 @@ config CHARGER_MAX77650
config CHARGER_MAX77693
tristate "Maxim MAX77693 battery charger driver"
depends on MFD_MAX77693
+ depends on EXTCON || !EXTCON
help
Say Y to enable support for the Maxim MAX77693 battery charger.
diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
index d59b1524b0a4..c2e8ae367381 100644
--- a/drivers/power/supply/max77693_charger.c
+++ b/drivers/power/supply/max77693_charger.c
@@ -5,6 +5,8 @@
// Copyright (C) 2014 Samsung Electronics
// Krzysztof Kozlowski <[email protected]>
+#include <linux/devm-helpers.h>
+#include <linux/extcon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
@@ -30,6 +32,13 @@ struct max77693_charger {
u32 batttery_overcurrent;
u32 fast_charge_current;
u32 charge_input_threshold_volt;
+
+ /* USB cable notifications */
+ struct {
+ struct extcon_dev *edev;
+ struct notifier_block nb;
+ struct work_struct work;
+ } cable;
};
static int max77693_get_charger_state(struct regmap *regmap, int *val)
@@ -711,16 +720,109 @@ static int max77693_reg_init(struct max77693_charger *chg)
chg->charge_input_threshold_volt);
}
+static int max77693_set_charging(struct max77693_charger *chg, bool enable)
+{
+ int is_enabled;
+ int ret = 0;
+
+ is_enabled = regulator_is_enabled(chg->regu);
+ if (is_enabled < 0)
+ return is_enabled;
+
+ if (enable && !is_enabled)
+ ret = regulator_enable(chg->regu);
+ else if (!enable && is_enabled)
+ ret = regulator_disable(chg->regu);
+
+ return ret;
+}
+
+static void max77693_charger_extcon_work(struct work_struct *work)
+{
+ struct max77693_charger *chg = container_of(work, struct max77693_charger,
+ cable.work);
+ struct extcon_dev *edev = chg->cable.edev;
+ int connector, state;
+ int ret;
+
+ for (connector = EXTCON_USB_HOST; connector <= EXTCON_CHG_USB_PD;
+ connector++) {
+ state = extcon_get_state(edev, connector);
+ if (state == 1)
+ break;
+ }
+
+ switch (connector) {
+ case EXTCON_CHG_USB_SDP:
+ case EXTCON_CHG_USB_DCP:
+ case EXTCON_CHG_USB_CDP:
+ case EXTCON_CHG_USB_ACA:
+ case EXTCON_CHG_USB_FAST:
+ case EXTCON_CHG_USB_SLOW:
+ case EXTCON_CHG_USB_PD:
+ ret = max77693_set_charging(chg, true);
+ if (ret) {
+ dev_err(chg->dev, "failed to enable charging\n");
+ break;
+ }
+ dev_info(chg->dev, "charging. connector type: %d\n",
+ connector);
+ break;
+ default:
+ ret = max77693_set_charging(chg, false);
+ if (ret) {
+ dev_err(chg->dev, "failed to disable charging\n");
+ break;
+ }
+ dev_info(chg->dev, "charging. connector type: %d\n",
+ connector);
+ break;
+ }
+
+ power_supply_changed(chg->charger);
+}
+
+static int max77693_charger_extcon_notifier(struct notifier_block *nb,
+ unsigned long event, void *param)
+{
+ struct max77693_charger *chg = container_of(nb, struct max77693_charger,
+ cable.nb);
+
+ schedule_work(&chg->cable.work);
+
+ return NOTIFY_OK;
+}
+
#ifdef CONFIG_OF
static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
{
struct device_node *np = dev->of_node;
+ struct device_node *np_conn, *np_edev;
if (!np) {
dev_err(dev, "no charger OF node\n");
return -EINVAL;
}
+ np_conn = of_parse_phandle(np, "maxim,usb-connector", 0);
+ np_edev = of_get_parent(np_conn);
+
+ chg->cable.edev = extcon_find_edev_by_node(np_edev);
+ if (IS_ERR(chg->cable.edev)) {
+ /*
+ * In case of deferred extcon probe, defer our probe as well
+ * until it appears.
+ */
+ if (PTR_ERR(chg->cable.edev) == -EPROBE_DEFER)
+ return PTR_ERR(chg->cable.edev);
+ /*
+ * Otherwise, ignore errors (the charger can run without a
+ * connector provided).
+ */
+ dev_warn(dev, "no extcon device found in device-tree (%ld)\n",
+ PTR_ERR(chg->cable.edev));
+ }
+
if (of_property_read_u32(np, "maxim,constant-microvolt",
&chg->constant_volt))
chg->constant_volt = DEFAULT_CONSTANT_VOLT;
@@ -787,6 +889,26 @@ static int max77693_charger_probe(struct platform_device *pdev)
psy_cfg.drv_data = chg;
+ /* Set up extcon if the USB connector node was found */
+ if (!IS_ERR(chg->cable.edev)) {
+ ret = devm_work_autocancel(&pdev->dev, &chg->cable.work,
+ max77693_charger_extcon_work);
+ if (ret) {
+ dev_err(&pdev->dev, "failed: initialize extcon work\n");
+ return ret;
+ }
+
+ chg->cable.nb.notifier_call = max77693_charger_extcon_notifier;
+
+ ret = devm_extcon_register_notifier_all(&pdev->dev,
+ chg->cable.edev,
+ &chg->cable.nb);
+ if (ret) {
+ dev_err(&pdev->dev, "failed: register extcon notifier\n");
+ return ret;
+ }
+ }
+
ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
if (ret) {
dev_err(&pdev->dev, "failed: create fast charge timer sysfs entry\n");
@@ -822,6 +944,9 @@ static int max77693_charger_probe(struct platform_device *pdev)
device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
+ devm_extcon_unregister_notifier_all(&pdev->dev, chg->cable.edev,
+ &chg->cable.nb);
+
return ret;
}
--
2.45.1
Building upon the newly added extcon detection support, add detection
for USB OTG cables (EXTCON_USB_HOST type), and enable/disable the OTG
bits as needed.
Signed-off-by: Artur Weber <[email protected]>
---
Downstream also sets "CHGIN output current limit in OTG mode" to 900mA
by writing (1 << 7) to the CHG_CNFG_02 register; while I would try to
add this here, I do not know which exact bits control the current limit
and how their value affects it (downstream has no docs other than what
I just mentioned), so it's impossible for me to know what the mask is.
---
drivers/power/supply/max77693_charger.c | 70 +++++++++++++++++++++++++++------
1 file changed, 59 insertions(+), 11 deletions(-)
diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
index c2e8ae367381..e548fd420e78 100644
--- a/drivers/power/supply/max77693_charger.c
+++ b/drivers/power/supply/max77693_charger.c
@@ -737,11 +737,41 @@ static int max77693_set_charging(struct max77693_charger *chg, bool enable)
return ret;
}
+static int max77693_set_otg(struct max77693_charger *chg, bool enable)
+{
+ struct regmap *regmap = chg->max77693->regmap;
+ unsigned int data;
+ bool is_enabled;
+ int ret;
+
+ ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_CNFG_00, &data);
+ if (ret)
+ return ret;
+
+ is_enabled = !!(data & CHG_CNFG_00_OTG_MASK);
+
+ if (enable && !is_enabled) {
+ /* OTG on, boost on, DIS_MUIC_CTRL on */
+ data |= CHG_CNFG_00_OTG_MASK | CHG_CNFG_00_BOOST_MASK \
+ | CHG_CNFG_00_DIS_MUIC_CTRL_MASK;
+
+ } else if (!enable && is_enabled) {
+ /* OTG off, boost off, DIS_MUIC_CTRL off */
+ data &= ~(CHG_CNFG_00_OTG_MASK | CHG_CNFG_00_BOOST_MASK \
+ | CHG_CNFG_00_DIS_MUIC_CTRL_MASK);
+ }
+
+ return regmap_write(chg->max77693->regmap,
+ MAX77693_CHG_REG_CHG_CNFG_00,
+ data);
+}
+
static void max77693_charger_extcon_work(struct work_struct *work)
{
struct max77693_charger *chg = container_of(work, struct max77693_charger,
cable.work);
struct extcon_dev *edev = chg->cable.edev;
+ bool set_charging, set_otg;
int connector, state;
int ret;
@@ -760,25 +790,43 @@ static void max77693_charger_extcon_work(struct work_struct *work)
case EXTCON_CHG_USB_FAST:
case EXTCON_CHG_USB_SLOW:
case EXTCON_CHG_USB_PD:
- ret = max77693_set_charging(chg, true);
- if (ret) {
- dev_err(chg->dev, "failed to enable charging\n");
- break;
- }
+ set_charging = true;
+ set_otg = false;
+
dev_info(chg->dev, "charging. connector type: %d\n",
connector);
break;
+ case EXTCON_USB_HOST:
+ set_charging = false;
+ set_otg = true;
+
+ dev_info(chg->dev, "USB host. connector type: %d\n",
+ connector);
+ break;
default:
- ret = max77693_set_charging(chg, false);
- if (ret) {
- dev_err(chg->dev, "failed to disable charging\n");
- break;
- }
- dev_info(chg->dev, "charging. connector type: %d\n",
+ set_charging = false;
+ set_otg = false;
+
+ dev_info(chg->dev, "disconnected. connector type: %d\n",
connector);
break;
}
+ /*
+ * The functions below already check if the change is necessary,
+ * so we don't need to do so here.
+ */
+ ret = max77693_set_charging(chg, set_charging);
+ if (ret) {
+ dev_err(chg->dev, "failed to set charging (%d)\n", ret);
+ goto out;
+ }
+
+ ret = max77693_set_otg(chg, set_otg);
+ if (ret)
+ dev_err(chg->dev, "failed to set OTG (%d)\n", ret);
+
+out:
power_supply_changed(chg->charger);
}
--
2.45.1
This behavior was observed on a downstream kernel - for chargers, the
current would be set to a fast charge current value, and it would be
bumped down for all other cable types.
If we leave only the fast charge current value applied, peripheral mode
stops working. If we stick to 500mA, charging becomes too slow. So, set
the charge input current limit accordingly to the cable type.
Signed-off-by: Artur Weber <[email protected]>
---
drivers/power/supply/max77693_charger.c | 35 ++++++++++++++++++++++++++-------
1 file changed, 28 insertions(+), 7 deletions(-)
diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
index e548fd420e78..76e08775f796 100644
--- a/drivers/power/supply/max77693_charger.c
+++ b/drivers/power/supply/max77693_charger.c
@@ -711,6 +711,10 @@ static int max77693_reg_init(struct max77693_charger *chg)
if (ret)
return ret;
+ /*
+ * Set it to a lower value by default; the DT fast charge current
+ * property is applied later, in the charger detection mechanism.
+ */
ret = max77693_set_input_current_limit(chg,
DEFAULT_FAST_CHARGE_CURRENT);
if (ret)
@@ -772,6 +776,7 @@ static void max77693_charger_extcon_work(struct work_struct *work)
cable.work);
struct extcon_dev *edev = chg->cable.edev;
bool set_charging, set_otg;
+ unsigned int input_current;
int connector, state;
int ret;
@@ -784,19 +789,28 @@ static void max77693_charger_extcon_work(struct work_struct *work)
switch (connector) {
case EXTCON_CHG_USB_SDP:
- case EXTCON_CHG_USB_DCP:
case EXTCON_CHG_USB_CDP:
+ case EXTCON_CHG_USB_SLOW:
+ input_current = 500000; /* 500 mA */
+ set_charging = true;
+ set_otg = false;
+
+ dev_info(chg->dev, "slow charging. connector type: %d\n",
+ connector);
+ break;
+ case EXTCON_CHG_USB_DCP:
case EXTCON_CHG_USB_ACA:
case EXTCON_CHG_USB_FAST:
- case EXTCON_CHG_USB_SLOW:
case EXTCON_CHG_USB_PD:
+ input_current = chg->fast_charge_current;
set_charging = true;
set_otg = false;
- dev_info(chg->dev, "charging. connector type: %d\n",
+ dev_info(chg->dev, "fast charging. connector type: %d\n",
connector);
break;
case EXTCON_USB_HOST:
+ input_current = 500000; /* 500 mA */
set_charging = false;
set_otg = true;
@@ -804,6 +818,7 @@ static void max77693_charger_extcon_work(struct work_struct *work)
connector);
break;
default:
+ input_current = 500000; /* 500 mA */
set_charging = false;
set_otg = false;
@@ -812,10 +827,12 @@ static void max77693_charger_extcon_work(struct work_struct *work)
break;
}
- /*
- * The functions below already check if the change is necessary,
- * so we don't need to do so here.
- */
+ ret = max77693_set_input_current_limit(chg, input_current);
+ if (ret) {
+ dev_err(chg->dev, "failed to set input current (%d)\n", ret);
+ goto out;
+ }
+
ret = max77693_set_charging(chg, set_charging);
if (ret) {
dev_err(chg->dev, "failed to set charging (%d)\n", ret);
@@ -887,6 +904,10 @@ static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
&chg->batttery_overcurrent))
chg->batttery_overcurrent = DEFAULT_BATTERY_OVERCURRENT;
+ if (of_property_read_u32(np, "maxim,fast-charge-current-microamp",
+ &chg->fast_charge_current))
+ chg->fast_charge_current = DEFAULT_FAST_CHARGE_CURRENT;
+
if (of_property_read_u32(np, "maxim,charge-input-threshold-microvolt",
&chg->charge_input_threshold_volt))
chg->charge_input_threshold_volt =
--
2.45.1
This value was verified by comparing register dumps of the MAX77693
charger with on mainline with a downstream kernel under Android; the
value on downstream was set to 1.8 amps when charging with a proper
charger.
Signed-off-by: Artur Weber <[email protected]>
---
arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi b/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
index e5254e32aa8f..b13c135bd944 100644
--- a/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
+++ b/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
@@ -158,6 +158,7 @@ charger {
maxim,min-system-microvolt = <3600000>;
maxim,thermal-regulation-celsius = <100>;
maxim,battery-overcurrent-microamp = <3500000>;
+ maxim,fast-charge-current-microamp = <1800000>;
maxim,charge-input-threshold-microvolt = <4300000>;
};
};
--
2.45.1
Add a subnode to the MAX77693 MFD for the MUIC and connect the USB
connector node to the charger to allow for charger type/OTG cable
detection.
Signed-off-by: Artur Weber <[email protected]>
---
arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi b/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
index b13c135bd944..2e7f7e8f6c3b 100644
--- a/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
+++ b/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
@@ -151,6 +151,17 @@ charger_reg: CHARGER {
};
};
+ muic {
+ compatible = "maxim,max77693-muic";
+
+ usb_conn: connector {
+ compatible = "samsung,usb-connector-11pin",
+ "usb-b-connector";
+ label = "micro-USB";
+ type = "micro";
+ };
+ };
+
charger {
compatible = "maxim,max77693-charger";
@@ -160,6 +171,8 @@ charger {
maxim,battery-overcurrent-microamp = <3500000>;
maxim,fast-charge-current-microamp = <1800000>;
maxim,charge-input-threshold-microvolt = <4300000>;
+
+ maxim,usb-connector = <&usb_conn>;
};
};
};
--
2.45.1
On 30/05/2024 10:55, Artur Weber wrote:
> From: Wolfgang Wiedmeyer <[email protected]>
>
> This prepares for an updated regulator and charger driver. The defines
> are needed to set the maximum input current and the fast charge
> current.
>
> Signed-off-by: Wolfgang Wiedmeyer <[email protected]>
> [[email protected]: small fix]
> Signed-off-by: Denis 'GNUtoo' Carikli <[email protected]>
> [Artur: swap hardcoded ILIM values for DEFAULT_FAST_CHARGE_CURRENT]
> Signed-off-by: Artur Weber <[email protected]>
> ---
> include/linux/mfd/max77693-private.h | 6 ++++++
> 1 file changed, 6 insertions(+)
Please squash it with the next patch using the defines. Having just
defines is not really a "change" on its own (I know that AMD will
disagree but they are wrong...).
Best regards,
Krzysztof
On 30/05/2024 10:55, Artur Weber wrote:
> Allow for specifying a USB connector to use for charger type/OTG cable
> detection.
>
> The way this is done is inspired by the rt5033-charger implementation.
>
> Signed-off-by: Artur Weber <[email protected]>
> ---
Reviewed-by: Krzysztof Kozlowski <[email protected]>
Best regards,
Krzysztof
On 30/05/2024 10:55, Artur Weber wrote:
> Add the maxim,fast-charge-current-microamp property, used to specify the
> current limit to use for fast charge (when plugged into a wall charger).
>
> Signed-off-by: Artur Weber <[email protected]>
> ---
> Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml
> index f5fd53debbc8..4f80cc5418f5 100644
> --- a/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml
> +++ b/Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml
> @@ -56,6 +56,13 @@ properties:
> maximum: 3500000
> default: 3500000
>
> + maxim,fast-charge-current-microamp:
> + description:
> + Current to use for fast charge in uA.
> + minimum: 60000
> + maximum: 2580000
> + default: 500000
That's a property of monitored-battery (and there is such there).
Best regards,
Krzysztof
On 30/05/2024 10:55, Artur Weber wrote:
> These three bits are used to enable OTG control in the charger
> driver. Add them in preparation for an updated driver.
>
> Signed-off-by: Artur Weber <[email protected]>
> ---
> include/linux/mfd/max77693-private.h | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h
> index 4570646e2f33..4448b024255d 100644
> --- a/include/linux/mfd/max77693-private.h
> +++ b/include/linux/mfd/max77693-private.h
> @@ -209,7 +209,10 @@ enum max77693_charger_battery_state {
>
> /* MAX77693 CHG_CNFG_00 register */
> #define CHG_CNFG_00_CHG_MASK 0x1
> +#define CHG_CNFG_00_OTG_MASK 0x2
> #define CHG_CNFG_00_BUCK_MASK 0x4
> +#define CHG_CNFG_00_BOOST_MASK 0x8
> +#define CHG_CNFG_00_DIS_MUIC_CTRL_MASK 0x20
Please squash.
Best regards,
Krzysztof
On 30/05/2024 10:55, Artur Weber wrote:
> There are two charger current limit registers:
>
> - Fast charge current limit (which controls current going from the
> charger to the battery);
> - CHGIN input current limit (which controls current going into the
> charger through the cable, and is managed by the CHARGER regulator).
>
> + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> + ret = max77693_get_input_current_limit(chg, &val->intval);
> + break;
> + case POWER_SUPPLY_PROP_CURRENT_MAX:
> + ret = max77693_get_fast_charge_current(regmap, &val->intval);
> + break;
> case POWER_SUPPLY_PROP_MODEL_NAME:
> val->strval = max77693_charger_model;
> break;
> @@ -680,6 +727,11 @@ static int max77693_charger_probe(struct platform_device *pdev)
> chg->dev = &pdev->dev;
> chg->max77693 = max77693;
>
> + chg->regu = devm_regulator_get(chg->dev, "CHARGER");
> + if (IS_ERR(chg->regu))
> + return dev_err_probe(&pdev->dev, PTR_ERR(chg->regu),
> + "failed to get charger regulator\n");\
This breaks users... and where is the binding?
Best regards,
Krzysztof
On 30/05/2024 10:55, Artur Weber wrote:
> There are two charger current limit registers:
>
> - Fast charge current limit (which controls current going from the
> charger to the battery);
> - CHGIN input current limit (which controls current going into the
> charger through the cable, and is managed by the CHARGER regulator).
>
> Add functions for setting both of the values, and set them to a
> safe default value of 500mA at initialization.
>
> The default value for the fast charge current limit can be modified
> by setting the maxim,fast-charge-current-microamp DT property; the
> CHGIN input current limit will be set up later in the charger detection
> mechanism.
>
> Signed-off-by: Artur Weber <[email protected]>
> ---
> drivers/power/supply/max77693_charger.c | 45 +++++++++++++++++++++++++++++++++
> 1 file changed, 45 insertions(+)
>
> diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
> index 894c35b750b3..d59b1524b0a4 100644
> --- a/drivers/power/supply/max77693_charger.c
> +++ b/drivers/power/supply/max77693_charger.c
> @@ -28,6 +28,7 @@ struct max77693_charger {
> u32 min_system_volt;
> u32 thermal_regulation_temp;
> u32 batttery_overcurrent;
> + u32 fast_charge_current;
> u32 charge_input_threshold_volt;
> };
>
> @@ -591,6 +592,35 @@ static int max77693_set_batttery_overcurrent(struct max77693_charger *chg,
> CHG_CNFG_12_B2SOVRC_MASK, data);
> }
>
> +static int max77693_set_input_current_limit(struct max77693_charger *chg,
> + unsigned int uamp)
> +{
> + dev_dbg(chg->dev, "CHGIN input current limit: %u\n", uamp);
That's quite useless debug. It duplicates
max77693_set_fast_charge_current(). Just drop entire wrapper.
> +
> + return regulator_set_current_limit(chg->regu, (int)uamp, (int)uamp);
> +}
> +
> +static int max77693_set_fast_charge_current(struct max77693_charger *chg,
> + unsigned int uamp)
> +{
> + unsigned int data;
> +
> + data = (uamp / 1000) * 10 / 333; /* 0.1A/3 steps */
> +
> + if (data > CHG_CNFG_02_CC_MASK) {
> + dev_err(chg->dev, "Wrong value for fast charge current\n");
> + return -EINVAL;
> + }
> +
> + data <<= CHG_CNFG_02_CC_SHIFT;
> +
> + dev_dbg(chg->dev, "Fast charge current: %u (0x%x)\n", uamp, data);
> +
> + return regmap_update_bits(chg->max77693->regmap,
> + MAX77693_CHG_REG_CHG_CNFG_02,
> + CHG_CNFG_02_CC_MASK, data);
I am surprised that you set current limit via regulator but actual
charging current value here. I think both should go to regulator in such
case.
>
Best regards,
Krzysztof
On 31.05.2024 11:47, Krzysztof Kozlowski wrote:
> On 30/05/2024 10:55, Artur Weber wrote:
>> There are two charger current limit registers:
>>
>> - Fast charge current limit (which controls current going from the
>> charger to the battery);
>> - CHGIN input current limit (which controls current going into the
>> charger through the cable, and is managed by the CHARGER regulator).
>>
>> Add functions for setting both of the values, and set them to a
>> safe default value of 500mA at initialization.
>>
>> The default value for the fast charge current limit can be modified
>> by setting the maxim,fast-charge-current-microamp DT property; the
>> CHGIN input current limit will be set up later in the charger detection
>> mechanism.
>>
>> Signed-off-by: Artur Weber <[email protected]>
>> ---
>> drivers/power/supply/max77693_charger.c | 45 +++++++++++++++++++++++++++++++++
>> 1 file changed, 45 insertions(+)
>>
>> diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
>> index 894c35b750b3..d59b1524b0a4 100644
>> --- a/drivers/power/supply/max77693_charger.c
>> +++ b/drivers/power/supply/max77693_charger.c
>> @@ -28,6 +28,7 @@ struct max77693_charger {
>> u32 min_system_volt;
>> u32 thermal_regulation_temp;
>> u32 batttery_overcurrent;
>> + u32 fast_charge_current;
>> u32 charge_input_threshold_volt;
>> };
>>
>> @@ -591,6 +592,35 @@ static int max77693_set_batttery_overcurrent(struct max77693_charger *chg,
>> CHG_CNFG_12_B2SOVRC_MASK, data);
>> }
>>
>> +static int max77693_set_input_current_limit(struct max77693_charger *chg,
>> + unsigned int uamp)
>> +{
>> + dev_dbg(chg->dev, "CHGIN input current limit: %u\n", uamp);
>
> That's quite useless debug. It duplicates
> max77693_set_fast_charge_current(). Just drop entire wrapper.
It doesn't duplicate max77693_set_fast_charge_current, they modify two
separate registers. Quote from the commit message:
> There are two charger current limit registers:
>
> - Fast charge current limit (which controls current going from the
> charger to the battery);
> - CHGIN input current limit (which controls current going into the
> charger through the cable, and is managed by the CHARGER regulator).
max77693_set_fast_charge_current sets up the "fast charge current"
register (in CNFG_02, CHG_CNFG_02_CC). The CHARGER regulators sets the
CHGIN input current (in CNFG_09, CHG_CNFG_09_CHGIN_ILIM).
(Apparently the CHARGER regulator is supposed to handle the fast
charge current, but it does not; I wrote about this in the "CHARGER
regulator" section of the patchset description.)
>> +
>> + return regulator_set_current_limit(chg->regu, (int)uamp, (int)uamp);
>> +}
>> +
>> +static int max77693_set_fast_charge_current(struct max77693_charger *chg,
>> + unsigned int uamp)
>> +{
>> + unsigned int data;
>> +
>> + data = (uamp / 1000) * 10 / 333; /* 0.1A/3 steps */
>> +
>> + if (data > CHG_CNFG_02_CC_MASK) {
>> + dev_err(chg->dev, "Wrong value for fast charge current\n");
>> + return -EINVAL;
>> + }
>> +
>> + data <<= CHG_CNFG_02_CC_SHIFT;
>> +
>> + dev_dbg(chg->dev, "Fast charge current: %u (0x%x)\n", uamp, data);
>> +
>> + return regmap_update_bits(chg->max77693->regmap,
>> + MAX77693_CHG_REG_CHG_CNFG_02,
>> + CHG_CNFG_02_CC_MASK, data);
>
> I am surprised that you set current limit via regulator but actual
> charging current value here. I think both should go to regulator in such
> case.
As in, both fast charge current and input current should be set up by
the CHARGER regulator? Sure, sounds good to me.
I've noticed that on the original kernel, both of the values are
modified together too (only exception is that fast charge current would
be set to 0 when the cable was unplugged, but the input current stayed
at 500mA. This doesn't seem to affect anything, though.).
At one point I actually considered going the other way around - moving
all charger register handling into the charger driver, instead of having
it be a regulator. As far as I can tell, only some Samsung-submitted
charger drivers (max77693, max8997, max8998, max14577) use a regulator
to manage the charger current (if anything, some power supply drivers
expose an OTG/VBUS regulator, might be something for us to consider as
well...).
I see you wrote at least the max14577 and part of the max77693 driver;
out of curiosity, what's the benefit of doing it through a current
regulator (as opposed to adding set functions for the relevant
properties in the charger driver)? I've noticed the downstream driver
has a very similar pattern[1], I wonder if it's just a port of that or
if there's a more concrete reason.
Best regards
Artur
[1] https://github.com/gr8nole/android_kernel_samsung_smdk4x12/blob/lineage-14.1/drivers/regulator/max77693.c (everything related to MAX77693_CHARGER)
On 31/05/2024 13:55, Artur Weber wrote:
> On 31.05.2024 11:47, Krzysztof Kozlowski wrote:
>> On 30/05/2024 10:55, Artur Weber wrote:
>>> There are two charger current limit registers:
>>>
>>> - Fast charge current limit (which controls current going from the
>>> charger to the battery);
>>> - CHGIN input current limit (which controls current going into the
>>> charger through the cable, and is managed by the CHARGER regulator).
>>>
>>> Add functions for setting both of the values, and set them to a
>>> safe default value of 500mA at initialization.
>>>
>>> The default value for the fast charge current limit can be modified
>>> by setting the maxim,fast-charge-current-microamp DT property; the
>>> CHGIN input current limit will be set up later in the charger detection
>>> mechanism.
>>>
>>> Signed-off-by: Artur Weber <[email protected]>
>>> ---
>>> drivers/power/supply/max77693_charger.c | 45 +++++++++++++++++++++++++++++++++
>>> 1 file changed, 45 insertions(+)
>>>
>>> diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
>>> index 894c35b750b3..d59b1524b0a4 100644
>>> --- a/drivers/power/supply/max77693_charger.c
>>> +++ b/drivers/power/supply/max77693_charger.c
>>> @@ -28,6 +28,7 @@ struct max77693_charger {
>>> u32 min_system_volt;
>>> u32 thermal_regulation_temp;
>>> u32 batttery_overcurrent;
>>> + u32 fast_charge_current;
>>> u32 charge_input_threshold_volt;
>>> };
>>>
>>> @@ -591,6 +592,35 @@ static int max77693_set_batttery_overcurrent(struct max77693_charger *chg,
>>> CHG_CNFG_12_B2SOVRC_MASK, data);
>>> }
>>>
>>> +static int max77693_set_input_current_limit(struct max77693_charger *chg,
>>> + unsigned int uamp)
>>> +{
>>> + dev_dbg(chg->dev, "CHGIN input current limit: %u\n", uamp);
>>
>> That's quite useless debug. It duplicates
>> max77693_set_fast_charge_current(). Just drop entire wrapper.
>
> It doesn't duplicate max77693_set_fast_charge_current, they modify two
> separate registers. Quote from the commit message:
But it is the same uamp value. Debug messages should not be per register
write, because we are not debugging here registers...
>
>> There are two charger current limit registers:
>>
>> - Fast charge current limit (which controls current going from the
>> charger to the battery);
>> - CHGIN input current limit (which controls current going into the
>> charger through the cable, and is managed by the CHARGER regulator).
>
> max77693_set_fast_charge_current sets up the "fast charge current"
> register (in CNFG_02, CHG_CNFG_02_CC). The CHARGER regulators sets the
> CHGIN input current (in CNFG_09, CHG_CNFG_09_CHGIN_ILIM).
>
> (Apparently the CHARGER regulator is supposed to handle the fast
> charge current, but it does not; I wrote about this in the "CHARGER
> regulator" section of the patchset description.)
>
>>> +
>>> + return regulator_set_current_limit(chg->regu, (int)uamp, (int)uamp);
>>> +}
>>> +
>>> +static int max77693_set_fast_charge_current(struct max77693_charger *chg,
>>> + unsigned int uamp)
>>> +{
>>> + unsigned int data;
>>> +
>>> + data = (uamp / 1000) * 10 / 333; /* 0.1A/3 steps */
>>> +
>>> + if (data > CHG_CNFG_02_CC_MASK) {
>>> + dev_err(chg->dev, "Wrong value for fast charge current\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + data <<= CHG_CNFG_02_CC_SHIFT;
>>> +
>>> + dev_dbg(chg->dev, "Fast charge current: %u (0x%x)\n", uamp, data);
>>> +
>>> + return regmap_update_bits(chg->max77693->regmap,
>>> + MAX77693_CHG_REG_CHG_CNFG_02,
>>> + CHG_CNFG_02_CC_MASK, data);
>>
>> I am surprised that you set current limit via regulator but actual
>> charging current value here. I think both should go to regulator in such
>> case.
>
> As in, both fast charge current and input current should be set up by
> the CHARGER regulator? Sure, sounds good to me.
>
> I've noticed that on the original kernel, both of the values are
> modified together too (only exception is that fast charge current would
> be set to 0 when the cable was unplugged, but the input current stayed
> at 500mA. This doesn't seem to affect anything, though.).
>
> At one point I actually considered going the other way around - moving
> all charger register handling into the charger driver, instead of having
> it be a regulator. As far as I can tell, only some Samsung-submitted
> charger drivers (max77693, max8997, max8998, max14577) use a regulator
> to manage the charger current (if anything, some power supply drivers
> expose an OTG/VBUS regulator, might be something for us to consider as
> well...).
regulator choice was to match userspace design that time (long time
ago), but I think now preference is to use writeable properties of power
supply class. I'll defer here to Sebastian.
>
> I see you wrote at least the max14577 and part of the max77693 driver;
> out of curiosity, what's the benefit of doing it through a current
> regulator (as opposed to adding set functions for the relevant
> properties in the charger driver)? I've noticed the downstream driver
> has a very similar pattern[1], I wonder if it's just a port of that or
> if there's a more concrete reason.
>
Best regards,
Krzysztof
On 31.05.2024 11:38, Krzysztof Kozlowski wrote:
> On 30/05/2024 10:55, Artur Weber wrote:
>> There are two charger current limit registers:
>>
>> - Fast charge current limit (which controls current going from the
>> charger to the battery);
>> - CHGIN input current limit (which controls current going into the
>> charger through the cable, and is managed by the CHARGER regulator).
>>
>
>
>
>> + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
>> + ret = max77693_get_input_current_limit(chg, &val->intval);
>> + break;
>> + case POWER_SUPPLY_PROP_CURRENT_MAX:
>> + ret = max77693_get_fast_charge_current(regmap, &val->intval);
>> + break;
>> case POWER_SUPPLY_PROP_MODEL_NAME:
>> val->strval = max77693_charger_model;
>> break;
>> @@ -680,6 +727,11 @@ static int max77693_charger_probe(struct platform_device *pdev)
>> chg->dev = &pdev->dev;
>> chg->max77693 = max77693;
>>
>> + chg->regu = devm_regulator_get(chg->dev, "CHARGER");
>> + if (IS_ERR(chg->regu))
>> + return dev_err_probe(&pdev->dev, PTR_ERR(chg->regu),
>> + "failed to get charger regulator\n");\
>
> This breaks users... and where is the binding?
Assuming "this" means "erroring out if the CHARGER regulator is not
found":
The way it works here is that the CHARGER regulator is fetched directly
from the parent max77693 device (it's defined in the regulator subnode
in DT). I suppose we could add a DT property for it, in the charger node
(like we do for the USB connector), though I don't know if anyone would
use any other regulator here than the CHARGER regulator of the max77693
regulator device. (And after all, we're using it here to modify charger
registers... maybe another point to my argument that we should be
handling all of this directly in the charger driver instead of deferring
it to a regulator.)
Best regards
Artur
[1] https://lore.kernel.org/all/20160927081344.GC4394@kozik-lap/
[2] https://lore.kernel.org/all/[email protected]/
On 31.05.2024 14:18, Krzysztof Kozlowski wrote:
> On 31/05/2024 13:55, Artur Weber wrote:
>> On 31.05.2024 11:47, Krzysztof Kozlowski wrote:
>>> On 30/05/2024 10:55, Artur Weber wrote:
>>>> There are two charger current limit registers:
>>>>
>>>> - Fast charge current limit (which controls current going from the
>>>> charger to the battery);
>>>> - CHGIN input current limit (which controls current going into the
>>>> charger through the cable, and is managed by the CHARGER regulator).
>>>>
>>>> Add functions for setting both of the values, and set them to a
>>>> safe default value of 500mA at initialization.
>>>>
>>>> The default value for the fast charge current limit can be modified
>>>> by setting the maxim,fast-charge-current-microamp DT property; the
>>>> CHGIN input current limit will be set up later in the charger detection
>>>> mechanism.
>>>>
>>>> Signed-off-by: Artur Weber <[email protected]>
>>>> ---
>>>> drivers/power/supply/max77693_charger.c | 45 +++++++++++++++++++++++++++++++++
>>>> 1 file changed, 45 insertions(+)
>>>>
>>>> diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
>>>> index 894c35b750b3..d59b1524b0a4 100644
>>>> --- a/drivers/power/supply/max77693_charger.c
>>>> +++ b/drivers/power/supply/max77693_charger.c
>>>> @@ -28,6 +28,7 @@ struct max77693_charger {
>>>> u32 min_system_volt;
>>>> u32 thermal_regulation_temp;
>>>> u32 batttery_overcurrent;
>>>> + u32 fast_charge_current;
>>>> u32 charge_input_threshold_volt;
>>>> };
>>>>
>>>> @@ -591,6 +592,35 @@ static int max77693_set_batttery_overcurrent(struct max77693_charger *chg,
>>>> CHG_CNFG_12_B2SOVRC_MASK, data);
>>>> }
>>>>
>>>> +static int max77693_set_input_current_limit(struct max77693_charger *chg,
>>>> + unsigned int uamp)
>>>> +{
>>>> + dev_dbg(chg->dev, "CHGIN input current limit: %u\n", uamp);
>>>
>>> That's quite useless debug. It duplicates
>>> max77693_set_fast_charge_current(). Just drop entire wrapper.
>>
>> It doesn't duplicate max77693_set_fast_charge_current, they modify two
>> separate registers. Quote from the commit message:
>
> But it is the same uamp value. Debug messages should not be per register
> write, because we are not debugging here registers...
>
>>
>>> There are two charger current limit registers:
>>>
>>> - Fast charge current limit (which controls current going from the
>>> charger to the battery);
>>> - CHGIN input current limit (which controls current going into the
>>> charger through the cable, and is managed by the CHARGER regulator).
>>
>> max77693_set_fast_charge_current sets up the "fast charge current"
>> register (in CNFG_02, CHG_CNFG_02_CC). The CHARGER regulators sets the
>> CHGIN input current (in CNFG_09, CHG_CNFG_09_CHGIN_ILIM).
>>
>> (Apparently the CHARGER regulator is supposed to handle the fast
>> charge current, but it does not; I wrote about this in the "CHARGER
>> regulator" section of the patchset description.)
>>
>>>> +
>>>> + return regulator_set_current_limit(chg->regu, (int)uamp, (int)uamp);
>>>> +}
>>>> +
>>>> +static int max77693_set_fast_charge_current(struct max77693_charger *chg,
>>>> + unsigned int uamp)
>>>> +{
>>>> + unsigned int data;
>>>> +
>>>> + data = (uamp / 1000) * 10 / 333; /* 0.1A/3 steps */
>>>> +
>>>> + if (data > CHG_CNFG_02_CC_MASK) {
>>>> + dev_err(chg->dev, "Wrong value for fast charge current\n");
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + data <<= CHG_CNFG_02_CC_SHIFT;
>>>> +
>>>> + dev_dbg(chg->dev, "Fast charge current: %u (0x%x)\n", uamp, data);
>>>> +
>>>> + return regmap_update_bits(chg->max77693->regmap,
>>>> + MAX77693_CHG_REG_CHG_CNFG_02,
>>>> + CHG_CNFG_02_CC_MASK, data);
>>>
>>> I am surprised that you set current limit via regulator but actual
>>> charging current value here. I think both should go to regulator in such
>>> case.
>>
>> As in, both fast charge current and input current should be set up by
>> the CHARGER regulator? Sure, sounds good to me.
Now that I look at it, there's one small problem with moving this to the
CHARGER regulator - the CHGIN input limit and the fast charge current
limit have different ranges for values; CHGIN input limit accepts values
from 60mA to 2.58A, whereas fast charge current accepts values from 0mA
to ~2.1A. (This also means the limits I described for the fast charge
current property in [PATCH 2/11] are wrong...)
Should we limit the CHARGER regulator to 2.1A (would require fixing
every DTS that defines the limits... though maybe they should be
hardcoded in the driver anyways?), or leave the limit as-is and cap the
fast charge current if the CHARGER current limit is set above 2.1A, or
something else entirely?
Best regards
Artur