[PATCH 00/10] Add RT5033 charger device driver

From: Jakob Hauser
Date: Tue Feb 28 2023 - 17:33:35 EST


Works on rt5033-charger has already been quite far but phased out. The last
patchset version I could find was of March 2015 [1].

Let's pick this up again.

Those patches of March 2015 are now patch 6 and patch 10. On those two
patches I actually would prefer to use From: and Co-developed-by: tags. I
contacted Beomho Seo beforehand but didn't receive an answer. Therefore I
applied Cc: tag.

Looking through the old patchset, there were quite several things I changed.
A detailed list of changes on those patches 6 and 10 can be found further down,
going from top to bottom through the patches.

Some comments on the end-of-charge behavior. The rt5033 chip offers three
options. In the Android driver, a forth option was implemented.
- By default, the rt5033 chip charges indefinitely. The current goes down but
there is always a charge voltage to the battery, which might not be too good
for the battery lifetime.
- There is the possibility to enable a fast charge timer. The timer can be
set to 4, 6, 8... 16 hours. After that time has elapsed, charging stops
and the battery gets discharged. This option with a timer of 4 hours was
chosen by Beomho Seo in the patchset of March 2015. However, that option
is confusing to the user. It doesn't initiate a re-charge cycle. So when
keeping plugged in the device over night, I find it discharging on the
next morning.
- The third option of the rt5033 chip is enabling charging termination. This
also enables a re-charge cycle. When the charging current sinks below the
end-of-charge current, the chip stops charging. The sysfs state changes to
"not charging". When the voltage gets 0.1 V below the end-of-charge constant
voltage, re-charging starts. Then again, when charging current sinks below
the end-of-charge current, the chip stops charging. And so on, going up and
down in re-charge cycles. In case the power consumption is high (e.g. tuning
on the display of the mobile device), the current goes into an equilibrium.
The downside of this charging termination option: When reaching the end-of-
charge current, the capacity might not have reached 100 % yet. The capacity
to reach probably depends on power consumption and battery wear. On my mobile
device, capacity reaches 98 %, drops to 96 % until re-charging kicks in,
climbs to 98 %, drops to 96 %, and so on. Not reaching 100 % is a bit
confusing to the user, too.
- In the Android driver, both timer and termination are turned off. Instead,
a self-written re-charge logic is implemented in the driver infrastructure.
On mobile device samsung-serranovelte, after passing the end-of-charge IRQ
trigger, it keeps on charging for approx. 42 mins and then stops. When the
voltage drops below 4.3 V, it starts charging again. 42 min later it stops
again. When below 4.3 V starts again, etc. This way, the capacity reaches
well 100 % and doesn't drop below. This behavior is not managed in
drivers/battery/rt5033_charger.c [2] but by the Samsung battery
infrastructure drivers/battery/sec_battery.c [3]. Some of the settings for
the re-charge behavior are set in arch/arm/boot/dts/samsung/msm8916/
msm8916-sec-serranovelte-battery-r01.dtsi [4] (for samsung-serranovelte
mobile device).

The forth option would be the best. But it would require a lot of additional
coding and testing for the driver. For the rt5033-charger driver submited here,
option 3 "charging termination" was selected. It possibly doesn't reach 100 %
capacity, which is confusing to the user, but at least it offers a re-charge
cycle without extra effort in the driver.

Patch 2 fixes the bits to be read out to get the chip revision. While testing,
I noticed a nasty "hardware" bug in the chip revision read-out. I have two
devices samsung-serranove. Both have rt5033 chip revision 6 (register 0x03
value 0x86, the last four bits are the revision). However, when I remove the
battery, wait a bit, put the battery back in and boot, then one of the devices
show chip revision 1 (register 0x03 value 0x81). It stays that way even when
powering off and booting again. Once I put in the charging cable for the first
time, register 0x03 changes to the correct value 0x86 and stays there, even
when rebooting. This happens only on one of the two devices. Interestingly,
in the Android driver there seems to be a quirk to handle this issue [5]. In
register 0x6b (RT5033_REG_OFF_EVENT) they set bit 0x01, wait 100 microseconds,
read the chip revision, then unset bit 0x01. I was thinking about adding this
quirk to the patchset. But I decided not to do so (at least for the time being)
because I don't know what's register 0x6b and what exactly does bit 0x01. At
another location [6] it says this bit enables OSC, possibly OSC stand for
oscillator, which I think is used for the internal clock. I don't know if there
might be some side effects when applying this quirk. So I prefer to do nothing
about this. Having a wrong chip revision in dmesg until the first charge isn't
a severe issue. The quirk might be needed at a later date when other quirks
(see next paragraph) need to be added conditionally on the chip revision.

There are more of such quirks in the Android driver, e.g. [7][8][9]. I haven't
noticed any bugs except the chip revision bug described above. I didn't add
these quirks because I'm not fully sure what they do and if it's really needed.
However, there is a possibility that some devices run into issues. Still I'd
avoid adding all kind of quirks without knowing anything about it. The
rt5033-charger driver sets the bits for voltages, currents and end-of-charge
behavior. So from a safety point of view the most important boundaries should
be set.

Additionally something that's missing compared to the Android driver is the IRQ
implementation and infrastructure in rt5033-charger [10][11][12].

The rt5033-charger driver returs a dmesg warning "DMA mask not set". I've read
that it would be related to platform_set_drvdata() in the probe function. But I
couldn't spot anything wrong there. It could also be related to the
devm_kzalloc() of rt5033_charger_data *chg in rt5033_charger_dt_init().
I couldn't solve it. As it seems to have no effect, I didn't do anything more
about it.

The patchset is organized as follows:
- Patches 1-5: Fixes and preparatory changes on rt5033 mfd
- Patches 6-7: Add and extend rt5033-charger
- Patch 9: Add status property to rt5033-battery
- Patch 10: Add documentation

The first patch is a lost one from a previous series [13].

The patches depend on each other, it would be good to apply the patchset as a
whole. Not sure if this patchset is organized well enough in terms of touching
several subsystems. Let me know if it should be arranged or handled in a
different way.

The patchset is based on torvalds/linux v6.2-rc5. The result of the patchset
can be seen on my GitHub fork [14].

Changes to the version of March 2015:

Patch 6

Commit message
- Corrected typos "adds", "supports", "provides", "modes", "The", "pre-charge",
"fast charge", "They vary in charge rate, the charge parameters...".

Makefile
- Changed "CONFIG_POWER_RT5033" to "CONFIG_CHARGER_RT5033", as noted by
Sebastian.
- Placed CHARGER_RT5033 directly below BATTERY_RT5033, like in the Kconfig
file.

Generally on rt5033_charger.c
- Added SPDX-License-Identifier tag to line 1.
- At the top of rt5033_charger.c, before "Free Software Foundation", added a
space between "by the", as mentioned by Paul.
- In function rt5033_init_const_charge(), rt5033_init_fast_charge(),
rt5033_init_pre_charge() and rt5033_charger_reg_init(), changed the pointer
of "struct rt5033_charger" from *psy to *charger. Firstly to avoid confusion
with "psy" within "struct rt5033_charger" [15]. Secondly to stay more
consistant to other functions like rt5033_charger_probe() or
rt5033_get_charger_state() where pointer *charger is used.
- At the end, added rt5033_charger_of_match[], MODULE_DEVICE_TABLE(of, ...)
and .of_match_table to probe the driver by device-tree.
- At the end, changed MODULE_LICENSE to "GPL v2", as noted by Sebastian and
Paul.

Function rt5033_get_charger_state()
- At declaration changed the order of "reg_data" and "state".
- Moved "state = POWER_SUPPLY_STATUS_UNKNOWN" from declaration area to
switch "default:".
- Data type for "reg_data" doesn't need to be "u32". Changed it to
"unsigned int". In the Android driver it's "int" [16].
- The RT5033_CHG_STAT_MASK needs to be 0x30 to cover the charge state options
0x00, 0x10, 0x20, 0x30 [17]. In the Android driver it's 0x30 as well [18].
Thus, changing that value in include/linux/mfd/rt5033-private.h is needed.
Interestingly it overlaps with RT5033_CHG_STAT_TYPE_MASK which is 0x60.
The STAT_TYPE is actually just one bit at 0x40. However, using 0x60 to
overlap with the STAT mask 0x30 allows to detect more states. If the
STAT is "charging" or "not charging" (failure), BIT(5) is 1 and the
STAT_TYPE can be "fast" or "trickle". On the other hand, if STAT is
"discharging" or "full", BIT(5) is 0 and that way STAT_TYPE can be set to
"none" or "unknown".

Function rt5033_get_charger_type()
- At declaration changed the order of "reg_data" and "state".
- Again changed data type for "reg_data" from "u32" to "unsigned int". In
the Android driver it's "int" [19].
- Moved "state =" from declaration area to switch "default:".
- Changed "state =" from UNKNOWN to NONE. This way the charger type is "none"
when no cable is attached.

Function rt5033_get_charger_current_limit()
- Renamed function rt5033_get_charger_current() to
rt5033_get_charger_current_limit(). It doesn't return a measured value, it
returns the current limit that was set.
- Removed the psp check and psp parameter, as suggested by Sebastian.
- Replaced "state" calculation "(reg_data >> RT5033_CHGCTRL5_ICHG_SHIFT) & 0x0f"
by "(reg_data & RT5033_CHGCTRL5_ICHG_MASK) >> RT5033_CHGCTRL5_ICHG_SHIFT".
The first is a shift by >> 4 and mask 00001111. The second is a mask 11110000
and a shift by >> 4. However, this way it's better represented by the values
defined in include/linux/battery/charger/rt5033_charger.h.
- Removed the limitation to RT5033_CHG_MAX_CURRENT. This function is reading
the current limit. If the current limit is higher than the defined max for
whatever reason, this should be visible in sysfs.
- Replaced "data" calculation "state * 100 + 700" by a calculation using values
RT5033_CHARGER_FAST_CURRENT_MIN and RT5033_CHARGER_FAST_CURRENT_STEP_NUM.

Function rt5033_get_charger_const_voltage()
- Renamed function rt5033_get_charge_voltage() to
rt5033_get_charger_const_voltage(). It doesn't measure the charge voltage,
it returns the const voltage that was set.
- Removed the psp check and psp parameter as suggested by Sebastian.
- Replaced "data" calculation "reg_data >> 2" by
"(reg_data & RT5033_CHGCTRL2_CV_MASK) >> RT5033_CHGCTRL2_CV_SHIFT;". This
is cleaner. However, the value RT5033_CHGCTRL2_CV_SHIFT needs to be added
to include/linux/mfd/rt5033-private.h.
- Removed the limitation to RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MAX. If the
const voltage is set higher than the defined max for whatever reason, this
should be visible in sysfs.

Function rt5033_init_const_charge()
- Added missing "int" to declaration "unsigned int val". Also adapted the
order of declarations to be similar to functions rt5033_init_fast_charge()
and rt5033_init_pre_charge().
- In remark "Set Constant voltage mode" wrote "constant" in lower case.
- Changed hex value 0x0 to two digits 0x00.
- In section "Set Constant voltage mode", the "reg_data" is wrongly set to
0xfc (decimal 252). That's the value of the const voltage mask 11111100,
which is RT5033_CHGCTRL2_CV_MASK. However, from 3,65 V to 4,4 V in
0,025 steps [20] are 30 steps. Thus, the max value should be 0x1e (decimal
30). Let's use a new define RT5033_CV_MAX_VOLTAGE here that contains that
value. Needs to be added to include/linux/mfd/rt5033-private.h.
- In section "Set Constant voltage mode" at regmap_update_bits(), replaced
shift value 2 by RT5033_CHGCTRL2_CV_SHIFT.
- In section "Set end of charge current" changed hex values 0x1 and 0x7 to
two digits 0x01 and 0x07.

Function rt5033_init_fast_charge()
- Changed AICR mask name from RT5033_AICR_MODE_MASK to
RT5033_CHGCTRL1_IAICR_MASK.
- Removed the block "Set internal timer". The fast charge timer stops charging
when the time has elapsed (TIMER4 is 4 hours). There is no re-charging. Thus,
this behavior is confusing because the device keeps discharging after the
timer has elapsed.
- In remark "Set fast-charge mode Carging current" fixed the word "Carging"
to "charging".
- In section "Set fast-charge mode charging current" changed hex value 0x0
to two digits 0x00.
- Moved declaration "unsigned int val" to the beginning of the function.
- Replaced max value 0xd0 (decimal 208) by RT5033_CHG_MAX_CURRENT (which is
0x0d, decimal 13). From 700 mA to 2000 mA in 100 mA steps [21] are 13 steps.
In the Android driver the value is written as 0xd [22], which is decimal 13.
- Calculation of "reg_data" as "0x10 + val" is unneccesary. 0x10 adds BIT(4)
but "val" needs to be shifted by 4 bits later on, thus the bit added here
gets lost and is therefore not needed.
- In section "Set fast-charge mode charging current", on regmap_update_bits()
the "reg_data" value has to be shifted by RT5033_CHGCTRL5_ICHG_SHIFT
(shift << 4) to meet RT5033_CHGCTRL5_ICHG_MASK (mask 11110000).

Function rt5033_init_pre_charge()
- Moved declaration "unsigned int val" to the beginning of the function.
- In section "Set pre-charge threshold voltage" changed "reg_data"
calculation from "0x00 + val" to simply "val", the 0x00 isn't needed.
- In section "Set pre-charge mode charging current", the min/max values are
350 mA and 650 mA according to include/linux/mfd/rt5033-private.h. And the
step size is 100 mA. These are 4 steps (350, 450, 550, 650 mA). The max
"reg_data" value is therefore 0x03. The value 0x18, on the contrary, is
the mask where that value needs to written into (mask 00011000). Therefore
add a new define RT5033_CHG_MAX_PRE_CURRENT to rt5033-private.h and use
this value for the "reg_data" max value.
- In section "Set pre-charge mode charging current", when writing the
"reg_data" to the register, it needs a shift by 3 bit to fit the
RT5033_CHGCTRL4_IPREC_MASK, which is mask 0x18 (00011000). Thus, replace
the "reg_data" calculation "0x08 + val" by simply "val", add a new define
RT5033_CHGCTRL4_IPREC_SHIFT to include/linux/mfd/rt5033-private.h and apply
this on regmap_update_bits().

Function rt5033_charger_reg_init()
- Removed the regmap_update_bits() of RT5033_CHARGER_MODE,
RT5033_CHARGER_UUG_ENABLE, RT5033_CFO_ENABLE and
RT5033_CHARGER_HZ_DISABLE. They set the same values that are already the
default settings of the chip. Therefore it's not neccessary to set them.
- Added new block "Enable charging termination". It stops charging when
reaching the end-of-charge current and enters a re-charging cycle. The
re-charging starts when the voltage gets 0.1 V below the constant voltage.
- Set minimum input voltage regulation (MIVR) to DISABLED. This increases
charging speed when using weak cables.

Array rt5033_charger_props[]
- Removed property POWER_SUPPLY_PROP_CURRENT_NOW, see further below.
- Removed property POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, see below.
- Added property POWER_SUPPLY_PROP_ONLINE. Userspace layer UPower expects
property "online" for a "line-power" device.

Function rt5033_charger_get_property()
- At declaration "struct rt5033_charger *charger =", replaced container_of()
by power_supply_get_drvdata().
- Removed property POWER_SUPPLY_PROP_CURRENT_NOW. The regmap doesn't offer
measured values of the charge current.
- Removed property POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX. The const
voltage isn't configurable in userspace, therefore it's not relevant to
the user.
- Assigned function rt5033_get_charger_current_limit() to property
POWER_SUPPLY_PROP_CURRENT_MAX. It represents the current limit that was set.
- Assigned function rt5033_get_charger_const_voltage() to the property
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE. It represents the const voltage
that was set.
- Added POWER_SUPPLY_PROP_ONLINE. For the time being, "online" is true when
the state is "charging". This will be optimized when implementing extcon
cable detection.

Function rt5033_charger_dt_init()
- Removed the remark "Charging current is decided by ...". It's true that the
fast charging speed is not solely the value imported from device-tree value
"richtek,fast-uamp" but instead gets chip-interenally managed by additional
factors. However, at the one hand that's not the ideal location to explain
this. Secondly, the external sensing register value of 10 mili ohm doesn't
get changed by the driver. I would just skip this comment.

Struct rt5033_charger_desc
- Added a power_supply_desc struct. Using this in probe for
devm_power_supply_register().
- Replaced type POWER_SUPPLY_TYPE_MAINS by POWER_SUPPLY_TYPE_USB.

Function rt5033_charger_probe()
- Replaced power_supply_register() by devm_power_supply_register(), as refered
by Sebastian. Accordingly, removed rt5033_charger_remove() and ".remove"
further down. Implemented "psy_cfg" in rt5033_charger_probe(). In
include/linux/mfd/rt5033.h, turned power_supply "psy" into a pointer.

Patch 10
- Changed the documentation to yaml format.
- Corrected lower-case/upper-case on some words ("Power Management ...",
"multifunction device").
- At the description, added that the battery fuel gauge uses a separate I2C
bus.
- Split out the regulator and charger documentation into a separate documents.
- In the example of the mfd, indicated that the battery fuel gauge is set to
another I2C bus.
- In the charger yaml in the description of "richtek,eoc-uamp" fixed the upper
level from 200 mA to 600 mA [23]. Also added description of the step sizes.
- In the charger yaml, added property "extcon" for phandle.
- In the charger yaml, generally minor wording fixes on the descriptions.
- Choose more careful charger values for the example.
- In the regulator yaml added the voltage ranges to the description.
- Skipped the change on Documentation/devicetree/bindings/vendor-prefixes.txt,
Richtek is already implemented in the vendors list by now.

References:
[1] https://lore.kernel.org/lkml/1425864191-4121-1-git-send-email-beomho.seo@xxxxxxxxxxx/T/#t
[2] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/battery/rt5033_charger.c
[3] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/battery/sec_battery.c
[4] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/arch/arm/boot/dts/samsung/msm8916/msm8916-sec-serranovelte-battery-r01.dtsi
[5] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/mfd/rt5033_core.c#L482-L486
[6] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/mfd/rt5033_core.c#L364-L365
[7] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/battery/rt5033_charger.c#L146-L158
[8] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/mfd/rt5033_core.c#L350-L390
[9] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/battery/rt5033_charger.c#L622-L627
[10] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/battery/rt5033_charger.c#L995-L1250
[11] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/include/linux/battery/charger/rt5033_charger.h#L52-L58
[12] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/mfd/rt5033_irq.c
[13] https://lore.kernel.org/linux-pm/YMeILEnjOCCzo61q@xxxxxxxxxxx
[14] https://github.com/Jakko3/linux/blob/rt5033-charger_v1/drivers/power/supply/rt5033_charger.c
[15] https://github.com/torvalds/linux/blob/v6.1-rc1/include/linux/mfd/rt5033.h#L54
[16] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/battery/rt5033_charger.c#L657
[17] https://github.com/torvalds/linux/blob/v6.1-rc1/include/linux/mfd/rt5033-private.h#L59-L62
[18] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/battery/rt5033_charger.c#L671
[19] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/battery/rt5033_charger.c#L705
[20] https://github.com/torvalds/linux/blob/v6.1-rc1/include/linux/mfd/rt5033-private.h#L155-L158
[21] https://github.com/torvalds/linux/blob/v6.1-rc1/include/linux/mfd/rt5033-private.h#L165-L168
[22] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/battery/rt5033_charger.c#L377-L382
[23] https://github.com/torvalds/linux/blob/v6.0/include/linux/mfd/rt5033-private.h#L178

Jakob Hauser (9):
mfd: rt5033: Fix chip revision readout
mfd: rt5033: Fix comments and style in includes
mfd: rt5033: Fix STAT_MASK, HZ_MASK and AICR defines
mfd: rt5033: Apply preparatory changes before adding rt5033-charger
driver
power: supply: rt5033_charger: Add RT5033 charger device driver
power: supply: rt5033_charger: Add cable detection and USB OTG supply
power: supply: rt5033_charger: Make use of high impedance mode
power: supply: rt5033_battery: Adopt status property from charger
dt-bindings: Add documentation for rt5033 mfd, regulator and charger

Stephan Gerhold (1):
mfd: rt5033: Drop rt5033-battery sub-device

.../bindings/mfd/richtek,rt5033.yaml | 102 +++
.../power/supply/richtek,rt5033-charger.yaml | 76 ++
.../regulator/richtek,rt5033-regulator.yaml | 45 +
drivers/mfd/rt5033.c | 11 +-
drivers/power/supply/Kconfig | 8 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/rt5033_battery.c | 24 +
drivers/power/supply/rt5033_charger.c | 769 ++++++++++++++++++
include/linux/mfd/rt5033-private.h | 81 +-
include/linux/mfd/rt5033.h | 15 +-
10 files changed, 1091 insertions(+), 41 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/richtek,rt5033.yaml
create mode 100644 Documentation/devicetree/bindings/power/supply/richtek,rt5033-charger.yaml
create mode 100644 Documentation/devicetree/bindings/regulator/richtek,rt5033-regulator.yaml
create mode 100644 drivers/power/supply/rt5033_charger.c

--
2.39.1