[PATCH v2 0/9] Add support for PMICs used on Tegra 3 devices

Patch set adds basic support for PMICs used in many Tegra 3 devices. All PMIC drivers are based on datasheets provided by vendors. Implemented API include regulator set/get voltage enable/disable and basic PMIC r/w capabilities.
Created poweroff API for pmic to be able to call do_poweroff directly from within pmic driver. PMIC responcible for poweroff shoud be marked as system-power-controller in device tree.
Drivers were tested by me on LG P895 (max77663), HTC One X (TPS80032) and ASUS TF300T (tps65911). Calculations and behavior were correct, predictable and reproducible.
--- Changes from v1: - created poweroff API for PMIC and integrated it call into do_poweroff - added poweroff function to submitted max77663, tps80032 and tps65911 drivers - fixed registers asignments for regulators with non-numeric name (ldoln, ldousb, vddctrl) ---
Svyatoslav Ryhel (9): power: pmic-uclass: implement poweroff ops cmd: boot: implement PMIC based poweroff power: pmic: palmas: support TI TPS65913 PMIC power: pmic: add the base MAX77663 PMIC support power: regulator: max77663: add regulator support power: pmic: add the base TPS80031 PMIC support power: regulator: tps80031: add regulator support power: pmic: tps65910: add TPS65911 PMIC support power: regulator: tps65911: add regulator support
cmd/Kconfig | 6 + cmd/boot.c | 40 ++ doc/device-tree-bindings/pmic/max77663.txt | 84 +++++ doc/device-tree-bindings/pmic/tps65911.txt | 78 ++++ doc/device-tree-bindings/pmic/tps80031.txt | 76 ++++ drivers/power/pmic/Kconfig | 12 + drivers/power/pmic/Makefile | 2 + drivers/power/pmic/max77663.c | 105 ++++++ drivers/power/pmic/palmas.c | 1 + drivers/power/pmic/pmic-uclass.c | 12 + drivers/power/pmic/pmic_tps65910_dm.c | 49 ++- drivers/power/pmic/tps80031.c | 97 +++++ drivers/power/regulator/Kconfig | 24 ++ drivers/power/regulator/Makefile | 3 + drivers/power/regulator/max77663_regulator.c | 358 ++++++++++++++++++ drivers/power/regulator/tps65911_regulator.c | 377 +++++++++++++++++++ drivers/power/regulator/tps80031_regulator.c | 296 +++++++++++++++ include/power/max77663.h | 44 +++ include/power/pmic.h | 13 + include/power/tps65910_pmic.h | 52 +++ include/power/tps80031.h | 45 +++ 21 files changed, 1771 insertions(+), 3 deletions(-) create mode 100644 doc/device-tree-bindings/pmic/max77663.txt create mode 100644 doc/device-tree-bindings/pmic/tps65911.txt create mode 100644 doc/device-tree-bindings/pmic/tps80031.txt create mode 100644 drivers/power/pmic/max77663.c create mode 100644 drivers/power/pmic/tps80031.c create mode 100644 drivers/power/regulator/max77663_regulator.c create mode 100644 drivers/power/regulator/tps65911_regulator.c create mode 100644 drivers/power/regulator/tps80031_regulator.c create mode 100644 include/power/max77663.h create mode 100644 include/power/tps80031.h

PMICs are responsible for device poweroff sequence so lets implement this function.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/power/pmic/pmic-uclass.c | 12 ++++++++++++ include/power/pmic.h | 13 +++++++++++++ 2 files changed, 25 insertions(+)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 0e2f5e1f41..23803bc96a 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -99,6 +99,18 @@ int pmic_get(const char *name, struct udevice **devp) return uclass_get_device_by_name(UCLASS_PMIC, name, devp); }
+int pmic_poweroff(struct udevice *dev) +{ + const struct dm_pmic_ops *ops = dev_get_driver_ops(dev); + struct uc_pmic_priv *priv = dev_get_uclass_priv(dev); + + if (!ops || !ops->poweroff || + !priv->sys_pow_ctrl) + return -ENOSYS; + + return ops->poweroff(dev); +} + int pmic_reg_count(struct udevice *dev) { const struct dm_pmic_ops *ops = dev_get_driver_ops(dev); diff --git a/include/power/pmic.h b/include/power/pmic.h index 636221692d..d8dd1ceaa3 100644 --- a/include/power/pmic.h +++ b/include/power/pmic.h @@ -157,11 +157,13 @@ struct pmic { * Should be implemented by UCLASS_PMIC device drivers. The standard * device operations provides the I/O interface for it's childs. * + * @poweroff: perform poweroff sequence * @reg_count: device's register count * @read: read 'len' bytes at "reg" and store it into the 'buffer' * @write: write 'len' bytes from the 'buffer' to the register at 'reg' address */ struct dm_pmic_ops { + int (*poweroff)(struct udevice *dev); int (*reg_count)(struct udevice *dev); int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int len); int (*write)(struct udevice *dev, uint reg, const uint8_t *buffer, @@ -242,6 +244,16 @@ int pmic_bind_children(struct udevice *pmic, ofnode parent, */ int pmic_get(const char *name, struct udevice **devp);
+/** + * pmic_poweroff: call the pmic poweroff sequence + * + * The required pmic device can be obtained by 'pmic_get()' + * + * @dev - pointer to the UCLASS_PMIC device + * Return: device turns off or negative value of errno. + */ +int pmic_poweroff(struct udevice *dev); + /** * pmic_reg_count: get the pmic register count * @@ -306,6 +318,7 @@ int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set); */ struct uc_pmic_priv { uint trans_len; + bool sys_pow_ctrl; };
#endif /* DM_PMIC */

Hi Svyatoslav,
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
PMICs are responsible for device poweroff sequence so lets implement this function.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
drivers/power/pmic/pmic-uclass.c | 12 ++++++++++++ include/power/pmic.h | 13 +++++++++++++ 2 files changed, 25 insertions(+)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 0e2f5e1f41..23803bc96a 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -99,6 +99,18 @@ int pmic_get(const char *name, struct udevice **devp) return uclass_get_device_by_name(UCLASS_PMIC, name, devp); }
+int pmic_poweroff(struct udevice *dev) +{
const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
struct uc_pmic_priv *priv = dev_get_uclass_priv(dev);
if (!ops || !ops->poweroff ||
!priv->sys_pow_ctrl)
return -ENOSYS;
return ops->poweroff(dev);
+}
int pmic_reg_count(struct udevice *dev) { const struct dm_pmic_ops *ops = dev_get_driver_ops(dev); diff --git a/include/power/pmic.h b/include/power/pmic.h index 636221692d..d8dd1ceaa3 100644 --- a/include/power/pmic.h +++ b/include/power/pmic.h @@ -157,11 +157,13 @@ struct pmic {
- Should be implemented by UCLASS_PMIC device drivers. The standard
- device operations provides the I/O interface for it's childs.
*/
- @poweroff: perform poweroff sequence
- @reg_count: device's register count
- @read: read 'len' bytes at "reg" and store it into the 'buffer'
- @write: write 'len' bytes from the 'buffer' to the register at 'reg' address
struct dm_pmic_ops {
int (*poweroff)(struct udevice *dev);
Please do remember to fully comment each method here too - see p2sb set_hide() for an example.
int (*reg_count)(struct udevice *dev); int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int len); int (*write)(struct udevice *dev, uint reg, const uint8_t *buffer,
@@ -242,6 +244,16 @@ int pmic_bind_children(struct udevice *pmic, ofnode parent, */ int pmic_get(const char *name, struct udevice **devp);
+/**
- pmic_poweroff: call the pmic poweroff sequence
- The required pmic device can be obtained by 'pmic_get()'
- @dev - pointer to the UCLASS_PMIC device
- Return: device turns off or negative value of errno.
- */
+int pmic_poweroff(struct udevice *dev);
/**
- pmic_reg_count: get the pmic register count
@@ -306,6 +318,7 @@ int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set); */ struct uc_pmic_priv { uint trans_len;
bool sys_pow_ctrl;
comment
};
#endif /* DM_PMIC */
2.39.2
This needs a test addition to test/dm/pmic.c (perhaps you have it in another patch).
Regards, Simon

чт, 20 лип. 2023 р. о 22:43 Simon Glass sjg@chromium.org пише:
Hi Svyatoslav,
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
PMICs are responsible for device poweroff sequence so lets implement this function.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
drivers/power/pmic/pmic-uclass.c | 12 ++++++++++++ include/power/pmic.h | 13 +++++++++++++ 2 files changed, 25 insertions(+)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 0e2f5e1f41..23803bc96a 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -99,6 +99,18 @@ int pmic_get(const char *name, struct udevice **devp) return uclass_get_device_by_name(UCLASS_PMIC, name, devp); }
+int pmic_poweroff(struct udevice *dev) +{
const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
struct uc_pmic_priv *priv = dev_get_uclass_priv(dev);
if (!ops || !ops->poweroff ||
!priv->sys_pow_ctrl)
return -ENOSYS;
return ops->poweroff(dev);
+}
int pmic_reg_count(struct udevice *dev) { const struct dm_pmic_ops *ops = dev_get_driver_ops(dev); diff --git a/include/power/pmic.h b/include/power/pmic.h index 636221692d..d8dd1ceaa3 100644 --- a/include/power/pmic.h +++ b/include/power/pmic.h @@ -157,11 +157,13 @@ struct pmic {
- Should be implemented by UCLASS_PMIC device drivers. The standard
- device operations provides the I/O interface for it's childs.
*/
- @poweroff: perform poweroff sequence
- @reg_count: device's register count
- @read: read 'len' bytes at "reg" and store it into the 'buffer'
- @write: write 'len' bytes from the 'buffer' to the register at 'reg' address
struct dm_pmic_ops {
int (*poweroff)(struct udevice *dev);
Please do remember to fully comment each method here too - see p2sb set_hide() for an example.
It is commented in description before ops + * @poweroff: perform poweroff sequence
p2sb set_hide() description is nice but it does not fit this case since its description is set before ops.
int (*reg_count)(struct udevice *dev); int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int len); int (*write)(struct udevice *dev, uint reg, const uint8_t *buffer,
@@ -242,6 +244,16 @@ int pmic_bind_children(struct udevice *pmic, ofnode parent, */ int pmic_get(const char *name, struct udevice **devp);
+/**
- pmic_poweroff: call the pmic poweroff sequence
- The required pmic device can be obtained by 'pmic_get()'
- @dev - pointer to the UCLASS_PMIC device
- Return: device turns off or negative value of errno.
- */
+int pmic_poweroff(struct udevice *dev);
/**
- pmic_reg_count: get the pmic register count
@@ -306,6 +318,7 @@ int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set); */ struct uc_pmic_priv { uint trans_len;
bool sys_pow_ctrl;
comment
};
#endif /* DM_PMIC */
2.39.2
This needs a test addition to test/dm/pmic.c (perhaps you have it in another patch).
I knew you would ask for this and I have thought about implementing a dm test but I have faced the next issue: poweroff call (in this context) performs device poweroff on success and -err on failure. Iirc sandbox pmic is not dedicated for poweroff and it can not be "emulated" correctly on sandbox (emulation will be just a passing value like for example pmic_reg_count test).
If this is mandatory then tell pls which behavior is expected in the test and I will try to replicate it.
Regards, Simon

Use new PMIC ops to perform device poweroff.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- cmd/Kconfig | 6 ++++++ cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index ecfd575237..47654297f8 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1439,6 +1439,12 @@ config CMD_POWEROFF help Poweroff/Shutdown the system
+config CMD_PMIC_POWEROFF + bool "PMIC poweroff" + select CMD_POWEROFF + help + Shutdown the system using PMIC poweroff sequence. + config CMD_READ bool "read - Read binary data from a partition" help diff --git a/cmd/boot.c b/cmd/boot.c index 14839c1ced..4270034194 100644 --- a/cmd/boot.c +++ b/cmd/boot.c @@ -9,7 +9,13 @@ */ #include <common.h> #include <command.h> +#include <dm.h> +#include <log.h> #include <net.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <linux/delay.h>
#ifdef CONFIG_CMD_GO
@@ -64,6 +70,40 @@ U_BOOT_CMD( );
#ifdef CONFIG_CMD_POWEROFF +#ifdef CONFIG_CMD_PMIC_POWEROFF +int do_poweroff(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct uc_pmic_priv *pmic_priv; + struct udevice *dev; + int ret; + + for (uclass_find_first_device(UCLASS_PMIC, &dev); + dev; + uclass_find_next_device(&dev)) { + if (dev && !device_active(dev)) { + ret = device_probe(dev); + if (ret) + return ret; + } + + /* value we need to check is set after probe */ + pmic_priv = dev_get_uclass_priv(dev); + if (pmic_priv->sys_pow_ctrl) { + ret = pmic_poweroff(dev); + + /* wait some time and then print error */ + mdelay(5000); + log_err("Failed to power off!!!\n"); + return ret; + } + } + + /* no device should reach here */ + return 1; +} +#endif + U_BOOT_CMD( poweroff, 1, 0, do_poweroff, "Perform POWEROFF of the device",

Hi Svyatoslav,
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
Use new PMIC ops to perform device poweroff.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
cmd/Kconfig | 6 ++++++ cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index ecfd575237..47654297f8 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1439,6 +1439,12 @@ config CMD_POWEROFF help Poweroff/Shutdown the system
+config CMD_PMIC_POWEROFF
bool "PMIC poweroff"
select CMD_POWEROFF
help
Shutdown the system using PMIC poweroff sequence.
config CMD_READ bool "read - Read binary data from a partition" help diff --git a/cmd/boot.c b/cmd/boot.c index 14839c1ced..4270034194 100644 --- a/cmd/boot.c +++ b/cmd/boot.c @@ -9,7 +9,13 @@ */ #include <common.h> #include <command.h> +#include <dm.h> +#include <log.h> #include <net.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <linux/delay.h>
#ifdef CONFIG_CMD_GO
@@ -64,6 +70,40 @@ U_BOOT_CMD( );
#ifdef CONFIG_CMD_POWEROFF +#ifdef CONFIG_CMD_PMIC_POWEROFF +int do_poweroff(struct cmd_tbl *cmdtp, int flag,
int argc, char *const argv[])
+{
struct uc_pmic_priv *pmic_priv;
struct udevice *dev;
int ret;
for (uclass_find_first_device(UCLASS_PMIC, &dev);
dev;
uclass_find_next_device(&dev)) {
if (dev && !device_active(dev)) {
ret = device_probe(dev);
if (ret)
return ret;
}
/* value we need to check is set after probe */
pmic_priv = dev_get_uclass_priv(dev);
if (pmic_priv->sys_pow_ctrl) {
ret = pmic_poweroff(dev);
/* wait some time and then print error */
mdelay(5000);
log_err("Failed to power off!!!\n");
return ret;
}
}
/* no device should reach here */
return 1;
+} +#endif
U_BOOT_CMD( poweroff, 1, 0, do_poweroff, "Perform POWEROFF of the device", -- 2.39.2
How does this relate to sysreset_walk(SYSRESET_POWER_OFF) and do_poweroff() in cmd/boot.c?
Regards, Simon

нд, 23 лип. 2023 р. о 06:48 Simon Glass sjg@chromium.org пише:
Hi Svyatoslav,
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
Use new PMIC ops to perform device poweroff.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
cmd/Kconfig | 6 ++++++ cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index ecfd575237..47654297f8 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1439,6 +1439,12 @@ config CMD_POWEROFF help Poweroff/Shutdown the system
+config CMD_PMIC_POWEROFF
bool "PMIC poweroff"
select CMD_POWEROFF
help
Shutdown the system using PMIC poweroff sequence.
config CMD_READ bool "read - Read binary data from a partition" help diff --git a/cmd/boot.c b/cmd/boot.c index 14839c1ced..4270034194 100644 --- a/cmd/boot.c +++ b/cmd/boot.c @@ -9,7 +9,13 @@ */ #include <common.h> #include <command.h> +#include <dm.h> +#include <log.h> #include <net.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <linux/delay.h>
#ifdef CONFIG_CMD_GO
@@ -64,6 +70,40 @@ U_BOOT_CMD( );
#ifdef CONFIG_CMD_POWEROFF +#ifdef CONFIG_CMD_PMIC_POWEROFF +int do_poweroff(struct cmd_tbl *cmdtp, int flag,
int argc, char *const argv[])
+{
struct uc_pmic_priv *pmic_priv;
struct udevice *dev;
int ret;
for (uclass_find_first_device(UCLASS_PMIC, &dev);
dev;
uclass_find_next_device(&dev)) {
if (dev && !device_active(dev)) {
ret = device_probe(dev);
if (ret)
return ret;
}
/* value we need to check is set after probe */
pmic_priv = dev_get_uclass_priv(dev);
if (pmic_priv->sys_pow_ctrl) {
ret = pmic_poweroff(dev);
/* wait some time and then print error */
mdelay(5000);
log_err("Failed to power off!!!\n");
return ret;
}
}
/* no device should reach here */
return 1;
+} +#endif
U_BOOT_CMD( poweroff, 1, 0, do_poweroff, "Perform POWEROFF of the device", -- 2.39.2
How does this relate to sysreset_walk(SYSRESET_POWER_OFF) and do_poweroff() in cmd/boot.c?
It does not, or at least I might misunderstand you. This is made more to fit embedded devices or at least devices where power off is controlled by PMIC. In this case the device must have DM pmic driver with poweroff function implemented and set in the dts as system-power-controller. If this is true, the maintainer in defconfig enables PMIC driver and CONFIG_CMD_PMIC_POWEROFF so the function above is unlocked and used. This avoids board definitions of do_poweroff like I had to do with my devices and at the same time does not break any other devices since CMD_POWEROFF is still available in unmodded state.
Regards, Simon

нд, 23 лип. 2023 р. о 06:48 Simon Glass sjg@chromium.org пише:
Hi Svyatoslav,
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
Use new PMIC ops to perform device poweroff.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
cmd/Kconfig | 6 ++++++ cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index ecfd575237..47654297f8 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1439,6 +1439,12 @@ config CMD_POWEROFF help Poweroff/Shutdown the system
+config CMD_PMIC_POWEROFF
bool "PMIC poweroff"
select CMD_POWEROFF
help
Shutdown the system using PMIC poweroff sequence.
config CMD_READ bool "read - Read binary data from a partition" help diff --git a/cmd/boot.c b/cmd/boot.c index 14839c1ced..4270034194 100644 --- a/cmd/boot.c +++ b/cmd/boot.c @@ -9,7 +9,13 @@ */ #include <common.h> #include <command.h> +#include <dm.h> +#include <log.h> #include <net.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <linux/delay.h>
#ifdef CONFIG_CMD_GO
@@ -64,6 +70,40 @@ U_BOOT_CMD( );
#ifdef CONFIG_CMD_POWEROFF +#ifdef CONFIG_CMD_PMIC_POWEROFF +int do_poweroff(struct cmd_tbl *cmdtp, int flag,
int argc, char *const argv[])
+{
struct uc_pmic_priv *pmic_priv;
struct udevice *dev;
int ret;
for (uclass_find_first_device(UCLASS_PMIC, &dev);
dev;
uclass_find_next_device(&dev)) {
if (dev && !device_active(dev)) {
ret = device_probe(dev);
if (ret)
return ret;
}
/* value we need to check is set after probe */
pmic_priv = dev_get_uclass_priv(dev);
if (pmic_priv->sys_pow_ctrl) {
ret = pmic_poweroff(dev);
/* wait some time and then print error */
mdelay(5000);
log_err("Failed to power off!!!\n");
return ret;
}
}
/* no device should reach here */
return 1;
+} +#endif
U_BOOT_CMD( poweroff, 1, 0, do_poweroff, "Perform POWEROFF of the device", -- 2.39.2
How does this relate to sysreset_walk(SYSRESET_POWER_OFF) and do_poweroff() in cmd/boot.c?
Yes, it seems that I have misunderstood you, but non the less, you say that I have to implement a new separate pmic subdriver just to support poweroff?
Regards, Simon

Hi Svyatoslav,
On Sun, 23 Jul 2023 at 03:00, Svyatoslav Ryhel clamor95@gmail.com wrote:
нд, 23 лип. 2023 р. о 06:48 Simon Glass sjg@chromium.org пише:
Hi Svyatoslav,
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
Use new PMIC ops to perform device poweroff.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
cmd/Kconfig | 6 ++++++ cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index ecfd575237..47654297f8 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1439,6 +1439,12 @@ config CMD_POWEROFF help Poweroff/Shutdown the system
+config CMD_PMIC_POWEROFF
bool "PMIC poweroff"
select CMD_POWEROFF
help
Shutdown the system using PMIC poweroff sequence.
config CMD_READ bool "read - Read binary data from a partition" help diff --git a/cmd/boot.c b/cmd/boot.c index 14839c1ced..4270034194 100644 --- a/cmd/boot.c +++ b/cmd/boot.c @@ -9,7 +9,13 @@ */ #include <common.h> #include <command.h> +#include <dm.h> +#include <log.h> #include <net.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <linux/delay.h>
#ifdef CONFIG_CMD_GO
@@ -64,6 +70,40 @@ U_BOOT_CMD( );
#ifdef CONFIG_CMD_POWEROFF +#ifdef CONFIG_CMD_PMIC_POWEROFF +int do_poweroff(struct cmd_tbl *cmdtp, int flag,
int argc, char *const argv[])
+{
struct uc_pmic_priv *pmic_priv;
struct udevice *dev;
int ret;
for (uclass_find_first_device(UCLASS_PMIC, &dev);
dev;
uclass_find_next_device(&dev)) {
if (dev && !device_active(dev)) {
ret = device_probe(dev);
if (ret)
return ret;
}
/* value we need to check is set after probe */
pmic_priv = dev_get_uclass_priv(dev);
if (pmic_priv->sys_pow_ctrl) {
ret = pmic_poweroff(dev);
/* wait some time and then print error */
mdelay(5000);
log_err("Failed to power off!!!\n");
return ret;
}
}
/* no device should reach here */
return 1;
+} +#endif
U_BOOT_CMD( poweroff, 1, 0, do_poweroff, "Perform POWEROFF of the device", -- 2.39.2
How does this relate to sysreset_walk(SYSRESET_POWER_OFF) and do_poweroff() in cmd/boot.c?
Yes, it seems that I have misunderstood you, but non the less, you say that I have to implement a new separate pmic subdriver just to support poweroff?
Well it sounds like a lot, but it is not that hard. We try to model devices by their functionality, which maps to a uclass, so when we have a multi-function device we tend to model that with children, each with an appropriate uclass.
Regards, Simon

24 липня 2023 р. 05:28:24 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Sun, 23 Jul 2023 at 03:00, Svyatoslav Ryhel clamor95@gmail.com wrote:
нд, 23 лип. 2023 р. о 06:48 Simon Glass sjg@chromium.org пише:
Hi Svyatoslav,
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
Use new PMIC ops to perform device poweroff.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
cmd/Kconfig | 6 ++++++ cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index ecfd575237..47654297f8 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1439,6 +1439,12 @@ config CMD_POWEROFF help Poweroff/Shutdown the system
+config CMD_PMIC_POWEROFF
bool "PMIC poweroff"
select CMD_POWEROFF
help
Shutdown the system using PMIC poweroff sequence.
config CMD_READ bool "read - Read binary data from a partition" help diff --git a/cmd/boot.c b/cmd/boot.c index 14839c1ced..4270034194 100644 --- a/cmd/boot.c +++ b/cmd/boot.c @@ -9,7 +9,13 @@ */ #include <common.h> #include <command.h> +#include <dm.h> +#include <log.h> #include <net.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <linux/delay.h>
#ifdef CONFIG_CMD_GO
@@ -64,6 +70,40 @@ U_BOOT_CMD( );
#ifdef CONFIG_CMD_POWEROFF +#ifdef CONFIG_CMD_PMIC_POWEROFF +int do_poweroff(struct cmd_tbl *cmdtp, int flag,
int argc, char *const argv[])
+{
struct uc_pmic_priv *pmic_priv;
struct udevice *dev;
int ret;
for (uclass_find_first_device(UCLASS_PMIC, &dev);
dev;
uclass_find_next_device(&dev)) {
if (dev && !device_active(dev)) {
ret = device_probe(dev);
if (ret)
return ret;
}
/* value we need to check is set after probe */
pmic_priv = dev_get_uclass_priv(dev);
if (pmic_priv->sys_pow_ctrl) {
ret = pmic_poweroff(dev);
/* wait some time and then print error */
mdelay(5000);
log_err("Failed to power off!!!\n");
return ret;
}
}
/* no device should reach here */
return 1;
+} +#endif
U_BOOT_CMD( poweroff, 1, 0, do_poweroff, "Perform POWEROFF of the device", -- 2.39.2
How does this relate to sysreset_walk(SYSRESET_POWER_OFF) and do_poweroff() in cmd/boot.c?
Yes, it seems that I have misunderstood you, but non the less, you say that I have to implement a new separate pmic subdriver just to support poweroff?
Well it sounds like a lot, but it is not that hard. We try to model devices by their functionality, which maps to a uclass, so when we have a multi-function device we tend to model that with children, each with an appropriate uclass.
Alright, fair. I have prepared and tested sysreset for all PMICs I have sent and for Tegra in general. Unfortunately, they cannot be sent before existing patches are accepted and merged.
Regards, Simon

Hi Svyatoslav,
On Sun, 30 Jul 2023 at 01:23, Svyatoslav Ryhel clamor95@gmail.com wrote:
24 липня 2023 р. 05:28:24 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Sun, 23 Jul 2023 at 03:00, Svyatoslav Ryhel clamor95@gmail.com wrote:
нд, 23 лип. 2023 р. о 06:48 Simon Glass sjg@chromium.org пише:
Hi Svyatoslav,
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
Use new PMIC ops to perform device poweroff.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
cmd/Kconfig | 6 ++++++ cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index ecfd575237..47654297f8 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1439,6 +1439,12 @@ config CMD_POWEROFF help Poweroff/Shutdown the system
+config CMD_PMIC_POWEROFF
bool "PMIC poweroff"
select CMD_POWEROFF
help
Shutdown the system using PMIC poweroff sequence.
config CMD_READ bool "read - Read binary data from a partition" help diff --git a/cmd/boot.c b/cmd/boot.c index 14839c1ced..4270034194 100644 --- a/cmd/boot.c +++ b/cmd/boot.c @@ -9,7 +9,13 @@ */ #include <common.h> #include <command.h> +#include <dm.h> +#include <log.h> #include <net.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <linux/delay.h>
#ifdef CONFIG_CMD_GO
@@ -64,6 +70,40 @@ U_BOOT_CMD( );
#ifdef CONFIG_CMD_POWEROFF +#ifdef CONFIG_CMD_PMIC_POWEROFF +int do_poweroff(struct cmd_tbl *cmdtp, int flag,
int argc, char *const argv[])
+{
struct uc_pmic_priv *pmic_priv;
struct udevice *dev;
int ret;
for (uclass_find_first_device(UCLASS_PMIC, &dev);
dev;
uclass_find_next_device(&dev)) {
if (dev && !device_active(dev)) {
ret = device_probe(dev);
if (ret)
return ret;
}
/* value we need to check is set after probe */
pmic_priv = dev_get_uclass_priv(dev);
if (pmic_priv->sys_pow_ctrl) {
ret = pmic_poweroff(dev);
/* wait some time and then print error */
mdelay(5000);
log_err("Failed to power off!!!\n");
return ret;
}
}
/* no device should reach here */
return 1;
+} +#endif
U_BOOT_CMD( poweroff, 1, 0, do_poweroff, "Perform POWEROFF of the device", -- 2.39.2
How does this relate to sysreset_walk(SYSRESET_POWER_OFF) and do_poweroff() in cmd/boot.c?
Yes, it seems that I have misunderstood you, but non the less, you say that I have to implement a new separate pmic subdriver just to support poweroff?
Well it sounds like a lot, but it is not that hard. We try to model devices by their functionality, which maps to a uclass, so when we have a multi-function device we tend to model that with children, each with an appropriate uclass.
Alright, fair. I have prepared and tested sysreset for all PMICs I have sent and for Tegra in general. Unfortunately, they cannot be sent before existing patches are accepted and merged.
You can ping the maintainer, or just note (in your cover letter) that they depend on another series, e.g. with a patchwork link. I often have 3-4 series backed up.
Regards, Simon
Regards, Simon

31 липня 2023 р. 20:08:06 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Sun, 30 Jul 2023 at 01:23, Svyatoslav Ryhel clamor95@gmail.com wrote:
24 липня 2023 р. 05:28:24 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Sun, 23 Jul 2023 at 03:00, Svyatoslav Ryhel clamor95@gmail.com wrote:
нд, 23 лип. 2023 р. о 06:48 Simon Glass sjg@chromium.org пише:
Hi Svyatoslav,
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
Use new PMIC ops to perform device poweroff.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
cmd/Kconfig | 6 ++++++ cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index ecfd575237..47654297f8 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1439,6 +1439,12 @@ config CMD_POWEROFF help Poweroff/Shutdown the system
+config CMD_PMIC_POWEROFF
bool "PMIC poweroff"
select CMD_POWEROFF
help
Shutdown the system using PMIC poweroff sequence.
config CMD_READ bool "read - Read binary data from a partition" help diff --git a/cmd/boot.c b/cmd/boot.c index 14839c1ced..4270034194 100644 --- a/cmd/boot.c +++ b/cmd/boot.c @@ -9,7 +9,13 @@ */ #include <common.h> #include <command.h> +#include <dm.h> +#include <log.h> #include <net.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <linux/delay.h>
#ifdef CONFIG_CMD_GO
@@ -64,6 +70,40 @@ U_BOOT_CMD( );
#ifdef CONFIG_CMD_POWEROFF +#ifdef CONFIG_CMD_PMIC_POWEROFF +int do_poweroff(struct cmd_tbl *cmdtp, int flag,
int argc, char *const argv[])
+{
struct uc_pmic_priv *pmic_priv;
struct udevice *dev;
int ret;
for (uclass_find_first_device(UCLASS_PMIC, &dev);
dev;
uclass_find_next_device(&dev)) {
if (dev && !device_active(dev)) {
ret = device_probe(dev);
if (ret)
return ret;
}
/* value we need to check is set after probe */
pmic_priv = dev_get_uclass_priv(dev);
if (pmic_priv->sys_pow_ctrl) {
ret = pmic_poweroff(dev);
/* wait some time and then print error */
mdelay(5000);
log_err("Failed to power off!!!\n");
return ret;
}
}
/* no device should reach here */
return 1;
+} +#endif
U_BOOT_CMD( poweroff, 1, 0, do_poweroff, "Perform POWEROFF of the device", -- 2.39.2
How does this relate to sysreset_walk(SYSRESET_POWER_OFF) and do_poweroff() in cmd/boot.c?
Yes, it seems that I have misunderstood you, but non the less, you say that I have to implement a new separate pmic subdriver just to support poweroff?
Well it sounds like a lot, but it is not that hard. We try to model devices by their functionality, which maps to a uclass, so when we have a multi-function device we tend to model that with children, each with an appropriate uclass.
Alright, fair. I have prepared and tested sysreset for all PMICs I have sent and for Tegra in general. Unfortunately, they cannot be sent before existing patches are accepted and merged.
You can ping the maintainer, or just note (in your cover letter) that they depend on another series, e.g. with a patchwork link. I often have 3-4 series backed up.
This is definitely a nice advice, thank you. Though, sysreset subdevices will change pmic mfd, so this basically is changing code which even does not exist yet.
P. S. If you remember our recent conversation about gpio cell of pmic, I have tested it with max77663 (single cell pmic) and modded gpio-uclass code. It worked surprisingly well with the regulator handling code I have proposed previously, and can provide gpios for keys and regulators.
Those regulator changes affect mostly rockchip and srm32mp1. Maybe if you have rk3399 or stm32mp1 could you test them?
Best regards, Svyatoslav R.
Regards, Simon
Regards, Simon

Hi Svyatoslav,
On Mon, 31 Jul 2023 at 12:07, Svyatoslav Ryhel clamor95@gmail.com wrote:
31 липня 2023 р. 20:08:06 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Sun, 30 Jul 2023 at 01:23, Svyatoslav Ryhel clamor95@gmail.com wrote:
24 липня 2023 р. 05:28:24 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Sun, 23 Jul 2023 at 03:00, Svyatoslav Ryhel clamor95@gmail.com wrote:
нд, 23 лип. 2023 р. о 06:48 Simon Glass sjg@chromium.org пише:
Hi Svyatoslav,
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote: > > Use new PMIC ops to perform device poweroff. > > Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com > --- > cmd/Kconfig | 6 ++++++ > cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 46 insertions(+) > > diff --git a/cmd/Kconfig b/cmd/Kconfig > index ecfd575237..47654297f8 100644 > --- a/cmd/Kconfig > +++ b/cmd/Kconfig > @@ -1439,6 +1439,12 @@ config CMD_POWEROFF > help > Poweroff/Shutdown the system > > +config CMD_PMIC_POWEROFF > + bool "PMIC poweroff" > + select CMD_POWEROFF > + help > + Shutdown the system using PMIC poweroff sequence. > + > config CMD_READ > bool "read - Read binary data from a partition" > help > diff --git a/cmd/boot.c b/cmd/boot.c > index 14839c1ced..4270034194 100644 > --- a/cmd/boot.c > +++ b/cmd/boot.c > @@ -9,7 +9,13 @@ > */ > #include <common.h> > #include <command.h> > +#include <dm.h> > +#include <log.h> > #include <net.h> > +#include <dm/device-internal.h> > +#include <dm/uclass-internal.h> > +#include <power/pmic.h> > +#include <linux/delay.h> > > #ifdef CONFIG_CMD_GO > > @@ -64,6 +70,40 @@ U_BOOT_CMD( > ); > > #ifdef CONFIG_CMD_POWEROFF > +#ifdef CONFIG_CMD_PMIC_POWEROFF > +int do_poweroff(struct cmd_tbl *cmdtp, int flag, > + int argc, char *const argv[]) > +{ > + struct uc_pmic_priv *pmic_priv; > + struct udevice *dev; > + int ret; > + > + for (uclass_find_first_device(UCLASS_PMIC, &dev); > + dev; > + uclass_find_next_device(&dev)) { > + if (dev && !device_active(dev)) { > + ret = device_probe(dev); > + if (ret) > + return ret; > + } > + > + /* value we need to check is set after probe */ > + pmic_priv = dev_get_uclass_priv(dev); > + if (pmic_priv->sys_pow_ctrl) { > + ret = pmic_poweroff(dev); > + > + /* wait some time and then print error */ > + mdelay(5000); > + log_err("Failed to power off!!!\n"); > + return ret; > + } > + } > + > + /* no device should reach here */ > + return 1; > +} > +#endif > + > U_BOOT_CMD( > poweroff, 1, 0, do_poweroff, > "Perform POWEROFF of the device", > -- > 2.39.2 >
How does this relate to sysreset_walk(SYSRESET_POWER_OFF) and do_poweroff() in cmd/boot.c?
Yes, it seems that I have misunderstood you, but non the less, you say that I have to implement a new separate pmic subdriver just to support poweroff?
Well it sounds like a lot, but it is not that hard. We try to model devices by their functionality, which maps to a uclass, so when we have a multi-function device we tend to model that with children, each with an appropriate uclass.
Alright, fair. I have prepared and tested sysreset for all PMICs I have sent and for Tegra in general. Unfortunately, they cannot be sent before existing patches are accepted and merged.
You can ping the maintainer, or just note (in your cover letter) that they depend on another series, e.g. with a patchwork link. I often have 3-4 series backed up.
This is definitely a nice advice, thank you. Though, sysreset subdevices will change pmic mfd, so this basically is changing code which even does not exist yet.
P. S. If you remember our recent conversation about gpio cell of pmic, I have tested it with max77663 (single cell pmic) and modded gpio-uclass code. It worked surprisingly well with the regulator handling code I have proposed previously, and can provide gpios for keys and regulators.
Those regulator changes affect mostly rockchip and srm32mp1. Maybe if you have rk3399 or stm32mp1 could you test them?
Yes I have rk3399, but I have lost track of your work. Can you please send me the patchwork link for your series?
Regards, Simon

9 серпня 2023 р. 05:03:41 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Mon, 31 Jul 2023 at 12:07, Svyatoslav Ryhel clamor95@gmail.com wrote:
31 липня 2023 р. 20:08:06 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Sun, 30 Jul 2023 at 01:23, Svyatoslav Ryhel clamor95@gmail.com wrote:
24 липня 2023 р. 05:28:24 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Sun, 23 Jul 2023 at 03:00, Svyatoslav Ryhel clamor95@gmail.com wrote:
нд, 23 лип. 2023 р. о 06:48 Simon Glass sjg@chromium.org пише: > > Hi Svyatoslav, > > On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote: > > > > Use new PMIC ops to perform device poweroff. > > > > Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com > > --- > > cmd/Kconfig | 6 ++++++ > > cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ > > 2 files changed, 46 insertions(+) > > > > diff --git a/cmd/Kconfig b/cmd/Kconfig > > index ecfd575237..47654297f8 100644 > > --- a/cmd/Kconfig > > +++ b/cmd/Kconfig > > @@ -1439,6 +1439,12 @@ config CMD_POWEROFF > > help > > Poweroff/Shutdown the system > > > > +config CMD_PMIC_POWEROFF > > + bool "PMIC poweroff" > > + select CMD_POWEROFF > > + help > > + Shutdown the system using PMIC poweroff sequence. > > + > > config CMD_READ > > bool "read - Read binary data from a partition" > > help > > diff --git a/cmd/boot.c b/cmd/boot.c > > index 14839c1ced..4270034194 100644 > > --- a/cmd/boot.c > > +++ b/cmd/boot.c > > @@ -9,7 +9,13 @@ > > */ > > #include <common.h> > > #include <command.h> > > +#include <dm.h> > > +#include <log.h> > > #include <net.h> > > +#include <dm/device-internal.h> > > +#include <dm/uclass-internal.h> > > +#include <power/pmic.h> > > +#include <linux/delay.h> > > > > #ifdef CONFIG_CMD_GO > > > > @@ -64,6 +70,40 @@ U_BOOT_CMD( > > ); > > > > #ifdef CONFIG_CMD_POWEROFF > > +#ifdef CONFIG_CMD_PMIC_POWEROFF > > +int do_poweroff(struct cmd_tbl *cmdtp, int flag, > > + int argc, char *const argv[]) > > +{ > > + struct uc_pmic_priv *pmic_priv; > > + struct udevice *dev; > > + int ret; > > + > > + for (uclass_find_first_device(UCLASS_PMIC, &dev); > > + dev; > > + uclass_find_next_device(&dev)) { > > + if (dev && !device_active(dev)) { > > + ret = device_probe(dev); > > + if (ret) > > + return ret; > > + } > > + > > + /* value we need to check is set after probe */ > > + pmic_priv = dev_get_uclass_priv(dev); > > + if (pmic_priv->sys_pow_ctrl) { > > + ret = pmic_poweroff(dev); > > + > > + /* wait some time and then print error */ > > + mdelay(5000); > > + log_err("Failed to power off!!!\n"); > > + return ret; > > + } > > + } > > + > > + /* no device should reach here */ > > + return 1; > > +} > > +#endif > > + > > U_BOOT_CMD( > > poweroff, 1, 0, do_poweroff, > > "Perform POWEROFF of the device", > > -- > > 2.39.2 > > > > How does this relate to sysreset_walk(SYSRESET_POWER_OFF) and > do_poweroff() in cmd/boot.c? >
Yes, it seems that I have misunderstood you, but non the less, you say that I have to implement a new separate pmic subdriver just to support poweroff?
Well it sounds like a lot, but it is not that hard. We try to model devices by their functionality, which maps to a uclass, so when we have a multi-function device we tend to model that with children, each with an appropriate uclass.
Alright, fair. I have prepared and tested sysreset for all PMICs I have sent and for Tegra in general. Unfortunately, they cannot be sent before existing patches are accepted and merged.
You can ping the maintainer, or just note (in your cover letter) that they depend on another series, e.g. with a patchwork link. I often have 3-4 series backed up.
This is definitely a nice advice, thank you. Though, sysreset subdevices will change pmic mfd, so this basically is changing code which even does not exist yet.
P. S. If you remember our recent conversation about gpio cell of pmic, I have tested it with max77663 (single cell pmic) and modded gpio-uclass code. It worked surprisingly well with the regulator handling code I have proposed previously, and can provide gpios for keys and regulators.
Those regulator changes affect mostly rockchip and srm32mp1. Maybe if you have rk3399 or stm32mp1 could you test them?
Yes I have rk3399, but I have lost track of your work. Can you please send me the patchwork link for your series?
Jonas Karlman proposed a nice adjustments making my patch less invasive. I enclose the link to regulator changes.
What I can tell from my side, it works correctly and I have adjusted dm tests so they pass and test implemented changes at the same time.
Currently I face i2c errors with pmic always-on/boot-on regulators (regulator probes ok, pmic probes and works ok, i2c gives error on xfer).
Error occurs only if pmic regulator is probed after bind, if it is probed on pmic post_probe I have no error. Maybe you can reproduse this on your hw.
Best regards, Svyatoslav R.
Link: https://patchwork.ozlabs.org/project/uboot/list/?series=367513
Regards, Simon

Hi Svyatoslav,
On Tue, 8 Aug 2023 at 22:46, Svyatoslav Ryhel clamor95@gmail.com wrote:
9 серпня 2023 р. 05:03:41 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Mon, 31 Jul 2023 at 12:07, Svyatoslav Ryhel clamor95@gmail.com wrote:
31 липня 2023 р. 20:08:06 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Sun, 30 Jul 2023 at 01:23, Svyatoslav Ryhel clamor95@gmail.com wrote:
24 липня 2023 р. 05:28:24 GMT+03:00, Simon Glass sjg@chromium.org написав(-ла):
Hi Svyatoslav,
On Sun, 23 Jul 2023 at 03:00, Svyatoslav Ryhel clamor95@gmail.com wrote: > > нд, 23 лип. 2023 р. о 06:48 Simon Glass sjg@chromium.org пише: > > > > Hi Svyatoslav, > > > > On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote: > > > > > > Use new PMIC ops to perform device poweroff. > > > > > > Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com > > > --- > > > cmd/Kconfig | 6 ++++++ > > > cmd/boot.c | 40 ++++++++++++++++++++++++++++++++++++++++ > > > 2 files changed, 46 insertions(+) > > > > > > diff --git a/cmd/Kconfig b/cmd/Kconfig > > > index ecfd575237..47654297f8 100644 > > > --- a/cmd/Kconfig > > > +++ b/cmd/Kconfig > > > @@ -1439,6 +1439,12 @@ config CMD_POWEROFF > > > help > > > Poweroff/Shutdown the system > > > > > > +config CMD_PMIC_POWEROFF > > > + bool "PMIC poweroff" > > > + select CMD_POWEROFF > > > + help > > > + Shutdown the system using PMIC poweroff sequence. > > > + > > > config CMD_READ > > > bool "read - Read binary data from a partition" > > > help > > > diff --git a/cmd/boot.c b/cmd/boot.c > > > index 14839c1ced..4270034194 100644 > > > --- a/cmd/boot.c > > > +++ b/cmd/boot.c > > > @@ -9,7 +9,13 @@ > > > */ > > > #include <common.h> > > > #include <command.h> > > > +#include <dm.h> > > > +#include <log.h> > > > #include <net.h> > > > +#include <dm/device-internal.h> > > > +#include <dm/uclass-internal.h> > > > +#include <power/pmic.h> > > > +#include <linux/delay.h> > > > > > > #ifdef CONFIG_CMD_GO > > > > > > @@ -64,6 +70,40 @@ U_BOOT_CMD( > > > ); > > > > > > #ifdef CONFIG_CMD_POWEROFF > > > +#ifdef CONFIG_CMD_PMIC_POWEROFF > > > +int do_poweroff(struct cmd_tbl *cmdtp, int flag, > > > + int argc, char *const argv[]) > > > +{ > > > + struct uc_pmic_priv *pmic_priv; > > > + struct udevice *dev; > > > + int ret; > > > + > > > + for (uclass_find_first_device(UCLASS_PMIC, &dev); > > > + dev; > > > + uclass_find_next_device(&dev)) { > > > + if (dev && !device_active(dev)) { > > > + ret = device_probe(dev); > > > + if (ret) > > > + return ret; > > > + } > > > + > > > + /* value we need to check is set after probe */ > > > + pmic_priv = dev_get_uclass_priv(dev); > > > + if (pmic_priv->sys_pow_ctrl) { > > > + ret = pmic_poweroff(dev); > > > + > > > + /* wait some time and then print error */ > > > + mdelay(5000); > > > + log_err("Failed to power off!!!\n"); > > > + return ret; > > > + } > > > + } > > > + > > > + /* no device should reach here */ > > > + return 1; > > > +} > > > +#endif > > > + > > > U_BOOT_CMD( > > > poweroff, 1, 0, do_poweroff, > > > "Perform POWEROFF of the device", > > > -- > > > 2.39.2 > > > > > > > How does this relate to sysreset_walk(SYSRESET_POWER_OFF) and > > do_poweroff() in cmd/boot.c? > > > > Yes, it seems that I have misunderstood you, but non the less, you say > that I have to implement a new separate pmic subdriver just to support > poweroff?
Well it sounds like a lot, but it is not that hard. We try to model devices by their functionality, which maps to a uclass, so when we have a multi-function device we tend to model that with children, each with an appropriate uclass.
Alright, fair. I have prepared and tested sysreset for all PMICs I have sent and for Tegra in general. Unfortunately, they cannot be sent before existing patches are accepted and merged.
You can ping the maintainer, or just note (in your cover letter) that they depend on another series, e.g. with a patchwork link. I often have 3-4 series backed up.
This is definitely a nice advice, thank you. Though, sysreset subdevices will change pmic mfd, so this basically is changing code which even does not exist yet.
P. S. If you remember our recent conversation about gpio cell of pmic, I have tested it with max77663 (single cell pmic) and modded gpio-uclass code. It worked surprisingly well with the regulator handling code I have proposed previously, and can provide gpios for keys and regulators.
Those regulator changes affect mostly rockchip and srm32mp1. Maybe if you have rk3399 or stm32mp1 could you test them?
Yes I have rk3399, but I have lost track of your work. Can you please send me the patchwork link for your series?
Jonas Karlman proposed a nice adjustments making my patch less invasive. I enclose the link to regulator changes.
What I can tell from my side, it works correctly and I have adjusted dm tests so they pass and test implemented changes at the same time.
Currently I face i2c errors with pmic always-on/boot-on regulators (regulator probes ok, pmic probes and works ok, i2c gives error on xfer).
Error occurs only if pmic regulator is probed after bind, if it is probed on pmic post_probe I have no error. Maybe you can reproduse this on your hw.
Well probe after bind is bad. We should let all devices be bound, then worry about probing them when needed.
If you need this to happen from the board, why? It really should be an lazy init, only when needed. If boards need this in general, then let's think about an event to kick it off.
I don't see any failures with that series on Bob,
Best regards, Svyatoslav R.
Link: https://patchwork.ozlabs.org/project/uboot/list/?series=367513
Regards, Simon
Regards, Simon

Existing PALMAS PMIC driver is fully compatible with TI TPS65913 PMIC found in many Tegra 4 devices, like Tegra Note 7 and ASUS TF701T. TPS65913 shares same structure of regulators like TPS659038 so data can be reused.
Tested-by: Svyatoslav Ryhel clamor95@gmail.com # NVIDIA Tegratab Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/power/pmic/palmas.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/power/pmic/palmas.c b/drivers/power/pmic/palmas.c index 6080cbff0b..d6eb9cb433 100644 --- a/drivers/power/pmic/palmas.c +++ b/drivers/power/pmic/palmas.c @@ -87,6 +87,7 @@ static struct dm_pmic_ops palmas_ops = {
static const struct udevice_id palmas_ids[] = { { .compatible = "ti,tps659038", .data = TPS659038 }, + { .compatible = "ti,tps65913" , .data = TPS659038 }, { .compatible = "ti,tps65917" , .data = TPS65917 }, { } };

Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- doc/device-tree-bindings/pmic/max77663.txt | 84 +++++++++++++++++ drivers/power/pmic/Kconfig | 6 ++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/max77663.c | 105 +++++++++++++++++++++ include/power/max77663.h | 44 +++++++++ 5 files changed, 240 insertions(+) create mode 100644 doc/device-tree-bindings/pmic/max77663.txt create mode 100644 drivers/power/pmic/max77663.c create mode 100644 include/power/max77663.h
diff --git a/doc/device-tree-bindings/pmic/max77663.txt b/doc/device-tree-bindings/pmic/max77663.txt new file mode 100644 index 0000000000..ddb7d3eb14 --- /dev/null +++ b/doc/device-tree-bindings/pmic/max77663.txt @@ -0,0 +1,84 @@ +MAXIM, MAX77663 PMIC + +This device uses two drivers: +- drivers/power/pmic/max77663.c (for parent device) +- drivers/power/regulator/max77663_regulator.c (for child regulators) + +This chapter describes the binding info for the PMIC driver and regulators. + +Required properties for PMIC: +- compatible: "maxim,max77663" +- reg: usually 0x1c or 0x3c + +With those two properties, the pmic device can be used for read/write only. +To bind each regulator, the optional regulators subnode should exists. + +Optional subnode: +- name: regulators (subnode list of each device's regulator) + +Regulators subnode contains set on supported regulators. + +Required properties: +- regulator-name: used for regulator uclass platform data '.name', + +List of supported regulator nodes names for max77663: +- sd0, sd1, sd2, sd3, ldo0, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8 + +Optional: +- regulator-min-microvolt: minimum allowed Voltage to set +- regulator-max-microvolt: minimum allowed Voltage to set +- regulator-always-on: regulator should be never disabled +- regulator-boot-on: regulator should be enabled by the bootloader + +Linux driver binding for this driver is compatible. + +Example: + +max77663@1c { + compatible = "maxim,max77663"; + reg = <0x1c>; + + regulators { + sd0 { + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1250000>; + regulator-always-on; + regulator-boot-on; + }; + + ... + + ldo0 { + regulator-name = "avdd_pll"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + ... + + ldo2 { + regulator-name = "avdd_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo3 { + regulator-name = "vdd_sdmmc3"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + }; + + ... + + ldo8 { + regulator-name = "avdd_dsi_csi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + }; +}; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 176fb07c65..abea0fe4ed 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -184,6 +184,12 @@ config SPL_DM_PMIC_PFUZE100 This config enables implementation of driver-model pmic uclass features for PMIC PFUZE100 in SPL. The driver implements read/write operations.
+config DM_PMIC_MAX77663 + bool "Enable Driver Model for PMIC MAX77663" + ---help--- + This config enables implementation of driver-model pmic uclass features + for PMIC MAX77663. The driver implements read/write operations. + config DM_PMIC_MAX77686 bool "Enable Driver Model for PMIC MAX77686" ---help--- diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 0b3b3d62d0..414a9d8225 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_$(SPL_TPL_)DM_PMIC) += pmic-uclass.o obj-$(CONFIG_$(SPL_)DM_PMIC_FAN53555) += fan53555.o obj-$(CONFIG_$(SPL_)DM_PMIC_DA9063) += da9063.o +obj-$(CONFIG_$(SPL_)DM_PMIC_MAX77663) += max77663.o obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o obj-$(CONFIG_DM_PMIC_MAX8998) += max8998.o obj-$(CONFIG_DM_PMIC_MC34708) += mc34708.o diff --git a/drivers/power/pmic/max77663.c b/drivers/power/pmic/max77663.c new file mode 100644 index 0000000000..dbc54fec71 --- /dev/null +++ b/drivers/power/pmic/max77663.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/max77663.h> +#include <dm/device.h> + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "ldo", .driver = MAX77663_LDO_DRIVER }, + { .prefix = "sd", .driver = MAX77663_SD_DRIVER }, + { }, +}; + +static int max77663_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + log_err("write error to device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int max77663_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + log_err("read error from device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int max77663_poweroff(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev, MAX77663_REG_ONOFF_CFG1); + if (ret < 0) + return ret; + + ret &= ~ONOFF_SFT_RST; + + return pmic_reg_write(dev, MAX77663_REG_ONOFF_CFG1, + ret | ONOFF_PWR_OFF); +} + +static int max77663_bind(struct udevice *dev) +{ + ofnode regulators_node; + int children; + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + log_err("%s regulators subnode not found!\n", dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + log_err("%s - no child found\n", dev->name); + + /* Always return success for this device */ + return 0; +} + +static int max77663_probe(struct udevice *dev) +{ + struct uc_pmic_priv *priv = dev_get_uclass_priv(dev); + + priv->sys_pow_ctrl = dev_read_bool(dev, "system-power-controller"); + return 0; +} + +static struct dm_pmic_ops max77663_ops = { + .poweroff = max77663_poweroff, + .read = max77663_read, + .write = max77663_write, +}; + +static const struct udevice_id max77663_ids[] = { + { .compatible = "maxim,max77663" }, + { } +}; + +U_BOOT_DRIVER(pmic_max77663) = { + .name = "max77663_pmic", + .id = UCLASS_PMIC, + .of_match = max77663_ids, + .bind = max77663_bind, + .probe = max77663_probe, + .ops = &max77663_ops, +}; diff --git a/include/power/max77663.h b/include/power/max77663.h new file mode 100644 index 0000000000..0a2cdf16bb --- /dev/null +++ b/include/power/max77663.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#ifndef _MAX77663_H_ +#define _MAX77663_H_ + +#define MAX77663_LDO_NUM 9 +#define MAX77663_SD_NUM 5 + +/* Drivers name */ +#define MAX77663_LDO_DRIVER "max77663_ldo" +#define MAX77663_SD_DRIVER "max77663_sd" + +#define MAX77663_SD_STATUS_MASK 0x30 + +#define MAX77663_SD0_VOLT_MAX_HEX 0x40 +#define MAX77663_SD1_VOLT_MAX_HEX 0X4C +#define MAX77663_SD_VOLT_MAX_HEX 0XFF +#define MAX77663_SD_VOLT_MIN_HEX 0x02 + +#define MAX77663_SD0_VOLT_MAX 1400000 +#define MAX77663_SD1_VOLT_MAX 1550000 +#define MAX77663_SD_VOLT_MAX 3787500 +#define MAX77663_SD_VOLT_MIN 625000 + +#define MAX77663_SD_VOLT_BASE 600000 + +#define MAX77663_LDO_STATUS_MASK 0xC0 +#define MAX77663_LDO_VOLT_MASK 0x3F +#define MAX77663_LDO_VOLT_MAX_HEX 0x3F + +#define MAX77663_LDO01_VOLT_MAX 2375000 +#define MAX77663_LDO4_VOLT_MAX 1587500 +#define MAX77663_LDO_VOLT_MAX 3950000 + +#define MAX77663_LDO_VOLT_BASE 800000 + +#define MAX77663_REG_ONOFF_CFG1 0x41 +#define ONOFF_SFT_RST BIT(7) +#define ONOFF_PWR_OFF BIT(1) + +#endif /* _MAX77663_H_ */

The driver provides regulator set/get voltage enable/disable functions for MAXIM MAX77663 PMICs.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/max77663_regulator.c | 358 +++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 drivers/power/regulator/max77663_regulator.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index eb5aa38c1c..571debd54e 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -141,6 +141,14 @@ config SPL_REGULATOR_PWM This config enables implementation of driver-model regulator uclass features for PWM regulators in SPL.
+config DM_REGULATOR_MAX77663 + bool "Enable Driver Model for REGULATOR MAX77663" + depends on DM_REGULATOR && DM_PMIC_MAX77663 + ---help--- + This config enables implementation of driver-model regulator uclass + features for REGULATOR MAX77663. The driver implements get/set api for: + value and enable. + config DM_REGULATOR_MAX77686 bool "Enable Driver Model for REGULATOR MAX77686" depends on DM_REGULATOR && DM_PMIC_MAX77686 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index d9e0cd5949..8d73169b50 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o obj-$(CONFIG_$(SPL_)REGULATOR_AXP) += axp_regulator.o obj-$(CONFIG_$(SPL_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_DA9063) += da9063.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_MAX77663) += max77663_regulator.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o diff --git a/drivers/power/regulator/max77663_regulator.c b/drivers/power/regulator/max77663_regulator.c new file mode 100644 index 0000000000..cbc275b30b --- /dev/null +++ b/drivers/power/regulator/max77663_regulator.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/max77663.h> + +/* fist row is control registers, second is voltage registers */ +static const char max77663_sd_reg[][MAX77663_SD_NUM] = { + { 0x1d, 0x1e, 0x1f, 0x20, 0x21 }, + { 0x16, 0x17, 0x18, 0x19, 0x2a }, +}; + +static const char max77663_ldo_reg[MAX77663_LDO_NUM] = { + 0x23, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x2f, 0x31, 0x33 +}; + +static int max77663_sd_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + if (ret & MAX77663_SD_STATUS_MASK) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + ret &= ~(MAX77663_SD_STATUS_MASK); + + if (*enable) + ret |= MAX77663_SD_STATUS_MASK; + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int max77663_sd_volt2hex(int sd, int uV) +{ + switch (sd) { + case 0: + /* SD0 has max voltage 1.4V */ + if (uV > MAX77663_SD0_VOLT_MAX) + return -EINVAL; + break; + case 1: + /* SD1 has max voltage 1.55V */ + if (uV > MAX77663_SD1_VOLT_MAX) + return -EINVAL; + break; + default: + /* SD2 and SD3 have max voltage 3.79V */ + if (uV > MAX77663_SD_VOLT_MAX) + return -EINVAL; + break; + }; + + if (uV < MAX77663_SD_VOLT_MIN) + uV = MAX77663_SD_VOLT_MIN; + + return (uV - MAX77663_SD_VOLT_BASE) / 12500; +} + +static int max77663_sd_hex2volt(int sd, int hex) +{ + switch (sd) { + case 0: + /* SD0 has max voltage 1.4V */ + if (hex > MAX77663_SD0_VOLT_MAX_HEX) + return -EINVAL; + break; + case 1: + /* SD1 has max voltage 1.55V */ + if (hex > MAX77663_SD1_VOLT_MAX_HEX) + return -EINVAL; + break; + default: + /* SD2 and SD3 have max voltage 3.79V */ + if (hex > MAX77663_SD_VOLT_MAX_HEX) + return -EINVAL; + break; + }; + + if (hex < MAX77663_SD_VOLT_MIN_HEX) + hex = MAX77663_SD_VOLT_MIN_HEX; + + return MAX77663_SD_VOLT_BASE + hex * 12500; +} + +static int max77663_sd_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int id = dev->driver_data; + int ret; + + if (op == PMIC_OP_GET) { + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + *uV = 0; + + ret = max77663_sd_hex2volt(id, ret); + if (ret < 0) + return ret; + *uV = ret; + + return 0; + } + + /* SD regulators use entire register for voltage */ + ret = max77663_sd_volt2hex(id, *uV); + if (ret < 0) + return ret; + + return pmic_reg_write(dev->parent, adr, ret); +} + +static int max77663_sd_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->ctrl_reg = max77663_sd_reg[0][dev->driver_data]; + uc_pdata->volt_reg = max77663_sd_reg[1][dev->driver_data]; + + return 0; +} + +static int sd_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = max77663_sd_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int sd_set_value(struct udevice *dev, int uV) +{ + return max77663_sd_val(dev, PMIC_OP_SET, &uV); +} + +static int sd_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = max77663_sd_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int sd_set_enable(struct udevice *dev, bool enable) +{ + return max77663_sd_enable(dev, PMIC_OP_SET, &enable); +} + +static const struct dm_regulator_ops max77663_sd_ops = { + .get_value = sd_get_value, + .set_value = sd_set_value, + .get_enable = sd_get_enable, + .set_enable = sd_set_enable, +}; + +U_BOOT_DRIVER(max77663_sd) = { + .name = MAX77663_SD_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &max77663_sd_ops, + .probe = max77663_sd_probe, +}; + +static int max77663_ldo_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + if (ret & MAX77663_LDO_STATUS_MASK) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + ret &= ~(MAX77663_LDO_STATUS_MASK); + + if (*enable) + ret |= MAX77663_LDO_STATUS_MASK; + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int max77663_ldo_volt2hex(int ldo, int uV) +{ + switch (ldo) { + case 0: + case 1: + if (uV > MAX77663_LDO01_VOLT_MAX) + return -EINVAL; + + return (uV - MAX77663_LDO_VOLT_BASE) / 25000; + case 4: + if (uV > MAX77663_LDO4_VOLT_MAX) + return -EINVAL; + + return (uV - MAX77663_LDO_VOLT_BASE) / 12500; + default: + if (uV > MAX77663_LDO_VOLT_MAX) + return -EINVAL; + + return (uV - MAX77663_LDO_VOLT_BASE) / 50000; + }; +} + +static int max77663_ldo_hex2volt(int ldo, int hex) +{ + if (hex > MAX77663_LDO_VOLT_MAX_HEX) + return -EINVAL; + + switch (ldo) { + case 0: + case 1: + return (hex * 25000) + MAX77663_LDO_VOLT_BASE; + case 4: + return (hex * 12500) + MAX77663_LDO_VOLT_BASE; + default: + return (hex * 50000) + MAX77663_LDO_VOLT_BASE; + }; +} + +static int max77663_ldo_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int id = dev->driver_data; + int hex, ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = max77663_ldo_hex2volt(id, ret & MAX77663_LDO_VOLT_MASK); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = max77663_ldo_volt2hex(id, *uV); + if (hex < 0) + return hex; + + ret &= ~(MAX77663_LDO_VOLT_MASK); + + return pmic_reg_write(dev->parent, adr, ret | hex); +} + +static int max77663_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->ctrl_reg = max77663_ldo_reg[dev->driver_data]; + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = max77663_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return max77663_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static int ldo_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = max77663_ldo_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + return max77663_ldo_enable(dev, PMIC_OP_SET, &enable); +} + +static const struct dm_regulator_ops max77663_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, +}; + +U_BOOT_DRIVER(max77663_ldo) = { + .name = MAX77663_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &max77663_ldo_ops, + .probe = max77663_ldo_probe, +};

Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- doc/device-tree-bindings/pmic/tps80031.txt | 76 +++++++++++++++++ drivers/power/pmic/Kconfig | 6 ++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/tps80031.c | 97 ++++++++++++++++++++++ include/power/tps80031.h | 45 ++++++++++ 5 files changed, 225 insertions(+) create mode 100644 doc/device-tree-bindings/pmic/tps80031.txt create mode 100644 drivers/power/pmic/tps80031.c create mode 100644 include/power/tps80031.h
diff --git a/doc/device-tree-bindings/pmic/tps80031.txt b/doc/device-tree-bindings/pmic/tps80031.txt new file mode 100644 index 0000000000..577e6de1c1 --- /dev/null +++ b/doc/device-tree-bindings/pmic/tps80031.txt @@ -0,0 +1,76 @@ +Texas Instruments, TPS80031/TPS80032 PMIC + +This device uses two drivers: +- drivers/power/pmic/tps80031.c (for parent device) +- drivers/power/regulator/tps80031_regulator.c (for child regulators) + +This chapter describes the binding info for the PMIC driver and regulators. + +Required properties for PMIC: +- compatible: "ti,tps80031" or "ti,tps80032" +- reg: 0x48 + +With those two properties, the pmic device can be used for read/write only. +To bind each regulator, the optional regulators subnode should exists. + +Optional subnode: +- name: regulators (subnode list of each device's regulator) + +Regulators subnode contains set on supported regulators. + +Required properties: +- regulator-name: used for regulator uclass platform data '.name', + +List of supported regulator nodes names for tps80031/tps80032: +- smps1, smps2, smps3, smps4, smps5 +- ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldoln, ldousb + +SMPS5 in Linux 3.1.10 is referred as vio, but datasheet clearly names it SMPS5. + +Optional: +- regulator-min-microvolt: minimum allowed Voltage to set +- regulator-max-microvolt: minimum allowed Voltage to set +- regulator-always-on: regulator should be never disabled +- regulator-boot-on: regulator should be enabled by the bootloader + +Example: + +tps80032@48 { + compatible = "ti,tps80032"; + reg = <0x48>; + + regulators { + smps1 { + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1250000>; + regulator-always-on; + regulator-boot-on; + }; + + ... + + smps5 { + regulator-name = "vdd_1v8_gen"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo1 { + regulator-name = "avdd_dsi_csi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + + ... + + ldousb { + regulator-name = "avdd_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + }; +}; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index abea0fe4ed..d25fe1ce0d 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -347,6 +347,12 @@ config DM_PMIC_TPS65910 DC-DC converter, 8 LDOs and a RTC. This driver binds the SMPS and LDO pmic children.
+config DM_PMIC_TPS80031 + bool "Enable driver for Texas Instruments TPS80031/TPS80032 PMIC" + ---help--- + The TPS80031/TPS80032 are PMIC's containing several LDOs, SMPS. + This driver binds the pmic children. + config PMIC_STPMIC1 bool "Enable support for STMicroelectronics STPMIC1 PMIC" depends on DM_I2C diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 414a9d8225..55ee614364 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_$(SPL_)PMIC_RN5T567) += rn5t567.o obj-$(CONFIG_PMIC_TPS65090) += tps65090.o obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o obj-$(CONFIG_DM_PMIC_TPS65910) += pmic_tps65910_dm.o +obj-$(CONFIG_$(SPL_)DM_PMIC_TPS80031) += tps80031.o obj-$(CONFIG_$(SPL_)PMIC_PALMAS) += palmas.o obj-$(CONFIG_$(SPL_)PMIC_LP873X) += lp873x.o obj-$(CONFIG_$(SPL_)PMIC_LP87565) += lp87565.o diff --git a/drivers/power/pmic/tps80031.c b/drivers/power/pmic/tps80031.c new file mode 100644 index 0000000000..bce01c6276 --- /dev/null +++ b/drivers/power/pmic/tps80031.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps80031.h> +#include <dm/device.h> + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "ldo", .driver = TPS80031_LDO_DRIVER }, + { .prefix = "smps", .driver = TPS80031_SMPS_DRIVER }, + { }, +}; + +static int tps80031_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + log_err("write error to device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int tps80031_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + log_err("read error from device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int tps80031_poweroff(struct udevice *dev) +{ + return pmic_reg_write(dev, TPS80031_PHOENIX_DEV_ON, DEVOFF); +} + +static int tps80031_bind(struct udevice *dev) +{ + ofnode regulators_node; + int children; + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + log_err("%s regulators subnode not found!\n", dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + log_err("%s - no child found\n", dev->name); + + /* Always return success for this device */ + return 0; +} + +static int tps80031_probe(struct udevice *dev) +{ + struct uc_pmic_priv *priv = dev_get_uclass_priv(dev); + + priv->sys_pow_ctrl = dev_read_bool(dev, "ti,system-power-controller"); + return 0; +} + +static struct dm_pmic_ops tps80031_ops = { + .poweroff = tps80031_poweroff, + .read = tps80031_read, + .write = tps80031_write, +}; + +static const struct udevice_id tps80031_ids[] = { + { .compatible = "ti,tps80031" }, + { .compatible = "ti,tps80032" }, + { } +}; + +U_BOOT_DRIVER(pmic_tps80031) = { + .name = "tps80031_pmic", + .id = UCLASS_PMIC, + .of_match = tps80031_ids, + .bind = tps80031_bind, + .probe = tps80031_probe, + .ops = &tps80031_ops, +}; diff --git a/include/power/tps80031.h b/include/power/tps80031.h new file mode 100644 index 0000000000..3d61ac54be --- /dev/null +++ b/include/power/tps80031.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#ifndef _TPS80031_H_ +#define _TPS80031_H_ + +#define TPS80031_LDO_NUM 9 +#define TPS80031_SMPS_NUM 5 + +/* Drivers name */ +#define TPS80031_LDO_DRIVER "tps80031_ldo" +#define TPS80031_SMPS_DRIVER "tps80031_smps" + +#define TPS80031_SMPS_OFFSET 0xE0 +#define TPS80031_OFFSET_FLAG BIT(3) + +#define TPS80031_REGULATOR_STATUS_MASK 0x3 +#define TPS80031_REGULATOR_MODE_ON 0x1 + +#define TPS80031_SMPS_VOLT_MASK 0x3F +#define TPS80031_SMPS_VOLT_MAX_HEX 0x39 +#define TPS80031_SMPS_VOLT_MAX 1400000 +#define TPS80031_SMPS_VOLT_BASE 600000 +#define TPS80031_SMPS_VOLT_BASE_OFFSET 700000 + +#define TPS80031_LDO_VOLT_MASK 0x3F +#define TPS80031_LDO_VOLT_MAX_HEX 0x18 +#define TPS80031_LDO_VOLT_MIN_HEX 0x01 +#define TPS80031_LDO_VOLT_MAX 3360000 +#define TPS80031_LDO_VOLT_MIN 1018000 +#define TPS80031_LDO_VOLT_BASE 916000 + +#define TPS80031_PHOENIX_DEV_ON 0x25 +#define DEVOFF BIT(0) + +/* register groups */ +enum { + CTRL, + VOLT, + OFFSET, +}; + +#endif /* _TPS80031_H_ */

The driver provides regulator set/get voltage enable/disable functions for TI TPS80031/TPS80032 PMICs.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/tps80031_regulator.c | 296 +++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/power/regulator/tps80031_regulator.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 571debd54e..f297e408cd 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -355,6 +355,14 @@ config DM_REGULATOR_TPS62360 implements the get/set api for value only, as the power line is always on.
+config DM_REGULATOR_TPS80031 + bool "Enable driver for TPS80031/TPS80032 PMIC regulators" + depends on DM_PMIC_TPS80031 + ---help--- + This enables implementation of driver-model regulator uclass + features for TPS80031/TPS80032 PMICs. The driver implements + get/set api for: value and enable. + config DM_REGULATOR_STPMIC1 bool "Enable driver for STPMIC1 regulators" depends on DM_REGULATOR && PMIC_STPMIC1 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 8d73169b50..3ef55dc534 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_TPS80031) += tps80031_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o diff --git a/drivers/power/regulator/tps80031_regulator.c b/drivers/power/regulator/tps80031_regulator.c new file mode 100644 index 0000000000..56b6a7b684 --- /dev/null +++ b/drivers/power/regulator/tps80031_regulator.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps80031.h> + +static const char tps80031_smps_reg[][TPS80031_SMPS_NUM] = { + { 0x54, 0x5a, 0x66, 0x42, 0x48 }, + { 0x56, 0x5c, 0x68, 0x44, 0x4a }, + { BIT(3), BIT(4), BIT(6), BIT(0), BIT(1) }, +}; + +static const char tps80031_ldo_reg[][TPS80031_LDO_NUM] = { + { 0x9e, 0x86, 0x8e, 0x8a, 0x9a, 0x92, 0xa6, 0x96, 0xa2 }, + { 0x9f, 0x87, 0x8f, 0x8b, 0x9b, 0x93, 0xa7, 0x97, 0xa3 }, +}; + +static int tps80031_regulator_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + if (ret & TPS80031_REGULATOR_MODE_ON) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + ret &= ~(TPS80031_REGULATOR_STATUS_MASK); + + if (*enable) + ret |= TPS80031_REGULATOR_MODE_ON; + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int tps80031_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = tps80031_regulator_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int tps80031_set_enable(struct udevice *dev, bool enable) +{ + return tps80031_regulator_enable(dev, PMIC_OP_SET, &enable); +} + +static int tps80031_ldo_volt2hex(int uV) +{ + if (uV > TPS80031_LDO_VOLT_MAX) + return -EINVAL; + + if (uV < TPS80031_LDO_VOLT_MIN) + uV = TPS80031_LDO_VOLT_MIN; + + return DIV_ROUND_UP(uV - TPS80031_LDO_VOLT_BASE, 102000); +} + +static int tps80031_ldo_hex2volt(int hex) +{ + if (hex > TPS80031_LDO_VOLT_MAX_HEX) + return -EINVAL; + + if (hex < TPS80031_LDO_VOLT_MIN_HEX) + hex = TPS80031_LDO_VOLT_MIN_HEX; + + return TPS80031_LDO_VOLT_BASE + hex * 102000; +} + +static int tps80031_ldo_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int hex, ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = tps80031_ldo_hex2volt(ret & TPS80031_LDO_VOLT_MASK); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = tps80031_ldo_volt2hex(*uV); + if (hex < 0) + return hex; + + ret &= ~TPS80031_LDO_VOLT_MASK; + + return pmic_reg_write(dev->parent, adr, ret | hex); +} + +static int tps80031_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + + /* check for ldoln and ldousb cases */ + if (!strcmp("ldoln", dev->name)) { + uc_pdata->ctrl_reg = tps80031_ldo_reg[CTRL][7]; + uc_pdata->volt_reg = tps80031_ldo_reg[VOLT][7]; + return 0; + } + + if (!strcmp("ldousb", dev->name)) { + uc_pdata->ctrl_reg = tps80031_ldo_reg[CTRL][8]; + uc_pdata->volt_reg = tps80031_ldo_reg[VOLT][8]; + return 0; + } + + if (dev->driver_data > 0) { + u8 idx = dev->driver_data - 1; + + uc_pdata->ctrl_reg = tps80031_ldo_reg[CTRL][idx]; + uc_pdata->volt_reg = tps80031_ldo_reg[VOLT][idx]; + } + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps80031_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return tps80031_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps80031_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = tps80031_get_enable, + .set_enable = tps80031_set_enable, +}; + +U_BOOT_DRIVER(tps80031_ldo) = { + .name = TPS80031_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps80031_ldo_ops, + .probe = tps80031_ldo_probe, +}; + +static int tps80031_smps_volt2hex(u32 base, int uV) +{ + if (uV > TPS80031_LDO_VOLT_MAX) + return -EINVAL; + + if (uV < base) + return 1; + + return DIV_ROUND_UP(uV - base, 12500); +} + +static int tps80031_smps_hex2volt(u32 base, int hex) +{ + if (hex > TPS80031_LDO_VOLT_MAX_HEX) + return -EINVAL; + + if (!hex) + return 0; + + return base + hex * 12500; +} + +static int tps80031_smps_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int base, hex, ret; + + /* If offset flag was set then base voltage is higher */ + if (uc_pdata->flags & TPS80031_OFFSET_FLAG) + base = TPS80031_SMPS_VOLT_BASE_OFFSET; + else + base = TPS80031_SMPS_VOLT_BASE; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = tps80031_smps_hex2volt(base, ret & TPS80031_SMPS_VOLT_MASK); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = tps80031_smps_volt2hex(base, *uV); + if (hex < 0) + return hex; + + ret &= ~TPS80031_SMPS_VOLT_MASK; + + return pmic_reg_write(dev->parent, adr, ret | hex); +} + +static int tps80031_smps_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + int idx = dev->driver_data - 1; + int ret; + + uc_pdata->type = REGULATOR_TYPE_BUCK; + + uc_pdata->ctrl_reg = tps80031_smps_reg[CTRL][idx]; + uc_pdata->volt_reg = tps80031_smps_reg[VOLT][idx]; + + /* Determine if smps regulator uses higher voltage */ + ret = pmic_reg_read(dev->parent, TPS80031_SMPS_OFFSET); + if (ret & tps80031_smps_reg[OFFSET][idx]) + uc_pdata->flags |= TPS80031_OFFSET_FLAG; + + return 0; +} + +static int smps_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps80031_smps_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int smps_set_value(struct udevice *dev, int uV) +{ + return tps80031_smps_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps80031_smps_ops = { + .get_value = smps_get_value, + .set_value = smps_set_value, + .get_enable = tps80031_get_enable, + .set_enable = tps80031_set_enable, +}; + +U_BOOT_DRIVER(tps80031_smps) = { + .name = TPS80031_SMPS_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps80031_smps_ops, + .probe = tps80031_smps_probe, +};

Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- doc/device-tree-bindings/pmic/tps65911.txt | 78 ++++++++++++++++++++++ drivers/power/pmic/pmic_tps65910_dm.c | 49 +++++++++++++- include/power/tps65910_pmic.h | 52 +++++++++++++++ 3 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 doc/device-tree-bindings/pmic/tps65911.txt
diff --git a/doc/device-tree-bindings/pmic/tps65911.txt b/doc/device-tree-bindings/pmic/tps65911.txt new file mode 100644 index 0000000000..29270efbfe --- /dev/null +++ b/doc/device-tree-bindings/pmic/tps65911.txt @@ -0,0 +1,78 @@ +Texas Instruments, TPS65911 PMIC + +This device uses two drivers: +- drivers/power/pmic/tps65910.c (for parent device) +- drivers/power/regulator/tps65911_regulator.c (for child regulators) + +This chapter describes the binding info for the PMIC driver and regulators. + +Required properties for PMIC: +- compatible: "ti,tps65911" +- reg: 0x2d + +With those two properties, the pmic device can be used for read/write only. +To bind each regulator, the optional regulators subnode should exists. + +Optional subnode: +- name: regulators (subnode list of each device's regulator) + +Regulators subnode contains set on supported regulators. + +Required properties: +- regulator-name: used for regulator uclass platform data '.name', + +List of supported regulator nodes names for tps65911: +- vdd1, vdd2, vddctrl, vddio +- ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8 + +vddio in datasheet is referred as vio, but for reduction of code and +unification of smps regulators it is named vddio. + +Optional: +- regulator-min-microvolt: minimum allowed Voltage to set +- regulator-max-microvolt: minimum allowed Voltage to set +- regulator-always-on: regulator should be never disabled +- regulator-boot-on: regulator should be enabled by the bootloader + +Example: + +tps65911@2d { + compatible = "ti,tps65911"; + reg = <0x2d>; + + regulators { + vdd1 { + regulator-name = "vdd_1v2_backlight"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + ... + + vddio { + regulator-name = "vdd_1v8_gen"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo1 { + regulator-name = "vdd_emmc_core"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + ... + + ldo8 { + regulator-name = "vdd_ddr_hs"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + }; + }; +}; diff --git a/drivers/power/pmic/pmic_tps65910_dm.c b/drivers/power/pmic/pmic_tps65910_dm.c index e03ddc98d7..770010d53d 100644 --- a/drivers/power/pmic/pmic_tps65910_dm.c +++ b/drivers/power/pmic/pmic_tps65910_dm.c @@ -11,13 +11,19 @@ #include <power/regulator.h> #include <power/tps65910_pmic.h>
-static const struct pmic_child_info pmic_children_info[] = { +static const struct pmic_child_info tps65910_children_info[] = { { .prefix = "ldo_", .driver = TPS65910_LDO_DRIVER }, { .prefix = "buck_", .driver = TPS65910_BUCK_DRIVER }, { .prefix = "boost_", .driver = TPS65910_BOOST_DRIVER }, { }, };
+static const struct pmic_child_info tps65911_children_info[] = { + { .prefix = "ldo", .driver = TPS65911_LDO_DRIVER }, + { .prefix = "vdd", .driver = TPS65911_VDD_DRIVER }, + { }, +}; + static int pmic_tps65910_reg_count(struct udevice *dev) { return TPS65910_NUM_REGS; @@ -47,10 +53,29 @@ static int pmic_tps65910_read(struct udevice *dev, uint reg, u8 *buffer, return ret; }
+static int pmic_tps65910_poweroff(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev, TPS65910_REG_DEVICE_CTRL); + if (ret < 0) + return ret; + + ret |= DEVCTRL_PWR_OFF_MASK; + + pmic_reg_write(dev, TPS65910_REG_DEVICE_CTRL, ret); + + ret |= DEVCTRL_DEV_OFF_MASK; + ret &= ~DEVCTRL_DEV_ON_MASK; + + return pmic_reg_write(dev, TPS65910_REG_DEVICE_CTRL, ret); +} + static int pmic_tps65910_bind(struct udevice *dev) { ofnode regulators_node; int children; + int type = dev_get_driver_data(dev);
regulators_node = dev_read_subnode(dev, "regulators"); if (!ofnode_valid(regulators_node)) { @@ -58,7 +83,19 @@ static int pmic_tps65910_bind(struct udevice *dev) return -EINVAL; }
- children = pmic_bind_children(dev, regulators_node, pmic_children_info); + switch (type) { + case TPS65910: + children = pmic_bind_children(dev, regulators_node, + tps65910_children_info); + break; + case TPS65911: + children = pmic_bind_children(dev, regulators_node, + tps65911_children_info); + break; + default: + log_err("unknown PMIC type\n"); + } + if (!children) debug("%s has no children (regulators)\n", dev->name);
@@ -67,6 +104,10 @@ static int pmic_tps65910_bind(struct udevice *dev)
static int pmic_tps65910_probe(struct udevice *dev) { + struct uc_pmic_priv *priv = dev_get_uclass_priv(dev); + + priv->sys_pow_ctrl = dev_read_bool(dev, "ti,system-power-controller"); + /* use I2C control interface instead of I2C smartreflex interface to * access smartrefelex registers VDD1_OP_REG, VDD1_SR_REG, VDD2_OP_REG * and VDD2_SR_REG @@ -76,13 +117,15 @@ static int pmic_tps65910_probe(struct udevice *dev) }
static struct dm_pmic_ops pmic_tps65910_ops = { + .poweroff = pmic_tps65910_poweroff, .reg_count = pmic_tps65910_reg_count, .read = pmic_tps65910_read, .write = pmic_tps65910_write, };
static const struct udevice_id pmic_tps65910_match[] = { - { .compatible = "ti,tps65910" }, + { .compatible = "ti,tps65910", .data = TPS65910 }, + { .compatible = "ti,tps65911", .data = TPS65911 }, { /* sentinel */ } };
diff --git a/include/power/tps65910_pmic.h b/include/power/tps65910_pmic.h index 66214786d3..282863805a 100644 --- a/include/power/tps65910_pmic.h +++ b/include/power/tps65910_pmic.h @@ -6,6 +6,9 @@ #ifndef __TPS65910_PMIC_H_ #define __TPS65910_PMIC_H_
+#define TPS65910 1 +#define TPS65911 2 + #define TPS65910_I2C_SEL_MASK (0x1 << 4) #define TPS65910_VDD_SR_MASK (0x1 << 7) #define TPS65910_GAIN_SEL_MASK (0x3 << 6) @@ -17,6 +20,11 @@ #define TPS65910_SUPPLY_STATE_OFF 0x0 #define TPS65910_SUPPLY_STATE_ON 0x1
+/* TPS65910 DEVICE_CTRL bits */ +#define DEVCTRL_PWR_OFF_MASK BIT(7) +#define DEVCTRL_DEV_ON_MASK BIT(2) +#define DEVCTRL_DEV_OFF_MASK BIT(0) + /* i2c registers */ enum { TPS65910_REG_RTC_SEC = 0x00, @@ -126,4 +134,48 @@ struct tps65910_regulator_pdata { #define TPS65910_BOOST_DRIVER "tps65910_boost" #define TPS65910_LDO_DRIVER "tps65910_ldo"
+/* tps65911 i2c registers */ +enum { + TPS65911_REG_VIO = 0x20, + TPS65911_REG_VDD1, + TPS65911_REG_VDD1_OP, + TPS65911_REG_VDD1_SR, + TPS65911_REG_VDD2, + TPS65911_REG_VDD2_OP, + TPS65911_REG_VDD2_SR, + TPS65911_REG_VDDCTRL, + TPS65911_REG_VDDCTRL_OP, + TPS65911_REG_VDDCTRL_SR, + TPS65911_REG_LDO1 = 0x30, + TPS65911_REG_LDO2, + TPS65911_REG_LDO5, + TPS65911_REG_LDO8, + TPS65911_REG_LDO7, + TPS65911_REG_LDO6, + TPS65911_REG_LDO4, + TPS65911_REG_LDO3, +}; + +#define TPS65911_VDD_NUM 4 +#define TPS65911_LDO_NUM 8 + +#define TPS65911_VDD_VOLT_MAX 1500000 +#define TPS65911_VDD_VOLT_MIN 600000 +#define TPS65911_VDD_VOLT_BASE 562500 + +#define TPS65911_LDO_VOLT_MAX 3300000 +#define TPS65911_LDO_VOLT_BASE 800000 + +#define TPS65911_LDO_SEL_MASK (0x3f << 2) + +#define TPS65911_LDO124_VOLT_MAX_HEX 0x32 +#define TPS65911_LDO358_VOLT_MAX_HEX 0x19 +#define TPS65911_LDO358_VOLT_MIN_HEX 0x02 + +#define TPS65911_LDO124_VOLT_STEP 50000 +#define TPS65911_LDO358_VOLT_STEP 100000 + +#define TPS65911_VDD_DRIVER "tps65911_vdd" +#define TPS65911_LDO_DRIVER "tps65911_ldo" + #endif /* __TPS65910_PMIC_H_ */

On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
doc/device-tree-bindings/pmic/tps65911.txt | 78 ++++++++++++++++++++++ drivers/power/pmic/pmic_tps65910_dm.c | 49 +++++++++++++- include/power/tps65910_pmic.h | 52 +++++++++++++++ 3 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 doc/device-tree-bindings/pmic/tps65911.txt
Reviewed-by: Simon Glass sjg@chromium.org
nits below
[..]
diff --git a/drivers/power/pmic/pmic_tps65910_dm.c b/drivers/power/pmic/pmic_tps65910_dm.c index e03ddc98d7..770010d53d 100644 --- a/drivers/power/pmic/pmic_tps65910_dm.c +++ b/drivers/power/pmic/pmic_tps65910_dm.c @@ -11,13 +11,19 @@ #include <power/regulator.h> #include <power/tps65910_pmic.h>
-static const struct pmic_child_info pmic_children_info[] = { +static const struct pmic_child_info tps65910_children_info[] = { { .prefix = "ldo_", .driver = TPS65910_LDO_DRIVER }, { .prefix = "buck_", .driver = TPS65910_BUCK_DRIVER }, { .prefix = "boost_", .driver = TPS65910_BOOST_DRIVER }, { }, };
+static const struct pmic_child_info tps65911_children_info[] = {
{ .prefix = "ldo", .driver = TPS65911_LDO_DRIVER },
{ .prefix = "vdd", .driver = TPS65911_VDD_DRIVER },
{ },
+};
static int pmic_tps65910_reg_count(struct udevice *dev) { return TPS65910_NUM_REGS; @@ -47,10 +53,29 @@ static int pmic_tps65910_read(struct udevice *dev, uint reg, u8 *buffer, return ret; }
+static int pmic_tps65910_poweroff(struct udevice *dev) +{
int ret;
ret = pmic_reg_read(dev, TPS65910_REG_DEVICE_CTRL);
if (ret < 0)
return ret;
ret |= DEVCTRL_PWR_OFF_MASK;
Please use a separate var for this. We use ret for return values, as you have above.
pmic_reg_write(dev, TPS65910_REG_DEVICE_CTRL, ret);
ret |= DEVCTRL_DEV_OFF_MASK;
ret &= ~DEVCTRL_DEV_ON_MASK;
return pmic_reg_write(dev, TPS65910_REG_DEVICE_CTRL, ret);
+}
static int pmic_tps65910_bind(struct udevice *dev) { ofnode regulators_node; int children;
int type = dev_get_driver_data(dev); regulators_node = dev_read_subnode(dev, "regulators"); if (!ofnode_valid(regulators_node)) {
@@ -58,7 +83,19 @@ static int pmic_tps65910_bind(struct udevice *dev) return -EINVAL; }
children = pmic_bind_children(dev, regulators_node, pmic_children_info);
switch (type) {
Why not always binding them?
case TPS65910:
children = pmic_bind_children(dev, regulators_node,
tps65910_children_info);
break;
case TPS65911:
children = pmic_bind_children(dev, regulators_node,
tps65911_children_info);
break;
default:
log_err("unknown PMIC type\n");
}
if (!children) debug("%s has no children (regulators)\n", dev->name);
@@ -67,6 +104,10 @@ static int pmic_tps65910_bind(struct udevice *dev)
static int pmic_tps65910_probe(struct udevice *dev) {
struct uc_pmic_priv *priv = dev_get_uclass_priv(dev);
priv->sys_pow_ctrl = dev_read_bool(dev, "ti,system-power-controller");
/* use I2C control interface instead of I2C smartreflex interface to * access smartrefelex registers VDD1_OP_REG, VDD1_SR_REG, VDD2_OP_REG * and VDD2_SR_REG
@@ -76,13 +117,15 @@ static int pmic_tps65910_probe(struct udevice *dev) }
static struct dm_pmic_ops pmic_tps65910_ops = {
.poweroff = pmic_tps65910_poweroff, .reg_count = pmic_tps65910_reg_count, .read = pmic_tps65910_read, .write = pmic_tps65910_write,
};
static const struct udevice_id pmic_tps65910_match[] = {
{ .compatible = "ti,tps65910" },
{ .compatible = "ti,tps65910", .data = TPS65910 },
{ .compatible = "ti,tps65911", .data = TPS65911 }, { /* sentinel */ }
};
diff --git a/include/power/tps65910_pmic.h b/include/power/tps65910_pmic.h index 66214786d3..282863805a 100644 --- a/include/power/tps65910_pmic.h +++ b/include/power/tps65910_pmic.h @@ -6,6 +6,9 @@ #ifndef __TPS65910_PMIC_H_ #define __TPS65910_PMIC_H_
+#define TPS65910 1 +#define TPS65911 2
#define TPS65910_I2C_SEL_MASK (0x1 << 4) #define TPS65910_VDD_SR_MASK (0x1 << 7) #define TPS65910_GAIN_SEL_MASK (0x3 << 6) @@ -17,6 +20,11 @@ #define TPS65910_SUPPLY_STATE_OFF 0x0 #define TPS65910_SUPPLY_STATE_ON 0x1
+/* TPS65910 DEVICE_CTRL bits */ +#define DEVCTRL_PWR_OFF_MASK BIT(7) +#define DEVCTRL_DEV_ON_MASK BIT(2) +#define DEVCTRL_DEV_OFF_MASK BIT(0)
/* i2c registers */ enum { TPS65910_REG_RTC_SEC = 0x00, @@ -126,4 +134,48 @@ struct tps65910_regulator_pdata { #define TPS65910_BOOST_DRIVER "tps65910_boost" #define TPS65910_LDO_DRIVER "tps65910_ldo"
+/* tps65911 i2c registers */ +enum {
TPS65911_REG_VIO = 0x20,
TPS65911_REG_VDD1,
TPS65911_REG_VDD1_OP,
TPS65911_REG_VDD1_SR,
TPS65911_REG_VDD2,
TPS65911_REG_VDD2_OP,
TPS65911_REG_VDD2_SR,
TPS65911_REG_VDDCTRL,
TPS65911_REG_VDDCTRL_OP,
TPS65911_REG_VDDCTRL_SR,
TPS65911_REG_LDO1 = 0x30,
TPS65911_REG_LDO2,
TPS65911_REG_LDO5,
TPS65911_REG_LDO8,
TPS65911_REG_LDO7,
TPS65911_REG_LDO6,
TPS65911_REG_LDO4,
TPS65911_REG_LDO3,
+};
+#define TPS65911_VDD_NUM 4 +#define TPS65911_LDO_NUM 8
+#define TPS65911_VDD_VOLT_MAX 1500000 +#define TPS65911_VDD_VOLT_MIN 600000 +#define TPS65911_VDD_VOLT_BASE 562500
+#define TPS65911_LDO_VOLT_MAX 3300000 +#define TPS65911_LDO_VOLT_BASE 800000
+#define TPS65911_LDO_SEL_MASK (0x3f << 2)
+#define TPS65911_LDO124_VOLT_MAX_HEX 0x32 +#define TPS65911_LDO358_VOLT_MAX_HEX 0x19 +#define TPS65911_LDO358_VOLT_MIN_HEX 0x02
+#define TPS65911_LDO124_VOLT_STEP 50000 +#define TPS65911_LDO358_VOLT_STEP 100000
+#define TPS65911_VDD_DRIVER "tps65911_vdd" +#define TPS65911_LDO_DRIVER "tps65911_ldo"
Drop the TPS65911 prefixes if you can...this is only included in one file i think.
#endif /* __TPS65910_PMIC_H_ */
2.39.2
Regards, Simon

чт, 20 лип. 2023 р. о 22:43 Simon Glass sjg@chromium.org пише:
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
doc/device-tree-bindings/pmic/tps65911.txt | 78 ++++++++++++++++++++++ drivers/power/pmic/pmic_tps65910_dm.c | 49 +++++++++++++- include/power/tps65910_pmic.h | 52 +++++++++++++++ 3 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 doc/device-tree-bindings/pmic/tps65911.txt
Reviewed-by: Simon Glass sjg@chromium.org
nits below
[..]
diff --git a/drivers/power/pmic/pmic_tps65910_dm.c b/drivers/power/pmic/pmic_tps65910_dm.c index e03ddc98d7..770010d53d 100644 --- a/drivers/power/pmic/pmic_tps65910_dm.c +++ b/drivers/power/pmic/pmic_tps65910_dm.c @@ -11,13 +11,19 @@ #include <power/regulator.h> #include <power/tps65910_pmic.h>
-static const struct pmic_child_info pmic_children_info[] = { +static const struct pmic_child_info tps65910_children_info[] = { { .prefix = "ldo_", .driver = TPS65910_LDO_DRIVER }, { .prefix = "buck_", .driver = TPS65910_BUCK_DRIVER }, { .prefix = "boost_", .driver = TPS65910_BOOST_DRIVER }, { }, };
+static const struct pmic_child_info tps65911_children_info[] = {
{ .prefix = "ldo", .driver = TPS65911_LDO_DRIVER },
{ .prefix = "vdd", .driver = TPS65911_VDD_DRIVER },
{ },
+};
static int pmic_tps65910_reg_count(struct udevice *dev) { return TPS65910_NUM_REGS; @@ -47,10 +53,29 @@ static int pmic_tps65910_read(struct udevice *dev, uint reg, u8 *buffer, return ret; }
+static int pmic_tps65910_poweroff(struct udevice *dev) +{
int ret;
ret = pmic_reg_read(dev, TPS65910_REG_DEVICE_CTRL);
if (ret < 0)
return ret;
ret |= DEVCTRL_PWR_OFF_MASK;
Please use a separate var for this. We use ret for return values, as you have above.
pmic_reg_write(dev, TPS65910_REG_DEVICE_CTRL, ret);
ret |= DEVCTRL_DEV_OFF_MASK;
ret &= ~DEVCTRL_DEV_ON_MASK;
return pmic_reg_write(dev, TPS65910_REG_DEVICE_CTRL, ret);
+}
static int pmic_tps65910_bind(struct udevice *dev) { ofnode regulators_node; int children;
int type = dev_get_driver_data(dev); regulators_node = dev_read_subnode(dev, "regulators"); if (!ofnode_valid(regulators_node)) {
@@ -58,7 +83,19 @@ static int pmic_tps65910_bind(struct udevice *dev) return -EINVAL; }
children = pmic_bind_children(dev, regulators_node, pmic_children_info);
switch (type) {
Why not always binding them?
They may not be enabled, each pmic has its own regulator driver and they can be enabled separately. Not sure if binding without enabled driver is permitted so I decided to preemptively separate them to avoid any issues.
case TPS65910:
children = pmic_bind_children(dev, regulators_node,
tps65910_children_info);
break;
case TPS65911:
children = pmic_bind_children(dev, regulators_node,
tps65911_children_info);
break;
default:
log_err("unknown PMIC type\n");
}
if (!children) debug("%s has no children (regulators)\n", dev->name);
@@ -67,6 +104,10 @@ static int pmic_tps65910_bind(struct udevice *dev)
static int pmic_tps65910_probe(struct udevice *dev) {
struct uc_pmic_priv *priv = dev_get_uclass_priv(dev);
priv->sys_pow_ctrl = dev_read_bool(dev, "ti,system-power-controller");
/* use I2C control interface instead of I2C smartreflex interface to * access smartrefelex registers VDD1_OP_REG, VDD1_SR_REG, VDD2_OP_REG * and VDD2_SR_REG
@@ -76,13 +117,15 @@ static int pmic_tps65910_probe(struct udevice *dev) }
static struct dm_pmic_ops pmic_tps65910_ops = {
.poweroff = pmic_tps65910_poweroff, .reg_count = pmic_tps65910_reg_count, .read = pmic_tps65910_read, .write = pmic_tps65910_write,
};
static const struct udevice_id pmic_tps65910_match[] = {
{ .compatible = "ti,tps65910" },
{ .compatible = "ti,tps65910", .data = TPS65910 },
{ .compatible = "ti,tps65911", .data = TPS65911 }, { /* sentinel */ }
};
diff --git a/include/power/tps65910_pmic.h b/include/power/tps65910_pmic.h index 66214786d3..282863805a 100644 --- a/include/power/tps65910_pmic.h +++ b/include/power/tps65910_pmic.h @@ -6,6 +6,9 @@ #ifndef __TPS65910_PMIC_H_ #define __TPS65910_PMIC_H_
+#define TPS65910 1 +#define TPS65911 2
#define TPS65910_I2C_SEL_MASK (0x1 << 4) #define TPS65910_VDD_SR_MASK (0x1 << 7) #define TPS65910_GAIN_SEL_MASK (0x3 << 6) @@ -17,6 +20,11 @@ #define TPS65910_SUPPLY_STATE_OFF 0x0 #define TPS65910_SUPPLY_STATE_ON 0x1
+/* TPS65910 DEVICE_CTRL bits */ +#define DEVCTRL_PWR_OFF_MASK BIT(7) +#define DEVCTRL_DEV_ON_MASK BIT(2) +#define DEVCTRL_DEV_OFF_MASK BIT(0)
/* i2c registers */ enum { TPS65910_REG_RTC_SEC = 0x00, @@ -126,4 +134,48 @@ struct tps65910_regulator_pdata { #define TPS65910_BOOST_DRIVER "tps65910_boost" #define TPS65910_LDO_DRIVER "tps65910_ldo"
+/* tps65911 i2c registers */ +enum {
TPS65911_REG_VIO = 0x20,
TPS65911_REG_VDD1,
TPS65911_REG_VDD1_OP,
TPS65911_REG_VDD1_SR,
TPS65911_REG_VDD2,
TPS65911_REG_VDD2_OP,
TPS65911_REG_VDD2_SR,
TPS65911_REG_VDDCTRL,
TPS65911_REG_VDDCTRL_OP,
TPS65911_REG_VDDCTRL_SR,
TPS65911_REG_LDO1 = 0x30,
TPS65911_REG_LDO2,
TPS65911_REG_LDO5,
TPS65911_REG_LDO8,
TPS65911_REG_LDO7,
TPS65911_REG_LDO6,
TPS65911_REG_LDO4,
TPS65911_REG_LDO3,
+};
+#define TPS65911_VDD_NUM 4 +#define TPS65911_LDO_NUM 8
+#define TPS65911_VDD_VOLT_MAX 1500000 +#define TPS65911_VDD_VOLT_MIN 600000 +#define TPS65911_VDD_VOLT_BASE 562500
+#define TPS65911_LDO_VOLT_MAX 3300000 +#define TPS65911_LDO_VOLT_BASE 800000
+#define TPS65911_LDO_SEL_MASK (0x3f << 2)
+#define TPS65911_LDO124_VOLT_MAX_HEX 0x32 +#define TPS65911_LDO358_VOLT_MAX_HEX 0x19 +#define TPS65911_LDO358_VOLT_MIN_HEX 0x02
+#define TPS65911_LDO124_VOLT_STEP 50000 +#define TPS65911_LDO358_VOLT_STEP 100000
+#define TPS65911_VDD_DRIVER "tps65911_vdd" +#define TPS65911_LDO_DRIVER "tps65911_ldo"
Drop the TPS65911 prefixes if you can...this is only included in one file i think.
TPS65911 prefixes are included in tps65910 pmic driver and tps65911 regulator driver respectively.
#endif /* __TPS65910_PMIC_H_ */
2.39.2
Regards, Simon

чт, 20 лип. 2023 р. о 22:43 Simon Glass sjg@chromium.org пише:
On Thu, 20 Jul 2023 at 02:48, Svyatoslav Ryhel clamor95@gmail.com wrote:
Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
doc/device-tree-bindings/pmic/tps65911.txt | 78 ++++++++++++++++++++++ drivers/power/pmic/pmic_tps65910_dm.c | 49 +++++++++++++- include/power/tps65910_pmic.h | 52 +++++++++++++++ 3 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 doc/device-tree-bindings/pmic/tps65911.txt
Reviewed-by: Simon Glass sjg@chromium.org
nits below
[..]
diff --git a/drivers/power/pmic/pmic_tps65910_dm.c b/drivers/power/pmic/pmic_tps65910_dm.c index e03ddc98d7..770010d53d 100644 --- a/drivers/power/pmic/pmic_tps65910_dm.c +++ b/drivers/power/pmic/pmic_tps65910_dm.c @@ -11,13 +11,19 @@ #include <power/regulator.h> #include <power/tps65910_pmic.h>
-static const struct pmic_child_info pmic_children_info[] = { +static const struct pmic_child_info tps65910_children_info[] = { { .prefix = "ldo_", .driver = TPS65910_LDO_DRIVER }, { .prefix = "buck_", .driver = TPS65910_BUCK_DRIVER }, { .prefix = "boost_", .driver = TPS65910_BOOST_DRIVER }, { }, };
+static const struct pmic_child_info tps65911_children_info[] = {
{ .prefix = "ldo", .driver = TPS65911_LDO_DRIVER },
{ .prefix = "vdd", .driver = TPS65911_VDD_DRIVER },
{ },
+};
static int pmic_tps65910_reg_count(struct udevice *dev) { return TPS65910_NUM_REGS; @@ -47,10 +53,29 @@ static int pmic_tps65910_read(struct udevice *dev, uint reg, u8 *buffer, return ret; }
+static int pmic_tps65910_poweroff(struct udevice *dev) +{
int ret;
ret = pmic_reg_read(dev, TPS65910_REG_DEVICE_CTRL);
if (ret < 0)
return ret;
ret |= DEVCTRL_PWR_OFF_MASK;
Please use a separate var for this. We use ret for return values, as you have above.
I have used ret here because pmic_reg_read returns either value of reg or -errno, I need to modify the value got from TPS65910_REG_DEVICE_CTRL and not to introduce new variable and perform unnecessary reassignments I continued with ret.
pmic_reg_write(dev, TPS65910_REG_DEVICE_CTRL, ret);
ret |= DEVCTRL_DEV_OFF_MASK;
ret &= ~DEVCTRL_DEV_ON_MASK;
return pmic_reg_write(dev, TPS65910_REG_DEVICE_CTRL, ret);
+}
static int pmic_tps65910_bind(struct udevice *dev) { ofnode regulators_node; int children;
int type = dev_get_driver_data(dev); regulators_node = dev_read_subnode(dev, "regulators"); if (!ofnode_valid(regulators_node)) {
@@ -58,7 +83,19 @@ static int pmic_tps65910_bind(struct udevice *dev) return -EINVAL; }
children = pmic_bind_children(dev, regulators_node, pmic_children_info);
switch (type) {
Why not always binding them?
case TPS65910:
children = pmic_bind_children(dev, regulators_node,
tps65910_children_info);
break;
case TPS65911:
children = pmic_bind_children(dev, regulators_node,
tps65911_children_info);
break;
default:
log_err("unknown PMIC type\n");
}
if (!children) debug("%s has no children (regulators)\n", dev->name);
@@ -67,6 +104,10 @@ static int pmic_tps65910_bind(struct udevice *dev)
static int pmic_tps65910_probe(struct udevice *dev) {
struct uc_pmic_priv *priv = dev_get_uclass_priv(dev);
priv->sys_pow_ctrl = dev_read_bool(dev, "ti,system-power-controller");
/* use I2C control interface instead of I2C smartreflex interface to * access smartrefelex registers VDD1_OP_REG, VDD1_SR_REG, VDD2_OP_REG * and VDD2_SR_REG
@@ -76,13 +117,15 @@ static int pmic_tps65910_probe(struct udevice *dev) }
static struct dm_pmic_ops pmic_tps65910_ops = {
.poweroff = pmic_tps65910_poweroff, .reg_count = pmic_tps65910_reg_count, .read = pmic_tps65910_read, .write = pmic_tps65910_write,
};
static const struct udevice_id pmic_tps65910_match[] = {
{ .compatible = "ti,tps65910" },
{ .compatible = "ti,tps65910", .data = TPS65910 },
{ .compatible = "ti,tps65911", .data = TPS65911 }, { /* sentinel */ }
};
diff --git a/include/power/tps65910_pmic.h b/include/power/tps65910_pmic.h index 66214786d3..282863805a 100644 --- a/include/power/tps65910_pmic.h +++ b/include/power/tps65910_pmic.h @@ -6,6 +6,9 @@ #ifndef __TPS65910_PMIC_H_ #define __TPS65910_PMIC_H_
+#define TPS65910 1 +#define TPS65911 2
#define TPS65910_I2C_SEL_MASK (0x1 << 4) #define TPS65910_VDD_SR_MASK (0x1 << 7) #define TPS65910_GAIN_SEL_MASK (0x3 << 6) @@ -17,6 +20,11 @@ #define TPS65910_SUPPLY_STATE_OFF 0x0 #define TPS65910_SUPPLY_STATE_ON 0x1
+/* TPS65910 DEVICE_CTRL bits */ +#define DEVCTRL_PWR_OFF_MASK BIT(7) +#define DEVCTRL_DEV_ON_MASK BIT(2) +#define DEVCTRL_DEV_OFF_MASK BIT(0)
/* i2c registers */ enum { TPS65910_REG_RTC_SEC = 0x00, @@ -126,4 +134,48 @@ struct tps65910_regulator_pdata { #define TPS65910_BOOST_DRIVER "tps65910_boost" #define TPS65910_LDO_DRIVER "tps65910_ldo"
+/* tps65911 i2c registers */ +enum {
TPS65911_REG_VIO = 0x20,
TPS65911_REG_VDD1,
TPS65911_REG_VDD1_OP,
TPS65911_REG_VDD1_SR,
TPS65911_REG_VDD2,
TPS65911_REG_VDD2_OP,
TPS65911_REG_VDD2_SR,
TPS65911_REG_VDDCTRL,
TPS65911_REG_VDDCTRL_OP,
TPS65911_REG_VDDCTRL_SR,
TPS65911_REG_LDO1 = 0x30,
TPS65911_REG_LDO2,
TPS65911_REG_LDO5,
TPS65911_REG_LDO8,
TPS65911_REG_LDO7,
TPS65911_REG_LDO6,
TPS65911_REG_LDO4,
TPS65911_REG_LDO3,
+};
+#define TPS65911_VDD_NUM 4 +#define TPS65911_LDO_NUM 8
+#define TPS65911_VDD_VOLT_MAX 1500000 +#define TPS65911_VDD_VOLT_MIN 600000 +#define TPS65911_VDD_VOLT_BASE 562500
+#define TPS65911_LDO_VOLT_MAX 3300000 +#define TPS65911_LDO_VOLT_BASE 800000
+#define TPS65911_LDO_SEL_MASK (0x3f << 2)
+#define TPS65911_LDO124_VOLT_MAX_HEX 0x32 +#define TPS65911_LDO358_VOLT_MAX_HEX 0x19 +#define TPS65911_LDO358_VOLT_MIN_HEX 0x02
+#define TPS65911_LDO124_VOLT_STEP 50000 +#define TPS65911_LDO358_VOLT_STEP 100000
+#define TPS65911_VDD_DRIVER "tps65911_vdd" +#define TPS65911_LDO_DRIVER "tps65911_ldo"
Drop the TPS65911 prefixes if you can...this is only included in one file i think.
#endif /* __TPS65910_PMIC_H_ */
2.39.2
Regards, Simon

The driver provides regulator set/get voltage enable/disable functions for TI TPS5911 PMIC.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/tps65911_regulator.c | 377 +++++++++++++++++++ 3 files changed, 386 insertions(+) create mode 100644 drivers/power/regulator/tps65911_regulator.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index f297e408cd..3398f0fbee 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -345,6 +345,14 @@ config DM_REGULATOR_TPS65910 regulator types of the TPS65910 (BUCK, BOOST and LDO). It implements the get/set api for value and enable.
+config DM_REGULATOR_TPS65911 + bool "Enable driver for TPS65911 PMIC regulators" + depends on DM_PMIC_TPS65910 + ---help--- + The TPS65911 PMIC provides 4 SMPSs and 8 LDOs. This driver supports all + regulator types of the TPS65911. It implements the get/set api for value + and enable. + config DM_REGULATOR_TPS62360 bool "Enable driver for TPS6236x Power Regulator" depends on DM_REGULATOR diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 3ef55dc534..c2ab1b7721 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o +obj-$(CONFIG_DM_REGULATOR_TPS65911) += tps65911_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_TPS80031) += tps80031_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o diff --git a/drivers/power/regulator/tps65911_regulator.c b/drivers/power/regulator/tps65911_regulator.c new file mode 100644 index 0000000000..f1b3a8295e --- /dev/null +++ b/drivers/power/regulator/tps65911_regulator.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps65910_pmic.h> + +/* fist row is control registers, second is voltage registers */ +static const char tps65911_vdd_reg[][TPS65911_VDD_NUM] = { + { TPS65911_REG_VDD1, TPS65911_REG_VDD2, + TPS65911_REG_VDDCTRL, TPS65911_REG_VIO }, + { TPS65911_REG_VDD1_OP, TPS65911_REG_VDD2_OP, + TPS65911_REG_VDDCTRL_OP, 0x00 }, +}; + +static const u32 tps65911_vio_range[] = { + 1500000, 1800000, 2500000, 3300000 +}; + +static const char tps65911_ldo_reg[TPS65911_LDO_NUM] = { + TPS65911_REG_LDO1, TPS65911_REG_LDO2, TPS65911_REG_LDO3, + TPS65911_REG_LDO4, TPS65911_REG_LDO5, TPS65911_REG_LDO6, + TPS65911_REG_LDO7, TPS65911_REG_LDO8 +}; + +static int tps65911_regulator_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + if (ret & TPS65910_SUPPLY_STATE_ON) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + ret &= ~(TPS65910_SUPPLY_STATE_MASK); + + if (*enable) + ret |= TPS65910_SUPPLY_STATE_ON; + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int tps65911_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = tps65911_regulator_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int tps65911_set_enable(struct udevice *dev, bool enable) +{ + return tps65911_regulator_enable(dev, PMIC_OP_SET, &enable); +} + +static int tps65911_vdd_volt2hex(int uV) +{ + if (uV > TPS65911_VDD_VOLT_MAX) + return -EINVAL; + + if (uV < TPS65911_VDD_VOLT_MIN) + uV = TPS65911_VDD_VOLT_MIN; + + return (uV - TPS65911_VDD_VOLT_BASE) / 12500; +} + +static int tps65911_vdd_hex2volt(int hex) +{ + if (hex > TPS65910_VDD_SEL_MAX) + return -EINVAL; + + if (hex < TPS65910_VDD_SEL_MIN) + hex = TPS65910_VDD_SEL_MIN; + + return TPS65911_VDD_VOLT_BASE + hex * 12500; +} + +static int tps65911_vio_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret &= TPS65910_SEL_MASK; + + *uV = tps65911_vio_range[ret >> 2]; + + return 0; + } + + ret &= ~TPS65910_SEL_MASK; + + /* vio has only 4 voltages available */ + switch (*uV) { + case 1500000: + ret |= 0x00 << 2; + break; + case 1800000: + ret |= 0x01 << 2; + break; + case 2500000: + ret |= 0x02 << 2; + break; + case 3300000: + ret |= 0x03 << 2; + break; + default: + return -EINVAL; + }; + + return pmic_reg_write(dev->parent, adr, ret); +} + +static int tps65911_vdd_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int ret; + + /* in case vdd is vio */ + if (!adr) + return tps65911_vio_val(dev, op, uV); + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = tps65911_vdd_hex2volt(ret); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + ret = tps65911_vdd_volt2hex(*uV); + if (ret < 0) + return ret; + + return pmic_reg_write(dev->parent, adr, ret); +} + +static int tps65911_vdd_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + + /* check for vddctrl and vddio cases */ + if (!strcmp("vddctrl", dev->name)) { + uc_pdata->ctrl_reg = tps65911_vdd_reg[0][2]; + uc_pdata->volt_reg = tps65911_vdd_reg[1][2]; + return 0; + } + + if (!strcmp("vddio", dev->name)) { + uc_pdata->ctrl_reg = tps65911_vdd_reg[0][3]; + uc_pdata->volt_reg = tps65911_vdd_reg[1][3]; + return 0; + } + + if (dev->driver_data > 0) { + u8 idx = dev->driver_data - 1; + + uc_pdata->ctrl_reg = tps65911_vdd_reg[0][idx]; + uc_pdata->volt_reg = tps65911_vdd_reg[1][idx]; + } + + return 0; +} + +static int vdd_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps65911_vdd_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int vdd_set_value(struct udevice *dev, int uV) +{ + return tps65911_vdd_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps65911_vdd_ops = { + .get_value = vdd_get_value, + .set_value = vdd_set_value, + .get_enable = tps65911_get_enable, + .set_enable = tps65911_set_enable, +}; + +U_BOOT_DRIVER(tps65911_vdd) = { + .name = TPS65911_VDD_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65911_vdd_ops, + .probe = tps65911_vdd_probe, +}; + +static int tps65911_ldo_volt2hex(int id, int uV) +{ + int step; + + if (uV > TPS65911_LDO_VOLT_MAX) + return -EINVAL; + + if (uV < TPS65911_LDO_VOLT_BASE) + uV = TPS65911_LDO_VOLT_BASE; + + switch (id) { + case 1: + case 2: + case 4: + step = TPS65911_LDO124_VOLT_STEP; + break; + case 3: + case 5: + case 6: + case 7: + case 8: + step = TPS65911_LDO358_VOLT_STEP; + break; + default: + return -EINVAL; + }; + + return ((uV - TPS65911_LDO_VOLT_BASE) / step) << 2; +} + +static int tps65911_ldo_hex2volt(int id, int hex) +{ + int step; + + switch (id) { + case 1: + case 2: + case 4: + if (hex > TPS65911_LDO124_VOLT_MAX_HEX) + return -EINVAL; + + step = TPS65911_LDO124_VOLT_STEP; + break; + case 3: + case 5: + case 6: + case 7: + case 8: + if (hex > TPS65911_LDO358_VOLT_MAX_HEX) + return -EINVAL; + + if (hex < TPS65911_LDO358_VOLT_MIN_HEX) + hex = TPS65911_LDO358_VOLT_MIN_HEX; + + step = TPS65911_LDO358_VOLT_STEP; + break; + default: + return -EINVAL; + }; + + return TPS65911_LDO_VOLT_BASE + hex * step; +} + +static int tps65911_ldo_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int id = dev->driver_data; + int hex, ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + ret &= TPS65911_LDO_SEL_MASK; + + ret = tps65911_ldo_hex2volt(id, ret >> 2); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = tps65911_ldo_volt2hex(id, *uV); + if (hex < 0) + return hex; + + ret &= ~(TPS65911_LDO_SEL_MASK); + + return pmic_reg_write(dev->parent, adr, ret | hex); +} + +static int tps65911_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u8 idx = dev->driver_data - 1; + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->ctrl_reg = tps65911_ldo_reg[idx]; + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps65911_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return tps65911_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps65911_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = tps65911_get_enable, + .set_enable = tps65911_set_enable, +}; + +U_BOOT_DRIVER(tps65911_ldo) = { + .name = TPS65911_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65911_ldo_ops, + .probe = tps65911_ldo_probe, +};
participants (2)
-
Simon Glass
-
Svyatoslav Ryhel