Skip to contentSkip to conversation

Power(full) framework based on Driver Model

RFC PATCH New Przemyslaw Marczak 6 participants 218 messages Oct 8, 2014 – Apr 24, 2015 Page 1 of 3

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover Power(full) framework based on Driver Model

Hello,
This piece of code was a base for prepare my presentation talk for the U-Boot Mini Summit, which taking place at ELCE2014 conference, 13-th October 2014 Dusseldorf, Germany.

The tittle of talk: "Power(full) framework based on Driver Model"

The presentation will be shared after the Summit.

This patchset introduces the new one PMIC framework for the U-Boot. It is still under the construction, but the basic functionality was achieved and tested. Please feel free to comment and share the opinion.

I think that each patch is described full enough, but for some more design details, please look into the documentation file. It includes some basic examples for the PMIC drivers.

Quick summary of:
Framework:
- The new approach - UCLASS_PMIC - simple and designed for device I/O only
- Add new uclass types: UCLASS_PMIC and UCLASS_PMIC_REGULATOR
- Two uclass drivers for above types
- A common regulator operations - will easy cover the real devices design

Drivers:
- Introduce new PMIC API for drivers - now everything base on "struct udevice"
- Introduce Regulator Voltage descriptors and Operation Mode descriptors which are usually taken from the device tree (board dependent data)
- Two uclass device drivers for MAX77686(PMIC+REGULATOR)

User Interface:
- command pmic, unchanged functionality and ported to the driver model
- command regulator(NEW) for safe regulator setup from commandline,
- now can check output Voltage and operation mode of the regulators,
- also can check the board Voltage limits and driver available modes

The future plans:
- Wait for the I2c Driver Model implementation
- Introduce a common way to bind pmic devices - now done by alias "pmic"
- Add additional uclass types: MUIC, CHARGER, BATTERY, MFD and maybe more.
- Introduce optimal operations for new uclasses
- Port all U-Boot drivers to the new Framework
- Remove the old drivers and the old PMIC Framework code

The assumptions of this work is:
- Add new code to independent files
- Keep two Frameworks as independent and without conflicts
- Don't mix OLD/NEW Framework code - for the readability
- Port all drivers using new API
- Remove the old Framework and the old drivers

A disadvantage:
- some parts of the present code is duplicated

Need help:
- After merge this, it is welcome to help with driver porting.
- Everything should be tested

The extra feature:
The first commit introduces errno_str() function.
It is a common function which I hope will be usefull for commands and not only. If any visible error says: -19 or some other magic number, then it means that this function should be used.

U-Boot Mini Summit members:
This code is maybe not as good as it could be, but the time was limited, and the conference is comming soon. I don't expects a code review of this now, but it would be nice if you take a look of this piece of code before our U-Boot Mini Summit. Of course it depends on you.

Best Regards and see you in Dusseldorf!
Przemyslaw Marczak

Przemyslaw Marczak (19):
lib: errno: introduce errno_str(): returns errno related message
exynos: config-common: enable errno_str() function
exynos: config-common: enable generic fs command
dm: pmic: add implementation of driver model pmic uclass
dm: pmic: add implementation of driver model regulator uclass
dm: common: board_r: add call and weak of power_init_dm()
dm: pmic: add max77686 pmic driver
dm: regulator: add max77686 regulator driver
dm: pmic: new commands: pmic and regulator
dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
doc: driver-model: pmic and regulator uclass documentation
samsung: board: lcd menu: check if any power framework is enabled
samsung: misc: power_key_pressed: add support to dm pmic framework
trats2: board: add support to dm pmic api
trats2: dts: max77686: add pmic alias and names cleanup
trats2: config: enable dm pmic, dm regulator api, dm max77686
odroid: board: add support to dm pmic api
odroid: dts: add 'voltage-regulators' description to max77686 node
odroid: config: enable dm pmic, dm regulator and max77686 driver

 Makefile                               |   1 +
 arch/arm/dts/exynos4412-odroid.dts     | 250 +++++++++-
 arch/arm/dts/exynos4412-trats2.dts     |  17 +-
 board/samsung/common/board.c           |   5 +-
 board/samsung/common/misc.c            |  21 +-
 board/samsung/odroid/odroid.c          |  31 +-
 board/samsung/trats2/trats2.c          | 208 ++------
 common/board_r.c                       |   8 +
 doc/driver-model/dm-pmic-framework.txt | 450 ++++++++++++++++++
 drivers/power/Makefile                 |   6 +-
 drivers/power/cmd_pmic.c               | 845 +++++++++++++++++++++++++++++++++
 drivers/power/pmic-uclass.c            | 255 ++++++++++
 drivers/power/pmic/Makefile            |   1 +
 drivers/power/pmic/max77686.c          |  89 ++++
 drivers/power/pmic_i2c.c               | 136 ++++++
 drivers/power/pmic_spi.c               | 137 ++++++
 drivers/power/regulator-uclass.c       | 250 ++++++++++
 drivers/power/regulator/Makefile       |   8 +
 drivers/power/regulator/max77686.c     | 749 +++++++++++++++++++++++++++++
 include/configs/exynos-common.h        |   3 +
 include/configs/odroid.h               |   8 +-
 include/configs/trats2.h               |  14 +-
 include/dm/uclass-id.h                 |   4 +
 include/errno.h                        |   3 +
 include/power/max77686_pmic.h          |  28 +-
 include/power/pmic.h                   |  82 ++++
 include/power/regulator.h              | 267 +++++++++++
 lib/Makefile                           |   1 +
 lib/errno_str.c                        | 147 ++++++
 29 files changed, 3823 insertions(+), 201 deletions(-)
 create mode 100644 doc/driver-model/dm-pmic-framework.txt
 create mode 100644 drivers/power/cmd_pmic.c
 create mode 100644 drivers/power/pmic-uclass.c
 create mode 100644 drivers/power/pmic/max77686.c
 create mode 100644 drivers/power/pmic_i2c.c
 create mode 100644 drivers/power/pmic_spi.c
 create mode 100644 drivers/power/regulator-uclass.c
 create mode 100644 drivers/power/regulator/Makefile
 create mode 100644 drivers/power/regulator/max77686.c
 create mode 100644 include/power/regulator.h
 create mode 100644 lib/errno_str.c
Signature · 1 line
1.9.1
#1 lore raw

PATCH 1/19 lib: errno: introduce errno_str(): returns errno related message

The functions error's numbers are standarized - but the error messages are not.

The errors are often handled with unclear error messages, so why not use an errno standarized messages.

Advantages:
- This could decrease the binary size.
- Appended with a detailed information,
the error message will be clear.

This commit introduces new function:
- const char *errno_to_str(int errno)

The functions returns a pointer to the errno corresponding text message:
- if errno is null or positive number - a pointer to "Success" message
- if errno is negative - a pointer to errno related message

---
 include/errno.h |   3 ++
 lib/Makefile    |   1 +
 lib/errno_str.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 151 insertions(+)
 create mode 100644 lib/errno_str.c
include/errno.h +3 -0
index e24a33b..14ac3cb 100644
@@ -6,4 +6,7 @@ extern int errno;
 
 #define __set_errno(val) do { errno = val; } while (0)
 
+#ifdef CONFIG_ERRNO_STR
+const char *errno_str(int errno);
+#endif
 #endif /* _ERRNO_H */
lib/Makefile +1 -0
index 320197a..6bb2e9b 100644
@@ -53,6 +53,7 @@ endif
 obj-$(CONFIG_ADDR_MAP) += addr_map.o
 obj-y += hashtable.o
 obj-y += errno.o
+obj-$(CONFIG_ERRNO_STR) += errno_str.o
 obj-y += display_options.o
 obj-$(CONFIG_BCH) += bch.o
 obj-y += crc32.o
lib/errno_str.c new file +147 -0

147 lines not on this page · raw message

Signature · 1 line
1.9.1
#2 lore raw

PATCH 1/19 lib: errno: introduce errno_str(): returns errno related message

Quoted 10 lines

From: Przemyslaw Marczak <[email protected]>

The functions error's numbers are standarized - but the error messages are not.

The errors are often handled with unclear error messages, so why not use an errno standarized messages.

Advantages:
- This could decrease the binary size.

Having an array of string ptrs adds some extra space needs. Each str needs a ptr and that ptr needs relocation, 8 bytes on 32 bits

If you want to save space do this instead
static const char const errno_message[] =
"Success\0Operation not permitted\0No such file or directory" etc.
Then count "\0" to find the error msg.

Jocke

#3 lore raw

PATCH 1/19 lib: errno: introduce errno_str(): returns errno related message

Hello Joakim,

Quoted 14 lines · Joakim Tjernlund
Quoted 10 lines

From: Przemyslaw Marczak <[email protected]>

The functions error's numbers are standarized - but the error messages are not.

The errors are often handled with unclear error messages, so why not use an errno standarized messages.

Advantages:
- This could decrease the binary size.

Having an array of string ptrs adds some extra space needs. Each str needs a ptr and that ptr needs relocation, 8 bytes on 32 bits

Quoted 7 lines

If you want to save space do this instead
static const char const errno_message[] =
"Success\0Operation not permitted\0No such file or directory" etc.
Then count "\0" to find the error msg.

Jocke

Is this really a problem to add some array with the pointers?

You are right, this array requires some additional space, but this is not the main reason of introducing this function. This can be enabled optional, so maybe for the less memory and slower devices this shouldn't be used - but in the other way, we see many text messages in the code that could be replaced with the one from that array.
So, which is better?

This helps me sometimes, so I added this as some extra feature.

Best Regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#4 lore raw

PATCH 1/19 lib: errno: introduce errno_str(): returns errno related message

Quoted 29 lines · Przemyslaw Marczak

Hello Joakim,

Quoted 15 lines · Joakim Tjernlund

From: Przemyslaw Marczak <[email protected]>

Quoted 10 lines

The functions error's numbers are standarized - but the error messages are not.

The errors are often handled with unclear error messages, so why not use an errno standarized messages.

Advantages:
- This could decrease the binary size.

Having an array of string ptrs adds some extra space needs. Each str needs a ptr and that ptr needs relocation, 8 bytes on 32 bits

Quoted 8 lines

If you want to save space do this instead
static const char const errno_message[] =
"Success\0Operation not permitted\0No such file or directory" etc.
Then count "\0" to find the error msg.

Jocke

Is this really a problem to add some array with the pointers?

Quoted 9 lines

You are right, this array requires some additional space, but this is not the main reason of introducing this function. This can be enabled optional, so maybe for the less memory and slower devices this shouldn't be used - but in the other way, we see many text messages in the code that could be replaced with the one from that array.
So, which is better?

This helps me sometimes, so I added this as some extra feature.

I think it looks good as it is as it is simple. My only suggestion is that the CONFIG should not remove the function, merely make it return an empty string or "Error strings unavailable". Otherwise you will force #ifdefs in the code.

Regards,
Simon

#5 lore raw

PATCH 1/19 lib: errno: introduce errno_str(): returns errno related message

In reply to Przemyslaw Marczak

Przemyslaw Marczak <[email protected]> wrote on 2014/10/09 18:23:54:

Quoted 23 lines

Hello Joakim,

Quoted 19 lines · Joakim Tjernlund
Quoted 10 lines

From: Przemyslaw Marczak <[email protected]>

The functions error's numbers are standarized - but the error messages are not.

The errors are often handled with unclear error messages, so why not use an errno standarized messages.

Advantages:
- This could decrease the binary size.

Having an array of string ptrs adds some extra space needs. Each str needs a ptr and that ptr needs relocation, 8 bytes on 32 bits

If you want to save space do this instead
static const char const errno_message[] =
"Success\0Operation not permitted\0No such file or directory" etc.
Then count "\0" to find the error msg.

Quoted 4 lines
Quoted 2 lines

Jocke

Is this really a problem to add some array with the pointers?

Probably not, I only mentioned this because you claimed it could reduces size
as if that was important to you.

Jocke

Quoted 10 lines

You are right, this array requires some additional space, but this is not the main reason of introducing this function. This can be enabled optional, so maybe for the less memory and slower devices this shouldn't

be used - but in the other way, we see many text messages in the code that could be replaced with the one from that array.
So, which is better?

This helps me sometimes, so I added this as some extra feature.

#6 lore raw

PATCH 1/19 lib: errno: introduce errno_str(): returns errno related message

Hello Joakim,

Quoted 27 lines · Joakim Tjernlund

Przemyslaw Marczak <[email protected]> wrote on 2014/10/09 18:23:54:

Quoted 26 lines

Hello Joakim,

Quoted 21 lines · Joakim Tjernlund
Quoted 10 lines

From: Przemyslaw Marczak <[email protected]>

The functions error's numbers are standarized - but the error messages are not.

The errors are often handled with unclear error messages, so why not use an errno standarized messages.

Advantages:
- This could decrease the binary size.

Having an array of string ptrs adds some extra space needs. Each str needs a ptr and that ptr needs relocation, 8 bytes on 32 bits

If you want to save space do this instead
static const char const errno_message[] =
"Success\0Operation not permitted\0No such file or directory" etc.
Then count "\0" to find the error msg.

Jocke

Quoted 8 lines
Quoted 1 line

Is this really a problem to add some array with the pointers?

Probably not, I only mentioned this because you claimed it could reduces size
as if that was important to you.

Jocke

I meant that it will reduce the size, if we could change the whole code "common message string parts" with a pointer to some standard message.

Quoted 5 lines
Quoted 4 lines

You are right, this array requires some additional space, but this is not the main reason of introducing this function. This can be enabled optional, so maybe for the less memory and slower devices this shouldn't

Quoted 8 lines
Quoted 5 lines

be used - but in the other way, we see many text messages in the code that could be replaced with the one from that array.
So, which is better?

This helps me sometimes, so I added this as some extra feature.

Your notice about the size was right, but I hope that U-Boot is used for machines, in which this doesn't make a difference - and the code is still simple and the "error messages" - means what they should mean.

Best Regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#7 lore raw

PATCH 1/19 lib: errno: introduce errno_str(): returns errno related message

In reply to Przemyslaw Marczak
Quoted 11 lines · Przemyslaw Marczak

The functions error's numbers are standarized - but the error messages are not.

The errors are often handled with unclear error messages, so why not use an errno standarized messages.

Advantages:
- This could decrease the binary size.
- Appended with a detailed information,
the error message will be clear.

Quoted 8 lines

This commit introduces new function:
- const char *errno_to_str(int errno)

The functions returns a pointer to the errno corresponding text message:
- if errno is null or positive number - a pointer to "Success" message
- if errno is negative - a pointer to errno related message

In and of itself,

Signature · 8 lines
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20141022/e45c54a3/attachment.pgp>
#8 lore raw

PATCH 1/19 lib: errno: introduce errno_str(): returns errno related message

Quoted 20 lines · Tom Rini
Quoted 18 lines · Przemyslaw Marczak

The functions error's numbers are standarized - but the error messages are not.

The errors are often handled with unclear error messages, so why not use an errno standarized messages.

Advantages:
- This could decrease the binary size.
- Appended with a detailed information,
the error message will be clear.

This commit introduces new function:
- const char *errno_to_str(int errno)

The functions returns a pointer to the errno corresponding text message:
- if errno is null or positive number - a pointer to "Success" message
- if errno is negative - a pointer to errno related message

Quoted 5 lines
Quoted 1 line

In and of itself,

Applied to u-boot-dm, thanks!

- Simon

#9 lore raw

PATCH 1/19 lib: errno: introduce errno_str(): returns errno related message

Hello Simon,

Quoted 23 lines · Simon Glass
Quoted 22 lines · Tom Rini
Quoted 19 lines · Przemyslaw Marczak

The functions error's numbers are standarized - but the error messages are not.

The errors are often handled with unclear error messages, so why not use an errno standarized messages.

Advantages:
- This could decrease the binary size.
- Appended with a detailed information,
the error message will be clear.

This commit introduces new function:
- const char *errno_to_str(int errno)

The functions returns a pointer to the errno corresponding text message:
- if errno is null or positive number - a pointer to "Success" message
- if errno is negative - a pointer to errno related message

Quoted 8 lines
Quoted 3 lines

In and of itself,

Applied to u-boot-dm, thanks!

- Simon

Thank, maybe today or tomorrow I will send the I2C for exynos and next I can continue work on pmic.

Best regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#10 lore raw

PATCH 1/19 lib: errno: introduce errno_str(): returns errno related message

Hi Przemyslaw,

Quoted 35 lines · Przemyslaw Marczak

Hello Simon,

Quoted 31 lines · Simon Glass
Quoted 27 lines · Tom Rini
Quoted 11 lines · Przemyslaw Marczak

The functions error's numbers are standarized - but the error messages are not.

The errors are often handled with unclear error messages, so why not use an errno standarized messages.

Advantages:
- This could decrease the binary size.
- Appended with a detailed information,
the error message will be clear.

Quoted 8 lines

This commit introduces new function:
- const char *errno_to_str(int errno)

The functions returns a pointer to the errno corresponding text message:
- if errno is null or positive number - a pointer to "Success" message
- if errno is negative - a pointer to errno related message

In and of itself,

Quoted 7 lines
Quoted 4 lines

Applied to u-boot-dm, thanks!

- Simon

Thank, maybe today or tomorrow I will send the I2C for exynos and next I can continue work on pmic.

OK sounds good. I've pushed DM I2C things to dm/master, although not Tegra yet while I wait on the nyan-big patch.

Regards,
Simon

#11 lore raw

PATCH 2/19 exynos: config-common: enable errno_str() function

In reply to Przemyslaw Marczak
---
 include/configs/exynos-common.h | 2 ++
 1 file changed, 2 insertions(+)
include/configs/exynos-common.h +2 -0
index 6ba9bb7..fa02cb2 100644
@@ -95,4 +95,6 @@
 #define CONFIG_SYS_NO_FLASH
 #undef CONFIG_CMD_IMLS
 
+#define CONFIG_ERRNO_STR
+
 #endif	/* __CONFIG_H */
Signature · 1 line
1.9.1
#12 lore raw

PATCH 3/19 exynos: config-common: enable generic fs command

In reply to Przemyslaw Marczak

This command is required for trats2 and odroid
environment, since those boards uses it.

---
 include/configs/exynos-common.h | 1 +
 1 file changed, 1 insertion(+)
include/configs/exynos-common.h +1 -0
index fa02cb2..3d30a25 100644
@@ -80,6 +80,7 @@
 #define CONFIG_EFI_PARTITION
 #define CONFIG_CMD_PART
 #define CONFIG_PARTITION_UUIDS
+#define CONFIG_CMD_FS_GENERIC
 
 /* Miscellaneous configurable options */
 #define CONFIG_SYS_LONGHELP		/* undef to save memory */
Signature · 1 line
1.9.1
#13 lore raw

PATCH 4/19 dm: pmic: add implementation of driver model pmic uclass

In reply to Przemyslaw Marczak

This is an introduction to driver-model multi class PMIC support. It starts with UCLASS_PMIC - a common PMIC class type for I/O, which doesn't need to implement any specific operations and features beside the platform data, which is the 'struct pmic_platdata' defined in file:
- 'include/power/pmic.h'

New files:
- pmic-uclass.c - provides a common code for UCLASS_PMIC drivers
- pmic_i2c.c - provides dm interface for i2c pmic drivers
- pmic_spi.c - provides dm interface for spi pmic drivers

Those files are based on a current PMIC framework files and code. The new files are introduced to keep code readability and allow to make an easy drivers migration. The old pmic framework is still kept and full independent.

Changes:
- new uclass-id: UCLASS_PMIC,
- new configs: CONFIG_DM_PMIC; CONFIG_DM_PMIC_SPI; CONFIG_DM_PMIC_I2C,

New pmic api is documented in: doc/README.power-framework-dm

---
 drivers/power/Makefile      |   3 +
 drivers/power/pmic-uclass.c | 255 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/power/pmic_i2c.c    | 136 +++++++++++++++++++++++
 drivers/power/pmic_spi.c    | 137 ++++++++++++++++++++++++
 include/dm/uclass-id.h      |   3 +
 include/power/pmic.h        |  64 +++++++++++
 6 files changed, 598 insertions(+)
 create mode 100644 drivers/power/pmic-uclass.c
 create mode 100644 drivers/power/pmic_i2c.c
 create mode 100644 drivers/power/pmic_spi.c
drivers/power/Makefile +3 -0
index dc64e4d..8def501 100644
@@ -19,3 +19,6 @@ obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
 obj-$(CONFIG_POWER_FSL) += power_fsl.o
 obj-$(CONFIG_POWER_I2C) += power_i2c.o
 obj-$(CONFIG_POWER_SPI) += power_spi.o
+obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
+obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
drivers/power/pmic-uclass.c new file +255 -0

255 lines not on this page · raw message

drivers/power/pmic_i2c.c new file +136 -0

136 lines not on this page · raw message

drivers/power/pmic_spi.c new file +137 -0

137 lines not on this page · raw message

include/dm/uclass-id.h +3 -0
index e3e9296..e6d9d39 100644
@@ -29,6 +29,9 @@ enum uclass_id {
 	UCLASS_SPI_FLASH,	/* SPI flash */
 	UCLASS_CROS_EC,		/* Chrome OS EC */
 
+	/* PMIC uclass and PMIC related uclasses */
+	UCLASS_PMIC,
+
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
 };
include/power/pmic.h +64 -0

89 lines not on this page · raw message

Signature · 1 line
1.9.1
#14 lore raw

PATCH 4/19 dm: pmic: add implementation of driver model pmic uclass

Hi,

Quoted 97 lines · Przemyslaw Marczak

This is an introduction to driver-model multi class PMIC support. It starts with UCLASS_PMIC - a common PMIC class type for I/O, which doesn't need to implement any specific operations and features beside the platform data, which is the 'struct pmic_platdata' defined in file:
- 'include/power/pmic.h'

New files:
- pmic-uclass.c - provides a common code for UCLASS_PMIC drivers
- pmic_i2c.c - provides dm interface for i2c pmic drivers
- pmic_spi.c - provides dm interface for spi pmic drivers

Those files are based on a current PMIC framework files and code. The new files are introduced to keep code readability and allow to make an easy drivers migration. The old pmic framework is still kept and full independent.

Changes:
- new uclass-id: UCLASS_PMIC,
- new configs: CONFIG_DM_PMIC; CONFIG_DM_PMIC_SPI; CONFIG_DM_PMIC_I2C,

New pmic api is documented in: doc/README.power-framework-dm

---
 drivers/power/Makefile      |   3 +
 drivers/power/pmic-uclass.c | 255 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/power/pmic_i2c.c    | 136 +++++++++++++++++++++++
 drivers/power/pmic_spi.c    | 137 ++++++++++++++++++++++++
 include/dm/uclass-id.h      |   3 +
 include/power/pmic.h        |  64 +++++++++++
 6 files changed, 598 insertions(+)
 create mode 100644 drivers/power/pmic-uclass.c
 create mode 100644 drivers/power/pmic_i2c.c
 create mode 100644 drivers/power/pmic_spi.c
drivers/power/Makefile +3 -0
index dc64e4d..8def501 100644
@@ -19,3 +19,6 @@ obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
 obj-$(CONFIG_POWER_FSL) += power_fsl.o
 obj-$(CONFIG_POWER_I2C) += power_i2c.o
 obj-$(CONFIG_POWER_SPI) += power_spi.o
+obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
+obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
drivers/power/pmic-uclass.c new file +45 -0
new file mode 100644
index 0000000..5e8494b
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <power/pmic.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/root.h>
+#include <dm/lists.h>
+#include <i2c.h>
+#include <compiler.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg)
+{
+       if (!p)
+               return -ENODEV;
+
+       if (reg >= p->regs_num) {
+               error("<reg num> = %d is invalid. Should be less than %d",
+                      reg, p->regs_num);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C

drivers/power/pmic-uclass.c line 55

Quoted 10 lines
drivers/power/pmic-uclass.c new file +10 -0
+               return pmic_i2c_reg_write(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+#ifdef CONFIG_DM_PMIC_SPI
+               return pmic_spi_reg_write(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif

Perhaps one day we should add another uclass which is some sort of register cache. It could be implemented by I2C and SPI drivers (or better perhaps the I2C and SPI uclasses could provide a register cache uclass device for their main when requested).

For now this seems fine though. I think the 'return -ENOSYS' can just go at the end of the function and appear once.

Quoted 54 lines
drivers/power/pmic-uclass.c new file +54 -0
+       default:
+               return -ENODEV;
+       }
+}
+
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C
+               return pmic_i2c_reg_read(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+#ifdef CONFIG_DM_PMIC_SPI
+               return pmic_spi_reg_read(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       default:
+               return -ENODEV;
+       }
+}
+
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C
+               return pmic_i2c_probe(dev);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+               if (!spi_slave)
+                       return -EINVAL;
+#ifdef CONFIG_DM_PMIC_SPI
+               spi_slave = pmic_spi_probe(dev);
+               if (!spi_slave)
+                       return -EIO;
+

drivers/power/pmic-uclass.c line 119

Quoted 10 lines
drivers/power/pmic-uclass.c new file +10 -0
+               return 0;
+#else
+               return -ENOSYS;
+#endif
+       default:
+               return -ENODEV;
+       }
+}
+
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int addr_cs)

This is an interesting function! Again we can probably improve things when we have an i2c uclass.

Quoted 74 lines
drivers/power/pmic-uclass.c new file +74 -0
+{
+       struct pmic_platdata *pl;
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       if (uclass_id < 0 || uclass_id >= UCLASS_COUNT) {
+               error("Bad uclass id.\n");
+               return NULL;
+       }
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not initialized!\n", uclass_id);
+               return NULL;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev || !dev->platdata)
+                       continue;
+
+               pl = dev_get_platdata(dev);
+
+               if (pl->bus != bus)
+                       continue;
+
+               switch (pl->interface) {
+               case PMIC_I2C:
+                       if (addr_cs != pl->hw.i2c.addr)
+                               continue;
+                       break;
+               case PMIC_SPI:
+                       if (addr_cs != pl->hw.spi.cs)
+                               continue;
+                       break;
+               default:
+                       error("Unsupported interface of: %s", dev->name);
+                       return NULL;
+               }
+
+               ret = device_probe(dev);
+               if (ret) {
+                       error("Dev: %s probe failed", dev->name);
+                       return NULL;
+               }
+               return dev;
+       }
+
+       return NULL;
+}
+
+struct udevice *pmic_get_by_name(int uclass_id, char *name)
+{
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       if (uclass_id < 0 || uclass_id >= UCLASS_COUNT) {
+               error("Bad uclass id.\n");
+               return NULL;
+       }
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not initialized!", uclass_id);
+               return NULL;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev)
+                       continue;
+
+               if (!strncmp(name, dev->name, strlen(name))) {
+                       ret = device_probe(dev);

drivers/power/pmic-uclass.c line 203

Quoted 10 lines
drivers/power/pmic-uclass.c new file +10 -0
+                       if (ret)
+                               error("Dev: %s probe failed", dev->name);
+                       return dev;
+               }
+       }
+
+       return NULL;
+}
+
+int pmic_init_dm(void)

I don't understand why you need this function at all. Driver model should find the pmics automatically. Is it because we don't have an i2c uclass? In that case I think it would be better to limit this hack to I2C. For SPI it should work correctly without this function.

Quoted 391 lines
drivers/power/pmic-uclass.c new file +52 -0
+{
+       const void *blob = gd->fdt_blob;
+       const struct fdt_property *prop;
+       struct udevice *dev = NULL;
+       const char *path;
+       const char *alias;
+       int alias_node, node, offset, ret = 0;
+       int alias_len;
+       int len;
+
+       alias = "pmic";
+       alias_len = strlen(alias);
+
+       alias_node = fdt_path_offset(blob, "/aliases");
+       offset = fdt_first_property_offset(blob, alias_node);
+
+       if (offset < 0) {
+               error("Alias node not found.");
+               return -ENODEV;
+       }
+
+       offset = fdt_first_property_offset(blob, alias_node);
+       for (; offset > 0; offset = fdt_next_property_offset(blob, offset)) {
+               prop = fdt_get_property_by_offset(blob, offset, &len);
+               if (!len)
+                       continue;
+
+               path = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
+
+               if (!strncmp(alias, path, alias_len))
+                       node = fdt_path_offset(blob, prop->data);
+               else
+                       node = 0;
+
+               if (node <= 0)
+                       continue;
+
+               ret = lists_bind_fdt(gd->dm_root, blob, node, &dev);
+               if (ret < 0)
+                       continue;
+
+               if (device_probe(dev))
+                       error("Device: %s, probe error", dev->name);
+       }
+
+       return 0;
+}
+
+UCLASS_DRIVER(pmic) = {
+       .id             = UCLASS_PMIC,
+       .name           = "pmic",
+};
drivers/power/pmic_i2c.c new file +136 -0
new file mode 100644
index 0000000..350d375
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <[email protected]>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic at denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+       unsigned char buf[4] = { 0 };
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EINVAL;
+
+       I2C_SET_BUS(p->bus);
+
+       switch (pmic_i2c_tx_num) {
+       case 3:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+                       buf[2] = (cpu_to_le32(val) >> 16) & 0xff;
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[0] = cpu_to_le32(val) & 0xff;
+               } else {
+                       buf[0] = (cpu_to_le32(val) >> 16) & 0xff;
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[2] = cpu_to_le32(val) & 0xff;
+               }
+               break;
+       case 2:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[0] = cpu_to_le32(val) & 0xff;
+               } else {
+                       buf[0] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[1] = cpu_to_le32(val) & 0xff;
+               }
+               break;
+       case 1:
+               buf[0] = cpu_to_le32(val) & 0xff;
+               break;
+       default:
+               printf("%s: invalid tx_num: %d", __func__, pmic_i2c_tx_num);
+               return -1;
+       }
+
+       if (i2c_write(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+               return -1;
+
+       return 0;
+}
+
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+       unsigned char buf[4] = { 0 };
+       u32 ret_val = 0;
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EINVAL;
+
+       I2C_SET_BUS(p->bus);
+
+       if (i2c_read(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+               return -1;
+
+       switch (pmic_i2c_tx_num) {
+       case 3:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+                       ret_val = le32_to_cpu(buf[2] << 16
+                                             | buf[1] << 8 | buf[0]);
+               else
+                       ret_val = le32_to_cpu(buf[0] << 16 |
+                                             buf[1] << 8 | buf[2]);
+               break;
+       case 2:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+                       ret_val = le32_to_cpu(buf[1] << 8 | buf[0]);
+               else
+                       ret_val = le32_to_cpu(buf[0] << 8 | buf[1]);
+               break;
+       case 1:
+               ret_val = le32_to_cpu(buf[0]);
+               break;
+       default:
+               printf("%s: invalid tx_num: %d", __func__, pmic_i2c_tx_num);
+               return -1;
+       }
+       memcpy(val, &ret_val, sizeof(ret_val));
+
+       return 0;
+}
+
+int pmic_i2c_probe(struct udevice *dev)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       i2c_set_bus_num(p->bus);
+       debug("Bus: %d PMIC:%s probed!\n", p->bus, dev->name);
+       if (i2c_probe(pmic_i2c_addr)) {
+               printf("Can't find PMIC:%s\n", dev->name);
+               return -1;
+       }
+
+       return 0;
+}
drivers/power/pmic_spi.c new file +137 -0
new file mode 100644
index 0000000..7851adf
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <[email protected]>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic at denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <spi.h>
+
+static struct spi_slave *slave;
+
+void pmic_spi_free(struct spi_slave *slave)
+{
+       if (slave)
+               spi_free_slave(slave);
+}
+
+struct spi_slave *pmic_spi_probe(struct udevice *dev)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return NULL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return NULL;
+
+       return spi_setup_slave(p->bus,
+               p->hw.spi.cs,
+               p->hw.spi.clk,
+               p->hw.spi.mode);
+}
+
+static int pmic_spi_reg(struct udevice *dev, unsigned reg, unsigned *val,
+                       int write)
+{
+       struct pmic_platdata *p;
+       u32 pmic_tx, pmic_rx;
+       u32 tmp;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (!slave) {
+               slave = pmic_spi_probe(p);
+
+               if (!slave)
+                       return -ENODEV;
+       }
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (spi_claim_bus(slave))
+               return -EIO;
+
+       pmic_tx = p->hw.spi.prepare_tx(reg, val, write);
+
+       tmp = cpu_to_be32(pmic_tx);
+
+       if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+                    pmic_spi_flags)) {
+               spi_release_bus(slave);
+               return -EIO;
+       }
+
+       if (write) {
+               pmic_tx = p->hw.spi.prepare_tx(reg, val, 0);
+               tmp = cpu_to_be32(pmic_tx);
+               if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+                            pmic_spi_flags)) {
+                       spi_release_bus(slave);
+                       return -EIO;
+               }
+       }
+
+       spi_release_bus(slave);
+       *val = cpu_to_be32(pmic_rx);
+
+       return 0;
+}
+
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (pmic_spi_reg(p, reg, &val, 1))
+               return -1;
+
+       return 0;
+}
+
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (pmic_spi_reg(p, reg, val, 0))
+               return -1;
+
+       return 0;
+}
include/dm/uclass-id.h +3 -0
index e3e9296..e6d9d39 100644
@@ -29,6 +29,9 @@ enum uclass_id {
        UCLASS_SPI_FLASH,       /* SPI flash */
        UCLASS_CROS_EC,         /* Chrome OS EC */
 
+       /* PMIC uclass and PMIC related uclasses */
+       UCLASS_PMIC,
+
        UCLASS_COUNT,
        UCLASS_INVALID = -1,
 };
include/power/pmic.h +17 -0
index afbc5aa..7114650 100644
@@ -1,4 +1,7 @@
 /*
+ *  Copyright (C) 2014 Samsung Electronics
+ *  Przemyslaw Marczak <[email protected]>
+ *
  *  Copyright (C) 2011-2012 Samsung Electronics
  *  Lukasz Majewski <[email protected]>
  *
@@ -8,9 +11,12 @@
 #ifndef __CORE_PMIC_H_
 #define __CORE_PMIC_H_
 
+#include <dm.h>
 #include <linux/list.h>
+#include <spi.h>
 #include <i2c.h>
 #include <power/power_chrg.h>
+#include <power/regulator.h>
 
 enum { PMIC_I2C, PMIC_SPI, PMIC_NONE};
 enum { I2C_PMIC, I2C_NUM, };
@@ -78,6 +84,63 @@ struct pmic {
        struct list_head list;
 };
 
+#ifdef CONFIG_DM_PMIC
+/* struct pmic_platdata - a standard descriptor for pmic device, which holds
+ * an informations about interface. It is common for all pmic devices.
+ *
+ * Note:
+ * Interface fields are the same as in: struct pmic.
+ * Note: struct pmic will be removed in the future after drivers migration
+ *
+ * @bus        - a physical bus on which device is connected
+ * @interface  - an interface type, one of enum: PMIC_I2C, PMIC_SPI, PMIC_NONE
+ * @byte_order - one of enum: PMIC_SENSOR_BYTE_ORDER*_LITTLE/_BIG

include/power/pmic.h struct pmic { · line 107

Quoted 10 lines
include/power/pmic.h +10 -0
+ * @regs_num   - number of device registers
+ * @hw         - one of union structure: p_i2c or p_spi
+ *               based on @interface field
+*/
+struct pmic_platdata {
+       int bus;
+       int interface;
+       int byte_order;
+       int regs_num;
+       union hw hw;

If we have a 'register cache' uclass (later, once i2c is done) then this could just be a device.

Quoted 40 lines
include/power/pmic.h +36 -0
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,
+ * for decrease a number of functions with the same code for read/write
+ * or get/set.
+ *
+ * @PMIC_OP_GET - get operation
+ * @PMIC_OP_SET - set operation
+*/
+enum pmic_op_type {
+       PMIC_OP_GET,
+       PMIC_OP_SET,
+};
+
+/* drivers/power/pmic-uclass.c */
+int power_init_dm(void);
+struct udevice *pmic_get_by_name(int uclass_id, char *name);
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int addr_cs);
+const char *pmic_if_str(int interface);
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg);
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave);
+
+/* drivers/power/pmic_i2c.c */
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_i2c_probe(struct udevice *dev);
+
+/* drivers/power/pmic_spi.c */
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+struct spi_slave *pmic_spi_probe(struct udevice *dev);
+#endif /* CONFIG_DM_PMIC */
+
+#ifdef CONFIG_POWER
 int pmic_init(unsigned char bus);
 int power_init_board(void);
 int pmic_dialog_init(unsigned char bus);

include/power/pmic.h int pmic_probe(struct pmic *p); · line 153

Quoted 3 lines
include/power/pmic.h +0 -0
@@ -88,6 +151,7 @@ int pmic_probe(struct pmic *p);
 int pmic_reg_read(struct pmic *p, u32 reg, u32 *val);
 int pmic_reg_write(struct pmic *p, u32 reg, u32 val);
 int pmic_set_output(struct pmic *p, u32 reg, int ldo, int on);

These should have comments.

Quoted 7 lines
include/power/pmic.h +1 -1
+#endif
 
 #define pmic_i2c_addr (p->hw.i2c.addr)
 #define pmic_i2c_tx_num (p->hw.i2c.tx_num)
--

1.9.1

Regards,
Simon

#15 lore raw

PATCH 4/19 dm: pmic: add implementation of driver model pmic uclass

Hello Simon,

Quoted 116 lines · Simon Glass

Hi,

Quoted 97 lines · Przemyslaw Marczak

This is an introduction to driver-model multi class PMIC support. It starts with UCLASS_PMIC - a common PMIC class type for I/O, which doesn't need to implement any specific operations and features beside the platform data, which is the 'struct pmic_platdata' defined in file:
- 'include/power/pmic.h'

New files:
- pmic-uclass.c - provides a common code for UCLASS_PMIC drivers
- pmic_i2c.c - provides dm interface for i2c pmic drivers
- pmic_spi.c - provides dm interface for spi pmic drivers

Those files are based on a current PMIC framework files and code. The new files are introduced to keep code readability and allow to make an easy drivers migration. The old pmic framework is still kept and full independent.

Changes:
- new uclass-id: UCLASS_PMIC,
- new configs: CONFIG_DM_PMIC; CONFIG_DM_PMIC_SPI; CONFIG_DM_PMIC_I2C,

New pmic api is documented in: doc/README.power-framework-dm

---
  drivers/power/Makefile      |   3 +
  drivers/power/pmic-uclass.c | 255 ++++++++++++++++++++++++++++++++++++++++++++
  drivers/power/pmic_i2c.c    | 136 +++++++++++++++++++++++
  drivers/power/pmic_spi.c    | 137 ++++++++++++++++++++++++
  include/dm/uclass-id.h      |   3 +
  include/power/pmic.h        |  64 +++++++++++
  6 files changed, 598 insertions(+)

create mode 100644 drivers/power/pmic-uclass.c
create mode 100644 drivers/power/pmic_i2c.c
create mode 100644 drivers/power/pmic_spi.c

drivers/power/Makefile +3 -0
index dc64e4d..8def501 100644
@@ -19,3 +19,6 @@ obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
  obj-$(CONFIG_POWER_FSL) += power_fsl.o
  obj-$(CONFIG_POWER_I2C) += power_i2c.o
  obj-$(CONFIG_POWER_SPI) += power_spi.o
+obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
+obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
drivers/power/pmic-uclass.c new file +45 -0
new file mode 100644
index 0000000..5e8494b
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <power/pmic.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/root.h>
+#include <dm/lists.h>
+#include <i2c.h>
+#include <compiler.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg)
+{
+       if (!p)
+               return -ENODEV;
+
+       if (reg >= p->regs_num) {
+               error("<reg num> = %d is invalid. Should be less than %d",
+                      reg, p->regs_num);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C

drivers/power/pmic-uclass.c line 55

Quoted 10 lines
drivers/power/pmic-uclass.c new file +10 -0
+               return pmic_i2c_reg_write(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+#ifdef CONFIG_DM_PMIC_SPI
+               return pmic_spi_reg_write(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif

Perhaps one day we should add another uclass which is some sort of register cache. It could be implemented by I2C and SPI drivers (or better perhaps the I2C and SPI uclasses could provide a register cache uclass device for their main when requested).

Quoted 3 lines

For now this seems fine though. I think the 'return -ENOSYS' can just go at the end of the function and appear once.

Why do we need the registers cache?
This maybe is good for a many read operations at a time in the system, but actually I think, that it is not required for the bootloader purposes.

If we write some data - sequential, then we probably would like to check it in the same time, e.g. update some range of I2C registers.

I don't know the Chromebook design, but how it could be useful for you?

Quoted 59 lines
Quoted 59 lines
drivers/power/pmic-uclass.c new file +59 -0
+       default:
+               return -ENODEV;
+       }
+}
+
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C
+               return pmic_i2c_reg_read(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+#ifdef CONFIG_DM_PMIC_SPI
+               return pmic_spi_reg_read(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       default:
+               return -ENODEV;
+       }
+}
+
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C
+               return pmic_i2c_probe(dev);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+               if (!spi_slave)
+                       return -EINVAL;
+#ifdef CONFIG_DM_PMIC_SPI
+               spi_slave = pmic_spi_probe(dev);
+               if (!spi_slave)
+                       return -EIO;
+
+               return 0;
+#else
+               return -ENOSYS;
+#endif
+       default:
Quoted 9 lines

drivers/power/pmic-uclass.c line 119

Quoted 5 lines
drivers/power/pmic-uclass.c new file +5 -0
+               return -ENODEV;
+       }
+}
+
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int addr_cs)

This is an interesting function! Again we can probably improve things when we have an i2c uclass.

Thanks! But even if we have i2c uclass, then we also should know how to recognize each device, e.g. in the board code. So the interface and address will probably no change.

Quoted 81 lines
Quoted 81 lines
drivers/power/pmic-uclass.c new file +81 -0
+{
+       struct pmic_platdata *pl;
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       if (uclass_id < 0 || uclass_id >= UCLASS_COUNT) {
+               error("Bad uclass id.\n");
+               return NULL;
+       }
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not initialized!\n", uclass_id);
+               return NULL;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev || !dev->platdata)
+                       continue;
+
+               pl = dev_get_platdata(dev);
+
+               if (pl->bus != bus)
+                       continue;
+
+               switch (pl->interface) {
+               case PMIC_I2C:
+                       if (addr_cs != pl->hw.i2c.addr)
+                               continue;
+                       break;
+               case PMIC_SPI:
+                       if (addr_cs != pl->hw.spi.cs)
+                               continue;
+                       break;
+               default:
+                       error("Unsupported interface of: %s", dev->name);
+                       return NULL;
+               }
+
+               ret = device_probe(dev);
+               if (ret) {
+                       error("Dev: %s probe failed", dev->name);
+                       return NULL;
+               }
+               return dev;
+       }
+
+       return NULL;
+}
+
+struct udevice *pmic_get_by_name(int uclass_id, char *name)
+{
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       if (uclass_id < 0 || uclass_id >= UCLASS_COUNT) {
+               error("Bad uclass id.\n");
+               return NULL;
+       }
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not initialized!", uclass_id);
+               return NULL;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev)
+                       continue;
+
+               if (!strncmp(name, dev->name, strlen(name))) {
+                       ret = device_probe(dev);
+                       if (ret)
+                               error("Dev: %s probe failed", dev->name);
+                       return dev;
+               }
+       }
+
+       return NULL;
Quoted 9 lines

drivers/power/pmic-uclass.c line 203

Quoted 3 lines
drivers/power/pmic-uclass.c new file +3 -0
+}
+
+int pmic_init_dm(void)

I don't understand why you need this function at all. Driver model should find the pmics automatically. Is it because we don't have an i2c uclass? In that case I think it would be better to limit this hack to I2C. For SPI it should work correctly without this function.

Currently, it shouldn't and yes - it isn't because of the I2C uclass missing. So no one check the I2C nodes - and its child subnodes. Actually, this can be easy fixed - just don't add the "pmic" alias to the dts. But I will also change fix the code.

And by the way - is there any check in the code, which protects for the device bind more than once?

Quoted 442 lines
Quoted 391 lines
drivers/power/pmic-uclass.c new file +52 -0
+{
+       const void *blob = gd->fdt_blob;
+       const struct fdt_property *prop;
+       struct udevice *dev = NULL;
+       const char *path;
+       const char *alias;
+       int alias_node, node, offset, ret = 0;
+       int alias_len;
+       int len;
+
+       alias = "pmic";
+       alias_len = strlen(alias);
+
+       alias_node = fdt_path_offset(blob, "/aliases");
+       offset = fdt_first_property_offset(blob, alias_node);
+
+       if (offset < 0) {
+               error("Alias node not found.");
+               return -ENODEV;
+       }
+
+       offset = fdt_first_property_offset(blob, alias_node);
+       for (; offset > 0; offset = fdt_next_property_offset(blob, offset)) {
+               prop = fdt_get_property_by_offset(blob, offset, &len);
+               if (!len)
+                       continue;
+
+               path = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
+
+               if (!strncmp(alias, path, alias_len))
+                       node = fdt_path_offset(blob, prop->data);
+               else
+                       node = 0;
+
+               if (node <= 0)
+                       continue;
+
+               ret = lists_bind_fdt(gd->dm_root, blob, node, &dev);
+               if (ret < 0)
+                       continue;
+
+               if (device_probe(dev))
+                       error("Device: %s, probe error", dev->name);
+       }
+
+       return 0;
+}
+
+UCLASS_DRIVER(pmic) = {
+       .id             = UCLASS_PMIC,
+       .name           = "pmic",
+};
drivers/power/pmic_i2c.c new file +136 -0
new file mode 100644
index 0000000..350d375
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <[email protected]>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic at denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+       unsigned char buf[4] = { 0 };
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EINVAL;
+
+       I2C_SET_BUS(p->bus);
+
+       switch (pmic_i2c_tx_num) {
+       case 3:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+                       buf[2] = (cpu_to_le32(val) >> 16) & 0xff;
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[0] = cpu_to_le32(val) & 0xff;
+               } else {
+                       buf[0] = (cpu_to_le32(val) >> 16) & 0xff;
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[2] = cpu_to_le32(val) & 0xff;
+               }
+               break;
+       case 2:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[0] = cpu_to_le32(val) & 0xff;
+               } else {
+                       buf[0] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[1] = cpu_to_le32(val) & 0xff;
+               }
+               break;
+       case 1:
+               buf[0] = cpu_to_le32(val) & 0xff;
+               break;
+       default:
+               printf("%s: invalid tx_num: %d", __func__, pmic_i2c_tx_num);
+               return -1;
+       }
+
+       if (i2c_write(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+               return -1;
+
+       return 0;
+}
+
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+       unsigned char buf[4] = { 0 };
+       u32 ret_val = 0;
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EINVAL;
+
+       I2C_SET_BUS(p->bus);
+
+       if (i2c_read(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+               return -1;
+
+       switch (pmic_i2c_tx_num) {
+       case 3:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+                       ret_val = le32_to_cpu(buf[2] << 16
+                                             | buf[1] << 8 | buf[0]);
+               else
+                       ret_val = le32_to_cpu(buf[0] << 16 |
+                                             buf[1] << 8 | buf[2]);
+               break;
+       case 2:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+                       ret_val = le32_to_cpu(buf[1] << 8 | buf[0]);
+               else
+                       ret_val = le32_to_cpu(buf[0] << 8 | buf[1]);
+               break;
+       case 1:
+               ret_val = le32_to_cpu(buf[0]);
+               break;
+       default:
+               printf("%s: invalid tx_num: %d", __func__, pmic_i2c_tx_num);
+               return -1;
+       }
+       memcpy(val, &ret_val, sizeof(ret_val));
+
+       return 0;
+}
+
+int pmic_i2c_probe(struct udevice *dev)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       i2c_set_bus_num(p->bus);
+       debug("Bus: %d PMIC:%s probed!\n", p->bus, dev->name);
+       if (i2c_probe(pmic_i2c_addr)) {
+               printf("Can't find PMIC:%s\n", dev->name);
+               return -1;
+       }
+
+       return 0;
+}
drivers/power/pmic_spi.c new file +137 -0
new file mode 100644
index 0000000..7851adf
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <[email protected]>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic at denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <spi.h>
+
+static struct spi_slave *slave;
+
+void pmic_spi_free(struct spi_slave *slave)
+{
+       if (slave)
+               spi_free_slave(slave);
+}
+
+struct spi_slave *pmic_spi_probe(struct udevice *dev)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return NULL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return NULL;
+
+       return spi_setup_slave(p->bus,
+               p->hw.spi.cs,
+               p->hw.spi.clk,
+               p->hw.spi.mode);
+}
+
+static int pmic_spi_reg(struct udevice *dev, unsigned reg, unsigned *val,
+                       int write)
+{
+       struct pmic_platdata *p;
+       u32 pmic_tx, pmic_rx;
+       u32 tmp;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (!slave) {
+               slave = pmic_spi_probe(p);
+
+               if (!slave)
+                       return -ENODEV;
+       }
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (spi_claim_bus(slave))
+               return -EIO;
+
+       pmic_tx = p->hw.spi.prepare_tx(reg, val, write);
+
+       tmp = cpu_to_be32(pmic_tx);
+
+       if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+                    pmic_spi_flags)) {
+               spi_release_bus(slave);
+               return -EIO;
+       }
+
+       if (write) {
+               pmic_tx = p->hw.spi.prepare_tx(reg, val, 0);
+               tmp = cpu_to_be32(pmic_tx);
+               if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+                            pmic_spi_flags)) {
+                       spi_release_bus(slave);
+                       return -EIO;
+               }
+       }
+
+       spi_release_bus(slave);
+       *val = cpu_to_be32(pmic_rx);
+
+       return 0;
+}
+
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (pmic_spi_reg(p, reg, &val, 1))
+               return -1;
+
+       return 0;
+}
+
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (pmic_spi_reg(p, reg, val, 0))
+               return -1;
+
+       return 0;
+}
include/dm/uclass-id.h +3 -0
index e3e9296..e6d9d39 100644
@@ -29,6 +29,9 @@ enum uclass_id {
         UCLASS_SPI_FLASH,       /* SPI flash */
         UCLASS_CROS_EC,         /* Chrome OS EC */
 
+       /* PMIC uclass and PMIC related uclasses */
+       UCLASS_PMIC,
+
         UCLASS_COUNT,
         UCLASS_INVALID = -1,
  };
include/power/pmic.h +17 -0
index afbc5aa..7114650 100644
@@ -1,4 +1,7 @@
  /*
+ *  Copyright (C) 2014 Samsung Electronics
+ *  Przemyslaw Marczak <[email protected]>
+ *
   *  Copyright (C) 2011-2012 Samsung Electronics
   *  Lukasz Majewski <[email protected]>
   *
@@ -8,9 +11,12 @@
  #ifndef __CORE_PMIC_H_
  #define __CORE_PMIC_H_
 
+#include <dm.h>
  #include <linux/list.h>
+#include <spi.h>
  #include <i2c.h>
  #include <power/power_chrg.h>
+#include <power/regulator.h>
 
  enum { PMIC_I2C, PMIC_SPI, PMIC_NONE};
  enum { I2C_PMIC, I2C_NUM, };
@@ -78,6 +84,63 @@ struct pmic {
         struct list_head list;
  };
 
+#ifdef CONFIG_DM_PMIC
+/* struct pmic_platdata - a standard descriptor for pmic device, which holds
+ * an informations about interface. It is common for all pmic devices.
+ *
+ * Note:
+ * Interface fields are the same as in: struct pmic.
+ * Note: struct pmic will be removed in the future after drivers migration
+ *
+ * @bus        - a physical bus on which device is connected
+ * @interface  - an interface type, one of enum: PMIC_I2C, PMIC_SPI, PMIC_NONE
+ * @byte_order - one of enum: PMIC_SENSOR_BYTE_ORDER*_LITTLE/_BIG

include/power/pmic.h struct pmic { · line 107

Quoted 10 lines
include/power/pmic.h +10 -0
+ * @regs_num   - number of device registers
+ * @hw         - one of union structure: p_i2c or p_spi
+ *               based on @interface field
+*/
+struct pmic_platdata {
+       int bus;
+       int interface;
+       int byte_order;
+       int regs_num;
+       union hw hw;

If we have a 'register cache' uclass (later, once i2c is done) then this could just be a device.

Quoted 37 lines
include/power/pmic.h +36 -0
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,
+ * for decrease a number of functions with the same code for read/write
+ * or get/set.
+ *
+ * @PMIC_OP_GET - get operation
+ * @PMIC_OP_SET - set operation
+*/
+enum pmic_op_type {
+       PMIC_OP_GET,
+       PMIC_OP_SET,
+};
+
+/* drivers/power/pmic-uclass.c */
+int power_init_dm(void);
+struct udevice *pmic_get_by_name(int uclass_id, char *name);
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int addr_cs);
+const char *pmic_if_str(int interface);
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg);
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave);
+
+/* drivers/power/pmic_i2c.c */
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_i2c_probe(struct udevice *dev);
+
+/* drivers/power/pmic_spi.c */
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+struct spi_slave *pmic_spi_probe(struct udevice *dev);
+#endif /* CONFIG_DM_PMIC */
+
+#ifdef CONFIG_POWER
  int pmic_init(unsigned char bus);
Quoted 9 lines

include/power/pmic.h int pmic_probe(struct pmic *p); · line 153

Quoted 6 lines
include/power/pmic.h +0 -0
  int power_init_board(void);
  int pmic_dialog_init(unsigned char bus);
@@ -88,6 +151,7 @@ int pmic_probe(struct pmic *p);
  int pmic_reg_read(struct pmic *p, u32 reg, u32 *val);
  int pmic_reg_write(struct pmic *p, u32 reg, u32 val);
  int pmic_set_output(struct pmic *p, u32 reg, int ldo, int on);

These should have comments.

Ok, I will add it.

Quoted 11 lines
Quoted 7 lines
include/power/pmic.h +1 -1
+#endif
 
  #define pmic_i2c_addr (p->hw.i2c.addr)
  #define pmic_i2c_tx_num (p->hw.i2c.tx_num)
--

1.9.1

Regards,
Simon

Thank you for the fast review :)

Best Regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#16 lore raw

PATCH 4/19 dm: pmic: add implementation of driver model pmic uclass

Hi,

Quoted 129 lines · Przemyslaw Marczak

Hello Simon,

Quoted 121 lines · Simon Glass

Hi,

Quoted 100 lines · Przemyslaw Marczak

This is an introduction to driver-model multi class PMIC support. It starts with UCLASS_PMIC - a common PMIC class type for I/O, which doesn't need to implement any specific operations and features beside the platform data, which is the 'struct pmic_platdata' defined in file:
- 'include/power/pmic.h'

New files:
- pmic-uclass.c - provides a common code for UCLASS_PMIC drivers
- pmic_i2c.c - provides dm interface for i2c pmic drivers
- pmic_spi.c - provides dm interface for spi pmic drivers

Those files are based on a current PMIC framework files and code. The new files are introduced to keep code readability and allow to make an easy drivers migration. The old pmic framework is still kept and full independent.

Changes:
- new uclass-id: UCLASS_PMIC,
- new configs: CONFIG_DM_PMIC; CONFIG_DM_PMIC_SPI; CONFIG_DM_PMIC_I2C,

New pmic api is documented in: doc/README.power-framework-dm

---
  drivers/power/Makefile      |   3 +
  drivers/power/pmic-uclass.c | 255
++++++++++++++++++++++++++++++++++++++++++++
  drivers/power/pmic_i2c.c    | 136 +++++++++++++++++++++++
  drivers/power/pmic_spi.c    | 137 ++++++++++++++++++++++++
  include/dm/uclass-id.h      |   3 +
  include/power/pmic.h        |  64 +++++++++++
  6 files changed, 598 insertions(+)

create mode 100644 drivers/power/pmic-uclass.c
create mode 100644 drivers/power/pmic_i2c.c
create mode 100644 drivers/power/pmic_spi.c

drivers/power/Makefile +3 -0
index dc64e4d..8def501 100644
@@ -19,3 +19,6 @@ obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
  obj-$(CONFIG_POWER_FSL) += power_fsl.o
  obj-$(CONFIG_POWER_I2C) += power_i2c.o
  obj-$(CONFIG_POWER_SPI) += power_spi.o
+obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
+obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
drivers/power/pmic-uclass.c new file +27 -0
new file mode 100644
index 0000000..5e8494b
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <power/pmic.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/root.h>
+#include <dm/lists.h>
+#include <i2c.h>
+#include <compiler.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg)
+{
+       if (!p)
+               return -ENODEV;
+
+       if (reg >= p->regs_num) {
+               error("<reg num> = %d is invalid. Should be less than

%d",

drivers/power/pmic-uclass.c new file +18 -0
+                      reg, p->regs_num);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C

drivers/power/pmic-uclass.c line 55

Quoted 10 lines
drivers/power/pmic-uclass.c new file +10 -0
+               return pmic_i2c_reg_write(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+#ifdef CONFIG_DM_PMIC_SPI
+               return pmic_spi_reg_write(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif

Perhaps one day we should add another uclass which is some sort of register cache. It could be implemented by I2C and SPI drivers (or better perhaps the I2C and SPI uclasses could provide a register cache uclass device for their main when requested).

Quoted 3 lines

For now this seems fine though. I think the 'return -ENOSYS' can just go at the end of the function and appear once.

Quoted 8 lines

Why do we need the registers cache?
This maybe is good for a many read operations at a time in the system, but actually I think, that it is not required for the bootloader purposes.

If we write some data - sequential, then we probably would like to check it in the same time, e.g. update some range of I2C registers.

I don't know the Chromebook design, but how it could be useful for you?

We don't need a register cache - I was just thinking of that from the kernel. But it feels like an interface like:

int reg_read(int reg, uint32_t *value)
int reg_write(int reg, uint32_t value)
int reg_write_range(int reg_start, int reg_count, uint32_t value[])

might be useful - it could be implemented for both I2C and SPI.

Quoted 72 lines
Quoted 63 lines
Quoted 63 lines
drivers/power/pmic-uclass.c new file +61 -0
+       default:
+               return -ENODEV;
+       }
+}
+
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C
+               return pmic_i2c_reg_read(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+#ifdef CONFIG_DM_PMIC_SPI
+               return pmic_spi_reg_read(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       default:
+               return -ENODEV;
+       }
+}
+
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C
+               return pmic_i2c_probe(dev);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+               if (!spi_slave)
+                       return -EINVAL;
+#ifdef CONFIG_DM_PMIC_SPI
+               spi_slave = pmic_spi_probe(dev);
+               if (!spi_slave)
+                       return -EIO;
+
+               return 0;
+#else
+               return -ENOSYS;
+#endif
+       default:
+               return -ENODEV;
+       }
Quoted 9 lines
Quoted 4 lines
drivers/power/pmic-uclass.c new file +3 -0
+}
+
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int

addr_cs)

This is an interesting function! Again we can probably improve things when we have an i2c uclass.

Quoted 3 lines

Thanks! But even if we have i2c uclass, then we also should know how to recognize each device, e.g. in the board code. So the interface and address will probably no change.

I'm thinking that one day you could do something like:

// Find the regmap associated with the device
reg_dev = device_get_child(UCLASS_REGMAP, dev);

// Call the regmap uclass to access registers whether it is I2C or SPI regmap_write(reg_dev, value);

Quoted 93 lines
Quoted 84 lines
Quoted 84 lines
drivers/power/pmic-uclass.c new file +82 -0
+{
+       struct pmic_platdata *pl;
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       if (uclass_id < 0 || uclass_id >= UCLASS_COUNT) {
+               error("Bad uclass id.\n");
+               return NULL;
+       }
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not initialized!\n", uclass_id);
+               return NULL;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev || !dev->platdata)
+                       continue;
+
+               pl = dev_get_platdata(dev);
+
+               if (pl->bus != bus)
+                       continue;
+
+               switch (pl->interface) {
+               case PMIC_I2C:
+                       if (addr_cs != pl->hw.i2c.addr)
+                               continue;
+                       break;
+               case PMIC_SPI:
+                       if (addr_cs != pl->hw.spi.cs)
+                               continue;
+                       break;
+               default:
+                       error("Unsupported interface of: %s", dev->name);
+                       return NULL;
+               }
+
+               ret = device_probe(dev);
+               if (ret) {
+                       error("Dev: %s probe failed", dev->name);
+                       return NULL;
+               }
+               return dev;
+       }
+
+       return NULL;
+}
+
+struct udevice *pmic_get_by_name(int uclass_id, char *name)
+{
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       if (uclass_id < 0 || uclass_id >= UCLASS_COUNT) {
+               error("Bad uclass id.\n");
+               return NULL;
+       }
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not initialized!", uclass_id);
+               return NULL;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev)
+                       continue;
+
+               if (!strncmp(name, dev->name, strlen(name))) {
+                       ret = device_probe(dev);
+                       if (ret)
+                               error("Dev: %s probe failed", dev->name);
+                       return dev;
+               }
+       }
+
+       return NULL;
+}
Quoted 9 lines

drivers/power/pmic-uclass.c

Quoted 2 lines
drivers/power/pmic-uclass.c new file +2 -0
+
+int pmic_init_dm(void)

I don't understand why you need this function at all. Driver model should find the pmics automatically. Is it because we don't have an i2c uclass? In that case I think it would be better to limit this hack to I2C. For SPI it should work correctly without this function.

Quoted 7 lines

Currently, it shouldn't and yes - it isn't because of the I2C uclass missing. So no one check the I2C nodes - and its child subnodes. Actually, this can be easy fixed - just don't add the "pmic" alias to the dts. But I will also change fix the code.

And by the way - is there any check in the code, which protects for the device bind more than once?

No there is no such check at present.

Quoted 473 lines
Quoted 454 lines
Quoted 403 lines
drivers/power/pmic-uclass.c new file +23 -0
+{
+       const void *blob = gd->fdt_blob;
+       const struct fdt_property *prop;
+       struct udevice *dev = NULL;
+       const char *path;
+       const char *alias;
+       int alias_node, node, offset, ret = 0;
+       int alias_len;
+       int len;
+
+       alias = "pmic";
+       alias_len = strlen(alias);
+
+       alias_node = fdt_path_offset(blob, "/aliases");
+       offset = fdt_first_property_offset(blob, alias_node);
+
+       if (offset < 0) {
+               error("Alias node not found.");
+               return -ENODEV;
+       }
+
+       offset = fdt_first_property_offset(blob, alias_node);
+       for (; offset > 0; offset = fdt_next_property_offset(blob,

offset)) {

drivers/power/pmic-uclass.c new file +29 -0
+               prop = fdt_get_property_by_offset(blob, offset, &len);
+               if (!len)
+                       continue;
+
+               path = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
+
+               if (!strncmp(alias, path, alias_len))
+                       node = fdt_path_offset(blob, prop->data);
+               else
+                       node = 0;
+
+               if (node <= 0)
+                       continue;
+
+               ret = lists_bind_fdt(gd->dm_root, blob, node, &dev);
+               if (ret < 0)
+                       continue;
+
+               if (device_probe(dev))
+                       error("Device: %s, probe error", dev->name);
+       }
+
+       return 0;
+}
+
+UCLASS_DRIVER(pmic) = {
+       .id             = UCLASS_PMIC,
+       .name           = "pmic",
+};
drivers/power/pmic_i2c.c new file +63 -0
new file mode 100644
index 0000000..350d375
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <[email protected]>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic at denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+       unsigned char buf[4] = { 0 };
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EINVAL;
+
+       I2C_SET_BUS(p->bus);
+
+       switch (pmic_i2c_tx_num) {
+       case 3:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+                       buf[2] = (cpu_to_le32(val) >> 16) & 0xff;
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[0] = cpu_to_le32(val) & 0xff;
+               } else {
+                       buf[0] = (cpu_to_le32(val) >> 16) & 0xff;
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[2] = cpu_to_le32(val) & 0xff;
+               }
+               break;
+       case 2:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[0] = cpu_to_le32(val) & 0xff;
+               } else {
+                       buf[0] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[1] = cpu_to_le32(val) & 0xff;
+               }
+               break;
+       case 1:
+               buf[0] = cpu_to_le32(val) & 0xff;
+               break;
+       default:
+               printf("%s: invalid tx_num: %d", __func__,

pmic_i2c_tx_num);

drivers/power/pmic_i2c.c new file +48 -0
+               return -1;
+       }
+
+       if (i2c_write(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+               return -1;
+
+       return 0;
+}
+
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+       unsigned char buf[4] = { 0 };
+       u32 ret_val = 0;
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EINVAL;
+
+       I2C_SET_BUS(p->bus);
+
+       if (i2c_read(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+               return -1;
+
+       switch (pmic_i2c_tx_num) {
+       case 3:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+                       ret_val = le32_to_cpu(buf[2] << 16
+                                             | buf[1] << 8 | buf[0]);
+               else
+                       ret_val = le32_to_cpu(buf[0] << 16 |
+                                             buf[1] << 8 | buf[2]);
+               break;
+       case 2:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+                       ret_val = le32_to_cpu(buf[1] << 8 | buf[0]);
+               else
+                       ret_val = le32_to_cpu(buf[0] << 8 | buf[1]);
+               break;
+       case 1:
+               ret_val = le32_to_cpu(buf[0]);
+               break;
+       default:
+               printf("%s: invalid tx_num: %d", __func__,

pmic_i2c_tx_num);

drivers/power/pmic_i2c.c new file +25 -0
+               return -1;
+       }
+       memcpy(val, &ret_val, sizeof(ret_val));
+
+       return 0;
+}
+
+int pmic_i2c_probe(struct udevice *dev)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       i2c_set_bus_num(p->bus);
+       debug("Bus: %d PMIC:%s probed!\n", p->bus, dev->name);
+       if (i2c_probe(pmic_i2c_addr)) {
+               printf("Can't find PMIC:%s\n", dev->name);
+               return -1;
+       }
+
+       return 0;
+}
drivers/power/pmic_spi.c new file +49 -0
new file mode 100644
index 0000000..7851adf
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <[email protected]>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic at denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <spi.h>
+
+static struct spi_slave *slave;
+
+void pmic_spi_free(struct spi_slave *slave)
+{
+       if (slave)
+               spi_free_slave(slave);
+}
+
+struct spi_slave *pmic_spi_probe(struct udevice *dev)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return NULL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return NULL;
+
+       return spi_setup_slave(p->bus,
+               p->hw.spi.cs,
+               p->hw.spi.clk,
+               p->hw.spi.mode);
+}
+
+static int pmic_spi_reg(struct udevice *dev, unsigned reg, unsigned

*val,

drivers/power/pmic_spi.c new file +88 -0
+                       int write)
+{
+       struct pmic_platdata *p;
+       u32 pmic_tx, pmic_rx;
+       u32 tmp;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (!slave) {
+               slave = pmic_spi_probe(p);
+
+               if (!slave)
+                       return -ENODEV;
+       }
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (spi_claim_bus(slave))
+               return -EIO;
+
+       pmic_tx = p->hw.spi.prepare_tx(reg, val, write);
+
+       tmp = cpu_to_be32(pmic_tx);
+
+       if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+                    pmic_spi_flags)) {
+               spi_release_bus(slave);
+               return -EIO;
+       }
+
+       if (write) {
+               pmic_tx = p->hw.spi.prepare_tx(reg, val, 0);
+               tmp = cpu_to_be32(pmic_tx);
+               if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+                            pmic_spi_flags)) {
+                       spi_release_bus(slave);
+                       return -EIO;
+               }
+       }
+
+       spi_release_bus(slave);
+       *val = cpu_to_be32(pmic_rx);
+
+       return 0;
+}
+
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (pmic_spi_reg(p, reg, &val, 1))
+               return -1;
+
+       return 0;
+}
+
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (pmic_spi_reg(p, reg, val, 0))
+               return -1;
+
+       return 0;
+}
include/dm/uclass-id.h +3 -0
index e3e9296..e6d9d39 100644
@@ -29,6 +29,9 @@ enum uclass_id {
         UCLASS_SPI_FLASH,       /* SPI flash */
         UCLASS_CROS_EC,         /* Chrome OS EC */
 
+       /* PMIC uclass and PMIC related uclasses */
+       UCLASS_PMIC,
+
         UCLASS_COUNT,
         UCLASS_INVALID = -1,
  };
include/power/pmic.h +8 -0
index afbc5aa..7114650 100644
@@ -1,4 +1,7 @@
  /*
+ *  Copyright (C) 2014 Samsung Electronics
+ *  Przemyslaw Marczak <[email protected]>
+ *
   *  Copyright (C) 2011-2012 Samsung Electronics
   *  Lukasz Majewski <[email protected]>
   *
@@ -8,9 +11,12 @@
  #ifndef __CORE_PMIC_H_
  #define __CORE_PMIC_H_
 
+#include <dm.h>
  #include <linux/list.h>
+#include <spi.h>
  #include <i2c.h>
  #include <power/power_chrg.h>
+#include <power/regulator.h>
 
  enum { PMIC_I2C, PMIC_SPI, PMIC_NONE};
  enum { I2C_PMIC, I2C_NUM, };
@@ -78,6 +84,63 @@ struct pmic {
         struct list_head list;
  };
 
+#ifdef CONFIG_DM_PMIC
+/* struct pmic_platdata - a standard descriptor for pmic device, which

holds
+ * an informations about interface. It is common for all pmic devices.
+ *
+ * Note:
+ * Interface fields are the same as in: struct pmic.
+ * Note: struct pmic will be removed in the future after drivers migration
+ *
+ * @bus - a physical bus on which device is connected
+ * @interface - an interface type, one of enum: PMIC_I2C, PMIC_SPI, PMIC_NONE
+ * @byte_order - one of enum: PMIC_SENSOR_BYTE_ORDER*_LITTLE/_BIG
+ * @regs_num - number of device registers
+ * @hw - one of union structure: p_i2c or p_spi
+ * based on @interface field

include/power/pmic.h struct pmic { · line 107

Quoted 7 lines
include/power/pmic.h +7 -0
+*/
+struct pmic_platdata {
+       int bus;
+       int interface;
+       int byte_order;
+       int regs_num;
+       union hw hw;

If we have a 'register cache' uclass (later, once i2c is done) then this could just be a device.

Quoted 39 lines
include/power/pmic.h +18 -0
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,
+ * for decrease a number of functions with the same code for read/write
+ * or get/set.
+ *
+ * @PMIC_OP_GET - get operation
+ * @PMIC_OP_SET - set operation
+*/
+enum pmic_op_type {
+       PMIC_OP_GET,
+       PMIC_OP_SET,
+};
+
+/* drivers/power/pmic-uclass.c */
+int power_init_dm(void);
+struct udevice *pmic_get_by_name(int uclass_id, char *name);
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int

addr_cs);

include/power/pmic.h +18 -0
+const char *pmic_if_str(int interface);
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg);
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave);
+
+/* drivers/power/pmic_i2c.c */
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_i2c_probe(struct udevice *dev);
+
+/* drivers/power/pmic_spi.c */
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+struct spi_slave *pmic_spi_probe(struct udevice *dev);
+#endif /* CONFIG_DM_PMIC */
+
+#ifdef CONFIG_POWER
  int pmic_init(unsigned char bus);
  int power_init_board(void);
Quoted 9 lines

include/power/pmic.h int pmic_probe(struct pmic *p); · line 153

Quoted 5 lines
  int pmic_dialog_init(unsigned char bus);
include/power/pmic.h +0 -0
@@ -88,6 +151,7 @@ int pmic_probe(struct pmic *p);
  int pmic_reg_read(struct pmic *p, u32 reg, u32 *val);
  int pmic_reg_write(struct pmic *p, u32 reg, u32 val);
  int pmic_set_output(struct pmic *p, u32 reg, int ldo, int on);

These should have comments.

Ok, I will add it.

Quoted 8 lines
Quoted 7 lines
include/power/pmic.h +1 -1
+#endif
 
  #define pmic_i2c_addr (p->hw.i2c.addr)
  #define pmic_i2c_tx_num (p->hw.i2c.tx_num)
--

1.9.1

Quoted 4 lines
Quoted 3 lines

Regards,
Simon

Thank you for the fast review :)

Very interested to see this development.

Regards,
Simon

#17 lore raw

PATCH 4/19 dm: pmic: add implementation of driver model pmic uclass

Hello Simon,

I missed some of your comments.

Quoted 141 lines · Simon Glass

Hi,

Quoted 129 lines · Przemyslaw Marczak

Hello Simon,

Quoted 121 lines · Simon Glass

Hi,

Quoted 100 lines · Przemyslaw Marczak

This is an introduction to driver-model multi class PMIC support. It starts with UCLASS_PMIC - a common PMIC class type for I/O, which doesn't need to implement any specific operations and features beside the platform data, which is the 'struct pmic_platdata' defined in file:
- 'include/power/pmic.h'

New files:
- pmic-uclass.c - provides a common code for UCLASS_PMIC drivers
- pmic_i2c.c - provides dm interface for i2c pmic drivers
- pmic_spi.c - provides dm interface for spi pmic drivers

Those files are based on a current PMIC framework files and code. The new files are introduced to keep code readability and allow to make an easy drivers migration. The old pmic framework is still kept and full independent.

Changes:
- new uclass-id: UCLASS_PMIC,
- new configs: CONFIG_DM_PMIC; CONFIG_DM_PMIC_SPI; CONFIG_DM_PMIC_I2C,

New pmic api is documented in: doc/README.power-framework-dm

---
   drivers/power/Makefile      |   3 +
   drivers/power/pmic-uclass.c | 255
++++++++++++++++++++++++++++++++++++++++++++
   drivers/power/pmic_i2c.c    | 136 +++++++++++++++++++++++
   drivers/power/pmic_spi.c    | 137 ++++++++++++++++++++++++
   include/dm/uclass-id.h      |   3 +
   include/power/pmic.h        |  64 +++++++++++
   6 files changed, 598 insertions(+)

create mode 100644 drivers/power/pmic-uclass.c
create mode 100644 drivers/power/pmic_i2c.c
create mode 100644 drivers/power/pmic_spi.c

drivers/power/Makefile +3 -0
index dc64e4d..8def501 100644
@@ -19,3 +19,6 @@ obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
   obj-$(CONFIG_POWER_FSL) += power_fsl.o
   obj-$(CONFIG_POWER_I2C) += power_i2c.o
   obj-$(CONFIG_POWER_SPI) += power_spi.o
+obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
+obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
drivers/power/pmic-uclass.c new file +27 -0
new file mode 100644
index 0000000..5e8494b
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <power/pmic.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/root.h>
+#include <dm/lists.h>
+#include <i2c.h>
+#include <compiler.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg)
+{
+       if (!p)
+               return -ENODEV;
+
+       if (reg >= p->regs_num) {
+               error("<reg num> = %d is invalid. Should be less than

%d",

drivers/power/pmic-uclass.c new file +18 -0
+                      reg, p->regs_num);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C

drivers/power/pmic-uclass.c line 55

Quoted 10 lines
drivers/power/pmic-uclass.c new file +10 -0
+               return pmic_i2c_reg_write(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+#ifdef CONFIG_DM_PMIC_SPI
+               return pmic_spi_reg_write(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif

Perhaps one day we should add another uclass which is some sort of register cache. It could be implemented by I2C and SPI drivers (or better perhaps the I2C and SPI uclasses could provide a register cache uclass device for their main when requested).

Quoted 3 lines

For now this seems fine though. I think the 'return -ENOSYS' can just go at the end of the function and appear once.

Quoted 8 lines

Why do we need the registers cache?
This maybe is good for a many read operations at a time in the system, but actually I think, that it is not required for the bootloader purposes.

If we write some data - sequential, then we probably would like to check it in the same time, e.g. update some range of I2C registers.

I don't know the Chromebook design, but how it could be useful for you?

Quoted 9 lines

We don't need a register cache - I was just thinking of that from the kernel. But it feels like an interface like:

int reg_read(int reg, uint32_t *value)
int reg_write(int reg, uint32_t value)
int reg_write_range(int reg_start, int reg_count, uint32_t value[])

might be useful - it could be implemented for both I2C and SPI.

Ok, now I see.

Quoted 76 lines
Quoted 72 lines
Quoted 63 lines
Quoted 63 lines
drivers/power/pmic-uclass.c new file +61 -0
+       default:
+               return -ENODEV;
+       }
+}
+
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C
+               return pmic_i2c_reg_read(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+#ifdef CONFIG_DM_PMIC_SPI
+               return pmic_spi_reg_read(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       default:
+               return -ENODEV;
+       }
+}
+
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C
+               return pmic_i2c_probe(dev);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+               if (!spi_slave)
+                       return -EINVAL;
+#ifdef CONFIG_DM_PMIC_SPI
+               spi_slave = pmic_spi_probe(dev);
+               if (!spi_slave)
+                       return -EIO;
+
+               return 0;
+#else
+               return -ENOSYS;
+#endif
+       default:
+               return -ENODEV;
+       }
Quoted 9 lines
Quoted 4 lines
drivers/power/pmic-uclass.c new file +3 -0
+}
+
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int

addr_cs)

This is an interesting function! Again we can probably improve things when we have an i2c uclass.

Quoted 3 lines

Thanks! But even if we have i2c uclass, then we also should know how to recognize each device, e.g. in the board code. So the interface and address will probably no change.

Quoted 8 lines

I'm thinking that one day you could do something like:

// Find the regmap associated with the device
reg_dev = device_get_child(UCLASS_REGMAP, dev);

// Call the regmap uclass to access registers whether it is I2C or SPI regmap_write(reg_dev, value);

This could be an another interesting possible feature of dm.

Quoted 579 lines
Quoted 93 lines
Quoted 84 lines
Quoted 84 lines
drivers/power/pmic-uclass.c new file +82 -0
+{
+       struct pmic_platdata *pl;
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       if (uclass_id < 0 || uclass_id >= UCLASS_COUNT) {
+               error("Bad uclass id.\n");
+               return NULL;
+       }
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not initialized!\n", uclass_id);
+               return NULL;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev || !dev->platdata)
+                       continue;
+
+               pl = dev_get_platdata(dev);
+
+               if (pl->bus != bus)
+                       continue;
+
+               switch (pl->interface) {
+               case PMIC_I2C:
+                       if (addr_cs != pl->hw.i2c.addr)
+                               continue;
+                       break;
+               case PMIC_SPI:
+                       if (addr_cs != pl->hw.spi.cs)
+                               continue;
+                       break;
+               default:
+                       error("Unsupported interface of: %s", dev->name);
+                       return NULL;
+               }
+
+               ret = device_probe(dev);
+               if (ret) {
+                       error("Dev: %s probe failed", dev->name);
+                       return NULL;
+               }
+               return dev;
+       }
+
+       return NULL;
+}
+
+struct udevice *pmic_get_by_name(int uclass_id, char *name)
+{
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       if (uclass_id < 0 || uclass_id >= UCLASS_COUNT) {
+               error("Bad uclass id.\n");
+               return NULL;
+       }
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not initialized!", uclass_id);
+               return NULL;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev)
+                       continue;
+
+               if (!strncmp(name, dev->name, strlen(name))) {
+                       ret = device_probe(dev);
+                       if (ret)
+                               error("Dev: %s probe failed", dev->name);
+                       return dev;
+               }
+       }
+
+       return NULL;
+}
Quoted 9 lines

drivers/power/pmic-uclass.c

Quoted 2 lines
drivers/power/pmic-uclass.c new file +2 -0
+
+int pmic_init_dm(void)

I don't understand why you need this function at all. Driver model should find the pmics automatically. Is it because we don't have an i2c uclass? In that case I think it would be better to limit this hack to I2C. For SPI it should work correctly without this function.

Quoted 7 lines

Currently, it shouldn't and yes - it isn't because of the I2C uclass missing. So no one check the I2C nodes - and its child subnodes. Actually, this can be easy fixed - just don't add the "pmic" alias to the dts. But I will also change fix the code.

And by the way - is there any check in the code, which protects for the device bind more than once?

No there is no such check at present.

Quoted 476 lines
Quoted 454 lines
Quoted 403 lines
drivers/power/pmic-uclass.c new file +23 -0
+{
+       const void *blob = gd->fdt_blob;
+       const struct fdt_property *prop;
+       struct udevice *dev = NULL;
+       const char *path;
+       const char *alias;
+       int alias_node, node, offset, ret = 0;
+       int alias_len;
+       int len;
+
+       alias = "pmic";
+       alias_len = strlen(alias);
+
+       alias_node = fdt_path_offset(blob, "/aliases");
+       offset = fdt_first_property_offset(blob, alias_node);
+
+       if (offset < 0) {
+               error("Alias node not found.");
+               return -ENODEV;
+       }
+
+       offset = fdt_first_property_offset(blob, alias_node);
+       for (; offset > 0; offset = fdt_next_property_offset(blob,

offset)) {

drivers/power/pmic-uclass.c new file +29 -0
+               prop = fdt_get_property_by_offset(blob, offset, &len);
+               if (!len)
+                       continue;
+
+               path = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
+
+               if (!strncmp(alias, path, alias_len))
+                       node = fdt_path_offset(blob, prop->data);
+               else
+                       node = 0;
+
+               if (node <= 0)
+                       continue;
+
+               ret = lists_bind_fdt(gd->dm_root, blob, node, &dev);
+               if (ret < 0)
+                       continue;
+
+               if (device_probe(dev))
+                       error("Device: %s, probe error", dev->name);
+       }
+
+       return 0;
+}
+
+UCLASS_DRIVER(pmic) = {
+       .id             = UCLASS_PMIC,
+       .name           = "pmic",
+};
drivers/power/pmic_i2c.c new file +63 -0
new file mode 100644
index 0000000..350d375
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <[email protected]>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic at denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+       unsigned char buf[4] = { 0 };
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EINVAL;
+
+       I2C_SET_BUS(p->bus);
+
+       switch (pmic_i2c_tx_num) {
+       case 3:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+                       buf[2] = (cpu_to_le32(val) >> 16) & 0xff;
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[0] = cpu_to_le32(val) & 0xff;
+               } else {
+                       buf[0] = (cpu_to_le32(val) >> 16) & 0xff;
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[2] = cpu_to_le32(val) & 0xff;
+               }
+               break;
+       case 2:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[0] = cpu_to_le32(val) & 0xff;
+               } else {
+                       buf[0] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[1] = cpu_to_le32(val) & 0xff;
+               }
+               break;
+       case 1:
+               buf[0] = cpu_to_le32(val) & 0xff;
+               break;
+       default:
+               printf("%s: invalid tx_num: %d", __func__,

pmic_i2c_tx_num);

drivers/power/pmic_i2c.c new file +48 -0
+               return -1;
+       }
+
+       if (i2c_write(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+               return -1;
+
+       return 0;
+}
+
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+       unsigned char buf[4] = { 0 };
+       u32 ret_val = 0;
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EINVAL;
+
+       I2C_SET_BUS(p->bus);
+
+       if (i2c_read(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+               return -1;
+
+       switch (pmic_i2c_tx_num) {
+       case 3:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+                       ret_val = le32_to_cpu(buf[2] << 16
+                                             | buf[1] << 8 | buf[0]);
+               else
+                       ret_val = le32_to_cpu(buf[0] << 16 |
+                                             buf[1] << 8 | buf[2]);
+               break;
+       case 2:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+                       ret_val = le32_to_cpu(buf[1] << 8 | buf[0]);
+               else
+                       ret_val = le32_to_cpu(buf[0] << 8 | buf[1]);
+               break;
+       case 1:
+               ret_val = le32_to_cpu(buf[0]);
+               break;
+       default:
+               printf("%s: invalid tx_num: %d", __func__,

pmic_i2c_tx_num);

drivers/power/pmic_i2c.c new file +25 -0
+               return -1;
+       }
+       memcpy(val, &ret_val, sizeof(ret_val));
+
+       return 0;
+}
+
+int pmic_i2c_probe(struct udevice *dev)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       i2c_set_bus_num(p->bus);
+       debug("Bus: %d PMIC:%s probed!\n", p->bus, dev->name);
+       if (i2c_probe(pmic_i2c_addr)) {
+               printf("Can't find PMIC:%s\n", dev->name);
+               return -1;
+       }
+
+       return 0;
+}
drivers/power/pmic_spi.c new file +49 -0
new file mode 100644
index 0000000..7851adf
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <[email protected]>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic at denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <spi.h>
+
+static struct spi_slave *slave;
+
+void pmic_spi_free(struct spi_slave *slave)
+{
+       if (slave)
+               spi_free_slave(slave);
+}
+
+struct spi_slave *pmic_spi_probe(struct udevice *dev)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return NULL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return NULL;
+
+       return spi_setup_slave(p->bus,
+               p->hw.spi.cs,
+               p->hw.spi.clk,
+               p->hw.spi.mode);
+}
+
+static int pmic_spi_reg(struct udevice *dev, unsigned reg, unsigned

*val,

drivers/power/pmic_spi.c new file +88 -0
+                       int write)
+{
+       struct pmic_platdata *p;
+       u32 pmic_tx, pmic_rx;
+       u32 tmp;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (!slave) {
+               slave = pmic_spi_probe(p);
+
+               if (!slave)
+                       return -ENODEV;
+       }
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (spi_claim_bus(slave))
+               return -EIO;
+
+       pmic_tx = p->hw.spi.prepare_tx(reg, val, write);
+
+       tmp = cpu_to_be32(pmic_tx);
+
+       if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+                    pmic_spi_flags)) {
+               spi_release_bus(slave);
+               return -EIO;
+       }
+
+       if (write) {
+               pmic_tx = p->hw.spi.prepare_tx(reg, val, 0);
+               tmp = cpu_to_be32(pmic_tx);
+               if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+                            pmic_spi_flags)) {
+                       spi_release_bus(slave);
+                       return -EIO;
+               }
+       }
+
+       spi_release_bus(slave);
+       *val = cpu_to_be32(pmic_rx);
+
+       return 0;
+}
+
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (pmic_spi_reg(p, reg, &val, 1))
+               return -1;
+
+       return 0;
+}
+
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (pmic_spi_reg(p, reg, val, 0))
+               return -1;
+
+       return 0;
+}
include/dm/uclass-id.h +3 -0
index e3e9296..e6d9d39 100644
@@ -29,6 +29,9 @@ enum uclass_id {
          UCLASS_SPI_FLASH,       /* SPI flash */
          UCLASS_CROS_EC,         /* Chrome OS EC */
 
+       /* PMIC uclass and PMIC related uclasses */
+       UCLASS_PMIC,
+
          UCLASS_COUNT,
          UCLASS_INVALID = -1,
   };
include/power/pmic.h +8 -0
index afbc5aa..7114650 100644
@@ -1,4 +1,7 @@
   /*
+ *  Copyright (C) 2014 Samsung Electronics
+ *  Przemyslaw Marczak <[email protected]>
+ *
    *  Copyright (C) 2011-2012 Samsung Electronics
    *  Lukasz Majewski <[email protected]>
    *
@@ -8,9 +11,12 @@
   #ifndef __CORE_PMIC_H_
   #define __CORE_PMIC_H_
 
+#include <dm.h>
   #include <linux/list.h>
+#include <spi.h>
   #include <i2c.h>
   #include <power/power_chrg.h>
+#include <power/regulator.h>
 
   enum { PMIC_I2C, PMIC_SPI, PMIC_NONE};
   enum { I2C_PMIC, I2C_NUM, };
@@ -78,6 +84,63 @@ struct pmic {
          struct list_head list;
   };
 
+#ifdef CONFIG_DM_PMIC
+/* struct pmic_platdata - a standard descriptor for pmic device, which

holds
+ * an informations about interface. It is common for all pmic devices.
+ *
+ * Note:
+ * Interface fields are the same as in: struct pmic.
+ * Note: struct pmic will be removed in the future after drivers migration
+ *
+ * @bus - a physical bus on which device is connected
+ * @interface - an interface type, one of enum: PMIC_I2C, PMIC_SPI, PMIC_NONE
+ * @byte_order - one of enum: PMIC_SENSOR_BYTE_ORDER*_LITTLE/_BIG
+ * @regs_num - number of device registers
+ * @hw - one of union structure: p_i2c or p_spi
+ * based on @interface field

include/power/pmic.h struct pmic { · line 107

Quoted 7 lines
include/power/pmic.h +7 -0
+*/
+struct pmic_platdata {
+       int bus;
+       int interface;
+       int byte_order;
+       int regs_num;
+       union hw hw;

If we have a 'register cache' uclass (later, once i2c is done) then this could just be a device.

Quoted 39 lines
include/power/pmic.h +18 -0
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,
+ * for decrease a number of functions with the same code for read/write
+ * or get/set.
+ *
+ * @PMIC_OP_GET - get operation
+ * @PMIC_OP_SET - set operation
+*/
+enum pmic_op_type {
+       PMIC_OP_GET,
+       PMIC_OP_SET,
+};
+
+/* drivers/power/pmic-uclass.c */
+int power_init_dm(void);
+struct udevice *pmic_get_by_name(int uclass_id, char *name);
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int

addr_cs);

include/power/pmic.h +18 -0
+const char *pmic_if_str(int interface);
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg);
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave);
+
+/* drivers/power/pmic_i2c.c */
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_i2c_probe(struct udevice *dev);
+
+/* drivers/power/pmic_spi.c */
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+struct spi_slave *pmic_spi_probe(struct udevice *dev);
+#endif /* CONFIG_DM_PMIC */
+
+#ifdef CONFIG_POWER
   int pmic_init(unsigned char bus);
   int power_init_board(void);
Quoted 9 lines

include/power/pmic.h int pmic_probe(struct pmic *p); · line 153

Quoted 5 lines
   int pmic_dialog_init(unsigned char bus);
include/power/pmic.h +0 -0
@@ -88,6 +151,7 @@ int pmic_probe(struct pmic *p);
   int pmic_reg_read(struct pmic *p, u32 reg, u32 *val);
   int pmic_reg_write(struct pmic *p, u32 reg, u32 val);
   int pmic_set_output(struct pmic *p, u32 reg, int ldo, int on);

These should have comments.

Ok, I will add it.

Quoted 11 lines
Quoted 7 lines
include/power/pmic.h +1 -1
+#endif
 
   #define pmic_i2c_addr (p->hw.i2c.addr)
   #define pmic_i2c_tx_num (p->hw.i2c.tx_num)
--

1.9.1

Regards,
Simon

Quoted 7 lines
Quoted 1 line

Thank you for the fast review :)

Very interested to see this development.

Regards,
Simon

Thank you again.
I'm going to check the i2c-working tree and maybe rebase the dm-pmic onto it.

Is it good idea?

Best Regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#18 lore raw

PATCH 4/19 dm: pmic: add implementation of driver model pmic uclass

Hi,

Quoted 864 lines · Przemyslaw Marczak

Hello Simon,

I missed some of your comments.

Quoted 151 lines · Simon Glass

Hi,

Quoted 139 lines · Przemyslaw Marczak

Hello Simon,

Quoted 125 lines · Simon Glass

Hi,

Quoted 101 lines · Przemyslaw Marczak

This is an introduction to driver-model multi class PMIC support. It starts with UCLASS_PMIC - a common PMIC class type for I/O, which doesn't need to implement any specific operations and features beside the platform data, which is the 'struct pmic_platdata' defined in file:
- 'include/power/pmic.h'

New files:
- pmic-uclass.c - provides a common code for UCLASS_PMIC drivers
- pmic_i2c.c - provides dm interface for i2c pmic drivers
- pmic_spi.c - provides dm interface for spi pmic drivers

Those files are based on a current PMIC framework files and code. The new files are introduced to keep code readability and allow to make an easy drivers migration. The old pmic framework is still kept and full independent.

Changes:
- new uclass-id: UCLASS_PMIC,
- new configs: CONFIG_DM_PMIC; CONFIG_DM_PMIC_SPI; CONFIG_DM_PMIC_I2C,

New pmic api is documented in: doc/README.power-framework-dm

---
   drivers/power/Makefile      |   3 +
   drivers/power/pmic-uclass.c | 255
++++++++++++++++++++++++++++++++++++++++++++
   drivers/power/pmic_i2c.c    | 136 +++++++++++++++++++++++
   drivers/power/pmic_spi.c    | 137 ++++++++++++++++++++++++
   include/dm/uclass-id.h      |   3 +
   include/power/pmic.h        |  64 +++++++++++
   6 files changed, 598 insertions(+)

create mode 100644 drivers/power/pmic-uclass.c
create mode 100644 drivers/power/pmic_i2c.c
create mode 100644 drivers/power/pmic_spi.c

drivers/power/Makefile +3 -0
index dc64e4d..8def501 100644
@@ -19,3 +19,6 @@ obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
   obj-$(CONFIG_POWER_FSL) += power_fsl.o
   obj-$(CONFIG_POWER_I2C) += power_i2c.o
   obj-$(CONFIG_POWER_SPI) += power_spi.o
+obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
+obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
drivers/power/pmic-uclass.c new file +27 -0
new file mode 100644
index 0000000..5e8494b
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <power/pmic.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/root.h>
+#include <dm/lists.h>
+#include <i2c.h>
+#include <compiler.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg)
+{
+       if (!p)
+               return -ENODEV;
+
+       if (reg >= p->regs_num) {
+               error("<reg num> = %d is invalid. Should be less than

%d",

drivers/power/pmic-uclass.c new file +18 -0
+                      reg, p->regs_num);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C

drivers/power/pmic-uclass.c line 55

Quoted 10 lines
drivers/power/pmic-uclass.c new file +10 -0
+               return pmic_i2c_reg_write(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+#ifdef CONFIG_DM_PMIC_SPI
+               return pmic_spi_reg_write(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif

Perhaps one day we should add another uclass which is some sort of register cache. It could be implemented by I2C and SPI drivers (or better perhaps the I2C and SPI uclasses could provide a register cache uclass device for their main when requested).

Quoted 3 lines

For now this seems fine though. I think the 'return -ENOSYS' can just go at the end of the function and appear once.

Why do we need the registers cache?
This maybe is good for a many read operations at a time in the system, but actually I think, that it is not required for the bootloader purposes.

Quoted 5 lines

If we write some data - sequential, then we probably would like to check it
in the same time, e.g. update some range of I2C registers.

I don't know the Chromebook design, but how it could be useful for you?

Quoted 9 lines

We don't need a register cache - I was just thinking of that from the kernel. But it feels like an interface like:

int reg_read(int reg, uint32_t *value)
int reg_write(int reg, uint32_t value)
int reg_write_range(int reg_start, int reg_count, uint32_t value[])

might be useful - it could be implemented for both I2C and SPI.

Ok, now I see.

Quoted 79 lines
Quoted 73 lines
Quoted 64 lines
Quoted 64 lines
drivers/power/pmic-uclass.c new file +62 -0
+       default:
+               return -ENODEV;
+       }
+}
+
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C
+               return pmic_i2c_reg_read(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+#ifdef CONFIG_DM_PMIC_SPI
+               return pmic_spi_reg_read(dev, reg, val);
+#else
+               return -ENOSYS;
+#endif
+       default:
+               return -ENODEV;
+       }
+}
+
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave)
+{
+       struct pmic_platdata *p;
+
+       p = dev_get_platdata(dev);
+       if (!p)
+               return -EPERM;
+
+       switch (p->interface) {
+       case PMIC_I2C:
+#ifdef CONFIG_DM_PMIC_I2C
+               return pmic_i2c_probe(dev);
+#else
+               return -ENOSYS;
+#endif
+       case PMIC_SPI:
+               if (!spi_slave)
+                       return -EINVAL;
+#ifdef CONFIG_DM_PMIC_SPI
+               spi_slave = pmic_spi_probe(dev);
+               if (!spi_slave)
+                       return -EIO;
+
+               return 0;
+#else
+               return -ENOSYS;
+#endif
+       default:
+               return -ENODEV;
+       }
+}
Quoted 9 lines
Quoted 3 lines
drivers/power/pmic-uclass.c new file +2 -0
+
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int

addr_cs)

This is an interesting function! Again we can probably improve things when we have an i2c uclass.

Quoted 4 lines

Thanks! But even if we have i2c uclass, then we also should know how to recognize each device, e.g. in the board code. So the interface and address
will probably no change.

Quoted 8 lines

I'm thinking that one day you could do something like:

// Find the regmap associated with the device
reg_dev = device_get_child(UCLASS_REGMAP, dev);

// Call the regmap uclass to access registers whether it is I2C or SPI regmap_write(reg_dev, value);

This could be an another interesting possible feature of dm.

Quoted 594 lines
Quoted 96 lines
Quoted 87 lines
Quoted 87 lines
drivers/power/pmic-uclass.c new file +37 -0
+{
+       struct pmic_platdata *pl;
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       if (uclass_id < 0 || uclass_id >= UCLASS_COUNT) {
+               error("Bad uclass id.\n");
+               return NULL;
+       }
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not initialized!\n", uclass_id);
+               return NULL;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev || !dev->platdata)
+                       continue;
+
+               pl = dev_get_platdata(dev);
+
+               if (pl->bus != bus)
+                       continue;
+
+               switch (pl->interface) {
+               case PMIC_I2C:
+                       if (addr_cs != pl->hw.i2c.addr)
+                               continue;
+                       break;
+               case PMIC_SPI:
+                       if (addr_cs != pl->hw.spi.cs)
+                               continue;
+                       break;
+               default:
+                       error("Unsupported interface of: %s",

dev->name);

drivers/power/pmic-uclass.c new file +39 -0
+                       return NULL;
+               }
+
+               ret = device_probe(dev);
+               if (ret) {
+                       error("Dev: %s probe failed", dev->name);
+                       return NULL;
+               }
+               return dev;
+       }
+
+       return NULL;
+}
+
+struct udevice *pmic_get_by_name(int uclass_id, char *name)
+{
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       if (uclass_id < 0 || uclass_id >= UCLASS_COUNT) {
+               error("Bad uclass id.\n");
+               return NULL;
+       }
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not initialized!", uclass_id);
+               return NULL;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev)
+                       continue;
+
+               if (!strncmp(name, dev->name, strlen(name))) {
+                       ret = device_probe(dev);
+                       if (ret)
+                               error("Dev: %s probe failed",

dev->name);

drivers/power/pmic-uclass.c new file +7 -0
+                       return dev;
+               }
+       }
+
+       return NULL;
+}
+
Quoted 9 lines
Quoted 1 line

+int pmic_init_dm(void)

I don't understand why you need this function at all. Driver model should find the pmics automatically. Is it because we don't have an i2c uclass? In that case I think it would be better to limit this hack to I2C. For SPI it should work correctly without this function.

Quoted 7 lines

Currently, it shouldn't and yes - it isn't because of the I2C uclass missing. So no one check the I2C nodes - and its child subnodes. Actually, this can be easy fixed - just don't add the "pmic" alias to the dts. But I will also change fix the code.

And by the way - is there any check in the code, which protects for the device bind more than once?

No there is no such check at present.

Quoted 487 lines
Quoted 465 lines
Quoted 407 lines
drivers/power/pmic-uclass.c new file +23 -0
+{
+       const void *blob = gd->fdt_blob;
+       const struct fdt_property *prop;
+       struct udevice *dev = NULL;
+       const char *path;
+       const char *alias;
+       int alias_node, node, offset, ret = 0;
+       int alias_len;
+       int len;
+
+       alias = "pmic";
+       alias_len = strlen(alias);
+
+       alias_node = fdt_path_offset(blob, "/aliases");
+       offset = fdt_first_property_offset(blob, alias_node);
+
+       if (offset < 0) {
+               error("Alias node not found.");
+               return -ENODEV;
+       }
+
+       offset = fdt_first_property_offset(blob, alias_node);
+       for (; offset > 0; offset = fdt_next_property_offset(blob,

offset)) {

drivers/power/pmic-uclass.c new file +29 -0
+               prop = fdt_get_property_by_offset(blob, offset, &len);
+               if (!len)
+                       continue;
+
+               path = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
+
+               if (!strncmp(alias, path, alias_len))
+                       node = fdt_path_offset(blob, prop->data);
+               else
+                       node = 0;
+
+               if (node <= 0)
+                       continue;
+
+               ret = lists_bind_fdt(gd->dm_root, blob, node, &dev);
+               if (ret < 0)
+                       continue;
+
+               if (device_probe(dev))
+                       error("Device: %s, probe error", dev->name);
+       }
+
+       return 0;
+}
+
+UCLASS_DRIVER(pmic) = {
+       .id             = UCLASS_PMIC,
+       .name           = "pmic",
+};
drivers/power/pmic_i2c.c new file +23 -0
new file mode 100644
index 0000000..350d375
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <[email protected]>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic at denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned

val)

drivers/power/pmic_i2c.c new file +40 -0
+{
+       struct pmic_platdata *p;
+       unsigned char buf[4] = { 0 };
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EINVAL;
+
+       I2C_SET_BUS(p->bus);
+
+       switch (pmic_i2c_tx_num) {
+       case 3:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+                       buf[2] = (cpu_to_le32(val) >> 16) & 0xff;
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[0] = cpu_to_le32(val) & 0xff;
+               } else {
+                       buf[0] = (cpu_to_le32(val) >> 16) & 0xff;
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[2] = cpu_to_le32(val) & 0xff;
+               }
+               break;
+       case 2:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+                       buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[0] = cpu_to_le32(val) & 0xff;
+               } else {
+                       buf[0] = (cpu_to_le32(val) >> 8) & 0xff;
+                       buf[1] = cpu_to_le32(val) & 0xff;
+               }
+               break;
+       case 1:
+               buf[0] = cpu_to_le32(val) & 0xff;
+               break;
+       default:
+               printf("%s: invalid tx_num: %d", __func__,

pmic_i2c_tx_num);

drivers/power/pmic_i2c.c new file +10 -0
+               return -1;
+       }
+
+       if (i2c_write(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+               return -1;
+
+       return 0;
+}
+
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned

*val)

drivers/power/pmic_i2c.c new file +38 -0
+{
+       struct pmic_platdata *p;
+       unsigned char buf[4] = { 0 };
+       u32 ret_val = 0;
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EINVAL;
+
+       I2C_SET_BUS(p->bus);
+
+       if (i2c_read(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+               return -1;
+
+       switch (pmic_i2c_tx_num) {
+       case 3:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+                       ret_val = le32_to_cpu(buf[2] << 16
+                                             | buf[1] << 8 | buf[0]);
+               else
+                       ret_val = le32_to_cpu(buf[0] << 16 |
+                                             buf[1] << 8 | buf[2]);
+               break;
+       case 2:
+               if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+                       ret_val = le32_to_cpu(buf[1] << 8 | buf[0]);
+               else
+                       ret_val = le32_to_cpu(buf[0] << 8 | buf[1]);
+               break;
+       case 1:
+               ret_val = le32_to_cpu(buf[0]);
+               break;
+       default:
+               printf("%s: invalid tx_num: %d", __func__,

pmic_i2c_tx_num);

drivers/power/pmic_i2c.c new file +25 -0
+               return -1;
+       }
+       memcpy(val, &ret_val, sizeof(ret_val));
+
+       return 0;
+}
+
+int pmic_i2c_probe(struct udevice *dev)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -ENODEV;
+
+       p = dev->platdata;
+
+       i2c_set_bus_num(p->bus);
+       debug("Bus: %d PMIC:%s probed!\n", p->bus, dev->name);
+       if (i2c_probe(pmic_i2c_addr)) {
+               printf("Can't find PMIC:%s\n", dev->name);
+               return -1;
+       }
+
+       return 0;
+}
drivers/power/pmic_spi.c new file +49 -0
new file mode 100644
index 0000000..7851adf
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <[email protected]>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic at denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <spi.h>
+
+static struct spi_slave *slave;
+
+void pmic_spi_free(struct spi_slave *slave)
+{
+       if (slave)
+               spi_free_slave(slave);
+}
+
+struct spi_slave *pmic_spi_probe(struct udevice *dev)
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return NULL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return NULL;
+
+       return spi_setup_slave(p->bus,
+               p->hw.spi.cs,
+               p->hw.spi.clk,
+               p->hw.spi.mode);
+}
+
+static int pmic_spi_reg(struct udevice *dev, unsigned reg, unsigned

*val,

drivers/power/pmic_spi.c new file +54 -0
+                       int write)
+{
+       struct pmic_platdata *p;
+       u32 pmic_tx, pmic_rx;
+       u32 tmp;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (!slave) {
+               slave = pmic_spi_probe(p);
+
+               if (!slave)
+                       return -ENODEV;
+       }
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (spi_claim_bus(slave))
+               return -EIO;
+
+       pmic_tx = p->hw.spi.prepare_tx(reg, val, write);
+
+       tmp = cpu_to_be32(pmic_tx);
+
+       if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+                    pmic_spi_flags)) {
+               spi_release_bus(slave);
+               return -EIO;
+       }
+
+       if (write) {
+               pmic_tx = p->hw.spi.prepare_tx(reg, val, 0);
+               tmp = cpu_to_be32(pmic_tx);
+               if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+                            pmic_spi_flags)) {
+                       spi_release_bus(slave);
+                       return -EIO;
+               }
+       }
+
+       spi_release_bus(slave);
+       *val = cpu_to_be32(pmic_rx);
+
+       return 0;
+}
+
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned

val)

drivers/power/pmic_spi.c new file +18 -0
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (pmic_spi_reg(p, reg, &val, 1))
+               return -1;
+
+       return 0;
+}
+
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned

*val)

drivers/power/pmic_spi.c new file +16 -0
+{
+       struct pmic_platdata *p;
+
+       if (!dev)
+               return -EINVAL;
+
+       p = dev->platdata;
+
+       if (pmic_check_reg(p, reg))
+               return -EFAULT;
+
+       if (pmic_spi_reg(p, reg, val, 0))
+               return -1;
+
+       return 0;
+}
include/dm/uclass-id.h +3 -0
index e3e9296..e6d9d39 100644
@@ -29,6 +29,9 @@ enum uclass_id {
          UCLASS_SPI_FLASH,       /* SPI flash */
          UCLASS_CROS_EC,         /* Chrome OS EC */
 
+       /* PMIC uclass and PMIC related uclasses */
+       UCLASS_PMIC,
+
          UCLASS_COUNT,
          UCLASS_INVALID = -1,
   };
include/power/pmic.h +8 -0
index afbc5aa..7114650 100644
@@ -1,4 +1,7 @@
   /*
+ *  Copyright (C) 2014 Samsung Electronics
+ *  Przemyslaw Marczak <[email protected]>
+ *
    *  Copyright (C) 2011-2012 Samsung Electronics
    *  Lukasz Majewski <[email protected]>
    *
@@ -8,9 +11,12 @@
   #ifndef __CORE_PMIC_H_
   #define __CORE_PMIC_H_
 
+#include <dm.h>
   #include <linux/list.h>
+#include <spi.h>
   #include <i2c.h>
   #include <power/power_chrg.h>
+#include <power/regulator.h>
 
   enum { PMIC_I2C, PMIC_SPI, PMIC_NONE};
   enum { I2C_PMIC, I2C_NUM, };
@@ -78,6 +84,63 @@ struct pmic {
          struct list_head list;
   };
 
+#ifdef CONFIG_DM_PMIC
+/* struct pmic_platdata - a standard descriptor for pmic device, which

holds
+ * an informations about interface. It is common for all pmic devices.
+ *
+ * Note:
+ * Interface fields are the same as in: struct pmic.
+ * Note: struct pmic will be removed in the future after drivers migration
+ *
+ * @bus - a physical bus on which device is connected
+ * @interface - an interface type, one of enum: PMIC_I2C, PMIC_SPI, PMIC_NONE
+ * @byte_order - one of enum: PMIC_SENSOR_BYTE_ORDER*_LITTLE/_BIG
+ * @regs_num - number of device registers
+ * @hw - one of union structure: p_i2c or p_spi
+ * based on @interface field

include/power/pmic.h struct pmic { · line 107

Quoted 7 lines
include/power/pmic.h +7 -0
+*/
+struct pmic_platdata {
+       int bus;
+       int interface;
+       int byte_order;
+       int regs_num;
+       union hw hw;

If we have a 'register cache' uclass (later, once i2c is done) then this could just be a device.

Quoted 45 lines
include/power/pmic.h +4 -0
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,
+ * for decrease a number of functions with the same code for

read/write
+ * or get/set.
+ *
+ * @PMIC_OP_GET - get operation
+ * @PMIC_OP_SET - set operation

include/power/pmic.h +10 -0
+*/
+enum pmic_op_type {
+       PMIC_OP_GET,
+       PMIC_OP_SET,
+};
+
+/* drivers/power/pmic-uclass.c */
+int power_init_dm(void);
+struct udevice *pmic_get_by_name(int uclass_id, char *name);
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int

addr_cs);

include/power/pmic.h +8 -0
+const char *pmic_if_str(int interface);
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg);
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave);
+
+/* drivers/power/pmic_i2c.c */
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned

val);
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val);

include/power/pmic.h +4 -0
+int pmic_i2c_probe(struct udevice *dev);
+
+/* drivers/power/pmic_spi.c */
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned

val);
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val);

include/power/pmic.h +4 -0
+struct spi_slave *pmic_spi_probe(struct udevice *dev);
+#endif /* CONFIG_DM_PMIC */
+
+#ifdef CONFIG_POWER
   int pmic_init(unsigned char bus);
   int power_init_board(void);
   int pmic_dialog_init(unsigned char bus);
Quoted 9 lines

include/power/pmic.h int pmic_probe(struct pmic *p); · line 153

Quoted 4 lines
include/power/pmic.h +0 -0
@@ -88,6 +151,7 @@ int pmic_probe(struct pmic *p);
   int pmic_reg_read(struct pmic *p, u32 reg, u32 *val);
   int pmic_reg_write(struct pmic *p, u32 reg, u32 val);
   int pmic_set_output(struct pmic *p, u32 reg, int ldo, int on);

These should have comments.

Ok, I will add it.

Quoted 11 lines
Quoted 7 lines
include/power/pmic.h +1 -1
+#endif
 
   #define pmic_i2c_addr (p->hw.i2c.addr)
   #define pmic_i2c_tx_num (p->hw.i2c.tx_num)
--

1.9.1

Regards,
Simon

Quoted 8 lines
Quoted 1 line

Thank you for the fast review :)

Very interested to see this development.

Regards,
Simon

Quoted 5 lines

Thank you again.
I'm going to check the i2c-working tree and maybe rebase the dm-pmic onto it.

Is it good idea?

Sounds good. Once I get the main DM patches landed (hopefully this week) I'll rebase the other series including I2C. But for now you should be good to use it.

Regards,
Simon

#19 lore raw

PATCH 4/19 dm: pmic: add implementation of driver model pmic uclass

Hello,

... snip ...

Quoted 5 lines
Quoted 5 lines

Thank you again.
I'm going to check the i2c-working tree and maybe rebase the dm-pmic onto it.

Quoted 9 lines
Quoted 1 line

Is it good idea?

Sounds good. Once I get the main DM patches landed (hopefully this week) I'll rebase the other series including I2C. But for now you should be good to use it.

Regards,
Simon

I have also some other tasks, but I hope to check this in this week.

Regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#20 lore raw

PATCH 4/19 dm: pmic: add implementation of driver model pmic uclass

Hi,

Quoted 17 lines · Przemyslaw Marczak

Hello,

... snip ...

Quoted 13 lines
Quoted 7 lines

Thank you again.
I'm going to check the i2c-working tree and maybe rebase the dm-pmic onto it.

Is it good idea?

Sounds good. Once I get the main DM patches landed (hopefully this week) I'll rebase the other series including I2C. But for now you should be good to use it.

Quoted 5 lines
Quoted 3 lines

Regards,
Simon

I have also some other tasks, but I hope to check this in this week.

How are you going with this? Also did you get driver model going for I2C on exynos? If not, need help?

Regards,
Simon

#21 lore raw

PATCH 4/19 dm: pmic: add implementation of driver model pmic uclass

Hello Simon,

Quoted 25 lines · Simon Glass

Hi,

Quoted 21 lines · Przemyslaw Marczak

Hello,

... snip ...

Quoted 16 lines
Quoted 7 lines

Thank you again.
I'm going to check the i2c-working tree and maybe rebase the dm-pmic onto it.

Is it good idea?

Sounds good. Once I get the main DM patches landed (hopefully this week) I'll rebase the other series including I2C. But for now you should be good to use it.

Regards,
Simon

Quoted 8 lines
Quoted 1 line

I have also some other tasks, but I hope to check this in this week.

How are you going with this? Also did you get driver model going for I2C on exynos? If not, need help?

Regards,
Simon

Each time, I would like to start working on it - some other task comes. Maybe tomorrow I will start this.

Best regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#22 lore raw

PATCH 4/19 dm: pmic: add implementation of driver model pmic uclass

Hi Prezemyslaw,

Quoted 41 lines · Przemyslaw Marczak

Hello Simon,

Quoted 37 lines · Simon Glass

Hi,

Quoted 21 lines · Przemyslaw Marczak

Hello,

... snip ...

Quoted 16 lines
Quoted 9 lines

Thank you again.
I'm going to check the i2c-working tree and maybe rebase the dm-pmic onto
it.

Is it good idea?

Sounds good. Once I get the main DM patches landed (hopefully this week) I'll rebase the other series including I2C. But for now you should be good to use it.

Quoted 5 lines
Quoted 3 lines

Regards,
Simon

I have also some other tasks, but I hope to check this in this week.

How are you going with this? Also did you get driver model going for I2C on exynos? If not, need help?

Quoted 6 lines
Quoted 3 lines

Regards,
Simon

Each time, I would like to start working on it - some other task comes. Maybe tomorrow I will start this.

OK. I posted driver model I2C for Tegra a few days ago so that will be a better base for exynos than the previous version.

Regards,
Simon

#23 lore raw

PATCH 5/19 dm: pmic: add implementation of driver model regulator uclass

In reply to Przemyslaw Marczak

This is the implementation of driver model regulator uclass api. To use it, the CONFIG_DM_PMIC is required with driver implementation, since it provides pmic devices I/O API.

The regulator framework is based on a 'structure dm_regulator_ops', which provides all regulator functions call types.

The optional and useful regulator features are two descriptor types:
- struct regulator_desc - describes the regulator name and value limits should be set by device driver for each regulator number.
- struct regulator_mode_desc - also should be defined as mode array for each regulator, since regulators supports few modes, at least: ON/OFF.

The regulator driver operations are clear and described in file:
include/power/regulator.h:

Each regulator "struct driver.ops" should point to "struct dm_regulator_ops". If do, then the drivers can use the regulator api(if implemented):
- pmic_ldo_cnt(...)
- pmic_buck_cnt(...)
- pmic_get_ldo_val(...)
- pmic_set_ldo_val(...)
- pmic_get_ldo_mode(...)
- pmic_set_ldo_mode(...)
- pmic_get_buck_val(...)
- pmic_set_buck_val(...)
- pmic_get_buck_mode(...)
- pmic_set_buck_mode(...)

To get the regulator device we can use two functions:
- pmic_get_by_name(...)
- pmic_get_by_interface(...)

Main files:
- drivers/power/regulator-uclass.c - provides regulator common functions api
- include/power/regulator.h - define all structures required by the regulator

Changes:
- new uclass-id: UCLASS_PMIC_REGULATOR
- new config: CONFIG_DM_REGULATOR

---
 drivers/power/Makefile           |   1 +
 drivers/power/regulator-uclass.c | 250 ++++++++++++++++++++++++++++++++++++
 include/dm/uclass-id.h           |   1 +
 include/power/pmic.h             |  18 +++
 include/power/regulator.h        | 267 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 537 insertions(+)
 create mode 100644 drivers/power/regulator-uclass.c
 create mode 100644 include/power/regulator.h
drivers/power/Makefile +1 -0
index 8def501..9a0b8c4 100644
@@ -22,3 +22,4 @@ obj-$(CONFIG_POWER_SPI) += power_spi.o
 obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
 obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
 obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
drivers/power/regulator-uclass.c new file +250 -0

250 lines not on this page · raw message

include/dm/uclass-id.h +1 -0
index e6d9d39..147222c 100644
@@ -31,6 +31,7 @@ enum uclass_id {
 
 	/* PMIC uclass and PMIC related uclasses */
 	UCLASS_PMIC,
+	UCLASS_PMIC_REGULATOR,
 
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
include/power/pmic.h +18 -0
index 7114650..5a06ba4 100644
@@ -119,6 +119,24 @@ enum pmic_op_type {
 	PMIC_OP_SET,
 };
 
+static __inline__ const void *pmic_get_uclass_ops(struct udevice *dev,
+						  int uclass_id)
+{
+	const void *ops;
+
+	if (!dev)
+		return NULL;
+
+	if (dev->driver->id != uclass_id)
+		return NULL;
+
+	ops = dev->driver->ops;
+	if (!ops)
+		return NULL;
+
+	return ops;
+}
+
 /* drivers/power/pmic-uclass.c */
 int power_init_dm(void);
 struct udevice *pmic_get_by_name(int uclass_id, char *name);
include/power/regulator.h new file +267 -0

267 lines not on this page · raw message

Signature · 1 line
1.9.1
#24 lore raw

PATCH 5/19 dm: pmic: add implementation of driver model regulator uclass

Hi,

Quoted 29 lines · Przemyslaw Marczak

This is the implementation of driver model regulator uclass api. To use it, the CONFIG_DM_PMIC is required with driver implementation, since it provides pmic devices I/O API.

The regulator framework is based on a 'structure dm_regulator_ops', which provides all regulator functions call types.

The optional and useful regulator features are two descriptor types:
- struct regulator_desc - describes the regulator name and value limits should be set by device driver for each regulator number.
- struct regulator_mode_desc - also should be defined as mode array for each regulator, since regulators supports few modes, at least: ON/OFF.

The regulator driver operations are clear and described in file:
include/power/regulator.h:

Each regulator "struct driver.ops" should point to "struct dm_regulator_ops". If do, then the drivers can use the regulator api(if implemented):
- pmic_ldo_cnt(...)
- pmic_buck_cnt(...)
- pmic_get_ldo_val(...)
- pmic_set_ldo_val(...)
- pmic_get_ldo_mode(...)
- pmic_set_ldo_mode(...)
- pmic_get_buck_val(...)
- pmic_set_buck_val(...)
- pmic_get_buck_mode(...)
- pmic_set_buck_mode(...)

Quoted 3 lines

To get the regulator device we can use two functions:
- pmic_get_by_name(...)
- pmic_get_by_interface(...)

I've just got a few high-level comment on this series so will respond to each patch.

Quoted 74 lines

Main files:
- drivers/power/regulator-uclass.c - provides regulator common functions api
- include/power/regulator.h - define all structures required by the regulator

Changes:
- new uclass-id: UCLASS_PMIC_REGULATOR
- new config: CONFIG_DM_REGULATOR

---
 drivers/power/Makefile           |   1 +
 drivers/power/regulator-uclass.c | 250 ++++++++++++++++++++++++++++++++++++
 include/dm/uclass-id.h           |   1 +
 include/power/pmic.h             |  18 +++
 include/power/regulator.h        | 267 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 537 insertions(+)
 create mode 100644 drivers/power/regulator-uclass.c
 create mode 100644 include/power/regulator.h
drivers/power/Makefile +1 -0
index 8def501..9a0b8c4 100644
@@ -22,3 +22,4 @@ obj-$(CONFIG_POWER_SPI) += power_spi.o
 obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
 obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
 obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
drivers/power/regulator-uclass.c new file +39 -0
new file mode 100644
index 0000000..4c9614e
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <power/pmic.h>
+#include <i2c.h>
+#include <compiler.h>
+#include <dm.h>
+#include <dm/device.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int pmic_ldo_cnt(struct udevice *dev)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->get_ldo_cnt)
+               return -EPERM;
+
+       return ops->get_ldo_cnt(dev);
+}
+
+int pmic_buck_cnt(struct udevice *dev)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);

drivers/power/regulator-uclass.c line 49

Quoted 10 lines
drivers/power/regulator-uclass.c new file +10 -0
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->get_buck_cnt)
+               return -EPERM;
+
+       return ops->get_buck_cnt(dev);
+}
+
+struct regulator_desc *pmic_ldo_desc(struct udevice *dev, int ldo)

I think these should return an error, with the struct * return as a parameter.

Quoted 65 lines
drivers/power/regulator-uclass.c new file +65 -0
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return NULL;
+
+       if (!ops->get_val_desc)
+               return NULL;
+
+       return ops->get_val_desc(dev, DESC_TYPE_LDO, ldo);
+}
+
+struct regulator_desc *pmic_buck_desc(struct udevice *dev, int buck)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return NULL;
+
+       if (!ops->get_val_desc)
+               return NULL;
+
+       return ops->get_val_desc(dev, DESC_TYPE_BUCK, buck);
+}
+
+struct regulator_mode_desc *pmic_ldo_mode_desc(struct udevice *dev, int ldo,
+                                              int *mode_cnt)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return NULL;
+
+       if (!ops->get_mode_desc_array)
+               return NULL;
+
+       return ops->get_mode_desc_array(dev, DESC_TYPE_LDO, ldo, mode_cnt);
+}
+
+struct regulator_mode_desc *pmic_buck_mode_desc(struct udevice *dev, int buck,
+                                               int *mode_cnt)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return NULL;
+
+       if (!ops->get_mode_desc_array)
+               return NULL;
+
+       return ops->get_mode_desc_array(dev, DESC_TYPE_BUCK, buck, mode_cnt);
+}
+
+int pmic_get_ldo_val(struct udevice *dev, int ldo)
+{
+       const struct dm_regulator_ops *ops;
+       int val = -1;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;

drivers/power/regulator-uclass.c line 124

Quoted 10 lines
drivers/power/regulator-uclass.c new file +10 -0
+
+       if (!ops->ldo_val)
+               return -EPERM;
+
+       if (ops->ldo_val(dev, PMIC_OP_GET, ldo, &val))
+               return -EIO;
+
+       return val;
+}
+

Regards,
Simon

#25 lore raw

PATCH 5/19 dm: pmic: add implementation of driver model regulator uclass

Quoted 32 lines · Simon Glass

Hi,

Quoted 29 lines · Przemyslaw Marczak

This is the implementation of driver model regulator uclass api. To use it, the CONFIG_DM_PMIC is required with driver implementation, since it provides pmic devices I/O API.

The regulator framework is based on a 'structure dm_regulator_ops', which provides all regulator functions call types.

The optional and useful regulator features are two descriptor types:
- struct regulator_desc - describes the regulator name and value limits should be set by device driver for each regulator number.
- struct regulator_mode_desc - also should be defined as mode array for each regulator, since regulators supports few modes, at least: ON/OFF.

The regulator driver operations are clear and described in file:
include/power/regulator.h:

Each regulator "struct driver.ops" should point to "struct dm_regulator_ops". If do, then the drivers can use the regulator api(if implemented):
- pmic_ldo_cnt(...)
- pmic_buck_cnt(...)
- pmic_get_ldo_val(...)
- pmic_set_ldo_val(...)
- pmic_get_ldo_mode(...)
- pmic_set_ldo_mode(...)
- pmic_get_buck_val(...)
- pmic_set_buck_val(...)
- pmic_get_buck_mode(...)
- pmic_set_buck_mode(...)

Quoted 7 lines
Quoted 3 lines

To get the regulator device we can use two functions:
- pmic_get_by_name(...)
- pmic_get_by_interface(...)

I've just got a few high-level comment on this series so will respond to each patch.

Ok

Quoted 78 lines
Quoted 78 lines

Main files:
- drivers/power/regulator-uclass.c - provides regulator common functions api
- include/power/regulator.h - define all structures required by the regulator

Changes:
- new uclass-id: UCLASS_PMIC_REGULATOR
- new config: CONFIG_DM_REGULATOR

---
  drivers/power/Makefile           |   1 +
  drivers/power/regulator-uclass.c | 250 ++++++++++++++++++++++++++++++++++++
  include/dm/uclass-id.h           |   1 +
  include/power/pmic.h             |  18 +++
  include/power/regulator.h        | 267 +++++++++++++++++++++++++++++++++++++++
  5 files changed, 537 insertions(+)

create mode 100644 drivers/power/regulator-uclass.c create mode 100644 include/power/regulator.h

drivers/power/Makefile +1 -0
index 8def501..9a0b8c4 100644
@@ -22,3 +22,4 @@ obj-$(CONFIG_POWER_SPI) += power_spi.o
  obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
  obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
  obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
drivers/power/regulator-uclass.c new file +43 -0
new file mode 100644
index 0000000..4c9614e
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <power/pmic.h>
+#include <i2c.h>
+#include <compiler.h>
+#include <dm.h>
+#include <dm/device.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int pmic_ldo_cnt(struct udevice *dev)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->get_ldo_cnt)
+               return -EPERM;
+
+       return ops->get_ldo_cnt(dev);
+}
+
+int pmic_buck_cnt(struct udevice *dev)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->get_buck_cnt)
Quoted 9 lines

drivers/power/regulator-uclass.c line 49

Quoted 6 lines
drivers/power/regulator-uclass.c new file +6 -0
+               return -EPERM;
+
+       return ops->get_buck_cnt(dev);
+}
+
+struct regulator_desc *pmic_ldo_desc(struct udevice *dev, int ldo)

I think these should return an error, with the struct * return as a parameter.

Ok, maybe this could be better.

Quoted 70 lines
Quoted 70 lines
drivers/power/regulator-uclass.c new file +70 -0
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return NULL;
+
+       if (!ops->get_val_desc)
+               return NULL;
+
+       return ops->get_val_desc(dev, DESC_TYPE_LDO, ldo);
+}
+
+struct regulator_desc *pmic_buck_desc(struct udevice *dev, int buck)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return NULL;
+
+       if (!ops->get_val_desc)
+               return NULL;
+
+       return ops->get_val_desc(dev, DESC_TYPE_BUCK, buck);
+}
+
+struct regulator_mode_desc *pmic_ldo_mode_desc(struct udevice *dev, int ldo,
+                                              int *mode_cnt)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return NULL;
+
+       if (!ops->get_mode_desc_array)
+               return NULL;
+
+       return ops->get_mode_desc_array(dev, DESC_TYPE_LDO, ldo, mode_cnt);
+}
+
+struct regulator_mode_desc *pmic_buck_mode_desc(struct udevice *dev, int buck,
+                                               int *mode_cnt)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return NULL;
+
+       if (!ops->get_mode_desc_array)
+               return NULL;
+
+       return ops->get_mode_desc_array(dev, DESC_TYPE_BUCK, buck, mode_cnt);
+}
+
+int pmic_get_ldo_val(struct udevice *dev, int ldo)
+{
+       const struct dm_regulator_ops *ops;
+       int val = -1;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->ldo_val)
+               return -EPERM;
+
+       if (ops->ldo_val(dev, PMIC_OP_GET, ldo, &val))
Quoted 9 lines

drivers/power/regulator-uclass.c line 124

Quoted 5 lines
drivers/power/regulator-uclass.c new file +5 -0
+               return -EIO;
+
+       return val;
+}
+

Regards,
Simon

Thanks,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#26 lore raw

PATCH 6/19 dm: common: board_r: add call and weak of power_init_dm()

In reply to Przemyslaw Marczak

This function call is required to init dm pmic framework and drivers before call to power_init_board().

---
 common/board_r.c | 8 ++++++++
 1 file changed, 8 insertions(+)
common/board_r.c +8 -0
index cd92288..e574130 100644
@@ -285,6 +285,11 @@ __weak int power_init_board(void)
 	return 0;
 }
 
+__weak int pmic_init_dm(void)
+{
+	return 0;
+}
+
 static int initr_announce(void)
 {
 	debug("Now running in RAM - U-Boot at: %08lx\n", gd->relocaddr);
@@ -775,6 +780,9 @@ init_fnc_t init_sequence_r[] = {
 #ifdef CONFIG_ARCH_EARLY_INIT_R
 	arch_early_init_r,
 #endif
+#ifdef CONFIG_DM_PMIC
+	pmic_init_dm,
+#endif
 	power_init_board,
 #ifndef CONFIG_SYS_NO_FLASH
 	initr_flash,
Signature · 1 line
1.9.1
#27 lore raw

PATCH 6/19 dm: common: board_r: add call and weak of power_init_dm()

Hi,

Quoted 25 lines · Przemyslaw Marczak

This function call is required to init dm pmic framework and drivers before call to power_init_board().

---
 common/board_r.c | 8 ++++++++
 1 file changed, 8 insertions(+)
common/board_r.c +5 -0
index cd92288..e574130 100644
@@ -285,6 +285,11 @@ __weak int power_init_board(void)
        return 0;
 }
 
+__weak int pmic_init_dm(void)
+{
+       return 0;
+}
+
 static int initr_announce(void)
 {
        debug("Now running in RAM - U-Boot at: %08lx\n", gd->relocaddr);

common/board_r.c init_fnc_t init_sequence_r[] = { · line 785

Quoted 6 lines
common/board_r.c +3 -0
@@ -775,6 +780,9 @@ init_fnc_t init_sequence_r[] = {
 #ifdef CONFIG_ARCH_EARLY_INIT_R
        arch_early_init_r,
 #endif
+#ifdef CONFIG_DM_PMIC
+       pmic_init_dm,
+#endif

Can the call to init this just go in initr_dm()?

Quoted 6 lines
common/board_r.c +0 -1
        power_init_board,
 #ifndef CONFIG_SYS_NO_FLASH
        initr_flash,
--

1.9.1

Regards,
Simon

#28 lore raw

PATCH 6/19 dm: common: board_r: add call and weak of power_init_dm()

Hello Simon,

Quoted 28 lines · Simon Glass

Hi,

Quoted 25 lines · Przemyslaw Marczak

This function call is required to init dm pmic framework and drivers before call to power_init_board().

---
  common/board_r.c | 8 ++++++++
  1 file changed, 8 insertions(+)
common/board_r.c +5 -0
index cd92288..e574130 100644
@@ -285,6 +285,11 @@ __weak int power_init_board(void)
         return 0;
  }
 
+__weak int pmic_init_dm(void)
+{
+       return 0;
+}
+
  static int initr_announce(void)
  {
         debug("Now running in RAM - U-Boot at: %08lx\n", gd->relocaddr);
@@ -775,6 +780,9 @@ init_fnc_t init_sequence_r[] = {
Quoted 9 lines

common/board_r.c init_fnc_t init_sequence_r[] = { · line 785

Quoted 6 lines
common/board_r.c +3 -0
  #ifdef CONFIG_ARCH_EARLY_INIT_R
         arch_early_init_r,
  #endif
+#ifdef CONFIG_DM_PMIC
+       pmic_init_dm,
+#endif

Can the call to init this just go in initr_dm()?

Yes, it can, but if the I2C uclass code is ready soon - then probably we don't need this call.

Quoted 10 lines
Quoted 6 lines
common/board_r.c +0 -1
         power_init_board,
  #ifndef CONFIG_SYS_NO_FLASH
         initr_flash,
--

1.9.1

Regards,
Simon

Best regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#29 lore raw

PATCH 7/19 dm: pmic: add max77686 pmic driver

In reply to Przemyslaw Marczak

This adds a simple implementation of driver model uclass pmic driver. This implementation includes two funcitons:
- max77686_ofdata_to_platdada(...) - init I/O data from fdt
- max77686_probe(...) - looks for max77686 regulator driver and bind it

If there is no regulator driver, then pmic can still provide read/write operations, and can be accessed by 'pmic' commands.

---
 drivers/power/pmic/Makefile   |  1 +
 drivers/power/pmic/max77686.c | 89 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 90 insertions(+)
 create mode 100644 drivers/power/pmic/max77686.c
drivers/power/pmic/Makefile +1 -0
index e7b07eb..49beffb 100644
@@ -10,6 +10,7 @@ obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o
 obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o
 obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o
 obj-$(CONFIG_POWER_MAX77686) += pmic_max77686.o
+obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o
 obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o
 obj-$(CONFIG_POWER_TPS65090_I2C) += pmic_tps65090.o
 obj-$(CONFIG_POWER_TPS65090_EC) += pmic_tps65090_ec.o
drivers/power/pmic/max77686.c new file +89 -0

89 lines not on this page · raw message

Signature · 1 line
1.9.1
#30 lore raw

PATCH 7/19 dm: pmic: add max77686 pmic driver

Quoted 9 lines · Przemyslaw Marczak

This adds a simple implementation of driver model uclass pmic driver. This implementation includes two funcitons:
- max77686_ofdata_to_platdada(...) - init I/O data from fdt
- max77686_probe(...) - looks for max77686 regulator driver and bind it

If there is no regulator driver, then pmic can still provide read/write operations, and can be accessed by 'pmic' commands.

[snip]

drivers/power/pmic/max77686.c line 43

Quoted 12 lines
drivers/power/pmic/max77686.c new file +12 -0
+	parent = fdt_parent_offset(blob, node);
+
+	if (parent < 0) {
+		error("%s: Cannot find node parent", __func__);
+		return -EINVAL;
+	}
+
+	pl->bus = i2c_get_bus_num_fdt(parent);
+	if (pl->bus < 0) {
+		debug("%s: Cannot find bus num\n", __func__);
+		return -EINVAL;
+	}

Both of those should be error() and please note (and fix globally!) that error() already has __FILE__, __LINE__ and __func__ so you can drop those duplications.

Otherwise:

Signature · 8 lines
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20141022/2098aa10/attachment.pgp>
#31 lore raw

PATCH 8/19 dm: regulator: add max77686 regulator driver

In reply to Przemyslaw Marczak

This commit adds support to max77686 regulator driver based on a uclass regulator driver-model api, which provides implementation of all uclass regulator api function calls.

New file: drivers/power/regulator/max77686.c
New config: CONFIG_DM_REGULATOR_MAX77686

---
 Makefile                           |   1 +
 drivers/power/Makefile             |   1 -
 drivers/power/regulator/Makefile   |   8 +
 drivers/power/regulator/max77686.c | 749 +++++++++++++++++++++++++++++++++++++
 include/power/max77686_pmic.h      |  28 +-
 5 files changed, 781 insertions(+), 6 deletions(-)
 create mode 100644 drivers/power/regulator/Makefile
 create mode 100644 drivers/power/regulator/max77686.c
Makefile +1 -0
index 85b90b7..696b0c0 100644
@@ -629,6 +629,7 @@ libs-y += drivers/power/ \
 	drivers/power/fuel_gauge/ \
 	drivers/power/mfd/ \
 	drivers/power/pmic/ \
+	drivers/power/regulator/ \
 	drivers/power/battery/
 libs-y += drivers/spi/
 libs-$(CONFIG_FMAN_ENET) += drivers/net/fm/
drivers/power/Makefile +0 -1
index 9a0b8c4..6a6333f 100644
@@ -13,7 +13,6 @@ obj-$(CONFIG_TPS6586X_POWER)	+= tps6586x.o
 obj-$(CONFIG_TWL4030_POWER)	+= twl4030.o
 obj-$(CONFIG_TWL6030_POWER)	+= twl6030.o
 obj-$(CONFIG_PALMAS_POWER)	+= palmas.o
-
 obj-$(CONFIG_POWER) += power_core.o
 obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
 obj-$(CONFIG_POWER_FSL) += power_fsl.o
drivers/power/regulator/Makefile new file +8 -0
new file mode 100644
index 0000000..9d282e3
@@ -0,0 +1,8 @@
+#
+# Copyright (C) 2014 Samsung Electronics
+# Przemyslaw Marczak <[email protected]>
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
drivers/power/regulator/max77686.c new file +749 -0

749 lines not on this page · raw message

include/power/max77686_pmic.h +23 -5
index c2a772a..10a2cc2 100644
@@ -8,8 +8,6 @@
 #ifndef __MAX77686_H_
 #define __MAX77686_H_
 
-#include <power/pmic.h>
-
 enum {
 	MAX77686_REG_PMIC_ID		= 0x0,
 	MAX77686_REG_PMIC_INTSRC,
@@ -126,7 +124,10 @@ enum {
 };
 
 /* I2C device address for pmic max77686 */
-#define MAX77686_I2C_ADDR (0x12 >> 1)
+#define MAX77686_I2C_ADDR	(0x12 >> 1)
+#define MAX77686_LDO_NUM	26
+#define MAX77686_BUCK_NUM	9
+#define MAX77686_DESC_NUM	(MAX77686_LDO_NUM + MAX77686_BUCK_NUM)
 
 enum {
 	REG_DISABLE = 0,
@@ -143,22 +144,30 @@ enum {
 
 enum {
 	OPMODE_OFF = 0,
-	OPMODE_STANDBY,
 	OPMODE_LPM,
+	OPMODE_STANDBY,
+	OPMODE_STANDBY_LPM,
 	OPMODE_ON,
 };
 
+#ifdef CONFIG_POWER
 int max77686_set_ldo_voltage(struct pmic *p, int ldo, ulong uV);
 int max77686_set_ldo_mode(struct pmic *p, int ldo, char opmode);
 int max77686_set_buck_mode(struct pmic *p, int buck, char opmode);
+#endif
 
 #define MAX77686_LDO_VOLT_MAX_HEX	0x3f
 #define MAX77686_LDO_VOLT_MASK		0x3f
 #define MAX77686_LDO_MODE_MASK		0xc0
 #define MAX77686_LDO_MODE_OFF		(0x00 << 0x06)
+#define MAX77686_LDO_MODE_LPM		(0x01 << 0x06)
 #define MAX77686_LDO_MODE_STANDBY	(0x01 << 0x06)
-#define MAX77686_LDO_MODE_LPM		(0x02 << 0x06)
+#define MAX77686_LDO_MODE_STANDBY_LPM	(0x02 << 0x06)
 #define MAX77686_LDO_MODE_ON		(0x03 << 0x06)
+#define MAX77686_BUCK234_VOLT_MAX_HEX	0xff
+#define MAX77686_BUCK234_VOLT_MASK	0xff
+#define MAX77686_BUCK_VOLT_MAX_HEX	0x3f
+#define MAX77686_BUCK_VOLT_MASK		0x3f
 #define MAX77686_BUCK_MODE_MASK		0x03
 #define MAX77686_BUCK_MODE_SHIFT_1	0x00
 #define MAX77686_BUCK_MODE_SHIFT_2	0x04
@@ -167,6 +176,15 @@ int max77686_set_buck_mode(struct pmic *p, int buck, char opmode);
 #define MAX77686_BUCK_MODE_LPM		0x02
 #define MAX77686_BUCK_MODE_ON		0x03
 
+/* For regulator hex<->volt conversion */
+#define MAX77686_LDO_UV_MIN		800000 /* Minimum LDO uV value */
+#define MAX77686_LDO_UV_LSTEP		25000 /* uV lower value step */
+#define MAX77686_LDO_UV_HSTEP		50000 /* uV higher value step */
+#define MAX77686_BUCK_UV_LMIN		600000 /* Lower minimun BUCK value */
+#define MAX77686_BUCK_UV_HMIN		750000 /* Higher minimun BUCK value */
+#define MAX77686_BUCK_UV_LSTEP		12500  /* uV lower value step */
+#define MAX77686_BUCK_UV_HSTEP		50000  /* uV higher value step */
+
 /* Buck1 1 volt value */
 #define MAX77686_BUCK1OUT_1V	0x5
 /* Buck1 1.05 volt value */
Signature · 1 line
1.9.1
#32 lore raw

PATCH 8/19 dm: regulator: add max77686 regulator driver

Quoted 9 lines · Przemyslaw Marczak

This commit adds support to max77686 regulator driver based on a uclass regulator driver-model api, which provides implementation of all uclass regulator api function calls.

New file: drivers/power/regulator/max77686.c
New config: CONFIG_DM_REGULATOR_MAX77686

[snip]

Quoted 4 lines
drivers/power/regulator/max77686.c new file +4 -0
+	if (ldo < 1 || ldo > 26) {
+		debug("%s: %d is wrong ldo number\n", __func__, ldo);
+		return -EINVAL;
+	}

[snip]

Quoted 4 lines
drivers/power/regulator/max77686.c new file +4 -0
+	if (buck < 1 || buck > 9) {
+		debug("%s: %d is wrong buck number\n", __func__, buck);
+		return -EINVAL;
+	}

[snip]

Quoted 4 lines
drivers/power/regulator/max77686.c new file +4 -0
+	if (ldo < 1 || ldo > 26) {
+		debug("%s: %d is wrong ldo number\n", __func__, ldo);
+		return -EINVAL;
+	}

[snip]

Quoted 5 lines
drivers/power/regulator/max77686.c new file +5 -0
+	if (mode == 0xff) {
+		debug("%s: %d is not supported on LDO%d\n", __func__, *opmode,
+							    ldo);
+		return -EINVAL;
+	}

[snip]

Quoted 4 lines
drivers/power/regulator/max77686.c new file +4 -0
+	if (buck < 1 || buck > 9) {
+		debug("%s: %d is wrong buck number\n", __func__, buck);
+		return -EINVAL;
+	}

[snip]

drivers/power/regulator/max77686.c line 617

Quoted 5 lines
drivers/power/regulator/max77686.c new file +5 -0
+	if (mode == 0xff) {
+		debug("%s: %d is not supported on BUCK%d\n", __func__, *opmode,
+							     buck);
+		return -EINVAL;
+	}

In my mind, all of these error messages are things the user would want to see because they passed the wrong value in, higher up in the chain, as these are eventually exposed at the cli, right? If so, they should be error() prints or otherwise changed around so it's clear to the user what they did wrong.

Signature · 8 lines
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20141022/05169e9e/attachment.pgp>
#33 lore raw

PATCH 9/19 dm: pmic: new commands: pmic and regulator

In reply to Przemyslaw Marczak

This introduces new commands:
- pmic (new) - CONFIG_DM_PMIC
- regulator - CONFIG_DM_PMIC
Both uses a common code and dm pmic api.

To avoid code mess the old pmic command is kept without changes.

Command pmic
This is based on an old pmic command code - the new one uses driver model pmic api. The previous pmic I/O functionalities, stays unchanged. And now read/write is the main pmic command purpose. This command can use only UCLASS_PMIC devices, since this uclass is designed for pmic I/O operations only.

Command options (pmic [option]):

- list                     - list available PMICs
- dev <id>                 - set id to current pmic device
- pmic dump                - dump registers
- pmic read <reg>          - read register

- pmic write <reg> <value> - write register

The user interface is similar to the 'mmc' command.

Each pmic device driver should provide at least UCLASS_PMIC support, as a basic pmic device I/O interface.

Command regulator
The extension for 'pmic' command is 'regulator' command. This actually uses the same code, but provides an interface to pmic optional 'UCLASS_PMIC_REGULATOR' operations.

If pmic device driver provides support to this another pmic uclass, then this command provides useful user interface.

The regulator operations and required structures can be found in this header: 'include/power/regulator.h'

This was designed to allow safe I/O access to pmic device without the pmic documentation. If driver provide each regulator
- value and mode descriptor - then user can operate on it.

Command options (regulator [option]):
- list - list UCLASS regulator devices
- dev [id] - show or set current regulator device
- dump - dump registers of current regulator
- [ldo/buck][N] [name/state/desc]- print regulator(s) info
- [ldoN/buckN] [setval/setmode] [mV/modeN] [-f] - set val (mV) (forcibly) or mode - only if desc available

Example of usage:
regulator list - look after regulator 'Id' number
regulator dev 'Id' - set current device
regulator ldo state - list state of current device all ldo's regulator ldo desc - list ldo's descriptors
regulator ldo1 setval 1000 - set device ldo 1 voltage to 1000mV
!!And the next one can be risky!!
regulator ldo1 setval 1200 -f - if value exceeds limits - set force regulator ldo1 setmode 5 - set device ldo 1 mode to '5' (force no available)

The same for 'buck' regulators.

The regulator descriptor 'min' and 'max' limits prevents setting unsafe value. But sometimes it is useful to change the regulator value for some test - so the force option (-f) is available. This option is not available for change the mode, since this depends on pmic device design, but the required voltage value can change, e.g. if some hardware uses pins header.

---
 drivers/power/Makefile   |   1 +
 drivers/power/cmd_pmic.c | 845 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 846 insertions(+)
 create mode 100644 drivers/power/cmd_pmic.c
drivers/power/Makefile +1 -0
index 6a6333f..2af63f2 100644
@@ -20,5 +20,6 @@ obj-$(CONFIG_POWER_I2C) += power_i2c.o
 obj-$(CONFIG_POWER_SPI) += power_spi.o
 obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
 obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
+obj-$(CONFIG_DM_PMIC) += cmd_pmic.o
 obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
 obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
drivers/power/cmd_pmic.c new file +845 -0

845 lines not on this page · raw message

Signature · 1 line
1.9.1
#34 lore raw

PATCH 9/19 dm: pmic: new commands: pmic and regulator

Quoted 69 lines · Przemyslaw Marczak

This introduces new commands:
- pmic (new) - CONFIG_DM_PMIC
- regulator - CONFIG_DM_PMIC
Both uses a common code and dm pmic api.

To avoid code mess the old pmic command is kept without changes.

Command pmic
This is based on an old pmic command code - the new one uses driver model pmic api. The previous pmic I/O functionalities, stays unchanged. And now read/write is the main pmic command purpose. This command can use only UCLASS_PMIC devices, since this uclass is designed for pmic I/O operations only.

Command options (pmic [option]):

- list                     - list available PMICs
- dev <id>                 - set id to current pmic device
- pmic dump                - dump registers
- pmic read <reg>          - read register

- pmic write <reg> <value> - write register

The user interface is similar to the 'mmc' command.

Each pmic device driver should provide at least UCLASS_PMIC support, as a basic pmic device I/O interface.

Command regulator
The extension for 'pmic' command is 'regulator' command. This actually uses the same code, but provides an interface to pmic optional 'UCLASS_PMIC_REGULATOR' operations.

If pmic device driver provides support to this another pmic uclass, then this command provides useful user interface.

The regulator operations and required structures can be found in this header: 'include/power/regulator.h'

This was designed to allow safe I/O access to pmic device without the pmic documentation. If driver provide each regulator
- value and mode descriptor - then user can operate on it.

Command options (regulator [option]):
- list - list UCLASS regulator devices
- dev [id] - show or set current regulator device
- dump - dump registers of current regulator
- [ldo/buck][N] [name/state/desc]- print regulator(s) info
- [ldoN/buckN] [setval/setmode] [mV/modeN] [-f] - set val (mV) (forcibly) or mode - only if desc available

Example of usage:
regulator list - look after regulator 'Id' number
regulator dev 'Id' - set current device
regulator ldo state - list state of current device all ldo's regulator ldo desc - list ldo's descriptors
regulator ldo1 setval 1000 - set device ldo 1 voltage to 1000mV
!!And the next one can be risky!!
regulator ldo1 setval 1200 -f - if value exceeds limits - set force regulator ldo1 setmode 5 - set device ldo 1 mode to '5' (force no available)

The same for 'buck' regulators.

The regulator descriptor 'min' and 'max' limits prevents setting unsafe value. But sometimes it is useful to change the regulator value for some test - so the force option (-f) is available. This option is not available for change the mode, since this depends on pmic device design, but the required voltage value can change, e.g. if some hardware uses pins header.

[snip]

Quoted 4 lines
drivers/power/cmd_pmic.c new file +4 -0
+	if (ret < 0) {
+		printf("Error: %s\n", errno_str(ret));
+		return CMD_RET_FAILURE;
+	}

[snip]

drivers/power/cmd_pmic.c line 725

Quoted 5 lines
drivers/power/cmd_pmic.c new file +5 -0
+finish:
+	if (ret < 0) {
+		printf("Error: %s\n", errno_str(ret));
+		return CMD_RET_FAILURE;
+	}

So, my main concern with this patch is we have here the only users of errno_str. In my reading over of the patch it looked like in nearly every case it was -ENODEV/-EINVAL and we had already printed a useful looking error message just before. Further, I'm not convinced (yet!) that the errno print is adding to the utility in discovering what was passed in wrong. Can you please re-work this patch to not need errno_str(), ensure that errors still print _something_ and see what the delta is, both in binary and lines of code? Thanks!

Signature · 8 lines
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20141022/cf9e34b5/attachment.pgp>
#35 lore raw

PATCH 10/19 dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC

In reply to Przemyslaw Marczak

In the power_init_board function call, regulator driver init is called, so before compile, make sure that any power framework is defined.

---
 board/samsung/common/board.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
board/samsung/common/board.c +1 -1
index e1fc123..e4cddc2 100644
@@ -166,7 +166,7 @@ int board_early_init_f(void)
 }
 #endif
 
-#if defined(CONFIG_POWER)
+#if defined(CONFIG_POWER) || defined(CONFIG_DM_PMIC)
 int power_init_board(void)
 {
 	set_ps_hold_ctrl();
Signature · 1 line
1.9.1
#36 lore raw

PATCH 11/19 doc: driver-model: pmic and regulator uclass documentation

In reply to Przemyslaw Marczak
---
 doc/driver-model/dm-pmic-framework.txt | 450 +++++++++++++++++++++++++++++++++
 1 file changed, 450 insertions(+)
 create mode 100644 doc/driver-model/dm-pmic-framework.txt
doc/driver-model/dm-pmic-framework.txt new file +450 -0
new file mode 100644
index 0000000..1b69eee
@@ -0,0 +1,450 @@
+#
+# (C) Copyright 2014 Samsung Electronics
+# Przemyslaw Marczak <[email protected]>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+PMIC framework based on Driver Model
+====================================
+TOC:
+1. Introduction
+2. How does it work
+3. Driver API
+4. Simple UCLASS_PMIC driver
+5. Pmic command
+6. Uclass UCLASS_PMIC_REGULATOR API
+7. Simple UCLASS_PMIC_REGULATOR driver
+8. Regulator command
+
+1. Introduction
+===============
+This is an introduction to driver-model multi class PMIC support.
+It starts with UCLASS_PMIC - a common PMIC class type for I/O, which
+doesn't need to implement any specific operations and features beside
+the platform data, which is a 'struct pmic_platdata' defined in file:
+- 'include/power/pmic.h'
+
+New files:
+- pmic-uclass.c - provides a common code for UCLASS_PMIC drivers
+- pmic_i2c.c    - provides dm interface for i2c pmic drivers
+- pmic_spi.c    - provides dm interface for spi pmic drivers
+
+Those files are based on a current PMIC framework files and code.
+And the new files are introduced to avoid code mess and allow to
+make an easy drivers migration. The old pmic is still kept.
+
+Changes:
+- add uclass-id: UCLASS_PMIC
+- add new configs:
+  CONFIG_DM_PMIC - enables driver model pmic framework and requires one
+  or both of:
+  CONFIG_DM_PMIC_SPI - enables an interface to pmic SPI
+  CONFIG_DM_PMIC_I2C - enables an interface to pmic I2C
+
+The old pmic interface API worked very well so it is kept, but the framework
+architecture is changed.
+
+2. How doees it work
+====================
+The framework init starts in file: drivers/power/pmic-uclass.c
+The function pmic_init_dm() looks after the 'pmic' alias in the device-tree
+and do the driver bind.
+If board uses more than one PMIC device, then a few 'pmic' aliases should
+be defined with numbers, e.g. pmic0, pmic1...
+
+Note:
+The I2C doesn't use the driver model yet, so the drivers are bind thanks to the
+'pmic' alias.
+
+3. PMIC drivers API
+===================
+The new API is as simple as previously:
+
+File: drivers/power/pmic-uclass.c:
+- void power_init_dm(void)
+  The main init function, called after the i2c init and before power_init_board.
+  Bind the UCLASS_PMIC drivers.
+
+- struct udevice *pmic_get_by_name(int uclass_id, char *name)
+  This function provides similar function as old pmic_get(), but the returned
+  device pointer doesn't require any allocation. 
+  The uclass_id argument if to chose proper device uclass, e.g. pmic or next
+  others, pmic related.
+
+- struct udevice *pmic_get_by_interface(int uclass_id, int bus, int addr_cs)
+  This is similar function as previous but is designed for boards with more
+  than one pmic device or the same device instances. In this case we can't
+  get the device by the name, so the best and accurate method is to get it by
+  known uclass, bus and address or cs(spi). This is new framework feature.
+
+- const char *pmic_if_str(int interface)
+  This function returns the interface name string for i2c or spi, and uses
+  pmic interface enum from: include/power/pmic.h 
+
+- int pmic_check_reg(struct pmic_platdata *p, unsigned reg)
+  This function check if the given register number is valid for the given
+  pmic_platdata.
+
+- int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+  The same functionality as in old framework but changed to driver-model.
+
+- int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+  The same functionality as in old framework but changed to driver-model.
+
+- int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave)
+  The same functionality as in an old framework but changed to driver model,
+  and extended by support to both pmic interfaces.
+
+The below functions, are implemented with use of common calls, described above.
+
+File: drivers/power/pmic_i2c.c:
+- int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+- int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+- int pmic_i2c_probe(struct udevice *dev)
+
+File: drivers/power/pmic_spi.c:
+- int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+- int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+- struct spi_slave *pmic_spi_probe(struct udevice *dev)
+
+4. Simple UCLASS_PMIC driver
+============================
+At this stage the framework supports only drivers that have proper fdt alias.
+The alias is "pmic" or "pmicN".
+
+The exaple of dts node():
+	aliases {
+		...
+		...
+		pmic0 = &simple_pmic0;
+	}
+
+	i2c at some_addr {
+		; some i2c data
+
+		simple_pmic0: simple_pmic at 09 {
+			compatible = "vandor, simple_pmic";
+			reg = <0x09 0 0>;
+		}
+
+The exaple of driver code:
+
+/**
+ * simple_pmic_probe(...) - this function is optional and is a preffered way
+ * to bind the "simple regulator" device. 
+ * Than we have:
+ * device: "simple_pmic"       - provides device I/O
+ * device: "simple regulator"  - provides regualtor operations and is binded
+ *                               as a child of "simple_pmic"
+ */
+static int simple_pmic_probe(struct udevice *parent)
+{
+	struct udevice *reg_dev;
+	struct driver *reg_drv;
+	int ret;
+
+	reg_drv = lists_driver_lookup_name("simple regulator");
+	if (!reg_drv) {
+		error("%s regulator driver not found!\n", parent->name);
+		return 0;
+	}
+
+	if (!parent->platdata) {
+		error("%s platdata not found!\n", parent->name);
+		return -EINVAL;
+	}
+
+	ret = device_bind(parent, reg_drv, parent->name, parent->platdata,
+			  parent->of_offset, &reg_dev);
+	if (ret)
+		error("%s regulator bind failed", parent->name);
+
+	/**
+	 * Return an error only if no parent platdata set
+	 * so if no regulator found - still have pmic I/O.
+	 * This scheme will be used in most board configs.
+	 */
+	return 0;
+}
+
+static int simple_pmic_ofdata_to_platdata(struct udevice *dev)
+{
+	struct pmic_platdata *pl = dev->platdata;
+
+	/**
+	 * Here, driver should init the platdata structure based on device-tree,
+	 * The fields bus, interface, byte_order and the struct spi or i2c,
+	 * provide information about I/O acces, so are required.
+	 */
+	const void *blob = gd->fdt_blob;
+	int node = dev->of_offset;
+	int parent;
+
+	pl->interface = PMIC_I2C;
+	pl->regs_num = PMIC_NUM_OF_REGS;
+
+	parent = fdt_parent_offset(blob, node);
+
+	if (parent < 0) {
+		error("%s: Cannot find node parent", __func__);
+		return -EINVAL;
+	}
+
+	pl->bus = i2c_get_bus_num_fdt(parent);
+	if (pl->bus < 0) {
+		debug("%s: Cannot find bus num\n", __func__);
+		return -EINVAL;
+	}
+
+	pl->hw.i2c.addr = fdtdec_get_int(blob, node, "reg", SIMPLE_PMIC_I2C_ADDR);
+	pl->hw.i2c.tx_num = 1;
+	
+	return 0;
+}
+
+static const struct udevice_id simple_pmic_ids[] = {
+	{ .compatible = "vendor,simple_pmic"},
+	{ }
+};
+
+U_BOOT_DRIVER(simple_pmic) = {
+	.name = "simple pmic",
+	.id = UCLASS_PMIC,
+	.of_match = simple_pmic_ids,
+	.probe = simple_pmic_probe,
+	.ofdata_to_platdata = simple_pmic_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct pmic_platdata),
+};
+
+5. Pmic command
+===============
+This is based on an old pmic command code - the new one uses
+the driver model pmic API. The previous pmic I/O functionalities,
+stays unchanged. And now read/write is the main pmic command
+purpose. This command can use only UCLASS_PMIC devices,
+since this uclass is designed for pmic I/O operations only.
+
+Command options (pmic [option]):
+- list                     - list available PMICs
+- dev <id>                 - set id to current pmic device
+- pmic dump                - dump registers
+- pmic read <reg>          - read register
+- pmic write <reg> <value> - write register
+
+Example of usage:
+# pmic list           - chose one dev Id, e.g. 3
+# pmic dev 3          - set dev 3 as current device
+# pmic dump           - dump the registers of pmic dev id 3
+# pmic read 0x0       - read the register of addres 0x0 
+# pmic write 0x0 0x1  - write 0x1 to the register of addres 0x0 
+
+The user interface is similar to the 'mmc' command.
+
+Each pmic device driver should provide at least UCLASS_PMIC support,
+as a basic pmic device I/O interface.
+
+6. Uclass UCLASS_PMIC_REGULATOR API
+===================================
+To use the regulator API, config: CONFIG_DM_REGULATOR is required.
+
+Driver API is simple and clear:
+To get the regulator device we can use two functions:
+- pmic_get_by_name(...)
+- pmic_get_by_interface(...)
+
+This returns "struct udevice *" pointer to uclass regulator device,
+which can provide API functions.
+
+To operate on device regulators, we can use API functions
+(if provided by the driver):
+- pmic_ldo_cnt(...)
+- pmic_buck_cnt(...)
+- pmic_get_ldo_val(...)
+- pmic_set_ldo_val(...)
+- pmic_get_ldo_mode(...)
+- pmic_set_ldo_mode(...)
+- pmic_get_buck_val(...)
+- pmic_set_buck_val(...)
+- pmic_get_buck_mode(...)
+- pmic_set_buck_mode(...)
+
+For detailed description please look into the file:
+- include/power/regulator.h
+
+7. Simple UCLASS_PMIC_REGULATOR driver
+======================================
+The regulator ops implementation can be different for each driver,
+so this is only a piece of driver design.
+As a reference, please take the MAX77686 pmic and regulator drivers.
+- drivers/power/pmic/max77686.c
+- drivers/power/regulator/max77686.c
+
+The regulator driver code should look like below:
+(Please read the 'include/power/regulator.h' for more details)
+
+Note:
+The regulator device should act as a child of pmic device.
+This is a natural scheme of a physical PMIC IC:
+
+Some PMIC physical IC:
+ |_ dev pmic        (parent) - simple and sufficient for the most board configs
+   |_ dev regulator (child)  - provide regulator operations
+   |_ dev other     (child)  - some other pmic uclasses, (in the future)
+
+struct regulator_desc *simple_regulator_get_val_desc(struct udevice *dev,
+						     int d_type, int d_num)
+{
+	/* Here returns regulator value descriptor */
+	return NULL;
+}
+
+static struct regulator_mode_desc *
+simple_regulator_get_mode_desc_array(struct udevice *dev, int d_type, int d_num,
+                              int *d_mode_cnt)
+{
+	/* Here return arra of mode descriptors for given device */
+	return NULL;
+}
+
+static int simple_regulator_get_ldo_cnt(struct udevice *dev)
+{
+	/* Here return regulators ldo count */
+	return 0;
+}
+
+static int simple_regulator_get_buck_cnt(struct udevice *dev)
+{
+	/* Here return regulator buck count */
+	return 0;
+}
+
+static int simple_regulator_ldo_val(struct udevice *dev, int op,
+				    int ldo, int *uV)
+{
+	/* Here return or sets given ldo value in uV */
+	return 0;
+}
+
+static int simple_regulator_buck_val(struct udevice *dev, int op,
+				    int buck, int *uV)
+{
+	/* Here return or sets given buck value in uV */
+	return 0;
+}
+
+static int simple_regulator_ldo_mode(struct udevice *dev, int op, int ldo,
+				     int *opmode)
+{
+	/* Here return or sets regulator ldo mode */
+	return 0;
+}
+
+static int simple_regulator_buck_mode(struct udevice *dev, int op, int buck,
+				      int *opmode)
+{
+	/* Here return or sets regulator buck mode */
+	return 0;
+}
+
+static int simple_regulator_ofdata_to_platdata(struct udevice *dev)
+{
+	/**
+	 * PMIC Interface, Case 1: common
+	 * If PMIC uses only one (SPI/I2C) physical interface for driving
+	 * it's functionalities.
+	 *
+	 * Then the bind of "simple regulator" device in the "simple pmic" probe
+	 * is called with args:
+	 * ret = device_bind(parent, reg_drv, parent->name, parent->platdata,
+	 *                   parent->of_offset, &reg_dev);
+	 */
+
+	/**
+	 * PMIC Interface, case 2: independent
+	 * If PMIC uses more then one (SPI/I2C) physical interfaces for driving
+	 * it's functionalities.
+	 *
+	 * Then the bind of "simple regulator" device in the "simple pmic"
+	 * drivers 'probe', is called with args:
+	 * ret = device_bind(parent, reg_drv, parent->name, NULL,
+	 *                   regulator_fdt_of_offset, &reg_dev);
+	 *
+	 * In this case "driver.platdata_auto_alloc_size"
+	 */
+
+	/**
+	 * Here driver should get the 'regulator-voltage' node data
+	 * into a proper descriptors.
+	 * Actually there is no standard voltage description in dts,
+	 * but please check trats2 or odroid dts files and regulator/max77686.c
+	 * driver file to check some simple solution.
+	 *
+	 */
+	return 0;
+}
+
+static const struct dm_regulator_ops simple_regulator_ops = {
+	.get_ldo_cnt	     = simple_regulator_get_ldo_cnt,
+	.get_buck_cnt	     = simple_regulator_get_buck_cnt,
+	.get_val_desc	     = simple_regulator_get_val_desc,
+	.get_mode_desc_array = simple_regulator_get_mode_desc_array,
+	.ldo_val	     = simple_regulator_ldo_val,
+	.buck_val	     = simple_regulator_buck_val,
+	.ldo_mode	     = simple_regulator_ldo_mode,
+	.buck_mode	     = simple_regulator_buck_mode,
+};
+
+U_BOOT_DRIVER(simple_regulator) = {
+	.name = "simple regulator",
+	.id = UCLASS_PMIC_REGULATOR,
+	.ops = &simple_regulator_ops,
+	.ofdata_to_platdata = reg_simple_regulator_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct simple_regulator_info),
+
+	/**
+	 * .platdata_auto_alloc_size - is optional, only if regulator uses
+	 *                             a different interface than parent pmic.
+	 */
+	.platdata_auto_alloc_size = sizeof(struct pmic_platdata),
+};
+
+8. Regulator command
+====================
+The extension for 'pmic' command is a 'regulator' command.
+This actually uses the same code, but provides an interface
+to pmic optional 'UCLASS_PMIC_REGULATOR' operations.
+
+If pmic device driver provides support to this another pmic
+uclass, then this command provides useful user interface.
+
+This was designed to allow safe I/O access to pmic device
+without the pmic documentation. If driver provide each regulator
+- value and mode descriptor - then user can operate on it.
+
+Command options (regulator [option]):
+- list     - list UCLASS regulator devices
+- dev [id] - show or set current regulator device
+- dump     - dump registers of current regulator
+- [ldo/buck][N] [name/state/desc]- print regulator(s) info
+- [ldoN/buckN] [setval/setmode] [mV/modeN] [-f] - set val (mV)
+(forcibly) or mode - only if desc available
+
+Example of usage:
+regulator list - look after regulator 'Id' number
+regulator dev 'Id'  - set current device
+regulator ldo state - list state of current device all ldo's
+regulator ldo desc  - list ldo's descriptors
+regulator ldo1 setval 1000 - set device ldo 1 voltage to 1000mV
+regulator ldo1 setval 1200 -f - if value exceeds limits - set force
+regulator ldo1 setmode 5 - set device ldo 1 mode to '5' (force no available)
+
+The same for 'buck' regulators.
+
+Note:
+The regulator descriptor 'min' and 'max' limits prevents setting
+unsafe value. But sometimes it is useful to change the regulator
+value for some test - so the force option (-f) is available.
+This option is not available for change the mode, since this depends
+on a pmic device design, but the required voltage value can change,
+e.g. if some hardware uses pins header.
Signature · 1 line
1.9.1
#37 lore raw

PATCH 11/19 doc: driver-model: pmic and regulator uclass documentation

Hi,

Quoted 161 lines · Przemyslaw Marczak
---
 doc/driver-model/dm-pmic-framework.txt | 450 +++++++++++++++++++++++++++++++++
 1 file changed, 450 insertions(+)
 create mode 100644 doc/driver-model/dm-pmic-framework.txt
doc/driver-model/dm-pmic-framework.txt new file +149 -0
new file mode 100644
index 0000000..1b69eee
@@ -0,0 +1,450 @@
+#
+# (C) Copyright 2014 Samsung Electronics
+# Przemyslaw Marczak <[email protected]>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+PMIC framework based on Driver Model
+====================================
+TOC:
+1. Introduction
+2. How does it work
+3. Driver API
+4. Simple UCLASS_PMIC driver
+5. Pmic command
+6. Uclass UCLASS_PMIC_REGULATOR API
+7. Simple UCLASS_PMIC_REGULATOR driver
+8. Regulator command
+
+1. Introduction
+===============
+This is an introduction to driver-model multi class PMIC support.
+It starts with UCLASS_PMIC - a common PMIC class type for I/O, which
+doesn't need to implement any specific operations and features beside
+the platform data, which is a 'struct pmic_platdata' defined in file:
+- 'include/power/pmic.h'
+
+New files:
+- pmic-uclass.c - provides a common code for UCLASS_PMIC drivers
+- pmic_i2c.c    - provides dm interface for i2c pmic drivers
+- pmic_spi.c    - provides dm interface for spi pmic drivers
+
+Those files are based on a current PMIC framework files and code.
+And the new files are introduced to avoid code mess and allow to
+make an easy drivers migration. The old pmic is still kept.
+
+Changes:
+- add uclass-id: UCLASS_PMIC
+- add new configs:
+  CONFIG_DM_PMIC - enables driver model pmic framework and requires one
+  or both of:
+  CONFIG_DM_PMIC_SPI - enables an interface to pmic SPI
+  CONFIG_DM_PMIC_I2C - enables an interface to pmic I2C
+
+The old pmic interface API worked very well so it is kept, but the framework
+architecture is changed.
+
+2. How doees it work
+====================
+The framework init starts in file: drivers/power/pmic-uclass.c
+The function pmic_init_dm() looks after the 'pmic' alias in the device-tree
+and do the driver bind.
+If board uses more than one PMIC device, then a few 'pmic' aliases should
+be defined with numbers, e.g. pmic0, pmic1...
+
+Note:
+The I2C doesn't use the driver model yet, so the drivers are bind thanks to the
+'pmic' alias.
+
+3. PMIC drivers API
+===================
+The new API is as simple as previously:
+
+File: drivers/power/pmic-uclass.c:
+- void power_init_dm(void)
+  The main init function, called after the i2c init and before power_init_board.
+  Bind the UCLASS_PMIC drivers.
+
+- struct udevice *pmic_get_by_name(int uclass_id, char *name)
+  This function provides similar function as old pmic_get(), but the returned
+  device pointer doesn't require any allocation.
+  The uclass_id argument if to chose proper device uclass, e.g. pmic or next
+  others, pmic related.
+
+- struct udevice *pmic_get_by_interface(int uclass_id, int bus, int addr_cs)
+  This is similar function as previous but is designed for boards with more
+  than one pmic device or the same device instances. In this case we can't
+  get the device by the name, so the best and accurate method is to get it by
+  known uclass, bus and address or cs(spi). This is new framework feature.
+
+- const char *pmic_if_str(int interface)
+  This function returns the interface name string for i2c or spi, and uses
+  pmic interface enum from: include/power/pmic.h
+
+- int pmic_check_reg(struct pmic_platdata *p, unsigned reg)
+  This function check if the given register number is valid for the given
+  pmic_platdata.
+
+- int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+  The same functionality as in old framework but changed to driver-model.
+
+- int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+  The same functionality as in old framework but changed to driver-model.
+
+- int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave)
+  The same functionality as in an old framework but changed to driver model,
+  and extended by support to both pmic interfaces.
+
+The below functions, are implemented with use of common calls, described above.
+
+File: drivers/power/pmic_i2c.c:
+- int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+- int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+- int pmic_i2c_probe(struct udevice *dev)
+
+File: drivers/power/pmic_spi.c:
+- int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+- int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+- struct spi_slave *pmic_spi_probe(struct udevice *dev)
+
+4. Simple UCLASS_PMIC driver
+============================
+At this stage the framework supports only drivers that have proper fdt alias.
+The alias is "pmic" or "pmicN".
+
+The exaple of dts node():
+       aliases {
+               ...
+               ...
+               pmic0 = &simple_pmic0;
+       }
+
+       i2c at some_addr {
+               ; some i2c data
+
+               simple_pmic0: simple_pmic at 09 {
+                       compatible = "vandor, simple_pmic";
+                       reg = <0x09 0 0>;
+               }
+
+The exaple of driver code:
+
+/**
+ * simple_pmic_probe(...) - this function is optional and is a preffered way
+ * to bind the "simple regulator" device.
+ * Than we have:
+ * device: "simple_pmic"       - provides device I/O
+ * device: "simple regulator"  - provides regualtor operations and is binded
+ *                               as a child of "simple_pmic"
+ */
+static int simple_pmic_probe(struct udevice *parent)
+{
+       struct udevice *reg_dev;
+       struct driver *reg_drv;
+       int ret;
+
+       reg_drv = lists_driver_lookup_name("simple regulator");
+       if (!reg_drv) {
+               error("%s regulator driver not found!\n", parent->name);

doc/driver-model/dm-pmic-framework.txt line 159

Quoted 10 lines
doc/driver-model/dm-pmic-framework.txt new file +10 -0
+               return 0;
+       }
+
+       if (!parent->platdata) {
+               error("%s platdata not found!\n", parent->name);
+               return -EINVAL;
+       }
+
+       ret = device_bind(parent, reg_drv, parent->name, parent->platdata,
+                         parent->of_offset, &reg_dev);

I wonder if we can avoid this? Really the regulator should be bound at init time, and this function should use something like uclass_get_device_by_of_offset() to find it.

Quoted 292 lines
doc/driver-model/dm-pmic-framework.txt new file +291 -1
+       if (ret)
+               error("%s regulator bind failed", parent->name);
+
+       /**
+        * Return an error only if no parent platdata set
+        * so if no regulator found - still have pmic I/O.
+        * This scheme will be used in most board configs.
+        */
+       return 0;
+}
+
+static int simple_pmic_ofdata_to_platdata(struct udevice *dev)
+{
+       struct pmic_platdata *pl = dev->platdata;
+
+       /**
+        * Here, driver should init the platdata structure based on device-tree,
+        * The fields bus, interface, byte_order and the struct spi or i2c,
+        * provide information about I/O acces, so are required.
+        */
+       const void *blob = gd->fdt_blob;
+       int node = dev->of_offset;
+       int parent;
+
+       pl->interface = PMIC_I2C;
+       pl->regs_num = PMIC_NUM_OF_REGS;
+
+       parent = fdt_parent_offset(blob, node);
+
+       if (parent < 0) {
+               error("%s: Cannot find node parent", __func__);
+               return -EINVAL;
+       }
+
+       pl->bus = i2c_get_bus_num_fdt(parent);
+       if (pl->bus < 0) {
+               debug("%s: Cannot find bus num\n", __func__);
+               return -EINVAL;
+       }
+
+       pl->hw.i2c.addr = fdtdec_get_int(blob, node, "reg", SIMPLE_PMIC_I2C_ADDR);
+       pl->hw.i2c.tx_num = 1;
+
+       return 0;
+}
+
+static const struct udevice_id simple_pmic_ids[] = {
+       { .compatible = "vendor,simple_pmic"},
+       { }
+};
+
+U_BOOT_DRIVER(simple_pmic) = {
+       .name = "simple pmic",
+       .id = UCLASS_PMIC,
+       .of_match = simple_pmic_ids,
+       .probe = simple_pmic_probe,
+       .ofdata_to_platdata = simple_pmic_ofdata_to_platdata,
+       .platdata_auto_alloc_size = sizeof(struct pmic_platdata),
+};
+
+5. Pmic command
+===============
+This is based on an old pmic command code - the new one uses
+the driver model pmic API. The previous pmic I/O functionalities,
+stays unchanged. And now read/write is the main pmic command
+purpose. This command can use only UCLASS_PMIC devices,
+since this uclass is designed for pmic I/O operations only.
+
+Command options (pmic [option]):
+- list                     - list available PMICs
+- dev <id>                 - set id to current pmic device
+- pmic dump                - dump registers
+- pmic read <reg>          - read register
+- pmic write <reg> <value> - write register
+
+Example of usage:
+# pmic list           - chose one dev Id, e.g. 3
+# pmic dev 3          - set dev 3 as current device
+# pmic dump           - dump the registers of pmic dev id 3
+# pmic read 0x0       - read the register of addres 0x0
+# pmic write 0x0 0x1  - write 0x1 to the register of addres 0x0
+
+The user interface is similar to the 'mmc' command.
+
+Each pmic device driver should provide at least UCLASS_PMIC support,
+as a basic pmic device I/O interface.
+
+6. Uclass UCLASS_PMIC_REGULATOR API
+===================================
+To use the regulator API, config: CONFIG_DM_REGULATOR is required.
+
+Driver API is simple and clear:
+To get the regulator device we can use two functions:
+- pmic_get_by_name(...)
+- pmic_get_by_interface(...)
+
+This returns "struct udevice *" pointer to uclass regulator device,
+which can provide API functions.
+
+To operate on device regulators, we can use API functions
+(if provided by the driver):
+- pmic_ldo_cnt(...)
+- pmic_buck_cnt(...)
+- pmic_get_ldo_val(...)
+- pmic_set_ldo_val(...)
+- pmic_get_ldo_mode(...)
+- pmic_set_ldo_mode(...)
+- pmic_get_buck_val(...)
+- pmic_set_buck_val(...)
+- pmic_get_buck_mode(...)
+- pmic_set_buck_mode(...)
+
+For detailed description please look into the file:
+- include/power/regulator.h
+
+7. Simple UCLASS_PMIC_REGULATOR driver
+======================================
+The regulator ops implementation can be different for each driver,
+so this is only a piece of driver design.
+As a reference, please take the MAX77686 pmic and regulator drivers.
+- drivers/power/pmic/max77686.c
+- drivers/power/regulator/max77686.c
+
+The regulator driver code should look like below:
+(Please read the 'include/power/regulator.h' for more details)
+
+Note:
+The regulator device should act as a child of pmic device.
+This is a natural scheme of a physical PMIC IC:
+
+Some PMIC physical IC:
+ |_ dev pmic        (parent) - simple and sufficient for the most board configs
+   |_ dev regulator (child)  - provide regulator operations
+   |_ dev other     (child)  - some other pmic uclasses, (in the future)
+
+struct regulator_desc *simple_regulator_get_val_desc(struct udevice *dev,
+                                                    int d_type, int d_num)
+{
+       /* Here returns regulator value descriptor */
+       return NULL;
+}
+
+static struct regulator_mode_desc *
+simple_regulator_get_mode_desc_array(struct udevice *dev, int d_type, int d_num,
+                              int *d_mode_cnt)
+{
+       /* Here return arra of mode descriptors for given device */
+       return NULL;
+}
+
+static int simple_regulator_get_ldo_cnt(struct udevice *dev)
+{
+       /* Here return regulators ldo count */
+       return 0;
+}
+
+static int simple_regulator_get_buck_cnt(struct udevice *dev)
+{
+       /* Here return regulator buck count */
+       return 0;
+}
+
+static int simple_regulator_ldo_val(struct udevice *dev, int op,
+                                   int ldo, int *uV)
+{
+       /* Here return or sets given ldo value in uV */
+       return 0;
+}
+
+static int simple_regulator_buck_val(struct udevice *dev, int op,
+                                   int buck, int *uV)
+{
+       /* Here return or sets given buck value in uV */
+       return 0;
+}
+
+static int simple_regulator_ldo_mode(struct udevice *dev, int op, int ldo,
+                                    int *opmode)
+{
+       /* Here return or sets regulator ldo mode */
+       return 0;
+}
+
+static int simple_regulator_buck_mode(struct udevice *dev, int op, int buck,
+                                     int *opmode)
+{
+       /* Here return or sets regulator buck mode */
+       return 0;
+}
+
+static int simple_regulator_ofdata_to_platdata(struct udevice *dev)
+{
+       /**
+        * PMIC Interface, Case 1: common
+        * If PMIC uses only one (SPI/I2C) physical interface for driving
+        * it's functionalities.
+        *
+        * Then the bind of "simple regulator" device in the "simple pmic" probe
+        * is called with args:
+        * ret = device_bind(parent, reg_drv, parent->name, parent->platdata,
+        *                   parent->of_offset, &reg_dev);
+        */
+
+       /**
+        * PMIC Interface, case 2: independent
+        * If PMIC uses more then one (SPI/I2C) physical interfaces for driving
+        * it's functionalities.
+        *
+        * Then the bind of "simple regulator" device in the "simple pmic"
+        * drivers 'probe', is called with args:
+        * ret = device_bind(parent, reg_drv, parent->name, NULL,
+        *                   regulator_fdt_of_offset, &reg_dev);
+        *
+        * In this case "driver.platdata_auto_alloc_size"
+        */
+
+       /**
+        * Here driver should get the 'regulator-voltage' node data
+        * into a proper descriptors.
+        * Actually there is no standard voltage description in dts,
+        * but please check trats2 or odroid dts files and regulator/max77686.c
+        * driver file to check some simple solution.
+        *
+        */
+       return 0;
+}
+
+static const struct dm_regulator_ops simple_regulator_ops = {
+       .get_ldo_cnt         = simple_regulator_get_ldo_cnt,
+       .get_buck_cnt        = simple_regulator_get_buck_cnt,
+       .get_val_desc        = simple_regulator_get_val_desc,
+       .get_mode_desc_array = simple_regulator_get_mode_desc_array,
+       .ldo_val             = simple_regulator_ldo_val,
+       .buck_val            = simple_regulator_buck_val,
+       .ldo_mode            = simple_regulator_ldo_mode,
+       .buck_mode           = simple_regulator_buck_mode,
+};
+
+U_BOOT_DRIVER(simple_regulator) = {
+       .name = "simple regulator",
+       .id = UCLASS_PMIC_REGULATOR,
+       .ops = &simple_regulator_ops,
+       .ofdata_to_platdata = reg_simple_regulator_ofdata_to_platdata,
+       .priv_auto_alloc_size = sizeof(struct simple_regulator_info),
+
+       /**
+        * .platdata_auto_alloc_size - is optional, only if regulator uses
+        *                             a different interface than parent pmic.
+        */
+       .platdata_auto_alloc_size = sizeof(struct pmic_platdata),
+};
+
+8. Regulator command
+====================
+The extension for 'pmic' command is a 'regulator' command.
+This actually uses the same code, but provides an interface
+to pmic optional 'UCLASS_PMIC_REGULATOR' operations.
+
+If pmic device driver provides support to this another pmic
+uclass, then this command provides useful user interface.
+
+This was designed to allow safe I/O access to pmic device
+without the pmic documentation. If driver provide each regulator
+- value and mode descriptor - then user can operate on it.
+
+Command options (regulator [option]):
+- list     - list UCLASS regulator devices
+- dev [id] - show or set current regulator device
+- dump     - dump registers of current regulator
+- [ldo/buck][N] [name/state/desc]- print regulator(s) info
+- [ldoN/buckN] [setval/setmode] [mV/modeN] [-f] - set val (mV)
+(forcibly) or mode - only if desc available
+
+Example of usage:
+regulator list - look after regulator 'Id' number
+regulator dev 'Id'  - set current device
+regulator ldo state - list state of current device all ldo's
+regulator ldo desc  - list ldo's descriptors
+regulator ldo1 setval 1000 - set device ldo 1 voltage to 1000mV
+regulator ldo1 setval 1200 -f - if value exceeds limits - set force
+regulator ldo1 setmode 5 - set device ldo 1 mode to '5' (force no available)
+
+The same for 'buck' regulators.
+
+Note:
+The regulator descriptor 'min' and 'max' limits prevents setting
+unsafe value. But sometimes it is useful to change the regulator
+value for some test - so the force option (-f) is available.
+This option is not available for change the mode, since this depends
+on a pmic device design, but the required voltage value can change,
+e.g. if some hardware uses pins header.
--
Quoted 2 lines

1.9.1

Regards,
Simon

#38 lore raw

PATCH 11/19 doc: driver-model: pmic and regulator uclass documentation

Hello,

Quoted 170 lines · Simon Glass

Hi,

Quoted 167 lines · Przemyslaw Marczak
---
  doc/driver-model/dm-pmic-framework.txt | 450 +++++++++++++++++++++++++++++++++
  1 file changed, 450 insertions(+)

create mode 100644 doc/driver-model/dm-pmic-framework.txt

doc/driver-model/dm-pmic-framework.txt new file +155 -0
new file mode 100644
index 0000000..1b69eee
@@ -0,0 +1,450 @@
+#
+# (C) Copyright 2014 Samsung Electronics
+# Przemyslaw Marczak <[email protected]>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+PMIC framework based on Driver Model
+====================================
+TOC:
+1. Introduction
+2. How does it work
+3. Driver API
+4. Simple UCLASS_PMIC driver
+5. Pmic command
+6. Uclass UCLASS_PMIC_REGULATOR API
+7. Simple UCLASS_PMIC_REGULATOR driver
+8. Regulator command
+
+1. Introduction
+===============
+This is an introduction to driver-model multi class PMIC support.
+It starts with UCLASS_PMIC - a common PMIC class type for I/O, which
+doesn't need to implement any specific operations and features beside
+the platform data, which is a 'struct pmic_platdata' defined in file:
+- 'include/power/pmic.h'
+
+New files:
+- pmic-uclass.c - provides a common code for UCLASS_PMIC drivers
+- pmic_i2c.c    - provides dm interface for i2c pmic drivers
+- pmic_spi.c    - provides dm interface for spi pmic drivers
+
+Those files are based on a current PMIC framework files and code.
+And the new files are introduced to avoid code mess and allow to
+make an easy drivers migration. The old pmic is still kept.
+
+Changes:
+- add uclass-id: UCLASS_PMIC
+- add new configs:
+  CONFIG_DM_PMIC - enables driver model pmic framework and requires one
+  or both of:
+  CONFIG_DM_PMIC_SPI - enables an interface to pmic SPI
+  CONFIG_DM_PMIC_I2C - enables an interface to pmic I2C
+
+The old pmic interface API worked very well so it is kept, but the framework
+architecture is changed.
+
+2. How doees it work
+====================
+The framework init starts in file: drivers/power/pmic-uclass.c
+The function pmic_init_dm() looks after the 'pmic' alias in the device-tree
+and do the driver bind.
+If board uses more than one PMIC device, then a few 'pmic' aliases should
+be defined with numbers, e.g. pmic0, pmic1...
+
+Note:
+The I2C doesn't use the driver model yet, so the drivers are bind thanks to the
+'pmic' alias.
+
+3. PMIC drivers API
+===================
+The new API is as simple as previously:
+
+File: drivers/power/pmic-uclass.c:
+- void power_init_dm(void)
+  The main init function, called after the i2c init and before power_init_board.
+  Bind the UCLASS_PMIC drivers.
+
+- struct udevice *pmic_get_by_name(int uclass_id, char *name)
+  This function provides similar function as old pmic_get(), but the returned
+  device pointer doesn't require any allocation.
+  The uclass_id argument if to chose proper device uclass, e.g. pmic or next
+  others, pmic related.
+
+- struct udevice *pmic_get_by_interface(int uclass_id, int bus, int addr_cs)
+  This is similar function as previous but is designed for boards with more
+  than one pmic device or the same device instances. In this case we can't
+  get the device by the name, so the best and accurate method is to get it by
+  known uclass, bus and address or cs(spi). This is new framework feature.
+
+- const char *pmic_if_str(int interface)
+  This function returns the interface name string for i2c or spi, and uses
+  pmic interface enum from: include/power/pmic.h
+
+- int pmic_check_reg(struct pmic_platdata *p, unsigned reg)
+  This function check if the given register number is valid for the given
+  pmic_platdata.
+
+- int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+  The same functionality as in old framework but changed to driver-model.
+
+- int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+  The same functionality as in old framework but changed to driver-model.
+
+- int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave)
+  The same functionality as in an old framework but changed to driver model,
+  and extended by support to both pmic interfaces.
+
+The below functions, are implemented with use of common calls, described above.
+
+File: drivers/power/pmic_i2c.c:
+- int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+- int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+- int pmic_i2c_probe(struct udevice *dev)
+
+File: drivers/power/pmic_spi.c:
+- int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+- int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+- struct spi_slave *pmic_spi_probe(struct udevice *dev)
+
+4. Simple UCLASS_PMIC driver
+============================
+At this stage the framework supports only drivers that have proper fdt alias.
+The alias is "pmic" or "pmicN".
+
+The exaple of dts node():
+       aliases {
+               ...
+               ...
+               pmic0 = &simple_pmic0;
+       }
+
+       i2c at some_addr {
+               ; some i2c data
+
+               simple_pmic0: simple_pmic at 09 {
+                       compatible = "vandor, simple_pmic";
+                       reg = <0x09 0 0>;
+               }
+
+The exaple of driver code:
+
+/**
+ * simple_pmic_probe(...) - this function is optional and is a preffered way
+ * to bind the "simple regulator" device.
+ * Than we have:
+ * device: "simple_pmic"       - provides device I/O
+ * device: "simple regulator"  - provides regualtor operations and is binded
+ *                               as a child of "simple_pmic"
+ */
+static int simple_pmic_probe(struct udevice *parent)
+{
+       struct udevice *reg_dev;
+       struct driver *reg_drv;
+       int ret;
+
+       reg_drv = lists_driver_lookup_name("simple regulator");
+       if (!reg_drv) {
+               error("%s regulator driver not found!\n", parent->name);
+               return 0;
+       }
+
+       if (!parent->platdata) {
+               error("%s platdata not found!\n", parent->name);
+               return -EINVAL;
Quoted 9 lines

doc/driver-model/dm-pmic-framework.txt line 159

Quoted 4 lines
doc/driver-model/dm-pmic-framework.txt new file +4 -0
+       }
+
+       ret = device_bind(parent, reg_drv, parent->name, parent->platdata,
+                         parent->of_offset, &reg_dev);

I wonder if we can avoid this? Really the regulator should be bound at init time, and this function should use something like uclass_get_device_by_of_offset() to find it.

Yes, if I2C class will be ready, then we can simplify this, but for now it is simple.

Quoted 289 lines
Quoted 289 lines
doc/driver-model/dm-pmic-framework.txt new file +289 -0
+       if (ret)
+               error("%s regulator bind failed", parent->name);
+
+       /**
+        * Return an error only if no parent platdata set
+        * so if no regulator found - still have pmic I/O.
+        * This scheme will be used in most board configs.
+        */
+       return 0;
+}
+
+static int simple_pmic_ofdata_to_platdata(struct udevice *dev)
+{
+       struct pmic_platdata *pl = dev->platdata;
+
+       /**
+        * Here, driver should init the platdata structure based on device-tree,
+        * The fields bus, interface, byte_order and the struct spi or i2c,
+        * provide information about I/O acces, so are required.
+        */
+       const void *blob = gd->fdt_blob;
+       int node = dev->of_offset;
+       int parent;
+
+       pl->interface = PMIC_I2C;
+       pl->regs_num = PMIC_NUM_OF_REGS;
+
+       parent = fdt_parent_offset(blob, node);
+
+       if (parent < 0) {
+               error("%s: Cannot find node parent", __func__);
+               return -EINVAL;
+       }
+
+       pl->bus = i2c_get_bus_num_fdt(parent);
+       if (pl->bus < 0) {
+               debug("%s: Cannot find bus num\n", __func__);
+               return -EINVAL;
+       }
+
+       pl->hw.i2c.addr = fdtdec_get_int(blob, node, "reg", SIMPLE_PMIC_I2C_ADDR);
+       pl->hw.i2c.tx_num = 1;
+
+       return 0;
+}
+
+static const struct udevice_id simple_pmic_ids[] = {
+       { .compatible = "vendor,simple_pmic"},
+       { }
+};
+
+U_BOOT_DRIVER(simple_pmic) = {
+       .name = "simple pmic",
+       .id = UCLASS_PMIC,
+       .of_match = simple_pmic_ids,
+       .probe = simple_pmic_probe,
+       .ofdata_to_platdata = simple_pmic_ofdata_to_platdata,
+       .platdata_auto_alloc_size = sizeof(struct pmic_platdata),
+};
+
+5. Pmic command
+===============
+This is based on an old pmic command code - the new one uses
+the driver model pmic API. The previous pmic I/O functionalities,
+stays unchanged. And now read/write is the main pmic command
+purpose. This command can use only UCLASS_PMIC devices,
+since this uclass is designed for pmic I/O operations only.
+
+Command options (pmic [option]):
+- list                     - list available PMICs
+- dev <id>                 - set id to current pmic device
+- pmic dump                - dump registers
+- pmic read <reg>          - read register
+- pmic write <reg> <value> - write register
+
+Example of usage:
+# pmic list           - chose one dev Id, e.g. 3
+# pmic dev 3          - set dev 3 as current device
+# pmic dump           - dump the registers of pmic dev id 3
+# pmic read 0x0       - read the register of addres 0x0
+# pmic write 0x0 0x1  - write 0x1 to the register of addres 0x0
+
+The user interface is similar to the 'mmc' command.
+
+Each pmic device driver should provide at least UCLASS_PMIC support,
+as a basic pmic device I/O interface.
+
+6. Uclass UCLASS_PMIC_REGULATOR API
+===================================
+To use the regulator API, config: CONFIG_DM_REGULATOR is required.
+
+Driver API is simple and clear:
+To get the regulator device we can use two functions:
+- pmic_get_by_name(...)
+- pmic_get_by_interface(...)
+
+This returns "struct udevice *" pointer to uclass regulator device,
+which can provide API functions.
+
+To operate on device regulators, we can use API functions
+(if provided by the driver):
+- pmic_ldo_cnt(...)
+- pmic_buck_cnt(...)
+- pmic_get_ldo_val(...)
+- pmic_set_ldo_val(...)
+- pmic_get_ldo_mode(...)
+- pmic_set_ldo_mode(...)
+- pmic_get_buck_val(...)
+- pmic_set_buck_val(...)
+- pmic_get_buck_mode(...)
+- pmic_set_buck_mode(...)
+
+For detailed description please look into the file:
+- include/power/regulator.h
+
+7. Simple UCLASS_PMIC_REGULATOR driver
+======================================
+The regulator ops implementation can be different for each driver,
+so this is only a piece of driver design.
+As a reference, please take the MAX77686 pmic and regulator drivers.
+- drivers/power/pmic/max77686.c
+- drivers/power/regulator/max77686.c
+
+The regulator driver code should look like below:
+(Please read the 'include/power/regulator.h' for more details)
+
+Note:
+The regulator device should act as a child of pmic device.
+This is a natural scheme of a physical PMIC IC:
+
+Some PMIC physical IC:
+ |_ dev pmic        (parent) - simple and sufficient for the most board configs
+   |_ dev regulator (child)  - provide regulator operations
+   |_ dev other     (child)  - some other pmic uclasses, (in the future)
+
+struct regulator_desc *simple_regulator_get_val_desc(struct udevice *dev,
+                                                    int d_type, int d_num)
+{
+       /* Here returns regulator value descriptor */
+       return NULL;
+}
+
+static struct regulator_mode_desc *
+simple_regulator_get_mode_desc_array(struct udevice *dev, int d_type, int d_num,
+                              int *d_mode_cnt)
+{
+       /* Here return arra of mode descriptors for given device */
+       return NULL;
+}
+
+static int simple_regulator_get_ldo_cnt(struct udevice *dev)
+{
+       /* Here return regulators ldo count */
+       return 0;
+}
+
+static int simple_regulator_get_buck_cnt(struct udevice *dev)
+{
+       /* Here return regulator buck count */
+       return 0;
+}
+
+static int simple_regulator_ldo_val(struct udevice *dev, int op,
+                                   int ldo, int *uV)
+{
+       /* Here return or sets given ldo value in uV */
+       return 0;
+}
+
+static int simple_regulator_buck_val(struct udevice *dev, int op,
+                                   int buck, int *uV)
+{
+       /* Here return or sets given buck value in uV */
+       return 0;
+}
+
+static int simple_regulator_ldo_mode(struct udevice *dev, int op, int ldo,
+                                    int *opmode)
+{
+       /* Here return or sets regulator ldo mode */
+       return 0;
+}
+
+static int simple_regulator_buck_mode(struct udevice *dev, int op, int buck,
+                                     int *opmode)
+{
+       /* Here return or sets regulator buck mode */
+       return 0;
+}
+
+static int simple_regulator_ofdata_to_platdata(struct udevice *dev)
+{
+       /**
+        * PMIC Interface, Case 1: common
+        * If PMIC uses only one (SPI/I2C) physical interface for driving
+        * it's functionalities.
+        *
+        * Then the bind of "simple regulator" device in the "simple pmic" probe
+        * is called with args:
+        * ret = device_bind(parent, reg_drv, parent->name, parent->platdata,
+        *                   parent->of_offset, &reg_dev);
+        */
+
+       /**
+        * PMIC Interface, case 2: independent
+        * If PMIC uses more then one (SPI/I2C) physical interfaces for driving
+        * it's functionalities.
+        *
+        * Then the bind of "simple regulator" device in the "simple pmic"
+        * drivers 'probe', is called with args:
+        * ret = device_bind(parent, reg_drv, parent->name, NULL,
+        *                   regulator_fdt_of_offset, &reg_dev);
+        *
+        * In this case "driver.platdata_auto_alloc_size"
+        */
+
+       /**
+        * Here driver should get the 'regulator-voltage' node data
+        * into a proper descriptors.
+        * Actually there is no standard voltage description in dts,
+        * but please check trats2 or odroid dts files and regulator/max77686.c
+        * driver file to check some simple solution.
+        *
+        */
+       return 0;
+}
+
+static const struct dm_regulator_ops simple_regulator_ops = {
+       .get_ldo_cnt         = simple_regulator_get_ldo_cnt,
+       .get_buck_cnt        = simple_regulator_get_buck_cnt,
+       .get_val_desc        = simple_regulator_get_val_desc,
+       .get_mode_desc_array = simple_regulator_get_mode_desc_array,
+       .ldo_val             = simple_regulator_ldo_val,
+       .buck_val            = simple_regulator_buck_val,
+       .ldo_mode            = simple_regulator_ldo_mode,
+       .buck_mode           = simple_regulator_buck_mode,
+};
+
+U_BOOT_DRIVER(simple_regulator) = {
+       .name = "simple regulator",
+       .id = UCLASS_PMIC_REGULATOR,
+       .ops = &simple_regulator_ops,
+       .ofdata_to_platdata = reg_simple_regulator_ofdata_to_platdata,
+       .priv_auto_alloc_size = sizeof(struct simple_regulator_info),
+
+       /**
+        * .platdata_auto_alloc_size - is optional, only if regulator uses
+        *                             a different interface than parent pmic.
+        */
+       .platdata_auto_alloc_size = sizeof(struct pmic_platdata),
+};
+
+8. Regulator command
+====================
+The extension for 'pmic' command is a 'regulator' command.
+This actually uses the same code, but provides an interface
+to pmic optional 'UCLASS_PMIC_REGULATOR' operations.
+
+If pmic device driver provides support to this another pmic
+uclass, then this command provides useful user interface.
+
+This was designed to allow safe I/O access to pmic device
+without the pmic documentation. If driver provide each regulator
+- value and mode descriptor - then user can operate on it.
+
+Command options (regulator [option]):
+- list     - list UCLASS regulator devices
+- dev [id] - show or set current regulator device
+- dump     - dump registers of current regulator
+- [ldo/buck][N] [name/state/desc]- print regulator(s) info
+- [ldoN/buckN] [setval/setmode] [mV/modeN] [-f] - set val (mV)
+(forcibly) or mode - only if desc available
+
+Example of usage:
+regulator list - look after regulator 'Id' number
+regulator dev 'Id'  - set current device
+regulator ldo state - list state of current device all ldo's
+regulator ldo desc  - list ldo's descriptors
+regulator ldo1 setval 1000 - set device ldo 1 voltage to 1000mV
+regulator ldo1 setval 1200 -f - if value exceeds limits - set force
+regulator ldo1 setmode 5 - set device ldo 1 mode to '5' (force no available)
+
+The same for 'buck' regulators.
+
+Note:
+The regulator descriptor 'min' and 'max' limits prevents setting
+unsafe value. But sometimes it is useful to change the regulator
+value for some test - so the force option (-f) is available.
+This option is not available for change the mode, since this depends
Quoted 9 lines
Quoted 5 lines
doc/driver-model/dm-pmic-framework.txt new file +2 -1
+on a pmic device design, but the required voltage value can change,
+e.g. if some hardware uses pins header.
--

1.9.1

Regards,
Simon

Thank you,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#39 lore raw

PATCH 12/19 samsung: board: lcd menu: check if any power framework is enabled

In reply to Przemyslaw Marczak

The lcd menu requires pmic framework to get the power key state. Now two pmic frameworks are available - the old one and the new one based on a driver-model.

---
 board/samsung/common/board.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
board/samsung/common/board.c +2 -1
index e4cddc2..450740a 100644
@@ -348,7 +348,8 @@ int misc_init_r(void)
 #ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
 	set_board_info();
 #endif
-#ifdef CONFIG_LCD_MENU
+#if defined(CONFIG_LCD_MENU) && (defined(CONFIG_POWER) || \
+				 defined(CONFIG_DM_PMIC))
 	keys_init();
 	check_boot_mode();
 #endif
Signature · 1 line
1.9.1
#40 lore raw

PATCH 13/19 samsung: misc: power_key_pressed: add support to dm pmic framework

In reply to Przemyslaw Marczak

In case of two pmic frameworks availability - enable support of both, since the new pmic framework is not fully finished and some boards still supports only the old framework.

---
 board/samsung/common/misc.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)
board/samsung/common/misc.c +18 -3
index 4538ac7..94308a9 100644
@@ -93,12 +93,13 @@ void set_board_info(void)
 }
 #endif /* CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG */
 
-#ifdef CONFIG_LCD_MENU
+#if defined(CONFIG_LCD_MENU)
 static int power_key_pressed(u32 reg)
 {
+	unsigned status;
+	unsigned mask;
+#if defined(CONFIG_POWER)
 	struct pmic *pmic;
-	u32 status;
-	u32 mask;
 
 	pmic = pmic_get(KEY_PWR_PMIC_NAME);
 	if (!pmic) {
@@ -108,7 +109,21 @@ static int power_key_pressed(u32 reg)
 
 	if (pmic_probe(pmic))
 		return 0;
+#elif defined(CONFIG_DM_PMIC)
+	struct udevice *pmic;
 
+	pmic = pmic_get_by_name(UCLASS_PMIC, KEY_PWR_PMIC_NAME);
+
+	if (!pmic) {
+		printf("%s: Not found\n", KEY_PWR_PMIC_NAME);
+		return 0;
+	}
+
+	if (pmic_probe(pmic, NULL))
+		return 0;
+#else
+#error one of: CONFIG_DM_PMIC or CONFIG_POWER must be defined!
+#endif
 	if (reg == KEY_PWR_STATUS_REG)
 		mask = KEY_PWR_STATUS_MASK;
 	else
Signature · 1 line
1.9.1
#41 lore raw

PATCH 13/19 samsung: misc: power_key_pressed: add support to dm pmic framework

Hi,

Quoted 31 lines · Przemyslaw Marczak

In case of two pmic frameworks availability - enable support of both, since the new pmic framework is not fully finished and some boards still supports only the old framework.

---
 board/samsung/common/misc.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)
board/samsung/common/misc.c +4 -3
index 4538ac7..94308a9 100644
@@ -93,12 +93,13 @@ void set_board_info(void)
 }
 #endif /* CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG */
 
-#ifdef CONFIG_LCD_MENU
+#if defined(CONFIG_LCD_MENU)
 static int power_key_pressed(u32 reg)
 {
+       unsigned status;
+       unsigned mask;
+#if defined(CONFIG_POWER)
        struct pmic *pmic;
-       u32 status;
-       u32 mask;
 
        pmic = pmic_get(KEY_PWR_PMIC_NAME);
        if (!pmic) {

board/samsung/common/misc.c static int power_key_pressed(u32 reg) · line 115

Quoted 7 lines
board/samsung/common/misc.c +3 -0
@@ -108,7 +109,21 @@ static int power_key_pressed(u32 reg)
 
        if (pmic_probe(pmic))
                return 0;
+#elif defined(CONFIG_DM_PMIC)
+       struct udevice *pmic;
 
+       pmic = pmic_get_by_name(UCLASS_PMIC, KEY_PWR_PMIC_NAME);

If we are going to get pmics by name, perhaps we should have uclass_get_device_by_name() instead? So long as the device has the right name, this should work, and avoids adding another layer of naming.

Quoted 15 lines
board/samsung/common/misc.c +11 -1
+
+       if (!pmic) {
+               printf("%s: Not found\n", KEY_PWR_PMIC_NAME);
+               return 0;
+       }
+
+       if (pmic_probe(pmic, NULL))
+               return 0;
+#else
+#error one of: CONFIG_DM_PMIC or CONFIG_POWER must be defined!
+#endif
        if (reg == KEY_PWR_STATUS_REG)
                mask = KEY_PWR_STATUS_MASK;
        else
--
Quoted 2 lines

1.9.1

Regards,
Simon

#42 lore raw

PATCH 14/19 trats2: board: add support to dm pmic api

In reply to Przemyslaw Marczak

Changes required to support dm pmic and dm regulator api:
- move call to board_init_i2c() into exynos_init() - earlier init the i2c
- remove redundant ldo setup - default hardware configuration is proper
- adjust pmic/regulator calls to new pmic api

---
 board/samsung/trats2/trats2.c | 208 +++++++++++-------------------------------
 1 file changed, 52 insertions(+), 156 deletions(-)
board/samsung/trats2/trats2.c +52 -156
index a737749..b107ba5 100644
@@ -23,6 +23,7 @@
 #include <usb.h>
 #include <usb/s3c_udc.h>
 #include <usb_mass_storage.h>
+#include <dm.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -151,7 +152,9 @@ int exynos_early_init_f(void)
 	return 0;
 }
 
+#ifdef CONFIG_DM_PMIC
 static int pmic_init_max77686(void);
+#endif
 
 int exynos_init(void)
 {
@@ -171,71 +174,16 @@ int exynos_init(void)
 	writel(0, &pwr->inform4);
 	writel(0, &pwr->inform5);
 
+#ifdef CONFIG_SYS_I2C_INIT_BOARD
+	board_init_i2c();
+#endif
+
 	return 0;
 }
 
 int exynos_power_init(void)
 {
-	int chrg;
-	struct power_battery *pb;
-	struct pmic *p_chrg, *p_muic, *p_fg, *p_bat;
-
-#ifdef CONFIG_SYS_I2C_INIT_BOARD
-	board_init_i2c();
-#endif
-	pmic_init(I2C_7);		/* I2C adapter 7 - bus name s3c24x0_7 */
 	pmic_init_max77686();
-	pmic_init_max77693(I2C_10);	/* I2C adapter 10 - bus name soft1 */
-	power_muic_init(I2C_10);	/* I2C adapter 10 - bus name soft1 */
-	power_fg_init(I2C_9);		/* I2C adapter 9 - bus name soft0 */
-	power_bat_init(0);
-
-	p_chrg = pmic_get("MAX77693_PMIC");
-	if (!p_chrg) {
-		puts("MAX77693_PMIC: Not found\n");
-		return -ENODEV;
-	}
-
-	p_muic = pmic_get("MAX77693_MUIC");
-	if (!p_muic) {
-		puts("MAX77693_MUIC: Not found\n");
-		return -ENODEV;
-	}
-
-	p_fg = pmic_get("MAX77693_FG");
-	if (!p_fg) {
-		puts("MAX17042_FG: Not found\n");
-		return -ENODEV;
-	}
-
-	if (p_chrg->chrg->chrg_bat_present(p_chrg) == 0)
-		puts("No battery detected\n");
-
-	p_bat = pmic_get("BAT_TRATS2");
-	if (!p_bat) {
-		puts("BAT_TRATS2: Not found\n");
-		return -ENODEV;
-	}
-
-	p_fg->parent =  p_bat;
-	p_chrg->parent = p_bat;
-	p_muic->parent = p_bat;
-
-	p_bat->pbat->battery_init(p_bat, p_fg, p_chrg, p_muic);
-
-	pb = p_bat->pbat;
-	chrg = p_muic->chrg->chrg_type(p_muic);
-	debug("CHARGER TYPE: %d\n", chrg);
-
-	if (!p_chrg->chrg->chrg_bat_present(p_chrg)) {
-		puts("No battery detected\n");
-		return 0;
-	}
-
-	p_fg->fg->fg_battery_check(p_fg, p_bat);
-
-	if (pb->bat->state == CHARGE && chrg == CHARGER_USB)
-		puts("CHARGE Battery !\n");
 
 	return 0;
 }
@@ -244,62 +192,22 @@ int exynos_power_init(void)
 static int s5pc210_phy_control(int on)
 {
 	int ret = 0;
-	unsigned int val;
-	struct pmic *p, *p_pmic, *p_muic;
+	struct udevice *pmic;
 
-	p_pmic = pmic_get("MAX77686_PMIC");
-	if (!p_pmic)
+	pmic = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+	if (!pmic)
 		return -ENODEV;
 
-	if (pmic_probe(p_pmic))
-		return -1;
-
-	p_muic = pmic_get("MAX77693_MUIC");
-	if (!p_muic)
-		return -ENODEV;
-
-	if (pmic_probe(p_muic))
-		return -1;
-
 	if (on) {
-		ret = max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
-		if (ret)
-			return -1;
-
-		p = pmic_get("MAX77693_PMIC");
-		if (!p)
-			return -ENODEV;
-
-		if (pmic_probe(p))
-			return -1;
-
-		/* SAFEOUT */
-		ret = pmic_reg_read(p, MAX77693_SAFEOUT, &val);
-		if (ret)
-			return -1;
-
-		val |= MAX77693_ENSAFEOUT1;
-		ret = pmic_reg_write(p, MAX77693_SAFEOUT, val);
+		ret = pmic_set_ldo_mode(pmic, 12, OPMODE_ON);
 		if (ret)
-			return -1;
-
-		/* PATH: USB */
-		ret = pmic_reg_write(p_muic, MAX77693_MUIC_CONTROL1,
-			MAX77693_MUIC_CTRL1_DN1DP2);
-
+			return -EIO;
 	} else {
-		ret = max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
+		ret = pmic_set_ldo_mode(pmic, 12, OPMODE_LPM);
 		if (ret)
-			return -1;
-
-		/* PATH: UART */
-		ret = pmic_reg_write(p_muic, MAX77693_MUIC_CONTROL1,
-			MAX77693_MUIC_CTRL1_UT1UR2);
+			return -EIO;
 	}
 
-	if (ret)
-		return -1;
-
 	return 0;
 }
 
@@ -319,66 +227,47 @@ int board_usb_init(int index, enum usb_init_type init)
 
 int g_dnl_board_usb_cable_connected(void)
 {
+#ifdef CONFIG_POWER
 	struct pmic *muic = pmic_get("MAX77693_MUIC");
 	if (!muic)
 		return 0;
 
 	return !!muic->chrg->chrg_type(muic);
+#else
+	return 1;
+#endif
 }
 #endif
-
+#ifdef CONFIG_DM_PMIC
 static int pmic_init_max77686(void)
 {
-	struct pmic *p = pmic_get("MAX77686_PMIC");
+	struct udevice *p;
 
-	if (pmic_probe(p))
-		return -1;
+	p = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+	if (!p) {
+		error("Regulator driver not found");
+		return -ENODEV;
+	}
 
 	/* BUCK/LDO Output Voltage */
-	max77686_set_ldo_voltage(p, 21, 2800000);	/* LDO21 VTF_2.8V */
-	max77686_set_ldo_voltage(p, 23, 3300000);	/* LDO23 TSP_AVDD_3.3V*/
-	max77686_set_ldo_voltage(p, 24, 1800000);	/* LDO24 TSP_VDD_1.8V */
+	pmic_set_ldo_val(p, 21, 2800000);	/* LDO21 VTF_2.8V */
+	pmic_set_ldo_val(p, 23, 3300000);	/* LDO23 TSP_AVDD_3.3V*/
+	pmic_set_ldo_val(p, 24, 1800000);	/* LDO24 TSP_VDD_1.8V */
 
 	/* BUCK/LDO Output Mode */
-	max77686_set_buck_mode(p, 1, OPMODE_STANDBY);	/* BUCK1 VMIF_1.1V_AP */
-	max77686_set_buck_mode(p, 2, OPMODE_ON);	/* BUCK2 VARM_1.0V_AP */
-	max77686_set_buck_mode(p, 3, OPMODE_ON);	/* BUCK3 VINT_1.0V_AP */
-	max77686_set_buck_mode(p, 4, OPMODE_ON);	/* BUCK4 VG3D_1.0V_AP */
-	max77686_set_buck_mode(p, 5, OPMODE_ON);	/* BUCK5 VMEM_1.2V_AP */
-	max77686_set_buck_mode(p, 6, OPMODE_ON);	/* BUCK6 VCC_SUB_1.35V*/
-	max77686_set_buck_mode(p, 7, OPMODE_ON);	/* BUCK7 VCC_SUB_2.0V */
-	max77686_set_buck_mode(p, 8, OPMODE_OFF);	/* VMEM_VDDF_2.85V */
-	max77686_set_buck_mode(p, 9, OPMODE_OFF);	/* CAM_ISP_CORE_1.2V*/
-
-	max77686_set_ldo_mode(p, 1, OPMODE_LPM);	/* LDO1 VALIVE_1.0V_AP*/
-	max77686_set_ldo_mode(p, 2, OPMODE_STANDBY);	/* LDO2 VM1M2_1.2V_AP */
-	max77686_set_ldo_mode(p, 3, OPMODE_LPM);	/* LDO3 VCC_1.8V_AP */
-	max77686_set_ldo_mode(p, 4, OPMODE_LPM);	/* LDO4 VCC_2.8V_AP */
-	max77686_set_ldo_mode(p, 5, OPMODE_OFF);	/* LDO5_VCC_1.8V_IO */
-	max77686_set_ldo_mode(p, 6, OPMODE_STANDBY);	/* LDO6 VMPLL_1.0V_AP */
-	max77686_set_ldo_mode(p, 7, OPMODE_STANDBY);	/* LDO7 VPLL_1.0V_AP */
-	max77686_set_ldo_mode(p, 8, OPMODE_LPM);	/* LDO8 VMIPI_1.0V_AP */
-	max77686_set_ldo_mode(p, 9, OPMODE_OFF);	/* CAM_ISP_MIPI_1.2*/
-	max77686_set_ldo_mode(p, 10, OPMODE_LPM);	/* LDO10 VMIPI_1.8V_AP*/
-	max77686_set_ldo_mode(p, 11, OPMODE_STANDBY);	/* LDO11 VABB1_1.8V_AP*/
-	max77686_set_ldo_mode(p, 12, OPMODE_LPM);	/* LDO12 VUOTG_3.0V_AP*/
-	max77686_set_ldo_mode(p, 13, OPMODE_OFF);	/* LDO13 VC2C_1.8V_AP */
-	max77686_set_ldo_mode(p, 14, OPMODE_STANDBY);	/* VABB02_1.8V_AP */
-	max77686_set_ldo_mode(p, 15, OPMODE_STANDBY);	/* LDO15 VHSIC_1.0V_AP*/
-	max77686_set_ldo_mode(p, 16, OPMODE_STANDBY);	/* LDO16 VHSIC_1.8V_AP*/
-	max77686_set_ldo_mode(p, 17, OPMODE_OFF);	/* CAM_SENSOR_CORE_1.2*/
-	max77686_set_ldo_mode(p, 18, OPMODE_OFF);	/* CAM_ISP_SEN_IO_1.8V*/
-	max77686_set_ldo_mode(p, 19, OPMODE_OFF);	/* LDO19 VT_CAM_1.8V */
-	max77686_set_ldo_mode(p, 20, OPMODE_ON);	/* LDO20 VDDQ_PRE_1.8V*/
-	max77686_set_ldo_mode(p, 21, OPMODE_OFF);	/* LDO21 VTF_2.8V */
-	max77686_set_ldo_mode(p, 22, OPMODE_OFF);	/* LDO22 VMEM_VDD_2.8V*/
-	max77686_set_ldo_mode(p, 23, OPMODE_OFF);	/* LDO23 TSP_AVDD_3.3V*/
-	max77686_set_ldo_mode(p, 24, OPMODE_OFF);	/* LDO24 TSP_VDD_1.8V */
-	max77686_set_ldo_mode(p, 25, OPMODE_OFF);	/* LDO25 VCC_3.3V_LCD */
-	max77686_set_ldo_mode(p, 26, OPMODE_OFF);	/*LDO26 VCC_3.0V_MOTOR*/
+	pmic_set_buck_mode(p, 1, OPMODE_STANDBY);	/* BUCK1 VMIF_1.1V_AP */
+	pmic_set_buck_mode(p, 2, OPMODE_ON);	/* BUCK2 VARM_1.0V_AP */
+	pmic_set_buck_mode(p, 3, OPMODE_ON);	/* BUCK3 VINT_1.0V_AP */
+	pmic_set_buck_mode(p, 4, OPMODE_ON);	/* BUCK4 VG3D_1.0V_AP */
+	pmic_set_buck_mode(p, 5, OPMODE_ON);	/* BUCK5 VMEM_1.2V_AP */
+	pmic_set_buck_mode(p, 6, OPMODE_ON);	/* BUCK6 VCC_SUB_1.35V*/
+	pmic_set_buck_mode(p, 7, OPMODE_ON);	/* BUCK7 VCC_SUB_2.0V */
+	pmic_set_buck_mode(p, 8, OPMODE_OFF);	/* VMEM_VDDF_2.85V */
+	pmic_set_buck_mode(p, 9, OPMODE_OFF);	/* CAM_ISP_CORE_1.2V*/
 
 	return 0;
 }
+#endif
 
 /*
  * LCD
@@ -387,19 +276,27 @@ static int pmic_init_max77686(void)
 #ifdef CONFIG_LCD
 int mipi_power(void)
 {
-	struct pmic *p = pmic_get("MAX77686_PMIC");
+	struct udevice *p = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+	if (!p) {
+		error("Regulator driver not found");
+		return -ENODEV;
+	}
 
 	/* LDO8 VMIPI_1.0V_AP */
-	max77686_set_ldo_mode(p, 8, OPMODE_ON);
+	pmic_set_ldo_mode(p, 8, OPMODE_ON);
 	/* LDO10 VMIPI_1.8V_AP */
-	max77686_set_ldo_mode(p, 10, OPMODE_ON);
+	pmic_set_ldo_mode(p, 10, OPMODE_ON);
 
 	return 0;
 }
 
 void exynos_lcd_power_on(void)
 {
-	struct pmic *p = pmic_get("MAX77686_PMIC");
+	struct udevice *p = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+	if (!p) {
+		error("Regulator driver not found\n");
+		return;
+	}
 
 	/* LCD_2.2V_EN: GPC0[1] */
 	gpio_request(EXYNOS4X12_GPIO_C01, "lcd_2v2_en");
@@ -407,9 +304,8 @@ void exynos_lcd_power_on(void)
 	gpio_direction_output(EXYNOS4X12_GPIO_C01, 1);
 
 	/* LDO25 VCC_3.1V_LCD */
-	pmic_probe(p);
-	max77686_set_ldo_voltage(p, 25, 3100000);
-	max77686_set_ldo_mode(p, 25, OPMODE_LPM);
+	pmic_set_ldo_val(p, 25, 3100000);
+	pmic_set_ldo_mode(p, 25, OPMODE_ON);
 }
 
 void exynos_reset_lcd(void)
Signature · 1 line
1.9.1
#43 lore raw

PATCH 14/19 trats2: board: add support to dm pmic api

Hi,

Quoted 23 lines · Przemyslaw Marczak

Changes required to support dm pmic and dm regulator api:
- move call to board_init_i2c() into exynos_init() - earlier init the i2c
- remove redundant ldo setup - default hardware configuration is proper
- adjust pmic/regulator calls to new pmic api

---
 board/samsung/trats2/trats2.c | 208 +++++++++++-------------------------------
 1 file changed, 52 insertions(+), 156 deletions(-)
board/samsung/trats2/trats2.c +1 -0
index a737749..b107ba5 100644
@@ -23,6 +23,7 @@
 #include <usb.h>
 #include <usb/s3c_udc.h>
 #include <usb_mass_storage.h>
+#include <dm.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 

board/samsung/trats2/trats2.c int exynos_early_init_f(void) · line 156

Quoted 5 lines
board/samsung/trats2/trats2.c +1 -0
@@ -151,7 +152,9 @@ int exynos_early_init_f(void)
        return 0;
 }
 
+#ifdef CONFIG_DM_PMIC
 static int pmic_init_max77686(void);

If you just ensure that CONFIG_DM_PMIC is defined by the board then you can avoid this.

Quoted 286 lines
board/samsung/trats2/trats2.c +50 -157
+#endif
 
 int exynos_init(void)
 {
@@ -171,71 +174,16 @@ int exynos_init(void)
        writel(0, &pwr->inform4);
        writel(0, &pwr->inform5);
 
+#ifdef CONFIG_SYS_I2C_INIT_BOARD
+       board_init_i2c();
+#endif
+
        return 0;
 }
 
 int exynos_power_init(void)
 {
-       int chrg;
-       struct power_battery *pb;
-       struct pmic *p_chrg, *p_muic, *p_fg, *p_bat;
-
-#ifdef CONFIG_SYS_I2C_INIT_BOARD
-       board_init_i2c();
-#endif
-       pmic_init(I2C_7);               /* I2C adapter 7 - bus name s3c24x0_7 */
        pmic_init_max77686();
-       pmic_init_max77693(I2C_10);     /* I2C adapter 10 - bus name soft1 */
-       power_muic_init(I2C_10);        /* I2C adapter 10 - bus name soft1 */
-       power_fg_init(I2C_9);           /* I2C adapter 9 - bus name soft0 */
-       power_bat_init(0);
-
-       p_chrg = pmic_get("MAX77693_PMIC");
-       if (!p_chrg) {
-               puts("MAX77693_PMIC: Not found\n");
-               return -ENODEV;
-       }
-
-       p_muic = pmic_get("MAX77693_MUIC");
-       if (!p_muic) {
-               puts("MAX77693_MUIC: Not found\n");
-               return -ENODEV;
-       }
-
-       p_fg = pmic_get("MAX77693_FG");
-       if (!p_fg) {
-               puts("MAX17042_FG: Not found\n");
-               return -ENODEV;
-       }
-
-       if (p_chrg->chrg->chrg_bat_present(p_chrg) == 0)
-               puts("No battery detected\n");
-
-       p_bat = pmic_get("BAT_TRATS2");
-       if (!p_bat) {
-               puts("BAT_TRATS2: Not found\n");
-               return -ENODEV;
-       }
-
-       p_fg->parent =  p_bat;
-       p_chrg->parent = p_bat;
-       p_muic->parent = p_bat;
-
-       p_bat->pbat->battery_init(p_bat, p_fg, p_chrg, p_muic);
-
-       pb = p_bat->pbat;
-       chrg = p_muic->chrg->chrg_type(p_muic);
-       debug("CHARGER TYPE: %d\n", chrg);
-
-       if (!p_chrg->chrg->chrg_bat_present(p_chrg)) {
-               puts("No battery detected\n");
-               return 0;
-       }
-
-       p_fg->fg->fg_battery_check(p_fg, p_bat);
-
-       if (pb->bat->state == CHARGE && chrg == CHARGER_USB)
-               puts("CHARGE Battery !\n");
 
        return 0;
 }
@@ -244,62 +192,22 @@ int exynos_power_init(void)
 static int s5pc210_phy_control(int on)
 {
        int ret = 0;
-       unsigned int val;
-       struct pmic *p, *p_pmic, *p_muic;
+       struct udevice *pmic;
 
-       p_pmic = pmic_get("MAX77686_PMIC");
-       if (!p_pmic)
+       pmic = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+       if (!pmic)
                return -ENODEV;
 
-       if (pmic_probe(p_pmic))
-               return -1;
-
-       p_muic = pmic_get("MAX77693_MUIC");
-       if (!p_muic)
-               return -ENODEV;
-
-       if (pmic_probe(p_muic))
-               return -1;
-
        if (on) {
-               ret = max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
-               if (ret)
-                       return -1;
-
-               p = pmic_get("MAX77693_PMIC");
-               if (!p)
-                       return -ENODEV;
-
-               if (pmic_probe(p))
-                       return -1;
-
-               /* SAFEOUT */
-               ret = pmic_reg_read(p, MAX77693_SAFEOUT, &val);
-               if (ret)
-                       return -1;
-
-               val |= MAX77693_ENSAFEOUT1;
-               ret = pmic_reg_write(p, MAX77693_SAFEOUT, val);
+               ret = pmic_set_ldo_mode(pmic, 12, OPMODE_ON);
                if (ret)
-                       return -1;
-
-               /* PATH: USB */
-               ret = pmic_reg_write(p_muic, MAX77693_MUIC_CONTROL1,
-                       MAX77693_MUIC_CTRL1_DN1DP2);
-
+                       return -EIO;
        } else {
-               ret = max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
+               ret = pmic_set_ldo_mode(pmic, 12, OPMODE_LPM);
                if (ret)
-                       return -1;
-
-               /* PATH: UART */
-               ret = pmic_reg_write(p_muic, MAX77693_MUIC_CONTROL1,
-                       MAX77693_MUIC_CTRL1_UT1UR2);
+                       return -EIO;
        }
 
-       if (ret)
-               return -1;
-
        return 0;
 }
 
@@ -319,66 +227,47 @@ int board_usb_init(int index, enum usb_init_type init)
 
 int g_dnl_board_usb_cable_connected(void)
 {
+#ifdef CONFIG_POWER
        struct pmic *muic = pmic_get("MAX77693_MUIC");
        if (!muic)
                return 0;
 
        return !!muic->chrg->chrg_type(muic);
+#else
+       return 1;
+#endif
 }
 #endif
-
+#ifdef CONFIG_DM_PMIC
 static int pmic_init_max77686(void)
 {
-       struct pmic *p = pmic_get("MAX77686_PMIC");
+       struct udevice *p;
 
-       if (pmic_probe(p))
-               return -1;
+       p = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+       if (!p) {
+               error("Regulator driver not found");
+               return -ENODEV;
+       }
 
        /* BUCK/LDO Output Voltage */
-       max77686_set_ldo_voltage(p, 21, 2800000);       /* LDO21 VTF_2.8V */
-       max77686_set_ldo_voltage(p, 23, 3300000);       /* LDO23 TSP_AVDD_3.3V*/
-       max77686_set_ldo_voltage(p, 24, 1800000);       /* LDO24 TSP_VDD_1.8V */
+       pmic_set_ldo_val(p, 21, 2800000);       /* LDO21 VTF_2.8V */
+       pmic_set_ldo_val(p, 23, 3300000);       /* LDO23 TSP_AVDD_3.3V*/
+       pmic_set_ldo_val(p, 24, 1800000);       /* LDO24 TSP_VDD_1.8V */
 
        /* BUCK/LDO Output Mode */
-       max77686_set_buck_mode(p, 1, OPMODE_STANDBY);   /* BUCK1 VMIF_1.1V_AP */
-       max77686_set_buck_mode(p, 2, OPMODE_ON);        /* BUCK2 VARM_1.0V_AP */
-       max77686_set_buck_mode(p, 3, OPMODE_ON);        /* BUCK3 VINT_1.0V_AP */
-       max77686_set_buck_mode(p, 4, OPMODE_ON);        /* BUCK4 VG3D_1.0V_AP */
-       max77686_set_buck_mode(p, 5, OPMODE_ON);        /* BUCK5 VMEM_1.2V_AP */
-       max77686_set_buck_mode(p, 6, OPMODE_ON);        /* BUCK6 VCC_SUB_1.35V*/
-       max77686_set_buck_mode(p, 7, OPMODE_ON);        /* BUCK7 VCC_SUB_2.0V */
-       max77686_set_buck_mode(p, 8, OPMODE_OFF);       /* VMEM_VDDF_2.85V */
-       max77686_set_buck_mode(p, 9, OPMODE_OFF);       /* CAM_ISP_CORE_1.2V*/
-
-       max77686_set_ldo_mode(p, 1, OPMODE_LPM);        /* LDO1 VALIVE_1.0V_AP*/
-       max77686_set_ldo_mode(p, 2, OPMODE_STANDBY);    /* LDO2 VM1M2_1.2V_AP */
-       max77686_set_ldo_mode(p, 3, OPMODE_LPM);        /* LDO3 VCC_1.8V_AP */
-       max77686_set_ldo_mode(p, 4, OPMODE_LPM);        /* LDO4 VCC_2.8V_AP */
-       max77686_set_ldo_mode(p, 5, OPMODE_OFF);        /* LDO5_VCC_1.8V_IO */
-       max77686_set_ldo_mode(p, 6, OPMODE_STANDBY);    /* LDO6 VMPLL_1.0V_AP */
-       max77686_set_ldo_mode(p, 7, OPMODE_STANDBY);    /* LDO7 VPLL_1.0V_AP */
-       max77686_set_ldo_mode(p, 8, OPMODE_LPM);        /* LDO8 VMIPI_1.0V_AP */
-       max77686_set_ldo_mode(p, 9, OPMODE_OFF);        /* CAM_ISP_MIPI_1.2*/
-       max77686_set_ldo_mode(p, 10, OPMODE_LPM);       /* LDO10 VMIPI_1.8V_AP*/
-       max77686_set_ldo_mode(p, 11, OPMODE_STANDBY);   /* LDO11 VABB1_1.8V_AP*/
-       max77686_set_ldo_mode(p, 12, OPMODE_LPM);       /* LDO12 VUOTG_3.0V_AP*/
-       max77686_set_ldo_mode(p, 13, OPMODE_OFF);       /* LDO13 VC2C_1.8V_AP */
-       max77686_set_ldo_mode(p, 14, OPMODE_STANDBY);   /* VABB02_1.8V_AP */
-       max77686_set_ldo_mode(p, 15, OPMODE_STANDBY);   /* LDO15 VHSIC_1.0V_AP*/
-       max77686_set_ldo_mode(p, 16, OPMODE_STANDBY);   /* LDO16 VHSIC_1.8V_AP*/
-       max77686_set_ldo_mode(p, 17, OPMODE_OFF);       /* CAM_SENSOR_CORE_1.2*/
-       max77686_set_ldo_mode(p, 18, OPMODE_OFF);       /* CAM_ISP_SEN_IO_1.8V*/
-       max77686_set_ldo_mode(p, 19, OPMODE_OFF);       /* LDO19 VT_CAM_1.8V */
-       max77686_set_ldo_mode(p, 20, OPMODE_ON);        /* LDO20 VDDQ_PRE_1.8V*/
-       max77686_set_ldo_mode(p, 21, OPMODE_OFF);       /* LDO21 VTF_2.8V */
-       max77686_set_ldo_mode(p, 22, OPMODE_OFF);       /* LDO22 VMEM_VDD_2.8V*/
-       max77686_set_ldo_mode(p, 23, OPMODE_OFF);       /* LDO23 TSP_AVDD_3.3V*/
-       max77686_set_ldo_mode(p, 24, OPMODE_OFF);       /* LDO24 TSP_VDD_1.8V */
-       max77686_set_ldo_mode(p, 25, OPMODE_OFF);       /* LDO25 VCC_3.3V_LCD */
-       max77686_set_ldo_mode(p, 26, OPMODE_OFF);       /*LDO26 VCC_3.0V_MOTOR*/
+       pmic_set_buck_mode(p, 1, OPMODE_STANDBY);       /* BUCK1 VMIF_1.1V_AP */
+       pmic_set_buck_mode(p, 2, OPMODE_ON);    /* BUCK2 VARM_1.0V_AP */
+       pmic_set_buck_mode(p, 3, OPMODE_ON);    /* BUCK3 VINT_1.0V_AP */
+       pmic_set_buck_mode(p, 4, OPMODE_ON);    /* BUCK4 VG3D_1.0V_AP */
+       pmic_set_buck_mode(p, 5, OPMODE_ON);    /* BUCK5 VMEM_1.2V_AP */
+       pmic_set_buck_mode(p, 6, OPMODE_ON);    /* BUCK6 VCC_SUB_1.35V*/
+       pmic_set_buck_mode(p, 7, OPMODE_ON);    /* BUCK7 VCC_SUB_2.0V */
+       pmic_set_buck_mode(p, 8, OPMODE_OFF);   /* VMEM_VDDF_2.85V */
+       pmic_set_buck_mode(p, 9, OPMODE_OFF);   /* CAM_ISP_CORE_1.2V*/
 
        return 0;
 }
+#endif
 
 /*
  * LCD
@@ -387,19 +276,27 @@ static int pmic_init_max77686(void)
 #ifdef CONFIG_LCD
 int mipi_power(void)
 {
-       struct pmic *p = pmic_get("MAX77686_PMIC");
+       struct udevice *p = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+       if (!p) {
+               error("Regulator driver not found");
+               return -ENODEV;
+       }
 
        /* LDO8 VMIPI_1.0V_AP */
-       max77686_set_ldo_mode(p, 8, OPMODE_ON);
+       pmic_set_ldo_mode(p, 8, OPMODE_ON);
        /* LDO10 VMIPI_1.8V_AP */
-       max77686_set_ldo_mode(p, 10, OPMODE_ON);
+       pmic_set_ldo_mode(p, 10, OPMODE_ON);
 
        return 0;
 }
 
 void exynos_lcd_power_on(void)
 {
-       struct pmic *p = pmic_get("MAX77686_PMIC");
+       struct udevice *p = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+       if (!p) {
+               error("Regulator driver not found\n");
+               return;
+       }
 
        /* LCD_2.2V_EN: GPC0[1] */
        gpio_request(EXYNOS4X12_GPIO_C01, "lcd_2v2_en");
@@ -407,9 +304,8 @@ void exynos_lcd_power_on(void)
        gpio_direction_output(EXYNOS4X12_GPIO_C01, 1);
 
        /* LDO25 VCC_3.1V_LCD */
-       pmic_probe(p);
-       max77686_set_ldo_voltage(p, 25, 3100000);
-       max77686_set_ldo_mode(p, 25, OPMODE_LPM);
+       pmic_set_ldo_val(p, 25, 3100000);
+       pmic_set_ldo_mode(p, 25, OPMODE_ON);
 }
 
 void exynos_reset_lcd(void)
--
Quoted 2 lines

1.9.1

Regards,
Simon

#44 lore raw

PATCH 14/19 trats2: board: add support to dm pmic api

Hello,

Quoted 29 lines · Simon Glass

Hi,

Quoted 26 lines · Przemyslaw Marczak

Changes required to support dm pmic and dm regulator api:
- move call to board_init_i2c() into exynos_init() - earlier init the i2c
- remove redundant ldo setup - default hardware configuration is proper
- adjust pmic/regulator calls to new pmic api

---
  board/samsung/trats2/trats2.c | 208 +++++++++++-------------------------------
  1 file changed, 52 insertions(+), 156 deletions(-)
board/samsung/trats2/trats2.c +1 -0
index a737749..b107ba5 100644
@@ -23,6 +23,7 @@
  #include <usb.h>
  #include <usb/s3c_udc.h>
  #include <usb_mass_storage.h>
+#include <dm.h>
 
  DECLARE_GLOBAL_DATA_PTR;
 
@@ -151,7 +152,9 @@ int exynos_early_init_f(void)
         return 0;
  }
 
Quoted 6 lines

board/samsung/trats2/trats2.c int exynos_early_init_f(void) · line 156

Quoted 2 lines
board/samsung/trats2/trats2.c +1 -0
+#ifdef CONFIG_DM_PMIC
  static int pmic_init_max77686(void);

If you just ensure that CONFIG_DM_PMIC is defined by the board then you can avoid this.

Ok

Quoted 284 lines
Quoted 284 lines
board/samsung/trats2/trats2.c +50 -156
+#endif
 
  int exynos_init(void)
  {
@@ -171,71 +174,16 @@ int exynos_init(void)
         writel(0, &pwr->inform4);
         writel(0, &pwr->inform5);
 
+#ifdef CONFIG_SYS_I2C_INIT_BOARD
+       board_init_i2c();
+#endif
+
         return 0;
  }
 
  int exynos_power_init(void)
  {
-       int chrg;
-       struct power_battery *pb;
-       struct pmic *p_chrg, *p_muic, *p_fg, *p_bat;
-
-#ifdef CONFIG_SYS_I2C_INIT_BOARD
-       board_init_i2c();
-#endif
-       pmic_init(I2C_7);               /* I2C adapter 7 - bus name s3c24x0_7 */
         pmic_init_max77686();
-       pmic_init_max77693(I2C_10);     /* I2C adapter 10 - bus name soft1 */
-       power_muic_init(I2C_10);        /* I2C adapter 10 - bus name soft1 */
-       power_fg_init(I2C_9);           /* I2C adapter 9 - bus name soft0 */
-       power_bat_init(0);
-
-       p_chrg = pmic_get("MAX77693_PMIC");
-       if (!p_chrg) {
-               puts("MAX77693_PMIC: Not found\n");
-               return -ENODEV;
-       }
-
-       p_muic = pmic_get("MAX77693_MUIC");
-       if (!p_muic) {
-               puts("MAX77693_MUIC: Not found\n");
-               return -ENODEV;
-       }
-
-       p_fg = pmic_get("MAX77693_FG");
-       if (!p_fg) {
-               puts("MAX17042_FG: Not found\n");
-               return -ENODEV;
-       }
-
-       if (p_chrg->chrg->chrg_bat_present(p_chrg) == 0)
-               puts("No battery detected\n");
-
-       p_bat = pmic_get("BAT_TRATS2");
-       if (!p_bat) {
-               puts("BAT_TRATS2: Not found\n");
-               return -ENODEV;
-       }
-
-       p_fg->parent =  p_bat;
-       p_chrg->parent = p_bat;
-       p_muic->parent = p_bat;
-
-       p_bat->pbat->battery_init(p_bat, p_fg, p_chrg, p_muic);
-
-       pb = p_bat->pbat;
-       chrg = p_muic->chrg->chrg_type(p_muic);
-       debug("CHARGER TYPE: %d\n", chrg);
-
-       if (!p_chrg->chrg->chrg_bat_present(p_chrg)) {
-               puts("No battery detected\n");
-               return 0;
-       }
-
-       p_fg->fg->fg_battery_check(p_fg, p_bat);
-
-       if (pb->bat->state == CHARGE && chrg == CHARGER_USB)
-               puts("CHARGE Battery !\n");
 
         return 0;
  }
@@ -244,62 +192,22 @@ int exynos_power_init(void)
  static int s5pc210_phy_control(int on)
  {
         int ret = 0;
-       unsigned int val;
-       struct pmic *p, *p_pmic, *p_muic;
+       struct udevice *pmic;
 
-       p_pmic = pmic_get("MAX77686_PMIC");
-       if (!p_pmic)
+       pmic = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+       if (!pmic)
                 return -ENODEV;
 
-       if (pmic_probe(p_pmic))
-               return -1;
-
-       p_muic = pmic_get("MAX77693_MUIC");
-       if (!p_muic)
-               return -ENODEV;
-
-       if (pmic_probe(p_muic))
-               return -1;
-
         if (on) {
-               ret = max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
-               if (ret)
-                       return -1;
-
-               p = pmic_get("MAX77693_PMIC");
-               if (!p)
-                       return -ENODEV;
-
-               if (pmic_probe(p))
-                       return -1;
-
-               /* SAFEOUT */
-               ret = pmic_reg_read(p, MAX77693_SAFEOUT, &val);
-               if (ret)
-                       return -1;
-
-               val |= MAX77693_ENSAFEOUT1;
-               ret = pmic_reg_write(p, MAX77693_SAFEOUT, val);
+               ret = pmic_set_ldo_mode(pmic, 12, OPMODE_ON);
                 if (ret)
-                       return -1;
-
-               /* PATH: USB */
-               ret = pmic_reg_write(p_muic, MAX77693_MUIC_CONTROL1,
-                       MAX77693_MUIC_CTRL1_DN1DP2);
-
+                       return -EIO;
         } else {
-               ret = max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
+               ret = pmic_set_ldo_mode(pmic, 12, OPMODE_LPM);
                 if (ret)
-                       return -1;
-
-               /* PATH: UART */
-               ret = pmic_reg_write(p_muic, MAX77693_MUIC_CONTROL1,
-                       MAX77693_MUIC_CTRL1_UT1UR2);
+                       return -EIO;
         }
 
-       if (ret)
-               return -1;
-
         return 0;
  }
 
@@ -319,66 +227,47 @@ int board_usb_init(int index, enum usb_init_type init)
 
  int g_dnl_board_usb_cable_connected(void)
  {
+#ifdef CONFIG_POWER
         struct pmic *muic = pmic_get("MAX77693_MUIC");
         if (!muic)
                 return 0;
 
         return !!muic->chrg->chrg_type(muic);
+#else
+       return 1;
+#endif
  }
  #endif
-
+#ifdef CONFIG_DM_PMIC
  static int pmic_init_max77686(void)
  {
-       struct pmic *p = pmic_get("MAX77686_PMIC");
+       struct udevice *p;
 
-       if (pmic_probe(p))
-               return -1;
+       p = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+       if (!p) {
+               error("Regulator driver not found");
+               return -ENODEV;
+       }
 
         /* BUCK/LDO Output Voltage */
-       max77686_set_ldo_voltage(p, 21, 2800000);       /* LDO21 VTF_2.8V */
-       max77686_set_ldo_voltage(p, 23, 3300000);       /* LDO23 TSP_AVDD_3.3V*/
-       max77686_set_ldo_voltage(p, 24, 1800000);       /* LDO24 TSP_VDD_1.8V */
+       pmic_set_ldo_val(p, 21, 2800000);       /* LDO21 VTF_2.8V */
+       pmic_set_ldo_val(p, 23, 3300000);       /* LDO23 TSP_AVDD_3.3V*/
+       pmic_set_ldo_val(p, 24, 1800000);       /* LDO24 TSP_VDD_1.8V */
 
         /* BUCK/LDO Output Mode */
-       max77686_set_buck_mode(p, 1, OPMODE_STANDBY);   /* BUCK1 VMIF_1.1V_AP */
-       max77686_set_buck_mode(p, 2, OPMODE_ON);        /* BUCK2 VARM_1.0V_AP */
-       max77686_set_buck_mode(p, 3, OPMODE_ON);        /* BUCK3 VINT_1.0V_AP */
-       max77686_set_buck_mode(p, 4, OPMODE_ON);        /* BUCK4 VG3D_1.0V_AP */
-       max77686_set_buck_mode(p, 5, OPMODE_ON);        /* BUCK5 VMEM_1.2V_AP */
-       max77686_set_buck_mode(p, 6, OPMODE_ON);        /* BUCK6 VCC_SUB_1.35V*/
-       max77686_set_buck_mode(p, 7, OPMODE_ON);        /* BUCK7 VCC_SUB_2.0V */
-       max77686_set_buck_mode(p, 8, OPMODE_OFF);       /* VMEM_VDDF_2.85V */
-       max77686_set_buck_mode(p, 9, OPMODE_OFF);       /* CAM_ISP_CORE_1.2V*/
-
-       max77686_set_ldo_mode(p, 1, OPMODE_LPM);        /* LDO1 VALIVE_1.0V_AP*/
-       max77686_set_ldo_mode(p, 2, OPMODE_STANDBY);    /* LDO2 VM1M2_1.2V_AP */
-       max77686_set_ldo_mode(p, 3, OPMODE_LPM);        /* LDO3 VCC_1.8V_AP */
-       max77686_set_ldo_mode(p, 4, OPMODE_LPM);        /* LDO4 VCC_2.8V_AP */
-       max77686_set_ldo_mode(p, 5, OPMODE_OFF);        /* LDO5_VCC_1.8V_IO */
-       max77686_set_ldo_mode(p, 6, OPMODE_STANDBY);    /* LDO6 VMPLL_1.0V_AP */
-       max77686_set_ldo_mode(p, 7, OPMODE_STANDBY);    /* LDO7 VPLL_1.0V_AP */
-       max77686_set_ldo_mode(p, 8, OPMODE_LPM);        /* LDO8 VMIPI_1.0V_AP */
-       max77686_set_ldo_mode(p, 9, OPMODE_OFF);        /* CAM_ISP_MIPI_1.2*/
-       max77686_set_ldo_mode(p, 10, OPMODE_LPM);       /* LDO10 VMIPI_1.8V_AP*/
-       max77686_set_ldo_mode(p, 11, OPMODE_STANDBY);   /* LDO11 VABB1_1.8V_AP*/
-       max77686_set_ldo_mode(p, 12, OPMODE_LPM);       /* LDO12 VUOTG_3.0V_AP*/
-       max77686_set_ldo_mode(p, 13, OPMODE_OFF);       /* LDO13 VC2C_1.8V_AP */
-       max77686_set_ldo_mode(p, 14, OPMODE_STANDBY);   /* VABB02_1.8V_AP */
-       max77686_set_ldo_mode(p, 15, OPMODE_STANDBY);   /* LDO15 VHSIC_1.0V_AP*/
-       max77686_set_ldo_mode(p, 16, OPMODE_STANDBY);   /* LDO16 VHSIC_1.8V_AP*/
-       max77686_set_ldo_mode(p, 17, OPMODE_OFF);       /* CAM_SENSOR_CORE_1.2*/
-       max77686_set_ldo_mode(p, 18, OPMODE_OFF);       /* CAM_ISP_SEN_IO_1.8V*/
-       max77686_set_ldo_mode(p, 19, OPMODE_OFF);       /* LDO19 VT_CAM_1.8V */
-       max77686_set_ldo_mode(p, 20, OPMODE_ON);        /* LDO20 VDDQ_PRE_1.8V*/
-       max77686_set_ldo_mode(p, 21, OPMODE_OFF);       /* LDO21 VTF_2.8V */
-       max77686_set_ldo_mode(p, 22, OPMODE_OFF);       /* LDO22 VMEM_VDD_2.8V*/
-       max77686_set_ldo_mode(p, 23, OPMODE_OFF);       /* LDO23 TSP_AVDD_3.3V*/
-       max77686_set_ldo_mode(p, 24, OPMODE_OFF);       /* LDO24 TSP_VDD_1.8V */
-       max77686_set_ldo_mode(p, 25, OPMODE_OFF);       /* LDO25 VCC_3.3V_LCD */
-       max77686_set_ldo_mode(p, 26, OPMODE_OFF);       /*LDO26 VCC_3.0V_MOTOR*/
+       pmic_set_buck_mode(p, 1, OPMODE_STANDBY);       /* BUCK1 VMIF_1.1V_AP */
+       pmic_set_buck_mode(p, 2, OPMODE_ON);    /* BUCK2 VARM_1.0V_AP */
+       pmic_set_buck_mode(p, 3, OPMODE_ON);    /* BUCK3 VINT_1.0V_AP */
+       pmic_set_buck_mode(p, 4, OPMODE_ON);    /* BUCK4 VG3D_1.0V_AP */
+       pmic_set_buck_mode(p, 5, OPMODE_ON);    /* BUCK5 VMEM_1.2V_AP */
+       pmic_set_buck_mode(p, 6, OPMODE_ON);    /* BUCK6 VCC_SUB_1.35V*/
+       pmic_set_buck_mode(p, 7, OPMODE_ON);    /* BUCK7 VCC_SUB_2.0V */
+       pmic_set_buck_mode(p, 8, OPMODE_OFF);   /* VMEM_VDDF_2.85V */
+       pmic_set_buck_mode(p, 9, OPMODE_OFF);   /* CAM_ISP_CORE_1.2V*/
 
         return 0;
  }
+#endif
 
  /*
   * LCD
@@ -387,19 +276,27 @@ static int pmic_init_max77686(void)
  #ifdef CONFIG_LCD
  int mipi_power(void)
  {
-       struct pmic *p = pmic_get("MAX77686_PMIC");
+       struct udevice *p = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+       if (!p) {
+               error("Regulator driver not found");
+               return -ENODEV;
+       }
 
         /* LDO8 VMIPI_1.0V_AP */
-       max77686_set_ldo_mode(p, 8, OPMODE_ON);
+       pmic_set_ldo_mode(p, 8, OPMODE_ON);
         /* LDO10 VMIPI_1.8V_AP */
-       max77686_set_ldo_mode(p, 10, OPMODE_ON);
+       pmic_set_ldo_mode(p, 10, OPMODE_ON);
 
         return 0;
  }
 
  void exynos_lcd_power_on(void)
  {
-       struct pmic *p = pmic_get("MAX77686_PMIC");
+       struct udevice *p = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
+       if (!p) {
+               error("Regulator driver not found\n");
+               return;
+       }
 
         /* LCD_2.2V_EN: GPC0[1] */
         gpio_request(EXYNOS4X12_GPIO_C01, "lcd_2v2_en");
@@ -407,9 +304,8 @@ void exynos_lcd_power_on(void)
         gpio_direction_output(EXYNOS4X12_GPIO_C01, 1);
 
         /* LDO25 VCC_3.1V_LCD */
-       pmic_probe(p);
-       max77686_set_ldo_voltage(p, 25, 3100000);
-       max77686_set_ldo_mode(p, 25, OPMODE_LPM);
+       pmic_set_ldo_val(p, 25, 3100000);
+       pmic_set_ldo_mode(p, 25, OPMODE_ON);
  }
 
Quoted 8 lines
Quoted 4 lines
board/samsung/trats2/trats2.c +0 -1
  void exynos_reset_lcd(void)
--

1.9.1

Regards,
Simon

Thanks,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#45 lore raw

PATCH 15/19 trats2: dts: max77686: add pmic alias and names cleanup

In reply to Przemyslaw Marczak

Changes to support new pmic api:
- add pmic alias - currently required for new pmic
- remove i2c addr from max77686 node name
- fix names of some regulators

---
 arch/arm/dts/exynos4412-trats2.dts | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)
arch/arm/dts/exynos4412-trats2.dts +9 -8
index 3b1e458..fac57d7 100644
@@ -32,6 +32,7 @@
 		mmc0 = "sdhci at 12510000";
 		mmc2 = "sdhci at 12530000";
 		mmc4 = "dwmmc at 12550000";
+		pmic0 = &max77686;
 	};
 
 	i2c at 138d0000 {
@@ -40,7 +41,7 @@
 		samsung,i2c-max-bus-freq = <100000>;
 		status = "okay";
 
-		max77686_pmic at 09 {
+		max77686: max77686 {
 			compatible = "maxim,max77686_pmic";
 			interrupts = <7 0>;
 			reg = <0x09 0 0>;
@@ -112,7 +113,7 @@
 
 				ldo8_reg: ldo8 {
 					regulator-compatible = "LDO8";
-					regulator-name = "VMIPI_1.0V";
+					regulator-name = "VMIPI_1.0V_AP";
 					regulator-min-microvolt = <1000000>;
 					regulator-max-microvolt = <1000000>;
 					regulator-mem-off;
@@ -251,7 +252,7 @@
 
 				ldo25_reg: ldo25 {
 					regulator-compatible = "LDO25";
-					regulator-name = "LCD_VCC_3.3V";
+					regulator-name = "LCD_VCC_2.8V";
 					regulator-min-microvolt = <2800000>;
 					regulator-max-microvolt = <2800000>;
 					regulator-mem-idle;
@@ -267,7 +268,7 @@
 
 				buck1_reg: buck1 {
 					regulator-compatible = "BUCK1";
-					regulator-name = "vdd_mif";
+					regulator-name = "VMIF_1.0V_AP";
 					regulator-min-microvolt = <850000>;
 					regulator-max-microvolt = <1100000>;
 					regulator-always-on;
@@ -277,7 +278,7 @@
 
 				buck2_reg: buck2 {
 					regulator-compatible = "BUCK2";
-					regulator-name = "vdd_arm";
+					regulator-name = "VARM_1.0V_AP";
 					regulator-min-microvolt = <850000>;
 					regulator-max-microvolt = <1500000>;
 					regulator-always-on;
@@ -287,7 +288,7 @@
 
 				buck3_reg: buck3 {
 					regulator-compatible = "BUCK3";
-					regulator-name = "vdd_int";
+					regulator-name = "VINT_1.1V_AP";
 					regulator-min-microvolt = <850000>;
 					regulator-max-microvolt = <1150000>;
 					regulator-always-on;
@@ -297,7 +298,7 @@
 
 				buck4_reg: buck4 {
 					regulator-compatible = "BUCK4";
-					regulator-name = "vdd_g3d";
+					regulator-name = "VG3D_1.0V_AP";
 					regulator-min-microvolt = <850000>;
 					regulator-max-microvolt = <1150000>;
 					regulator-boot-on;
@@ -330,7 +331,7 @@
 
 				buck8_reg: buck8 {
 					regulator-compatible = "BUCK8";
-					regulator-name = "VMEM_VDDF_3.0V";
+					regulator-name = "VMEM_VDDF_2.85V";
 					regulator-min-microvolt = <2850000>;
 					regulator-max-microvolt = <2850000>;
 					regulator-always-on;
Signature · 1 line
1.9.1
#46 lore raw

PATCH 16/19 trats2: config: enable dm pmic, dm regulator api, dm max77686

In reply to Przemyslaw Marczak

This change enables the configs required to init and setup max77686 regulator driver, using the new driver model pmic API.

Enabled configs:
- CONFIG_DM_PMIC
- CONFIG_DM_PMIC_MAX77686
- CONFIG_DM_PMIC_I2C
- CONFIG_DM_REGULATOR
- CONFIG_DM_REGULATOR_MAX77686

---
 include/configs/trats2.h | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)
include/configs/trats2.h +6 -8
index 42481ab..6d04498 100644
@@ -185,13 +185,11 @@ int get_soft_i2c_sda_pin(void);
 #define CONFIG_SOFT_I2C_GPIO_SDA	get_soft_i2c_sda_pin()
 
 /* POWER */
-#define CONFIG_POWER
-#define CONFIG_POWER_I2C
-#define CONFIG_POWER_MAX77686
-#define CONFIG_POWER_PMIC_MAX77693
-#define CONFIG_POWER_MUIC_MAX77693
-#define CONFIG_POWER_FG_MAX77693
-#define CONFIG_POWER_BATTERY_TRATS2
+#define CONFIG_DM_PMIC
+#define CONFIG_DM_PMIC_MAX77686
+#define CONFIG_DM_PMIC_I2C
+#define CONFIG_DM_REGULATOR
+#define CONFIG_DM_REGULATOR_MAX77686
 
 /* Security subsystem - enable hw_rand() */
 #define CONFIG_EXYNOS_ACE_SHA
@@ -210,7 +208,7 @@ int get_soft_i2c_sda_pin(void);
 #ifndef __ASSEMBLY__
 #include <power/max77686_pmic.h>
 
-#define KEY_PWR_PMIC_NAME		"MAX77686_PMIC"
+#define KEY_PWR_PMIC_NAME		"max77686"
 #define KEY_PWR_STATUS_REG		MAX77686_REG_PMIC_STATUS1
 #define KEY_PWR_STATUS_MASK		(1 << 0)
 #define KEY_PWR_INTERRUPT_REG		MAX77686_REG_PMIC_INT1
Signature · 1 line
1.9.1
#47 lore raw

PATCH 16/19 trats2: config: enable dm pmic, dm regulator api, dm max77686

Hi,

Quoted 48 lines · Przemyslaw Marczak

This change enables the configs required to init and setup max77686 regulator driver, using the new driver model pmic API.

Enabled configs:
- CONFIG_DM_PMIC
- CONFIG_DM_PMIC_MAX77686
- CONFIG_DM_PMIC_I2C
- CONFIG_DM_REGULATOR
- CONFIG_DM_REGULATOR_MAX77686

---
 include/configs/trats2.h | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)
include/configs/trats2.h +6 -9
index 42481ab..6d04498 100644
@@ -185,13 +185,11 @@ int get_soft_i2c_sda_pin(void);
 #define CONFIG_SOFT_I2C_GPIO_SDA       get_soft_i2c_sda_pin()
 
 /* POWER */
-#define CONFIG_POWER
-#define CONFIG_POWER_I2C
-#define CONFIG_POWER_MAX77686
-#define CONFIG_POWER_PMIC_MAX77693
-#define CONFIG_POWER_MUIC_MAX77693
-#define CONFIG_POWER_FG_MAX77693
-#define CONFIG_POWER_BATTERY_TRATS2
+#define CONFIG_DM_PMIC
+#define CONFIG_DM_PMIC_MAX77686
+#define CONFIG_DM_PMIC_I2C
+#define CONFIG_DM_REGULATOR
+#define CONFIG_DM_REGULATOR_MAX77686
 
 /* Security subsystem - enable hw_rand() */
 #define CONFIG_EXYNOS_ACE_SHA
@@ -210,7 +208,7 @@ int get_soft_i2c_sda_pin(void);
 #ifndef __ASSEMBLY__
 #include <power/max77686_pmic.h>
 
-#define KEY_PWR_PMIC_NAME              "MAX77686_PMIC"
+#define KEY_PWR_PMIC_NAME              "max77686"
 #define KEY_PWR_STATUS_REG             MAX77686_REG_PMIC_STATUS1
 #define KEY_PWR_STATUS_MASK            (1 << 0)
 #define KEY_PWR_INTERRUPT_REG          MAX77686_REG_PMIC_INT1
--
Quoted 2 lines

1.9.1

I suppose at some point we should start putting these into Kconfig board trats_defconfig instead of board config files.

Regards,
Simon

#48 lore raw

PATCH 16/19 trats2: config: enable dm pmic, dm regulator api, dm max77686

Hello,

Quoted 57 lines · Simon Glass

Hi,

Quoted 48 lines · Przemyslaw Marczak

This change enables the configs required to init and setup max77686 regulator driver, using the new driver model pmic API.

Enabled configs:
- CONFIG_DM_PMIC
- CONFIG_DM_PMIC_MAX77686
- CONFIG_DM_PMIC_I2C
- CONFIG_DM_REGULATOR
- CONFIG_DM_REGULATOR_MAX77686

---
  include/configs/trats2.h | 14 ++++++--------
  1 file changed, 6 insertions(+), 8 deletions(-)
include/configs/trats2.h +6 -9
index 42481ab..6d04498 100644
@@ -185,13 +185,11 @@ int get_soft_i2c_sda_pin(void);
  #define CONFIG_SOFT_I2C_GPIO_SDA       get_soft_i2c_sda_pin()
 
  /* POWER */
-#define CONFIG_POWER
-#define CONFIG_POWER_I2C
-#define CONFIG_POWER_MAX77686
-#define CONFIG_POWER_PMIC_MAX77693
-#define CONFIG_POWER_MUIC_MAX77693
-#define CONFIG_POWER_FG_MAX77693
-#define CONFIG_POWER_BATTERY_TRATS2
+#define CONFIG_DM_PMIC
+#define CONFIG_DM_PMIC_MAX77686
+#define CONFIG_DM_PMIC_I2C
+#define CONFIG_DM_REGULATOR
+#define CONFIG_DM_REGULATOR_MAX77686
 
  /* Security subsystem - enable hw_rand() */
  #define CONFIG_EXYNOS_ACE_SHA
@@ -210,7 +208,7 @@ int get_soft_i2c_sda_pin(void);
  #ifndef __ASSEMBLY__
  #include <power/max77686_pmic.h>
 
-#define KEY_PWR_PMIC_NAME              "MAX77686_PMIC"
+#define KEY_PWR_PMIC_NAME              "max77686"
  #define KEY_PWR_STATUS_REG             MAX77686_REG_PMIC_STATUS1
  #define KEY_PWR_STATUS_MASK            (1 << 0)
  #define KEY_PWR_INTERRUPT_REG          MAX77686_REG_PMIC_INT1
--
Quoted 2 lines

1.9.1

I suppose at some point we should start putting these into Kconfig board trats_defconfig instead of board config files.

Quoted 3 lines

Regards,
Simon

You, know - the aim of PMIC framework is to support such feature as something common - so this should be some device soon. I think that we can leave it here temporary.

Thanks,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#49 lore raw

PATCH 17/19 odroid: board: add support to dm pmic api

In reply to Przemyslaw Marczak

Changes added to support new pmic api:
- call board_init_i2c() in exynos_init(),
do i2c init before pmic_init_dm()
- change max77686 regulator calls to new pmic api calls

---
 board/samsung/odroid/odroid.c | 31 +++++++++++++++++--------------
 1 file changed, 17 insertions(+), 14 deletions(-)
board/samsung/odroid/odroid.c +17 -14
index fd5d2d2..eac578f 100644
@@ -378,15 +378,19 @@ static void board_gpio_init(void)
 
 static int pmic_init_max77686(void)
 {
-	struct pmic *p = pmic_get("MAX77686_PMIC");
+	struct udevice *p = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
 
-	if (pmic_probe(p))
+	if (pmic_probe(p, NULL))
 		return -ENODEV;
 
 	/* Set LDO Voltage */
-	max77686_set_ldo_voltage(p, 20, 1800000);	/* LDO20 eMMC */
-	max77686_set_ldo_voltage(p, 21, 2800000);	/* LDO21 SD */
-	max77686_set_ldo_voltage(p, 22, 2800000);	/* LDO22 eMMC */
+	pmic_set_ldo_val(p, 20, 1800000);	/* LDO20 eMMC */
+	pmic_set_ldo_val(p, 21, 2800000);	/* LDO21 SD */
+	pmic_set_ldo_val(p, 22, 2800000);	/* LDO22 eMMC */
+
+	pmic_set_ldo_mode(p, 20, OPMODE_ON);
+	pmic_set_ldo_mode(p, 21, OPMODE_ON);
+	pmic_set_ldo_mode(p, 22, OPMODE_ON);
 
 	return 0;
 }
@@ -414,15 +418,14 @@ int exynos_init(void)
 	gd->ram_size -= SZ_1M;
 	gd->bd->bi_dram[CONFIG_NR_DRAM_BANKS - 1].size -= SZ_1M;
 
+#ifdef CONFIG_SYS_I2C_INIT_BOARD
+	board_init_i2c();
+#endif
 	return 0;
 }
 
 int exynos_power_init(void)
 {
-#ifdef CONFIG_SYS_I2C_INIT_BOARD
-	board_init_i2c();
-#endif
-	pmic_init(I2C_0);
 	pmic_init_max77686();
 
 	return 0;
@@ -431,19 +434,19 @@ int exynos_power_init(void)
 #ifdef CONFIG_USB_GADGET
 static int s5pc210_phy_control(int on)
 {
-	struct pmic *p_pmic;
+	struct udevice *p_pmic;
 
-	p_pmic = pmic_get("MAX77686_PMIC");
+	p_pmic = pmic_get_by_name(UCLASS_PMIC_REGULATOR, "max77686");
 	if (!p_pmic)
 		return -ENODEV;
 
-	if (pmic_probe(p_pmic))
+	if (pmic_probe(p_pmic, NULL))
 		return -1;
 
 	if (on)
-		return max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
+		return pmic_set_ldo_mode(p_pmic, 12, OPMODE_ON);
 	else
-		return max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
+		return pmic_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
 }
 
 struct s3c_plat_otg_data s5pc210_otg_data = {
Signature · 1 line
1.9.1
#50 lore raw

PATCH 18/19 odroid: dts: add 'voltage-regulators' description to max77686 node

In reply to Przemyslaw Marczak

Adding regulators subnode to fdt max77686 node allows properly init regulators descriptors from by the max77686 regulator driver. This enables the complete functionality of the regulator command.

---
 arch/arm/dts/exynos4412-odroid.dts | 250 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 249 insertions(+), 1 deletion(-)
arch/arm/dts/exynos4412-odroid.dts +249 -1
index 4c5e2b3..b6abd51 100644
@@ -20,6 +20,7 @@
 		console = "/serial at 13810000";
 		mmc2 = "sdhci at 12530000";
 		mmc4 = "dwmmc at 12550000";
+		pmic = &max77686;
 	};
 
 	i2c at 13860000 {
@@ -28,11 +29,258 @@
 		samsung,i2c-max-bus-freq = <100000>;
 		status = "okay";
 
-		max77686_pmic at 09 {
+		max77686: max77686 at 09 {
 			compatible = "maxim,max77686_pmic";
 			interrupts = <7 0>;
 			reg = <0x09 0 0>;
 			#clock-cells = <1>;
+
+			voltage-regulators {
+				ldo1_reg: ldo1 {
+					regulator-compatible = "LDO1";
+					regulator-name = "VDD_ALIVE_1.0V";
+					regulator-min-microvolt = <1000000>;
+					regulator-max-microvolt = <1000000>;
+				};
+
+				ldo2_reg: ldo2 {
+					regulator-compatible = "LDO2";
+					regulator-name = "VDDQ_VM1M2_1.2V";
+					regulator-min-microvolt = <1200000>;
+					regulator-max-microvolt = <1200000>;
+				};
+
+				ldo3_reg: ldo3 {
+					regulator-compatible = "LDO3";
+					regulator-name = "VCC_1.8V_AP";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo4_reg: ldo4 {
+					regulator-compatible = "LDO4";
+					regulator-name = "VDDQ_MMC2_2.8V";
+					regulator-min-microvolt = <2800000>;
+					regulator-max-microvolt = <2800000>;
+				};
+
+				ldo5_reg: ldo5 {
+					regulator-compatible = "LDO5";
+					regulator-name = "VDDQ_MMC0/1/3_1.8V";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo6_reg: ldo6 {
+					regulator-compatible = "LDO6";
+					regulator-name = "VMPLL_1.0V";
+					regulator-min-microvolt = <1100000>;
+					regulator-max-microvolt = <1100000>;
+				};
+
+				ldo7_reg: ldo7 {
+					regulator-compatible = "LDO7";
+					regulator-name = "VPLL_1.1V";
+					regulator-min-microvolt = <1100000>;
+					regulator-max-microvolt = <1100000>;
+				};
+
+				ldo8_reg: ldo8 {
+					regulator-compatible = "LDO8";
+					regulator-name = "VDD_MIPI/HDMI_1.0V";
+					regulator-min-microvolt = <1000000>;
+					regulator-max-microvolt = <1000000>;
+				};
+
+				ldo9_reg: ldo9 {
+					regulator-compatible = "LDO9";
+					regulator-name = "nc";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo10_reg: ldo10 {
+					regulator-compatible = "LDO10";
+					regulator-name = "VDD_MIPI/HDMI_1.8V";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo11_reg: ldo11 {
+					regulator-compatible = "LDO11";
+					regulator-name = "VDD_ABB1_1.8V";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo12_reg: ldo12 {
+					regulator-compatible = "LDO12";
+					regulator-name = "VDD_UOTG_3.0V";
+					regulator-min-microvolt = <3000000>;
+					regulator-max-microvolt = <3000000>;
+				};
+
+				ldo13_reg: ldo13 {
+					regulator-compatible = "LDO13";
+					regulator-name = "VDD_C2C_1.8V";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo14_reg: ldo14 {
+					regulator-compatible = "LDO14";
+					regulator-name = "VDD_ABB02_1.8V";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo15_reg: ldo15 {
+					regulator-compatible = "LDO15";
+					regulator-name = "VDD_HSIC/OTG_1.0V";
+					regulator-min-microvolt = <1000000>;
+					regulator-max-microvolt = <1000000>;
+				};
+
+				ldo16_reg: ldo16 {
+					regulator-compatible = "LDO16";
+					regulator-name = "VDD_HSIC_1.8V";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo17_reg: ldo17 {
+					regulator-compatible = "LDO17";
+					regulator-name = "VDDQ_CAM_1.2V";
+					regulator-min-microvolt = <1200000>;
+					regulator-max-microvolt = <1200000>;
+				};
+
+				ldo18_reg: ldo18 {
+					regulator-compatible = "LDO18";
+					regulator-name = "nc";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo19_reg: ldo19 {
+					regulator-compatible = "LDO19";
+					regulator-name = "nc";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo20_reg: ldo20 {
+					regulator-compatible = "LDO20";
+					regulator-name = "VDDQ_EMMC_1.8V";
+					regulator-min-microvolt = <1800000>;
+					regulator-max-microvolt = <1800000>;
+				};
+
+				ldo21_reg: ldo21 {
+					regulator-compatible = "LDO21";
+					regulator-name = "TFLASH_2.8V";
+					regulator-min-microvolt = <2800000>;
+					regulator-max-microvolt = <2800000>;
+				};
+
+				ldo22_reg: ldo22 {
+					regulator-compatible = "LDO22";
+					regulator-name = "VDDQ_EMMC_2.8V";
+					regulator-min-microvolt = <2800000>;
+					regulator-max-microvolt = <2800000>;
+				};
+
+				ldo23_reg: ldo23 {
+					regulator-compatible = "LDO23";
+					regulator-name = "nc";
+					regulator-min-microvolt = <3300000>;
+					regulator-max-microvolt = <3300000>;
+				};
+
+				ldo24_reg: ldo24 {
+					regulator-compatible = "LDO24";
+					regulator-name = "nc";
+					regulator-min-microvolt = <3000000>;
+					regulator-max-microvolt = <3000000>;
+				};
+
+				ldo25_reg: ldo25 {
+					regulator-compatible = "LDO25";
+					regulator-name = "VDDQ_LCD_3.0V";
+					regulator-min-microvolt = <3000000>;
+					regulator-max-microvolt = <3000000>;
+				};
+
+				ldo26_reg: ldo26 {
+					regulator-compatible = "LDO26";
+					regulator-name = "nc";
+					regulator-min-microvolt = <3000000>;
+					regulator-max-microvolt = <3000000>;
+				};
+
+				buck1_reg: buck1 {
+					regulator-compatible = "BUCK1";
+					regulator-name = "VDD_MIF_1.0V";
+					regulator-min-microvolt = <8500000>;
+					regulator-max-microvolt = <1100000>;
+				};
+
+				buck2_reg: buck2 {
+					regulator-compatible = "BUCK2";
+					regulator-name = "VDD_ARM_1.0V";
+					regulator-min-microvolt = <850000>;
+					regulator-max-microvolt = <1500000>;
+				};
+
+				buck3_reg: buck3 {
+					regulator-compatible = "BUCK3";
+					regulator-name = "VDD_INT_1.1V";
+					regulator-min-microvolt = <850000>;
+					regulator-max-microvolt = <1150000>;
+				};
+
+				buck4_reg: buck4 {
+					regulator-compatible = "BUCK4";
+					regulator-name = "VDD_G3D_1.0V";
+					regulator-min-microvolt = <850000>;
+					regulator-max-microvolt = <1150000>;
+				};
+
+				buck5_reg: buck5 {
+					regulator-compatible = "BUCK5";
+					regulator-name = "VDDQ_AP_1.2V";
+					regulator-min-microvolt = <1200000>;
+					regulator-max-microvolt = <1200000>;
+				};
+
+				buck6_reg: buck6 {
+					regulator-compatible = "BUCK6";
+					regulator-name = "VCC_INL1/7_1.35V";
+					regulator-min-microvolt = <1350000>;
+					regulator-max-microvolt = <1350000>;
+				};
+
+				buck7_reg: buck7 {
+					regulator-compatible = "BUCK7";
+					regulator-name = "VCC_INL2/3/5_2.0V";
+					regulator-min-microvolt = <2000000>;
+					regulator-max-microvolt = <2000000>;
+				};
+
+				buck8_reg: buck8 {
+					regulator-compatible = "BUCK8";
+					regulator-name = "VCC_P3V3_2.85V";
+					regulator-min-microvolt = <2850000>;
+					regulator-max-microvolt = <3300000>;
+				};
+
+				buck9_reg: buck9 {
+					regulator-compatible = "BUCK9";
+					regulator-name = "nc";
+					regulator-min-microvolt = <1200000>;
+					regulator-max-microvolt = <1200000>;
+				};
+			};
 		};
 	};
 
Signature · 1 line
1.9.1
#51 lore raw

PATCH 19/19 odroid: config: enable dm pmic, dm regulator and max77686 driver

In reply to Przemyslaw Marczak

This change enables the configs required to init and setup max77686 regulator driver, using the new driver model pmic API.

Enabled configs:
- CONFIG_DM_PMIC
- CONFIG_DM_PMIC_MAX77686
- CONFIG_DM_PMIC_I2C
- CONFIG_DM_REGULATOR
- CONFIG_DM_REGULATOR_MAX77686

---
 include/configs/odroid.h | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
include/configs/odroid.h +5 -3
index b928af8..ccc602d 100644
@@ -185,9 +185,11 @@
 #define CONFIG_SYS_I2C_INIT_BOARD
 
 /* POWER */
-#define CONFIG_POWER
-#define CONFIG_POWER_I2C
-#define CONFIG_POWER_MAX77686
+#define CONFIG_DM_PMIC
+#define CONFIG_DM_PMIC_MAX77686
+#define CONFIG_DM_PMIC_I2C
+#define CONFIG_DM_REGULATOR
+#define CONFIG_DM_REGULATOR_MAX77686
 
 /* GPT */
 #define CONFIG_RANDOM_UUID
Signature · 1 line
1.9.1
#52 lore raw

cover Power(full) framework based on Driver Model

In reply to Przemyslaw Marczak

Hello all,

Quoted 124 lines · Przemyslaw Marczak

Hello,
This piece of code was a base for prepare my presentation talk for the U-Boot Mini Summit, which taking place at ELCE2014 conference, 13-th October 2014 Dusseldorf, Germany.

The tittle of talk: "Power(full) framework based on Driver Model"

The presentation will be shared after the Summit.

This patchset introduces the new one PMIC framework for the U-Boot. It is still under the construction, but the basic functionality was achieved and tested. Please feel free to comment and share the opinion.

I think that each patch is described full enough, but for some more design details, please look into the documentation file. It includes some basic examples for the PMIC drivers.

Quick summary of:
Framework:
- The new approach - UCLASS_PMIC - simple and designed for device I/O only
- Add new uclass types: UCLASS_PMIC and UCLASS_PMIC_REGULATOR
- Two uclass drivers for above types
- A common regulator operations - will easy cover the real devices design

Drivers:
- Introduce new PMIC API for drivers - now everything base on "struct udevice"
- Introduce Regulator Voltage descriptors and Operation Mode descriptors which are usually taken from the device tree (board dependent data)
- Two uclass device drivers for MAX77686(PMIC+REGULATOR)

User Interface:
- command pmic, unchanged functionality and ported to the driver model
- command regulator(NEW) for safe regulator setup from commandline,
- now can check output Voltage and operation mode of the regulators,
- also can check the board Voltage limits and driver available modes

The future plans:
- Wait for the I2c Driver Model implementation
- Introduce a common way to bind pmic devices - now done by alias "pmic"
- Add additional uclass types: MUIC, CHARGER, BATTERY, MFD and maybe more.
- Introduce optimal operations for new uclasses
- Port all U-Boot drivers to the new Framework
- Remove the old drivers and the old PMIC Framework code

The assumptions of this work is:
- Add new code to independent files
- Keep two Frameworks as independent and without conflicts
- Don't mix OLD/NEW Framework code - for the readability
- Port all drivers using new API
- Remove the old Framework and the old drivers

A disadvantage:
- some parts of the present code is duplicated

Need help:
- After merge this, it is welcome to help with driver porting.
- Everything should be tested

The extra feature:
The first commit introduces errno_str() function.
It is a common function which I hope will be usefull for commands and not only. If any visible error says: -19 or some other magic number, then it means that this function should be used.

U-Boot Mini Summit members:
This code is maybe not as good as it could be, but the time was limited, and the conference is comming soon. I don't expects a code review of this now, but it would be nice if you take a look of this piece of code before our U-Boot Mini Summit. Of course it depends on you.

Best Regards and see you in Dusseldorf!
Przemyslaw Marczak

Przemyslaw Marczak (19):
lib: errno: introduce errno_str(): returns errno related message
exynos: config-common: enable errno_str() function
exynos: config-common: enable generic fs command
dm: pmic: add implementation of driver model pmic uclass
dm: pmic: add implementation of driver model regulator uclass
dm: common: board_r: add call and weak of power_init_dm()
dm: pmic: add max77686 pmic driver
dm: regulator: add max77686 regulator driver
dm: pmic: new commands: pmic and regulator
dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
doc: driver-model: pmic and regulator uclass documentation
samsung: board: lcd menu: check if any power framework is enabled
samsung: misc: power_key_pressed: add support to dm pmic framework
trats2: board: add support to dm pmic api
trats2: dts: max77686: add pmic alias and names cleanup
trats2: config: enable dm pmic, dm regulator api, dm max77686
odroid: board: add support to dm pmic api
odroid: dts: add 'voltage-regulators' description to max77686 node
odroid: config: enable dm pmic, dm regulator and max77686 driver

  Makefile                               |   1 +
  arch/arm/dts/exynos4412-odroid.dts     | 250 +++++++++-
  arch/arm/dts/exynos4412-trats2.dts     |  17 +-
  board/samsung/common/board.c           |   5 +-
  board/samsung/common/misc.c            |  21 +-
  board/samsung/odroid/odroid.c          |  31 +-
  board/samsung/trats2/trats2.c          | 208 ++------
  common/board_r.c                       |   8 +
  doc/driver-model/dm-pmic-framework.txt | 450 ++++++++++++++++++
  drivers/power/Makefile                 |   6 +-
  drivers/power/cmd_pmic.c               | 845 +++++++++++++++++++++++++++++++++
  drivers/power/pmic-uclass.c            | 255 ++++++++++
  drivers/power/pmic/Makefile            |   1 +
  drivers/power/pmic/max77686.c          |  89 ++++
  drivers/power/pmic_i2c.c               | 136 ++++++
  drivers/power/pmic_spi.c               | 137 ++++++
  drivers/power/regulator-uclass.c       | 250 ++++++++++
  drivers/power/regulator/Makefile       |   8 +
  drivers/power/regulator/max77686.c     | 749 +++++++++++++++++++++++++++++
  include/configs/exynos-common.h        |   3 +
  include/configs/odroid.h               |   8 +-
  include/configs/trats2.h               |  14 +-
  include/dm/uclass-id.h                 |   4 +
  include/errno.h                        |   3 +
  include/power/max77686_pmic.h          |  28 +-
  include/power/pmic.h                   |  82 ++++
  include/power/regulator.h              | 267 +++++++++++
  lib/Makefile                           |   1 +
  lib/errno_str.c                        | 147 ++++++
  29 files changed, 3823 insertions(+), 201 deletions(-)
Quoted 12 lines

create mode 100644 doc/driver-model/dm-pmic-framework.txt create mode 100644 drivers/power/cmd_pmic.c create mode 100644 drivers/power/pmic-uclass.c create mode 100644 drivers/power/pmic/max77686.c create mode 100644 drivers/power/pmic_i2c.c create mode 100644 drivers/power/pmic_spi.c create mode 100644 drivers/power/regulator-uclass.c create mode 100644 drivers/power/regulator/Makefile create mode 100644 drivers/power/regulator/max77686.c create mode 100644 include/power/regulator.h create mode 100644 lib/errno_str.c

I missed one thing - this should be applied on the top of the u-boot-dm/working tree, which is now:
"dm: gpio: Remove unused get_state() uclass method" (2109ad5b8d4298d4ee9e9ba612d151c2bf65dd1a)

Best Regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#53 lore raw

cover Power(full) framework based on Driver Model

Hi,

Quoted 4 lines · Przemyslaw Marczak

Hello all,

On 10/08/2014 10:48 PM, Przemyslaw Marczak wrote:

[snip]

Quoted 5 lines

I missed one thing - this should be applied on the top of the u-boot-dm/working tree, which is now:
"dm: gpio: Remove unused get_state() uclass method" (2109ad5b8d4298d4ee9e9ba612d151c2bf65dd1a)

Thanks - also is it available in a git tree somewhere please?

Regards,
Simon

#54 lore raw

cover Power(full) framework based on Driver Model

Hello,

Quoted 16 lines · Simon Glass

Hi,

Quoted 4 lines · Przemyslaw Marczak

Hello all,

On 10/08/2014 10:48 PM, Przemyslaw Marczak wrote:

[snip]

Quoted 5 lines

I missed one thing - this should be applied on the top of the u-boot-dm/working tree, which is now:
"dm: gpio: Remove unused get_state() uclass method" (2109ad5b8d4298d4ee9e9ba612d151c2bf65dd1a)

Quoted 5 lines

Thanks - also is it available in a git tree somewhere please?

Regards,
Simon

No problem, it is available here:

https://bobenstein at github.com/bobenstein/u-boot.git

branch: dm-working-pmic

Best regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#55 lore raw

cover Power(full) framework based on Driver Model

In reply to Przemyslaw Marczak
Quoted 64 lines · Przemyslaw Marczak

Hello,
This piece of code was a base for prepare my presentation talk for the U-Boot Mini Summit, which taking place at ELCE2014 conference, 13-th October 2014 Dusseldorf, Germany.

The tittle of talk: "Power(full) framework based on Driver Model"

The presentation will be shared after the Summit.

This patchset introduces the new one PMIC framework for the U-Boot. It is still under the construction, but the basic functionality was achieved and tested. Please feel free to comment and share the opinion.

I think that each patch is described full enough, but for some more design details, please look into the documentation file. It includes some basic examples for the PMIC drivers.

Quick summary of:
Framework:
- The new approach - UCLASS_PMIC - simple and designed for device I/O only
- Add new uclass types: UCLASS_PMIC and UCLASS_PMIC_REGULATOR
- Two uclass drivers for above types
- A common regulator operations - will easy cover the real devices design

Drivers:
- Introduce new PMIC API for drivers - now everything base on "struct udevice"
- Introduce Regulator Voltage descriptors and Operation Mode descriptors which are usually taken from the device tree (board dependent data)
- Two uclass device drivers for MAX77686(PMIC+REGULATOR)

User Interface:
- command pmic, unchanged functionality and ported to the driver model
- command regulator(NEW) for safe regulator setup from commandline,
- now can check output Voltage and operation mode of the regulators,
- also can check the board Voltage limits and driver available modes

The future plans:
- Wait for the I2c Driver Model implementation
- Introduce a common way to bind pmic devices - now done by alias "pmic"
- Add additional uclass types: MUIC, CHARGER, BATTERY, MFD and maybe more.
- Introduce optimal operations for new uclasses
- Port all U-Boot drivers to the new Framework
- Remove the old drivers and the old PMIC Framework code

The assumptions of this work is:
- Add new code to independent files
- Keep two Frameworks as independent and without conflicts
- Don't mix OLD/NEW Framework code - for the readability
- Port all drivers using new API
- Remove the old Framework and the old drivers

A disadvantage:
- some parts of the present code is duplicated

Need help:
- After merge this, it is welcome to help with driver porting.
- Everything should be tested

The extra feature:
The first commit introduces errno_str() function.
It is a common function which I hope will be usefull for commands and not only. If any visible error says: -19 or some other magic number, then it means that this function should be used.

Quoted 5 lines

U-Boot Mini Summit members:
This code is maybe not as good as it could be, but the time was limited, and the conference is comming soon. I don't expects a code review of this now, but it would be nice if you take a look of this piece of code before our U-Boot Mini Summit. Of course it depends on you.

Per the mini-summit, please add a patch adding yourself as pmic custodian (so something to doc/git-mailrc, shoot me your patchwork username and Detlev an ssh key so we can get a repo setup). For this series, I'm going to review/ack and I want Simon to pick it up for the DM tree since I think that makes the most sense, yes? Thanks!

And for all the patches in the series I don't reply to, they seem fine but aren't for me to ack (being board specific stuff, that looked fine but I don't have the HW) or I just agree with Simon's comments.

Signature · 8 lines
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20141022/cdafd78a/attachment.pgp>
#56 lore raw

cover Power(full) framework based on Driver Model

Hello,

On 10/22/2014 05:31 PM, Tom Rini wrote:

... snip ...

Quoted 11 lines

Per the mini-summit, please add a patch adding yourself as pmic custodian (so something to doc/git-mailrc, shoot me your patchwork username and Detlev an ssh key so we can get a repo setup). For this series, I'm going to review/ack and I want Simon to pick it up for the DM tree since I think that makes the most sense, yes? Thanks!

And for all the patches in the series I don't reply to, they seem fine but aren't for me to ack (being board specific stuff, that looked fine but I don't have the HW) or I just agree with Simon's comments.

Tom, thank you for review. And I'm sorry, but I will take care of this all in the next week.

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#57 lore raw

cover Power(full) framework based on Driver Model

In reply to Tom Rini

Hello Tom,

Quoted 78 lines · Tom Rini
Quoted 64 lines · Przemyslaw Marczak

Hello,
This piece of code was a base for prepare my presentation talk for the U-Boot Mini Summit, which taking place at ELCE2014 conference, 13-th October 2014 Dusseldorf, Germany.

The tittle of talk: "Power(full) framework based on Driver Model"

The presentation will be shared after the Summit.

This patchset introduces the new one PMIC framework for the U-Boot. It is still under the construction, but the basic functionality was achieved and tested. Please feel free to comment and share the opinion.

I think that each patch is described full enough, but for some more design details, please look into the documentation file. It includes some basic examples for the PMIC drivers.

Quick summary of:
Framework:
- The new approach - UCLASS_PMIC - simple and designed for device I/O only
- Add new uclass types: UCLASS_PMIC and UCLASS_PMIC_REGULATOR
- Two uclass drivers for above types
- A common regulator operations - will easy cover the real devices design

Drivers:
- Introduce new PMIC API for drivers - now everything base on "struct udevice"
- Introduce Regulator Voltage descriptors and Operation Mode descriptors which are usually taken from the device tree (board dependent data)
- Two uclass device drivers for MAX77686(PMIC+REGULATOR)

User Interface:
- command pmic, unchanged functionality and ported to the driver model
- command regulator(NEW) for safe regulator setup from commandline,
- now can check output Voltage and operation mode of the regulators,
- also can check the board Voltage limits and driver available modes

The future plans:
- Wait for the I2c Driver Model implementation
- Introduce a common way to bind pmic devices - now done by alias "pmic"
- Add additional uclass types: MUIC, CHARGER, BATTERY, MFD and maybe more.
- Introduce optimal operations for new uclasses
- Port all U-Boot drivers to the new Framework
- Remove the old drivers and the old PMIC Framework code

The assumptions of this work is:
- Add new code to independent files
- Keep two Frameworks as independent and without conflicts
- Don't mix OLD/NEW Framework code - for the readability
- Port all drivers using new API
- Remove the old Framework and the old drivers

A disadvantage:
- some parts of the present code is duplicated

Need help:
- After merge this, it is welcome to help with driver porting.
- Everything should be tested

The extra feature:
The first commit introduces errno_str() function.
It is a common function which I hope will be usefull for commands and not only. If any visible error says: -19 or some other magic number, then it means that this function should be used.

Quoted 5 lines

U-Boot Mini Summit members:
This code is maybe not as good as it could be, but the time was limited, and the conference is comming soon. I don't expects a code review of this now, but it would be nice if you take a look of this piece of code before our U-Boot Mini Summit. Of course it depends on you.

Per the mini-summit, please add a patch adding yourself as pmic custodian (so something to doc/git-mailrc, shoot me your patchwork username and Detlev an ssh key so we can get a repo setup). For this series, I'm going to review/ack and I want Simon to pick it up for the DM tree since I think that makes the most sense, yes? Thanks!

Quoted 4 lines

And for all the patches in the series I don't reply to, they seem fine but aren't for me to ack (being board specific stuff, that looked fine but I don't have the HW) or I just agree with Simon's comments.

Ok, so this code will be rebased on to u-boot-dm/i2c-working. And I will send the required informations to you and Detlev.

Best Regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#58 lore raw

v2 cover Power(full) framework based on Driver Model

In reply to Przemyslaw Marczak

Hello,
Here is the second RFC version of the new PMIC framework. The changes made in this version are described below each commit.

So again, a quick summary of:
Framework:
- Add new uclass types:
-- UCLASS_PMIC(for device I/O)
-- UCLASS_PMIC_REGULATOR (for common regulator ops)
- Two uclass drivers for the above types
- A common regulator operations - will easy cover the real devices design
- V2: pmic: add read/write ops
- V2: regulator: use regulator type as an argument - not as function name

Drivers:
- Introduce new PMIC API for drivers - now everything base on "struct udevice"
- Introduce Regulator Voltage descriptors and Operation Mode descriptors which are usually taken from the device tree (board dependent data)
- Two uclass device drivers for MAX77686(PMIC+REGULATOR)
- V2: don't use the 'hw union' from old pmic
- V2: remove the files: pmic_i2c.c/pmic_spi.c - now using bus drivers
- V2: cleanup the pmic_get() functions
- V2: add pmic_io_dev() function for getting the proper I/O dev for devices
- V2: add function calls for getting pmic devices platdata
- V2: remove regulator type from regulator operations function calls, use type as an argument

User Interface:
- command pmic, unchanged functionality and ported to the driver model
- command regulator(NEW) for safe regulator setup from commandline,
- now can check output Voltage and operation mode of the regulators,
- also can check the board Voltage limits and driver available modes
- V2: simplify the code after remove the regulator type from function naming
- V2: add on/off command

Supported boards:
- Odroid U3
- V2: drop the commits for Trats2 - wait for charger and muic uclass types

The assumptions of this work is:
- Add new code to independent files
- Keep two Frameworks as independent and without conflicts
- Don't mix OLD/NEW Framework code - for the readability

The future plans:
- Add additional uclass types: MUIC, CHARGER, BATTERY, MFD and maybe more.
- Port all U-Boot drivers to the new Framework
- Remove the old drivers and the old PMIC Framework code

Need help:
- After merge this, it is welcome to help with driver porting
- Every new driver should be tested on real hardware

Best regards

Przemyslaw Marczak (12):
exynos5: fix build break by adding CONFIG_POWER
dm: device: add function device_get_first_child_by_uclass_id()
dm: pmic: add implementation of driver model pmic uclass
dm: pmic: add implementation of driver model regulator uclass
dm: pmic: new commands: pmic and regulator
dm: pmic: add max77686 pmic driver
dm: regulator: add max77686 regulator driver
doc: driver-model: pmic and regulator uclass documentation
dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
odroid: board: add support to dm pmic api
odroid: dts: add 'voltage-regulators' description to max77686 node
odroid: config: enable dm pmic, dm regulator and max77686 driver

 Makefile                               |   1 +
 arch/arm/dts/exynos4412-odroid.dts     | 249 ++++++++-
 board/samsung/common/board.c           |   4 +-
 board/samsung/common/misc.c            |   1 +
 board/samsung/odroid/odroid.c          |  52 +-
 configs/odroid_defconfig               |   1 -
 doc/driver-model/dm-pmic-framework.txt | 367 +++++++++++++
 drivers/core/device.c                  |  15 +
 drivers/power/Makefile                 |   5 +-
 drivers/power/cmd_pmic.c               | 820 +++++++++++++++++++++++++++++
 drivers/power/pmic-uclass.c            | 191 +++++++
 drivers/power/pmic/Makefile            |   1 +
 drivers/power/pmic/max77686.c          | 102 ++++
 drivers/power/pmic/pmic_max77686.c     |   2 +-
 drivers/power/regulator-uclass.c       | 227 ++++++++
 drivers/power/regulator/Makefile       |   8 +
 drivers/power/regulator/max77686.c     | 926 +++++++++++++++++++++++++++++++++
 include/configs/exynos5-common.h       |   4 +
 include/configs/odroid.h               |   9 +-
 include/dm/device.h                    |  16 +
 include/dm/uclass-id.h                 |   4 +
 include/power/max77686_pmic.h          |  26 +-
 include/power/pmic.h                   | 265 ++++++++++
 include/power/regulator.h              | 310 +++++++++++
 24 files changed, 3573 insertions(+), 33 deletions(-)
 create mode 100644 doc/driver-model/dm-pmic-framework.txt
 create mode 100644 drivers/power/cmd_pmic.c
 create mode 100644 drivers/power/pmic-uclass.c
 create mode 100644 drivers/power/pmic/max77686.c
 create mode 100644 drivers/power/regulator-uclass.c
 create mode 100644 drivers/power/regulator/Makefile
 create mode 100644 drivers/power/regulator/max77686.c
 create mode 100644 include/power/regulator.h
Signature · 1 line
1.9.1
#59 lore raw

PATCH v2 1/12 exynos5: fix build break by adding CONFIG_POWER

Move the configs listed below from exynos5-dt-common.h to exynos5-common.h:
- CONFIG_POWER
- CONFIG_POWER_I2C
fixes build break for Arndale and Smdk5250 boards.

---
 include/configs/exynos5-common.h | 4 ++++
 1 file changed, 4 insertions(+)
include/configs/exynos5-common.h +4 -0
index 3ab8d55..3ee9482 100644
@@ -149,6 +149,10 @@
 #define CONFIG_OF_SPI
 #endif
 
+/* Power */
+#define CONFIG_POWER
+#define CONFIG_POWER_I2C
+
 #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
 #define CONFIG_ENV_SPI_MODE	SPI_MODE_0
 #define CONFIG_ENV_SECT_SIZE	CONFIG_ENV_SIZE
Signature · 1 line
1.9.1
#60 lore raw

PATCH v2 1/12 exynos5: fix build break by adding CONFIG_POWER

Quoted 16 lines · Przemyslaw Marczak

Move the configs listed below from exynos5-dt-common.h to exynos5-common.h:
- CONFIG_POWER
- CONFIG_POWER_I2C
fixes build break for Arndale and Smdk5250 boards.

---
 include/configs/exynos5-common.h | 4 ++++
 1 file changed, 4 insertions(+)
include/configs/exynos5-common.h +0 -0
index 3ab8d55..3ee9482 100644

include/configs/exynos5-common.h line 158

Quoted 10 lines
include/configs/exynos5-common.h +4 -0
@@ -149,6 +149,10 @@
 #define CONFIG_OF_SPI
 #endif
 
+/* Power */
+#define CONFIG_POWER
+#define CONFIG_POWER_I2C
+
 #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
 #define CONFIG_ENV_SPI_MODE	SPI_MODE_0
 #define CONFIG_ENV_SECT_SIZE	CONFIG_ENV_SIZE

Thanks,
Minkyu Kang.

#61 lore raw

PATCH v2 2/12 dm: device: add function device_get_first_child_by_uclass_id()

In reply to Przemyslaw Marczak

To implement functionality for devices connected by some external interface, sometimes there is need to implement more then one, and different uclass type drivers.

But only one i2c/spi dm chip can exists, per each bus i2c address or spi select. Then, it seems to be useful, to get the child device by uclass type, for the parent with known chip address.

So, this change will be useful for the pmic case:
|- i2c bus
'- pmic i2c chip (parent)
'- uclass regulator (child 1)
'- uclass charger (child 2)

This will allow to get the regulator or charger device if knows only parent i2c/spi address.

---
Changes V2:
- new commit

---
 drivers/core/device.c | 15 +++++++++++++++
 include/dm/device.h   | 16 ++++++++++++++++
 2 files changed, 31 insertions(+)
drivers/core/device.c +15 -0
index 73c3e07..76b22cf 100644
@@ -397,6 +397,21 @@ int device_find_child_by_of_offset(struct udevice *parent, int of_offset,
 	return -ENODEV;
 }
 
+int device_get_first_child_by_uclass_id(struct udevice *parent, int uclass_id,
+					struct udevice **devp)
+{
+	struct udevice *dev;
+
+	*devp = NULL;
+
+	list_for_each_entry(dev, &parent->child_head, sibling_node) {
+		if (dev->driver->id == uclass_id)
+			return device_get_device_tail(dev, 0, devp);
+	}
+
+	return -ENODEV;
+}
+
 int device_get_child_by_of_offset(struct udevice *parent, int seq,
 				  struct udevice **devp)
 {
include/dm/device.h +16 -0
index 7a48eb8..9f0d6ce 100644
@@ -335,6 +335,22 @@ int device_get_child_by_of_offset(struct udevice *parent, int seq,
 				  struct udevice **devp);
 
 /**
+ * device_get_first_child_by_uclass_id() - Get the first child device based
+ *                                         on UCLASS_ID
+ *
+ * Locates a child device by its uclass id.
+ *
+ * The device is probed to activate it ready for use.
+ *
+ * @parent: Parent device
+ * @uclass_id: child uclass id
+ * @devp: Returns pointer to device if found, otherwise this is set to NULL
+ * @return 0 if OK, -ve on error
+ */
+int device_get_first_child_by_uclass_id(struct udevice *parent, int uclass_id,
+					struct udevice **devp);
+
+/**
  * device_find_first_child() - Find the first child of a device
  *
  * @parent: Parent device to search
Signature · 1 line
1.9.1
#62 lore raw

PATCH v2 2/12 dm: device: add function device_get_first_child_by_uclass_id()

Hi Przemyslaw,

Quoted 32 lines · Przemyslaw Marczak

To implement functionality for devices connected by some external interface, sometimes there is need to implement more then one, and different uclass type drivers.

But only one i2c/spi dm chip can exists, per each bus i2c address or spi select. Then, it seems to be useful, to get the child device by uclass type, for the parent with known chip address.

So, this change will be useful for the pmic case:
|- i2c bus
'- pmic i2c chip (parent)
'- uclass regulator (child 1)
'- uclass charger (child 2)

This will allow to get the regulator or charger device if knows only parent i2c/spi address.

---
Changes V2:
- new commit

---
 drivers/core/device.c | 15 +++++++++++++++
 include/dm/device.h   | 16 ++++++++++++++++
 2 files changed, 31 insertions(+)
drivers/core/device.c +0 -0
index 73c3e07..76b22cf 100644

drivers/core/device.c int device_find_child_by_of_offset(struct udevice *parent, int of_offset, · line 400

Quoted 4 lines
drivers/core/device.c +1 -0
@@ -397,6 +397,21 @@ int device_find_child_by_of_offset(struct udevice *parent, int of_offset,
        return -ENODEV;
 }
 
+int device_get_first_child_by_uclass_id(struct udevice *parent, int uclass_id,

Can you please use the enum here instead of int?

Quoted 46 lines
drivers/core/device.c +14 -0
+                                       struct udevice **devp)
+{
+       struct udevice *dev;
+
+       *devp = NULL;
+
+       list_for_each_entry(dev, &parent->child_head, sibling_node) {
+               if (dev->driver->id == uclass_id)
+                       return device_get_device_tail(dev, 0, devp);
+       }
+
+       return -ENODEV;
+}
+
 int device_get_child_by_of_offset(struct udevice *parent, int seq,
                                  struct udevice **devp)
 {
include/dm/device.h +16 -1
index 7a48eb8..9f0d6ce 100644
@@ -335,6 +335,22 @@ int device_get_child_by_of_offset(struct udevice *parent, int seq,
                                  struct udevice **devp);
 
 /**
+ * device_get_first_child_by_uclass_id() - Get the first child device based
+ *                                         on UCLASS_ID
+ *
+ * Locates a child device by its uclass id.
+ *
+ * The device is probed to activate it ready for use.
+ *
+ * @parent: Parent device
+ * @uclass_id: child uclass id
+ * @devp: Returns pointer to device if found, otherwise this is set to NULL
+ * @return 0 if OK, -ve on error
+ */
+int device_get_first_child_by_uclass_id(struct udevice *parent, int uclass_id,
+                                       struct udevice **devp);
+
+/**
  * device_find_first_child() - Find the first child of a device
  *
  * @parent: Parent device to search
--
Quoted 2 lines

1.9.1

Regards,
Simon

#63 lore raw

PATCH v2 2/12 dm: device: add function device_get_first_child_by_uclass_id()

Hello Simon,

Quoted 85 lines · Simon Glass

Hi Przemyslaw,

Quoted 32 lines · Przemyslaw Marczak

To implement functionality for devices connected by some external interface, sometimes there is need to implement more then one, and different uclass type drivers.

But only one i2c/spi dm chip can exists, per each bus i2c address or spi select. Then, it seems to be useful, to get the child device by uclass type, for the parent with known chip address.

So, this change will be useful for the pmic case:
|- i2c bus
'- pmic i2c chip (parent)
'- uclass regulator (child 1)
'- uclass charger (child 2)

This will allow to get the regulator or charger device if knows only parent i2c/spi address.

---
Changes V2:
- new commit

---
  drivers/core/device.c | 15 +++++++++++++++
  include/dm/device.h   | 16 ++++++++++++++++
  2 files changed, 31 insertions(+)
drivers/core/device.c +0 -0
index 73c3e07..76b22cf 100644

drivers/core/device.c int device_find_child_by_of_offset(struct udevice *parent, int of_offset, · line 400

Quoted 4 lines
drivers/core/device.c +1 -0
@@ -397,6 +397,21 @@ int device_find_child_by_of_offset(struct udevice *parent, int of_offset,
         return -ENODEV;
  }
 
+int device_get_first_child_by_uclass_id(struct udevice *parent, int uclass_id,

Can you please use the enum here instead of int?

Quoted 43 lines
drivers/core/device.c +14 -0
+                                       struct udevice **devp)
+{
+       struct udevice *dev;
+
+       *devp = NULL;
+
+       list_for_each_entry(dev, &parent->child_head, sibling_node) {
+               if (dev->driver->id == uclass_id)
+                       return device_get_device_tail(dev, 0, devp);
+       }
+
+       return -ENODEV;
+}
+
  int device_get_child_by_of_offset(struct udevice *parent, int seq,
                                   struct udevice **devp)
  {
include/dm/device.h +16 -0
index 7a48eb8..9f0d6ce 100644
@@ -335,6 +335,22 @@ int device_get_child_by_of_offset(struct udevice *parent, int seq,
                                   struct udevice **devp);
 
  /**
+ * device_get_first_child_by_uclass_id() - Get the first child device based
+ *                                         on UCLASS_ID
+ *
+ * Locates a child device by its uclass id.
+ *
+ * The device is probed to activate it ready for use.
+ *
+ * @parent: Parent device
+ * @uclass_id: child uclass id
+ * @devp: Returns pointer to device if found, otherwise this is set to NULL
+ * @return 0 if OK, -ve on error
+ */
+int device_get_first_child_by_uclass_id(struct udevice *parent, int uclass_id,
+                                       struct udevice **devp);
+
+/**
   * device_find_first_child() - Find the first child of a device
Quoted 9 lines
Quoted 5 lines
include/dm/device.h +0 -1
   *
   * @parent: Parent device to search
--

1.9.1

Regards,
Simon

This function is discarded in the V3.

Best regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#64 lore raw

PATCH v2 3/12 dm: pmic: add implementation of driver model pmic uclass

In reply to Przemyslaw Marczak

This is an introduction to driver-model multi uclass PMIC support. It starts with UCLASS_PMIC - a common PMIC devices uclass type to provide device read/write operations only.

Beside two basic operations the pmic platform data is introduced, which provides basic informations about the pmic device I/O interface and is shared with all childs (and should also for childs new uclass types in the future).

Usually PMIC devices provides various functionalities with single or multiple I/O interfaces.
Using this new framework and new uclass types introduced in the future, it can be handle like this:

_ root device

|
|_ BUS 0 device (e.g. I2C0)                - UCLASS_I2C/SPI/...
| |_ PMIC device 1 (read/write ops)        - UCLASS_PMIC
|   |_ REGULATOR device (ldo/buck/... ops) - UCLASS_REGULATOR
|   |_ CHARGER device (charger ops)        - UCLASS_CHARGER (in the future)
|   |_ MUIC device (microUSB con ops)      - UCLASS_MUIC    (in the future)
|   |_ ...
|
|_ BUS 1 device (e.g. I2C1)                - UCLASS_I2C/SPI/...

|_ PMIC device 2 (read/write ops) - UCLASS_PMIC
|_ RTC device (rtc ops) - UCLASS_MUIC (in the future)

For each PMIC device interface, new UCLASS_PMIC device is bind with proper pmic driver, and it's child devices provides some specified operations.

All new definitions can be found in file:
- 'include/power/pmic.h'

Uclass file:
- pmic-uclass.c - provides a common code for UCLASS_PMIC device drivers

The old pmic framework is still kept and is independent.

Changes:
- new uclass-id: UCLASS_PMIC
- new config: CONFIG_DM_PMIC

New pmic api is documented in: doc/README.power-framework-dm

---
Changes V2:
- pmic uclass: adjust uclass code to the mainline changes
- pmic uclass: remove pmic_i2c and pmic_spi
- pmic uclass: modify pmic_platdata
- pmic uclass: add pmic_if_* functions
- pmic uclass: remove pmic_init_dm()
- pmic uclass: cleanup
- pmic.h: define pmic ops structure (read/write operations)
- pmic.h: add comments to functions

---
 drivers/power/Makefile      |   1 +
 drivers/power/pmic-uclass.c | 191 +++++++++++++++++++++++++++++++
 include/dm/uclass-id.h      |   3 +
 include/power/pmic.h        | 265 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 460 insertions(+)
 create mode 100644 drivers/power/pmic-uclass.c
drivers/power/Makefile +1 -0
index 2145652..5c9a189 100644
@@ -21,3 +21,4 @@ obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
 obj-$(CONFIG_POWER_FSL) += power_fsl.o
 obj-$(CONFIG_POWER_I2C) += power_i2c.o
 obj-$(CONFIG_POWER_SPI) += power_spi.o
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
drivers/power/pmic-uclass.c new file +191 -0

191 lines not on this page · raw message

include/dm/uclass-id.h +3 -0
index 91bb90d..ff360ac 100644
@@ -35,6 +35,9 @@ enum uclass_id {
 	UCLASS_I2C_EEPROM,	/* I2C EEPROM device */
 	UCLASS_MOD_EXP,		/* RSA Mod Exp device */
 
+	/* PMIC uclass and PMIC related uclasses */
+	UCLASS_PMIC,
+
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
 };
include/power/pmic.h +265 -0

292 lines not on this page · raw message

Signature · 1 line
1.9.1
#65 lore raw

PATCH v2 3/12 dm: pmic: add implementation of driver model pmic uclass

Hi Przemyslaw,

Quoted 55 lines · Przemyslaw Marczak

This is an introduction to driver-model multi uclass PMIC support. It starts with UCLASS_PMIC - a common PMIC devices uclass type to provide device read/write operations only.

Beside two basic operations the pmic platform data is introduced, which provides basic informations about the pmic device I/O interface and is shared with all childs (and should also for childs new uclass types in the future).

Usually PMIC devices provides various functionalities with single or multiple I/O interfaces.
Using this new framework and new uclass types introduced in the future, it can be handle like this:

_ root device

|
|_ BUS 0 device (e.g. I2C0)                - UCLASS_I2C/SPI/...
| |_ PMIC device 1 (read/write ops)        - UCLASS_PMIC
|   |_ REGULATOR device (ldo/buck/... ops) - UCLASS_REGULATOR
|   |_ CHARGER device (charger ops)        - UCLASS_CHARGER (in the future)
|   |_ MUIC device (microUSB con ops)      - UCLASS_MUIC    (in the future)
|   |_ ...
|
|_ BUS 1 device (e.g. I2C1)                - UCLASS_I2C/SPI/...

|_ PMIC device 2 (read/write ops) - UCLASS_PMIC
|_ RTC device (rtc ops) - UCLASS_MUIC (in the future)

For each PMIC device interface, new UCLASS_PMIC device is bind with proper pmic driver, and it's child devices provides some specified operations.

All new definitions can be found in file:
- 'include/power/pmic.h'

Uclass file:
- pmic-uclass.c - provides a common code for UCLASS_PMIC device drivers

The old pmic framework is still kept and is independent.

Changes:
- new uclass-id: UCLASS_PMIC
- new config: CONFIG_DM_PMIC

New pmic api is documented in: doc/README.power-framework-dm

---
Changes V2:
- pmic uclass: adjust uclass code to the mainline changes
- pmic uclass: remove pmic_i2c and pmic_spi
- pmic uclass: modify pmic_platdata
- pmic uclass: add pmic_if_* functions
- pmic uclass: remove pmic_init_dm()
- pmic uclass: cleanup
- pmic.h: define pmic ops structure (read/write operations)
- pmic.h: add comments to functions

Quoted 7 lines
---
 drivers/power/Makefile      |   1 +
 drivers/power/pmic-uclass.c | 191 +++++++++++++++++++++++++++++++
 include/dm/uclass-id.h      |   3 +
 include/power/pmic.h        | 265 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 460 insertions(+)
 create mode 100644 drivers/power/pmic-uclass.c

This should have a Kconfig file.

Quoted 212 lines
drivers/power/Makefile +1 -0
index 2145652..5c9a189 100644
@@ -21,3 +21,4 @@ obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
 obj-$(CONFIG_POWER_FSL) += power_fsl.o
 obj-$(CONFIG_POWER_I2C) += power_i2c.o
 obj-$(CONFIG_POWER_SPI) += power_spi.o
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
drivers/power/pmic-uclass.c new file +191 -0
new file mode 100644
index 0000000..309463e
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <dm.h>
+#include <power/pmic.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/root.h>
+#include <dm/lists.h>
+#include <compiler.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static char * const pmic_interfaces[] = {
+       "I2C",
+       "SPI",
+       "--",
+};
+
+const char *pmic_if_str(struct udevice *pmic)
+{
+       int if_types = ARRAY_SIZE(pmic_interfaces);
+       int if_type;
+
+       if_type = pmic_if_type(pmic);
+       if (if_type < 0 || if_type >= if_types)
+               return pmic_interfaces[if_types - 1];
+
+       return pmic_interfaces[if_type];
+}
+
+int pmic_if_type(struct udevice *pmic)
+{
+       struct pmic_platdata *pl = pmic->platdata;
+
+       return pl->if_type;
+}
+
+int pmic_if_bus_num(struct udevice *pmic)
+{
+       struct pmic_platdata *pl = pmic->platdata;
+
+       return pl->if_bus_num;
+}
+
+int pmic_if_addr_cs(struct udevice *pmic)
+{
+       struct pmic_platdata *pl = pmic->platdata;
+
+       return pl->if_addr_cs;
+}
+
+int pmic_if_max_offset(struct udevice *pmic)
+{
+       struct pmic_platdata *pl = pmic->platdata;
+
+       return pl->if_max_offset;
+}
+
+int pmic_read(struct udevice *pmic, unsigned reg, unsigned char *val)
+{
+       const struct dm_pmic_ops *ops;
+
+       ops = pmic_get_uclass_ops(pmic, UCLASS_PMIC);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->read)
+               return -EPERM;
+
+       if (ops->read(pmic, reg, val))
+               return -EIO;
+
+       return 0;
+}
+
+int pmic_write(struct udevice *pmic, unsigned reg, unsigned char val)
+{
+       const struct dm_pmic_ops *ops;
+
+       ops = pmic_get_uclass_ops(pmic, UCLASS_PMIC);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->write)
+               return -EPERM;
+
+       if (ops->write(pmic, reg, val))
+               return -EIO;
+
+       return 0;
+}
+
+int pmic_get(char *name, struct udevice **pmic)
+{
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       ret = uclass_get(UCLASS_PMIC, &uc);
+       if (ret) {
+               error("PMIC uclass not initialized!");
+               return ret;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev)
+                       continue;
+
+               if (!strncmp(name, dev->name, strlen(name))) {
+                       ret = device_probe(dev);
+                       if (ret)
+                               error("Dev: %s probe failed", dev->name);
+                       *pmic = dev;
+                       return ret;
+               }
+       }
+
+       *pmic = NULL;
+       return -EINVAL;
+}
+
+int pmic_i2c_get(int bus, int addr, struct udevice **pmic)
+{
+       struct udevice *dev;
+       int ret;
+
+       ret = i2c_get_chip_for_busnum(bus, addr, 1, &dev);
+       if (ret) {
+               error("Chip: %d on bus %d not found!", addr, bus);
+               *pmic = NULL;
+               return ret;
+       }
+
+       *pmic = dev;
+       return 0;
+}
+
+int pmic_spi_get(int bus, int cs, struct udevice **pmic)
+{
+       struct udevice *busp, *devp;
+       int ret;
+
+       ret = spi_find_bus_and_cs(bus, cs, &busp, &devp);
+       if (ret) {
+               error("Chip: %d on bus %d not found!", cs, bus);
+               *pmic = NULL;
+               return ret;
+       }
+
+       *pmic = devp;
+       return 0;
+}
+
+int pmic_io_dev(struct udevice *pmic, struct udevice **pmic_io)
+{
+       struct pmic_platdata *pl;
+       int ret;
+
+       pl = dev_get_platdata(pmic);
+       if (!pl) {
+               error("pmic: %s platdata not found!", pmic->name);
+               return -EINVAL;
+       }
+
+       switch (pl->if_type) {
+       case PMIC_I2C:
+               ret = pmic_i2c_get(pl->if_bus_num, pl->if_addr_cs, pmic_io);
+               break;
+       case PMIC_SPI:
+               ret = pmic_spi_get(pl->if_bus_num, pl->if_addr_cs, pmic_io);
+               break;
+       default:
+               error("Unsupported interface for: %s", pmic->name);
+               ret = -ENXIO;
+       }
+
+       return ret;
+}
+
+UCLASS_DRIVER(pmic) = {
+       .id             = UCLASS_PMIC,
+       .name           = "pmic",
+};
include/dm/uclass-id.h +0 -0
index 91bb90d..ff360ac 100644

include/dm/uclass-id.h enum uclass_id { · line 38

Quoted 4 lines
include/dm/uclass-id.h +1 -0
@@ -35,6 +35,9 @@ enum uclass_id {
        UCLASS_I2C_EEPROM,      /* I2C EEPROM device */
        UCLASS_MOD_EXP,         /* RSA Mod Exp device */
 
+       /* PMIC uclass and PMIC related uclasses */

PMIC-related

Quoted 109 lines
include/dm/uclass-id.h +2 -0
+       UCLASS_PMIC,
+
        UCLASS_COUNT,
        UCLASS_INVALID = -1,
 };
include/power/pmic.h +80 -0
index afbc5aa..94171ac 100644
@@ -1,4 +1,7 @@
 /*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <[email protected]>
+ *
  *  Copyright (C) 2011-2012 Samsung Electronics
  *  Lukasz Majewski <[email protected]>
  *
@@ -9,10 +12,13 @@
 #define __CORE_PMIC_H_
 
 #include <linux/list.h>
+#include <spi.h>
 #include <i2c.h>
 #include <power/power_chrg.h>
 
 enum { PMIC_I2C, PMIC_SPI, PMIC_NONE};
+
+#ifdef CONFIG_POWER
 enum { I2C_PMIC, I2C_NUM, };
 enum { PMIC_READ, PMIC_WRITE, };
 enum { PMIC_SENSOR_BYTE_ORDER_LITTLE, PMIC_SENSOR_BYTE_ORDER_BIG, };
@@ -77,7 +83,265 @@ struct pmic {
        struct pmic *parent;
        struct list_head list;
 };
+#endif /* CONFIG_POWER */
+
+#ifdef CONFIG_DM_PMIC
+/**
+ * Driver model pmic framework.
+ * The PMIC_UCLASS uclass is designed to provide a common I/O
+ * interface for pmic child devices of various uclass types.
+ *
+ * Usually PMIC devices provides more than one functionality,
+ * which means that we should implement uclass operations for
+ * each functionality - one driver per uclass.
+ *
+ * And usually PMIC devices provide those various functionalities
+ * by one or more interfaces. And this could looks like this:
+ *
+ *_ root device
+ * |_ BUS 0 device (e.g. I2C0)                 - UCLASS_I2C/SPI/...
+ * | |_ PMIC device (READ/WRITE ops)           - UCLASS_PMIC
+ * |   |_ REGULATOR device (ldo/buck/... ops)  - UCLASS_REGULATOR
+ * |   |_ CHARGER device (charger ops)         - UCLASS_CHARGER (in the future)
+ * |   |_ MUIC device (microUSB connector ops) - UCLASS_MUIC    (in the future)
+ * |   |_ ...
+ * |
+ * |_ BUS 1 device (e.g. I2C1)                 - UCLASS_I2C/SPI/...
+ *   |_ PMIC device (READ/WRITE ops)           - UCLASS_PMIC
+ *     |_ RTC device (rtc ops)                 - UCLASS_MUIC (in the future)
+ *
+ * Two PMIC types are possible:
+ * - single I/O interface
+ *   Then UCLASS_PMIC device should be a parent of all pmic devices, where each
+ *   is usually different uclass type, but need to access the same interface
+ *
+ * - multiple I/O interfaces
+ *   For each interface the UCLASS_PMIC device should be a parent of only those
+ *   devices (different uclass types), which needs to access the specified
+ *   interface.
+ *
+ * For each case binding should be done automatically. If only device tree
+ * nodes/subnodes are proper defined, then:
+ * |_ the ROOT driver will bind the device for I2C/SPI node:
+ *   |_ the I2C/SPI driver should bind a device for pmic node:
+ *     |_ the PMIC driver should bind devices for its childs:
+ *       |_ regulator (child)
+ *       |_ charger   (child)
+ *       |_ other     (child)
+ *
+ * The same for other bus nodes, if pmic uses more then one interface.
+ *
+ * Note:
+ * Each PMIC interface driver should use different compatible string.
+ *
+ * If each pmic child device driver need access the pmic specified registers,
+ * it need to know only the register address and the access is done through
+ * the parent pmic driver. Like in the example:
+ *
+ *_ root driver
+ * |_ dev: bus I2C0                                         - UCLASS_I2C
+ * | |_ dev: my_pmic (read/write)              (is parent)  - UCLASS_PMIC
+ * |   |_ dev: my_regulator (set value/etc..)  (is child)   - UCLASS_REGULATOR
+ *
+ *
+ * To ensure such device relationship, the pmic device driver should also bind
+ * all its child devices, like in this example:
+ *
+ * my_pmic.c - pmic driver:
+ *
+ * int my_pmic_bind(struct udevice *my_pmic)
+ * {
+ *         // A list of other drivers for this pmic
+ *         char *my_pmic_drv_list = {"my_regulator", "my_charger", "my_rtc"};
+ *         struct udevice *my_pmic_child_dev[3];
+ * ...
+ *         for (i=0; i < ARRAY_SIZE(my_pmic_drv_list); i++) {
+ *                 // Look for child device driver

include/power/pmic.h struct pmic { · line 169

Quoted 10 lines
include/power/pmic.h +10 -0
+ *                 drv_my_reg = lists_driver_lookup_name(my_pmic_drv_list[i]);
+ *
+ *                 // If driver found, then bind the new device to it
+ *                 if (drv_my_reg)
+ *                         ret = device_bind(my_pmic, my_pmic_drv_list[i],
+ *                                           my_pmic->name, my_pmic->platdata,
+ *                                           my_pmic->of_offset,
+ *                                           my_pmic_child_dev[i]);
+ *                 ...
+ *         }

Can we not automatically do this with dm_scan_fdt_node() as in spi_post_bind(), etc.?

Quoted 31 lines
include/power/pmic.h +31 -0
+ * ...
+ * }
+ *
+ * my_regulator.c - regulator driver (child of pmic I/O driver):
+ *
+ * int my_regulator_set_value(struct udevice *dev, int type, int num, int *val)
+ * {
+ * ...
+ *         some_val = ...;
+ *
+ *         // dev->parent == my_pmic
+ *         pmic_write(dev->parent, some_reg, some_val);
+ * ...
+ * }
+ *
+ * In this example pmic driver is always a parent for other pmic devices,
+ * because the first argument of device_bind() call is the parent device,
+ * and here it is 'my_pmic'.
+ *
+ * For all childs the device->platdata is the same, and provide an info about
+ * the same interface - it's used for getting the devices by interface address
+ * and uclass types.
+ */
+
+/**
+ * struct pmic_platdata - PMIC chip interface info
+ *
+ * This is required for pmic command purposes and should be shared
+ * by pmic device and it's childs as the same platdata pointer.
+ *
+ * @if_type:       one of: PMIC_I2C, PMIC_SPI, PMIC_NONE

include/power/pmic.h struct pmic { · line 210

Quoted 10 lines
include/power/pmic.h +10 -0
+ * @if_bus_num:    usually alias seq of i2c/spi bus device
+ * @if_addr_cd:    i2c/spi chip addr or cs
+ * @if_max_offset: max register offset within the chip
+ */
+struct pmic_platdata {
+       int if_type;
+       int if_bus_num;
+       int if_addr_cs;
+       int if_max_offset;
+};

Can we drop the if_ prefix and use an enum for if_type?

For bus_num, this will be the parent of the pmic. There should be no need to store it.

For addr_cs, there should be no need to store this. The pmic will be either on an i2c or spi bus - both of these provide the address information in dev_get_parent_platdata().

if_max_offset should be in the PMIC platdata but not that of its children. So I don't think they should all use the same platdata.

include/power/pmic.h struct pmic { · line 222

Quoted 12 lines
include/power/pmic.h +12 -0
+
+/**
+ * struct dm_pmic_ops - PMIC device I/O interface
+ *
+ * Should be implemented by UCLASS_PMIC device drivers. The standard
+ * device operations provides the I/O interface for it's childs.
+ *
+ * @read:   called for read "reg" value into "val" through "pmic" parent
+ * @write:  called for write "val" into "reg" through the "pmic" parent
+ */
+struct dm_pmic_ops {
+       int (*read)(struct udevice *pmic, unsigned reg, unsigned char *val);

Can any pmics support reading 16 bits or more?

include/power/pmic.h struct pmic { · line 226

Quoted 4 lines
include/power/pmic.h +4 -0
+       int (*write)(struct udevice *pmic, unsigned reg, unsigned char val);
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,

Comment style

Quoted 11 lines
include/power/pmic.h +11 -0
+ * for reduce a number of lines with the same code for read/write or get/set.
+ *
+ * @PMIC_OP_GET - get operation
+ * @PMIC_OP_SET - set operation
+*/
+enum pmic_op_type {
+       PMIC_OP_GET,
+       PMIC_OP_SET,
+};
+
+/**

include/power/pmic.h struct pmic { · line 247

Quoted 10 lines
include/power/pmic.h +10 -0
+ * pmic_get_uclass_ops - inline function for getting device uclass operations
+ *
+ * @dev       - device to check
+ * @uclass_id - device uclass id
+ *
+ * Returns:
+ * void pointer to device operations or NULL pointer on error
+ */
+static __inline__ const void *pmic_get_uclass_ops(struct udevice *dev,
+                                                 int uclass_id)

enum

Quoted 30 lines
include/power/pmic.h +30 -0
+{
+       const void *ops;
+
+       if (!dev)
+               return NULL;
+
+       if (dev->driver->id != uclass_id)
+               return NULL;
+
+       ops = dev->driver->ops;
+       if (!ops)
+               return NULL;
+
+       return ops;
+}
+
+/* drivers/power/pmic-uclass.c */
+
+/**
+ * pmic_..._get: looking for the pmic device using the specified arguments:
+ *
+ * NAME:
+ *  @name    - device name (useful for single specific names)
+ *
+ * or SPI/I2C interface:
+ *  @bus     - device I2C/SPI bus number
+ *  @addr_cs - device specific address for I2C or chip select for SPI,
+ *             on the given bus number
+ *
+ * Returns:

include/power/pmic.h struct pmic { · line 287

Quoted 10 lines
include/power/pmic.h +10 -0
+ * @pmic    - returned pointer to the pmic device
+ * Returns: 0 on success or negative value of errno.
+ *
+ * The returned pmic device can be used with:
+ * - pmic_read/write calls
+ * - pmic_if_* calls
+ */
+int pmic_get(char *name, struct udevice **pmic);
+int pmic_i2c_get(int bus, int addr, struct udevice **pmic);
+int pmic_spi_get(int bus, int cs, struct udevice **pmic);

These last two should be internal to the pmic framework I think. Clients should not care.

Also we should reference SPI and I2C devices with a struct udevice, not a bus/addr or bus/cs pair.

Quoted 27 lines
include/power/pmic.h +27 -0
+
+/**
+ * pmic_io_dev: returns given pmic/regulator, uclass pmic I/O udevice
+ *
+ * @pmic    - pointer to pmic_io child device
+ *
+ * Returns:
+ * @pmic_io - pointer to the I/O chip device
+ *
+ * This is used for pmic command, and also can be used if
+ * needs raw read/write operations for uclass regulator device.
+ * Example of use:
+ *  pmic_io_dev(regulator, &pmic_io);
+ *  pmic_write(pmic_io, 0x0, 0x1);
+ *
+ * This base on pmic_platdata info, so for the given pmic i2c/spi chip,
+ * as '@pmic', it should return the same chip, if platdata is filled.
+ */
+int pmic_io_dev(struct udevice *pmic, struct udevice **pmic_io);
+
+/**
+ * pmic_if_* functions: returns one of given pmic interface attribute:
+ * - type                         - one of enum { PMIC_I2C, PMIC_SPI, PMIC_NONE}
+ * - bus number                   - usually i2c/spi bus seq number
+ * - addres for i2c or cs for spi - basaed on dm_i2c/spi_chip
+ * - max offset                   - device internal address range
+ * - string - "I2C"/"SPI"/"--"

include/power/pmic.h struct pmic { · line 324

Quoted 10 lines
include/power/pmic.h +10 -0
+ *
+ * @pmic - pointer to the pmic device
+ *
+ * Returns: one of listed attribute on success, or a negative value of errno.
+ */
+int pmic_if_type(struct udevice *pmic);
+int pmic_if_bus_num(struct udevice *pmic);
+int pmic_if_addr_cs(struct udevice *pmic);
+int pmic_if_max_offset(struct udevice *pmic);
+const char *pmic_if_str(struct udevice *pmic);

I hope that none of these is needed in fact.

Quoted 4 lines
include/power/pmic.h +4 -0
+
+/**
+ * pmic_read/write: read/write to the UCLASS_PMIC device
+ *

include/power/pmic.h struct pmic { · line 338

Quoted 10 lines
include/power/pmic.h +10 -0
+ * The required pmic device can be obtained by:
+ * - pmic_i2c_get()
+ * - pmic_spi_get()
+ * - pmic_io_dev() - for pmic command purposes
+ *
+ * @pmic - pointer to the UCLASS_PMIC device
+ * @reg  - device register offset
+ * @val  - value for write or its pointer to read
+ *
+ * Returns: offset value on success or negative value of errno.

s/offset/register/ ?

Quoted 18 lines
include/power/pmic.h +6 -1
+ */
+int pmic_read(struct udevice *pmic, unsigned reg, unsigned char *val);
+int pmic_write(struct udevice *pmic, unsigned reg, unsigned char val);
+#endif /* CONFIG_DM_PMIC */
 
+#ifdef CONFIG_POWER
 int pmic_init(unsigned char bus);
 int power_init_board(void);
 int pmic_dialog_init(unsigned char bus);
@@ -88,6 +352,7 @@ int pmic_probe(struct pmic *p);
 int pmic_reg_read(struct pmic *p, u32 reg, u32 *val);
 int pmic_reg_write(struct pmic *p, u32 reg, u32 val);
 int pmic_set_output(struct pmic *p, u32 reg, int ldo, int on);
+#endif
 
 #define pmic_i2c_addr (p->hw.i2c.addr)
 #define pmic_i2c_tx_num (p->hw.i2c.tx_num)
--
Quoted 2 lines

1.9.1

Regards,
Simon

#66 lore raw

PATCH v2 3/12 dm: pmic: add implementation of driver model pmic uclass

Hello Simon,

Quoted 401 lines · Simon Glass

Hi Przemyslaw,

Quoted 55 lines · Przemyslaw Marczak

This is an introduction to driver-model multi uclass PMIC support. It starts with UCLASS_PMIC - a common PMIC devices uclass type to provide device read/write operations only.

Beside two basic operations the pmic platform data is introduced, which provides basic informations about the pmic device I/O interface and is shared with all childs (and should also for childs new uclass types in the future).

Usually PMIC devices provides various functionalities with single or multiple I/O interfaces.
Using this new framework and new uclass types introduced in the future, it can be handle like this:

_ root device

|
|_ BUS 0 device (e.g. I2C0)                - UCLASS_I2C/SPI/...
| |_ PMIC device 1 (read/write ops)        - UCLASS_PMIC
|   |_ REGULATOR device (ldo/buck/... ops) - UCLASS_REGULATOR
|   |_ CHARGER device (charger ops)        - UCLASS_CHARGER (in the future)
|   |_ MUIC device (microUSB con ops)      - UCLASS_MUIC    (in the future)
|   |_ ...
|
|_ BUS 1 device (e.g. I2C1)                - UCLASS_I2C/SPI/...

|_ PMIC device 2 (read/write ops) - UCLASS_PMIC
|_ RTC device (rtc ops) - UCLASS_MUIC (in the future)

For each PMIC device interface, new UCLASS_PMIC device is bind with proper pmic driver, and it's child devices provides some specified operations.

All new definitions can be found in file:
- 'include/power/pmic.h'

Uclass file:
- pmic-uclass.c - provides a common code for UCLASS_PMIC device drivers

The old pmic framework is still kept and is independent.

Changes:
- new uclass-id: UCLASS_PMIC
- new config: CONFIG_DM_PMIC

New pmic api is documented in: doc/README.power-framework-dm

---
Changes V2:
- pmic uclass: adjust uclass code to the mainline changes
- pmic uclass: remove pmic_i2c and pmic_spi
- pmic uclass: modify pmic_platdata
- pmic uclass: add pmic_if_* functions
- pmic uclass: remove pmic_init_dm()
- pmic uclass: cleanup
- pmic.h: define pmic ops structure (read/write operations)
- pmic.h: add comments to functions

Quoted 7 lines
---
  drivers/power/Makefile      |   1 +
  drivers/power/pmic-uclass.c | 191 +++++++++++++++++++++++++++++++
  include/dm/uclass-id.h      |   3 +
  include/power/pmic.h        | 265 ++++++++++++++++++++++++++++++++++++++++++++
  4 files changed, 460 insertions(+)

create mode 100644 drivers/power/pmic-uclass.c

This should have a Kconfig file.

Quoted 212 lines
drivers/power/Makefile +1 -0
index 2145652..5c9a189 100644
@@ -21,3 +21,4 @@ obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
  obj-$(CONFIG_POWER_FSL) += power_fsl.o
  obj-$(CONFIG_POWER_I2C) += power_i2c.o
  obj-$(CONFIG_POWER_SPI) += power_spi.o
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
drivers/power/pmic-uclass.c new file +191 -0
new file mode 100644
index 0000000..309463e
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <dm.h>
+#include <power/pmic.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/root.h>
+#include <dm/lists.h>
+#include <compiler.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static char * const pmic_interfaces[] = {
+       "I2C",
+       "SPI",
+       "--",
+};
+
+const char *pmic_if_str(struct udevice *pmic)
+{
+       int if_types = ARRAY_SIZE(pmic_interfaces);
+       int if_type;
+
+       if_type = pmic_if_type(pmic);
+       if (if_type < 0 || if_type >= if_types)
+               return pmic_interfaces[if_types - 1];
+
+       return pmic_interfaces[if_type];
+}
+
+int pmic_if_type(struct udevice *pmic)
+{
+       struct pmic_platdata *pl = pmic->platdata;
+
+       return pl->if_type;
+}
+
+int pmic_if_bus_num(struct udevice *pmic)
+{
+       struct pmic_platdata *pl = pmic->platdata;
+
+       return pl->if_bus_num;
+}
+
+int pmic_if_addr_cs(struct udevice *pmic)
+{
+       struct pmic_platdata *pl = pmic->platdata;
+
+       return pl->if_addr_cs;
+}
+
+int pmic_if_max_offset(struct udevice *pmic)
+{
+       struct pmic_platdata *pl = pmic->platdata;
+
+       return pl->if_max_offset;
+}
+
+int pmic_read(struct udevice *pmic, unsigned reg, unsigned char *val)
+{
+       const struct dm_pmic_ops *ops;
+
+       ops = pmic_get_uclass_ops(pmic, UCLASS_PMIC);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->read)
+               return -EPERM;
+
+       if (ops->read(pmic, reg, val))
+               return -EIO;
+
+       return 0;
+}
+
+int pmic_write(struct udevice *pmic, unsigned reg, unsigned char val)
+{
+       const struct dm_pmic_ops *ops;
+
+       ops = pmic_get_uclass_ops(pmic, UCLASS_PMIC);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->write)
+               return -EPERM;
+
+       if (ops->write(pmic, reg, val))
+               return -EIO;
+
+       return 0;
+}
+
+int pmic_get(char *name, struct udevice **pmic)
+{
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       ret = uclass_get(UCLASS_PMIC, &uc);
+       if (ret) {
+               error("PMIC uclass not initialized!");
+               return ret;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev)
+                       continue;
+
+               if (!strncmp(name, dev->name, strlen(name))) {
+                       ret = device_probe(dev);
+                       if (ret)
+                               error("Dev: %s probe failed", dev->name);
+                       *pmic = dev;
+                       return ret;
+               }
+       }
+
+       *pmic = NULL;
+       return -EINVAL;
+}
+
+int pmic_i2c_get(int bus, int addr, struct udevice **pmic)
+{
+       struct udevice *dev;
+       int ret;
+
+       ret = i2c_get_chip_for_busnum(bus, addr, 1, &dev);
+       if (ret) {
+               error("Chip: %d on bus %d not found!", addr, bus);
+               *pmic = NULL;
+               return ret;
+       }
+
+       *pmic = dev;
+       return 0;
+}
+
+int pmic_spi_get(int bus, int cs, struct udevice **pmic)
+{
+       struct udevice *busp, *devp;
+       int ret;
+
+       ret = spi_find_bus_and_cs(bus, cs, &busp, &devp);
+       if (ret) {
+               error("Chip: %d on bus %d not found!", cs, bus);
+               *pmic = NULL;
+               return ret;
+       }
+
+       *pmic = devp;
+       return 0;
+}
+
+int pmic_io_dev(struct udevice *pmic, struct udevice **pmic_io)
+{
+       struct pmic_platdata *pl;
+       int ret;
+
+       pl = dev_get_platdata(pmic);
+       if (!pl) {
+               error("pmic: %s platdata not found!", pmic->name);
+               return -EINVAL;
+       }
+
+       switch (pl->if_type) {
+       case PMIC_I2C:
+               ret = pmic_i2c_get(pl->if_bus_num, pl->if_addr_cs, pmic_io);
+               break;
+       case PMIC_SPI:
+               ret = pmic_spi_get(pl->if_bus_num, pl->if_addr_cs, pmic_io);
+               break;
+       default:
+               error("Unsupported interface for: %s", pmic->name);
+               ret = -ENXIO;
+       }
+
+       return ret;
+}
+
+UCLASS_DRIVER(pmic) = {
+       .id             = UCLASS_PMIC,
+       .name           = "pmic",
+};
include/dm/uclass-id.h +0 -0
index 91bb90d..ff360ac 100644

include/dm/uclass-id.h enum uclass_id { · line 38

Quoted 4 lines
include/dm/uclass-id.h +1 -0
@@ -35,6 +35,9 @@ enum uclass_id {
         UCLASS_I2C_EEPROM,      /* I2C EEPROM device */
         UCLASS_MOD_EXP,         /* RSA Mod Exp device */
 
+       /* PMIC uclass and PMIC related uclasses */

PMIC-related

Quoted 114 lines
include/dm/uclass-id.h +2 -0
+       UCLASS_PMIC,
+
         UCLASS_COUNT,
         UCLASS_INVALID = -1,
  };
include/power/pmic.h +85 -0
index afbc5aa..94171ac 100644
@@ -1,4 +1,7 @@
  /*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <[email protected]>
+ *
   *  Copyright (C) 2011-2012 Samsung Electronics
   *  Lukasz Majewski <[email protected]>
   *
@@ -9,10 +12,13 @@
  #define __CORE_PMIC_H_
 
  #include <linux/list.h>
+#include <spi.h>
  #include <i2c.h>
  #include <power/power_chrg.h>
 
  enum { PMIC_I2C, PMIC_SPI, PMIC_NONE};
+
+#ifdef CONFIG_POWER
  enum { I2C_PMIC, I2C_NUM, };
  enum { PMIC_READ, PMIC_WRITE, };
  enum { PMIC_SENSOR_BYTE_ORDER_LITTLE, PMIC_SENSOR_BYTE_ORDER_BIG, };
@@ -77,7 +83,265 @@ struct pmic {
         struct pmic *parent;
         struct list_head list;
  };
+#endif /* CONFIG_POWER */
+
+#ifdef CONFIG_DM_PMIC
+/**
+ * Driver model pmic framework.
+ * The PMIC_UCLASS uclass is designed to provide a common I/O
+ * interface for pmic child devices of various uclass types.
+ *
+ * Usually PMIC devices provides more than one functionality,
+ * which means that we should implement uclass operations for
+ * each functionality - one driver per uclass.
+ *
+ * And usually PMIC devices provide those various functionalities
+ * by one or more interfaces. And this could looks like this:
+ *
+ *_ root device
+ * |_ BUS 0 device (e.g. I2C0)                 - UCLASS_I2C/SPI/...
+ * | |_ PMIC device (READ/WRITE ops)           - UCLASS_PMIC
+ * |   |_ REGULATOR device (ldo/buck/... ops)  - UCLASS_REGULATOR
+ * |   |_ CHARGER device (charger ops)         - UCLASS_CHARGER (in the future)
+ * |   |_ MUIC device (microUSB connector ops) - UCLASS_MUIC    (in the future)
+ * |   |_ ...
+ * |
+ * |_ BUS 1 device (e.g. I2C1)                 - UCLASS_I2C/SPI/...
+ *   |_ PMIC device (READ/WRITE ops)           - UCLASS_PMIC
+ *     |_ RTC device (rtc ops)                 - UCLASS_MUIC (in the future)
+ *
+ * Two PMIC types are possible:
+ * - single I/O interface
+ *   Then UCLASS_PMIC device should be a parent of all pmic devices, where each
+ *   is usually different uclass type, but need to access the same interface
+ *
+ * - multiple I/O interfaces
+ *   For each interface the UCLASS_PMIC device should be a parent of only those
+ *   devices (different uclass types), which needs to access the specified
+ *   interface.
+ *
+ * For each case binding should be done automatically. If only device tree
+ * nodes/subnodes are proper defined, then:
+ * |_ the ROOT driver will bind the device for I2C/SPI node:
+ *   |_ the I2C/SPI driver should bind a device for pmic node:
+ *     |_ the PMIC driver should bind devices for its childs:
+ *       |_ regulator (child)
+ *       |_ charger   (child)
+ *       |_ other     (child)
+ *
+ * The same for other bus nodes, if pmic uses more then one interface.
+ *
+ * Note:
+ * Each PMIC interface driver should use different compatible string.
+ *
+ * If each pmic child device driver need access the pmic specified registers,
+ * it need to know only the register address and the access is done through
+ * the parent pmic driver. Like in the example:
+ *
+ *_ root driver
+ * |_ dev: bus I2C0                                         - UCLASS_I2C
+ * | |_ dev: my_pmic (read/write)              (is parent)  - UCLASS_PMIC
+ * |   |_ dev: my_regulator (set value/etc..)  (is child)   - UCLASS_REGULATOR
+ *
+ *
+ * To ensure such device relationship, the pmic device driver should also bind
+ * all its child devices, like in this example:
+ *
+ * my_pmic.c - pmic driver:
+ *
+ * int my_pmic_bind(struct udevice *my_pmic)
+ * {
+ *         // A list of other drivers for this pmic
+ *         char *my_pmic_drv_list = {"my_regulator", "my_charger", "my_rtc"};
+ *         struct udevice *my_pmic_child_dev[3];
+ * ...
+ *         for (i=0; i < ARRAY_SIZE(my_pmic_drv_list); i++) {
+ *                 // Look for child device driver
+ *                 drv_my_reg = lists_driver_lookup_name(my_pmic_drv_list[i]);
+ *
+ *                 // If driver found, then bind the new device to it
+ *                 if (drv_my_reg)
+ *                         ret = device_bind(my_pmic, my_pmic_drv_list[i],
Quoted 9 lines

include/power/pmic.h struct pmic { · line 169

Quoted 5 lines
include/power/pmic.h +5 -0
+ *                                           my_pmic->name, my_pmic->platdata,
+ *                                           my_pmic->of_offset,
+ *                                           my_pmic_child_dev[i]);
+ *                 ...
+ *         }

Can we not automatically do this with dm_scan_fdt_node() as in spi_post_bind(), etc.?

This was solved partially in V3, in which I added the common function to bind childs by checking compatible string in the property given by arg. Looking at few dts files, we can found that some boards uses "compatible", and some uses "regulator-compatible" for the compatible property name of the regulators. I think, that this is not bad.

Quoted 47 lines
Quoted 31 lines
include/power/pmic.h +31 -0
+ * ...
+ * }
+ *
+ * my_regulator.c - regulator driver (child of pmic I/O driver):
+ *
+ * int my_regulator_set_value(struct udevice *dev, int type, int num, int *val)
+ * {
+ * ...
+ *         some_val = ...;
+ *
+ *         // dev->parent == my_pmic
+ *         pmic_write(dev->parent, some_reg, some_val);
+ * ...
+ * }
+ *
+ * In this example pmic driver is always a parent for other pmic devices,
+ * because the first argument of device_bind() call is the parent device,
+ * and here it is 'my_pmic'.
+ *
+ * For all childs the device->platdata is the same, and provide an info about
+ * the same interface - it's used for getting the devices by interface address
+ * and uclass types.
+ */
+
+/**
+ * struct pmic_platdata - PMIC chip interface info
+ *
+ * This is required for pmic command purposes and should be shared
+ * by pmic device and it's childs as the same platdata pointer.
+ *
+ * @if_type:       one of: PMIC_I2C, PMIC_SPI, PMIC_NONE

include/power/pmic.h struct pmic { · line 210

Quoted 10 lines
include/power/pmic.h +10 -0
+ * @if_bus_num:    usually alias seq of i2c/spi bus device
+ * @if_addr_cd:    i2c/spi chip addr or cs
+ * @if_max_offset: max register offset within the chip
+ */
+struct pmic_platdata {
+       int if_type;
+       int if_bus_num;
+       int if_addr_cs;
+       int if_max_offset;
+};

Can we drop the if_ prefix and use an enum for if_type?

For bus_num, this will be the parent of the pmic. There should be no need to store it.

Quoted 7 lines

For addr_cs, there should be no need to store this. The pmic will be either on an i2c or spi bus - both of these provide the address information in dev_get_parent_platdata().

if_max_offset should be in the PMIC platdata but not that of its children. So I don't think they should all use the same platdata.

Right, this feature was not pretty, and is removed in V3.

Quoted 6 lines
Quoted 6 lines
include/power/pmic.h +6 -0
+
+/**
+ * struct dm_pmic_ops - PMIC device I/O interface
+ *
+ * Should be implemented by UCLASS_PMIC device drivers. The standard
+ * device operations provides the I/O interface for it's childs.
Quoted 9 lines

include/power/pmic.h struct pmic { · line 222

Quoted 6 lines
include/power/pmic.h +6 -0
+ *
+ * @read:   called for read "reg" value into "val" through "pmic" parent
+ * @write:  called for write "val" into "reg" through the "pmic" parent
+ */
+struct dm_pmic_ops {
+       int (*read)(struct udevice *pmic, unsigned reg, unsigned char *val);

Can any pmics support reading 16 bits or more?

Shame on me for missing this. The new declarations are fixed with the '*buffer' and 'len'.

Quoted 75 lines

include/power/pmic.h struct pmic { · line 226

Quoted 4 lines
include/power/pmic.h +4 -0
+       int (*write)(struct udevice *pmic, unsigned reg, unsigned char val);
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,

Comment style

Quoted 11 lines
include/power/pmic.h +11 -0
+ * for reduce a number of lines with the same code for read/write or get/set.
+ *
+ * @PMIC_OP_GET - get operation
+ * @PMIC_OP_SET - set operation
+*/
+enum pmic_op_type {
+       PMIC_OP_GET,
+       PMIC_OP_SET,
+};
+
+/**

include/power/pmic.h struct pmic { · line 247

Quoted 10 lines
include/power/pmic.h +10 -0
+ * pmic_get_uclass_ops - inline function for getting device uclass operations
+ *
+ * @dev       - device to check
+ * @uclass_id - device uclass id
+ *
+ * Returns:
+ * void pointer to device operations or NULL pointer on error
+ */
+static __inline__ const void *pmic_get_uclass_ops(struct udevice *dev,
+                                                 int uclass_id)

enum

Quoted 30 lines
include/power/pmic.h +30 -0
+{
+       const void *ops;
+
+       if (!dev)
+               return NULL;
+
+       if (dev->driver->id != uclass_id)
+               return NULL;
+
+       ops = dev->driver->ops;
+       if (!ops)
+               return NULL;
+
+       return ops;
+}
+
+/* drivers/power/pmic-uclass.c */
+
+/**
+ * pmic_..._get: looking for the pmic device using the specified arguments:
+ *
+ * NAME:
+ *  @name    - device name (useful for single specific names)
+ *
+ * or SPI/I2C interface:
+ *  @bus     - device I2C/SPI bus number
+ *  @addr_cs - device specific address for I2C or chip select for SPI,
+ *             on the given bus number
+ *
+ * Returns:

include/power/pmic.h struct pmic { · line 287

Quoted 10 lines
include/power/pmic.h +10 -0
+ * @pmic    - returned pointer to the pmic device
+ * Returns: 0 on success or negative value of errno.
+ *
+ * The returned pmic device can be used with:
+ * - pmic_read/write calls
+ * - pmic_if_* calls
+ */
+int pmic_get(char *name, struct udevice **pmic);
+int pmic_i2c_get(int bus, int addr, struct udevice **pmic);
+int pmic_spi_get(int bus, int cs, struct udevice **pmic);

These last two should be internal to the pmic framework I think. Clients should not care.

Quoted 3 lines

Also we should reference SPI and I2C devices with a struct udevice, not a bus/addr or bus/cs pair.

After rework, the V3 doesn't use this.

Quoted 72 lines
Quoted 27 lines
include/power/pmic.h +27 -0
+
+/**
+ * pmic_io_dev: returns given pmic/regulator, uclass pmic I/O udevice
+ *
+ * @pmic    - pointer to pmic_io child device
+ *
+ * Returns:
+ * @pmic_io - pointer to the I/O chip device
+ *
+ * This is used for pmic command, and also can be used if
+ * needs raw read/write operations for uclass regulator device.
+ * Example of use:
+ *  pmic_io_dev(regulator, &pmic_io);
+ *  pmic_write(pmic_io, 0x0, 0x1);
+ *
+ * This base on pmic_platdata info, so for the given pmic i2c/spi chip,
+ * as '@pmic', it should return the same chip, if platdata is filled.
+ */
+int pmic_io_dev(struct udevice *pmic, struct udevice **pmic_io);
+
+/**
+ * pmic_if_* functions: returns one of given pmic interface attribute:
+ * - type                         - one of enum { PMIC_I2C, PMIC_SPI, PMIC_NONE}
+ * - bus number                   - usually i2c/spi bus seq number
+ * - addres for i2c or cs for spi - basaed on dm_i2c/spi_chip
+ * - max offset                   - device internal address range
+ * - string - "I2C"/"SPI"/"--"

include/power/pmic.h struct pmic { · line 324

Quoted 10 lines
include/power/pmic.h +10 -0
+ *
+ * @pmic - pointer to the pmic device
+ *
+ * Returns: one of listed attribute on success, or a negative value of errno.
+ */
+int pmic_if_type(struct udevice *pmic);
+int pmic_if_bus_num(struct udevice *pmic);
+int pmic_if_addr_cs(struct udevice *pmic);
+int pmic_if_max_offset(struct udevice *pmic);
+const char *pmic_if_str(struct udevice *pmic);

I hope that none of these is needed in fact.

Quoted 4 lines
include/power/pmic.h +4 -0
+
+/**
+ * pmic_read/write: read/write to the UCLASS_PMIC device
+ *

include/power/pmic.h struct pmic { · line 338

Quoted 10 lines
include/power/pmic.h +10 -0
+ * The required pmic device can be obtained by:
+ * - pmic_i2c_get()
+ * - pmic_spi_get()
+ * - pmic_io_dev() - for pmic command purposes
+ *
+ * @pmic - pointer to the UCLASS_PMIC device
+ * @reg  - device register offset
+ * @val  - value for write or its pointer to read
+ *
+ * Returns: offset value on success or negative value of errno.

s/offset/register/ ?

Quoted 15 lines
include/power/pmic.h +6 -0
+ */
+int pmic_read(struct udevice *pmic, unsigned reg, unsigned char *val);
+int pmic_write(struct udevice *pmic, unsigned reg, unsigned char val);
+#endif /* CONFIG_DM_PMIC */
 
+#ifdef CONFIG_POWER
  int pmic_init(unsigned char bus);
  int power_init_board(void);
  int pmic_dialog_init(unsigned char bus);
@@ -88,6 +352,7 @@ int pmic_probe(struct pmic *p);
  int pmic_reg_read(struct pmic *p, u32 reg, u32 *val);
  int pmic_reg_write(struct pmic *p, u32 reg, u32 val);
  int pmic_set_output(struct pmic *p, u32 reg, int ldo, int on);
+#endif
 
Quoted 9 lines
Quoted 5 lines
include/power/pmic.h +0 -1
  #define pmic_i2c_addr (p->hw.i2c.addr)
  #define pmic_i2c_tx_num (p->hw.i2c.tx_num)
--

1.9.1

Regards,
Simon

Thank you for comments.

Best regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#67 lore raw

PATCH v2 4/12 dm: pmic: add implementation of driver model regulator uclass

In reply to Przemyslaw Marczak

This is the implementation of driver model regulator uclass api. To use it, the CONFIG_DM_PMIC is required with driver implementation, since it provides pmic devices basic I/O API.

The regulator framework is based on a 'struct dm_regulator_ops'. It provides a common function calls, for it's basic features:
- regulator_get_cnt() - number of outputs each type
- regulator_get_value_desc() - describe output value limits

- regulator_get_mode_desc()  - describe output operation modes
- regulator_get/set_value()  - output value (uV)
- regulator_get/set_state()  - output on/off state
- regulator_get/set_mode()   - output operation mode

To get the regulator device:
- regulator_get() - by name only
- regulator_i2c_get() - by i2c bus address (of pmic parent)
- regulator_spi_get() - by spi bus address (of pmic parent)

An optional and useful regulator framework features are two descriptors:
- struct regulator_desc - describes the regulator name and output value limits should be defined by device driver for each regulator output.

- struct regulator_mode_desc - (array) describes a number of operation modes supported by each regulator output.

The regulator framework features are described in file:
- include/power/regulator.h

Main files:
- drivers/power/regulator-uclass.c - provides regulator common functions api
- include/power/regulator.h - define all structures required by the regulator

Changes:
- new uclass-id: UCLASS_PMIC_REGULATOR
- new config: CONFIG_DM_REGULATOR

---
Changes V2:
- new operations for regulator uclass:
-- get/set output state - for output on/off setting --- add enum: REGULATOR_OFF, REGULATOR_ON

- regulator uclass code rework and cleanup:
-- change name of:
--- enum 'regulator_desc_type' to 'regulator_type' --- add type DVS
--- struct 'regulator_desc' to 'regulator_value_desc'

-- regulator ops function calls:
--- remove 'ldo/buck' from naming
--- add new argument 'type' for define regulator type

-- regulator.h - update comments

---
 drivers/power/Makefile           |   1 +
 drivers/power/regulator-uclass.c | 227 ++++++++++++++++++++++++++++
 include/dm/uclass-id.h           |   1 +
 include/power/regulator.h        | 310 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 539 insertions(+)
 create mode 100644 drivers/power/regulator-uclass.c
 create mode 100644 include/power/regulator.h
drivers/power/Makefile +1 -0
index 5c9a189..a6b7012 100644
@@ -22,3 +22,4 @@ obj-$(CONFIG_POWER_FSL) += power_fsl.o
 obj-$(CONFIG_POWER_I2C) += power_i2c.o
 obj-$(CONFIG_POWER_SPI) += power_spi.o
 obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
drivers/power/regulator-uclass.c new file +227 -0

227 lines not on this page · raw message

include/dm/uclass-id.h +1 -0
index ff360ac..0a1e664 100644
@@ -37,6 +37,7 @@ enum uclass_id {
 
 	/* PMIC uclass and PMIC related uclasses */
 	UCLASS_PMIC,
+	UCLASS_PMIC_REGULATOR,
 
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
include/power/regulator.h new file +310 -0

310 lines not on this page · raw message

Signature · 1 line
1.9.1
#68 lore raw

PATCH v2 4/12 dm: pmic: add implementation of driver model regulator uclass

Hi Przemyslaw,

Quoted 11 lines · Przemyslaw Marczak

This is the implementation of driver model regulator uclass api. To use it, the CONFIG_DM_PMIC is required with driver implementation, since it provides pmic devices basic I/O API.

The regulator framework is based on a 'struct dm_regulator_ops'. It provides a common function calls, for it's basic features:
- regulator_get_cnt() - number of outputs each type
- regulator_get_value_desc() - describe output value limits

- regulator_get_mode_desc()  - describe output operation modes
- regulator_get/set_value()  - output value (uV)
- regulator_get/set_state()  - output on/off state

Would get/set_enable() be better?

Quoted 6 lines

- regulator_get/set_mode() - output operation mode

To get the regulator device:
- regulator_get() - by name only
- regulator_i2c_get() - by i2c bus address (of pmic parent)
- regulator_spi_get() - by spi bus address (of pmic parent)

For the last two, why are we using bus address? This should be by a phandle reference or nane.

Quoted 78 lines

An optional and useful regulator framework features are two descriptors:
- struct regulator_desc - describes the regulator name and output value limits should be defined by device driver for each regulator output.

- struct regulator_mode_desc - (array) describes a number of operation modes supported by each regulator output.

The regulator framework features are described in file:
- include/power/regulator.h

Main files:
- drivers/power/regulator-uclass.c - provides regulator common functions api
- include/power/regulator.h - define all structures required by the regulator

Changes:
- new uclass-id: UCLASS_PMIC_REGULATOR
- new config: CONFIG_DM_REGULATOR

---
Changes V2:
- new operations for regulator uclass:
-- get/set output state - for output on/off setting --- add enum: REGULATOR_OFF, REGULATOR_ON

- regulator uclass code rework and cleanup:
-- change name of:
--- enum 'regulator_desc_type' to 'regulator_type' --- add type DVS
--- struct 'regulator_desc' to 'regulator_value_desc'

-- regulator ops function calls:
--- remove 'ldo/buck' from naming
--- add new argument 'type' for define regulator type

-- regulator.h - update comments

---
 drivers/power/Makefile           |   1 +
 drivers/power/regulator-uclass.c | 227 ++++++++++++++++++++++++++++
 include/dm/uclass-id.h           |   1 +
 include/power/regulator.h        | 310 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 539 insertions(+)
 create mode 100644 drivers/power/regulator-uclass.c
 create mode 100644 include/power/regulator.h
drivers/power/Makefile +1 -0
index 5c9a189..a6b7012 100644
@@ -22,3 +22,4 @@ obj-$(CONFIG_POWER_FSL) += power_fsl.o
 obj-$(CONFIG_POWER_I2C) += power_i2c.o
 obj-$(CONFIG_POWER_SPI) += power_spi.o
 obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
drivers/power/regulator-uclass.c new file +17 -0
new file mode 100644
index 0000000..6b5c678
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <compiler.h>
+#include <dm/device.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <errno.h>

drivers/power/regulator-uclass.c line 27

Quoted 10 lines
drivers/power/regulator-uclass.c new file +10 -0
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int regulator_get_cnt(struct udevice *dev, int type, int *cnt)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;

This is an error so you should be able to just assert(). Failing that let's use -ENXIO as you do below.

drivers/power/regulator-uclass.c line 30

Quoted 3 lines
drivers/power/regulator-uclass.c new file +3 -0
+
+       if (!ops->get_cnt)
+               return -EPERM;

-ENOSYS for these.

Quoted 229 lines
drivers/power/regulator-uclass.c new file +197 -0
+
+       return ops->get_cnt(dev, type, cnt);
+}
+
+int regulator_get_value_desc(struct udevice *dev, int type, int number,
+                            struct regulator_value_desc **desc)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENXIO;
+
+       if (!ops->get_value_desc)
+               return -EPERM;
+
+       return ops->get_value_desc(dev, type, number, desc);
+}
+
+int regulator_get_mode_desc(struct udevice *dev, int type, int number,
+                           int *mode_cnt, struct regulator_mode_desc **desc)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENXIO;
+
+       if (!ops->get_mode_desc_array)
+               return -EPERM;
+
+       return ops->get_mode_desc_array(dev, type, number, mode_cnt, desc);
+}
+
+int regulator_get_value(struct udevice *dev, int type, int number, int *value)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->get_value)
+               return -EPERM;
+
+       return ops->get_value(dev, type, number, value);
+}
+
+int regulator_set_value(struct udevice *dev, int type, int number, int value)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->set_value)
+               return -EPERM;
+
+       return ops->set_value(dev, type, number, value);
+}
+
+int regulator_get_state(struct udevice *dev, int type, int number, int *state)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->get_state)
+               return -EPERM;
+
+       return ops->get_state(dev, type, number, state);
+}
+
+int regulator_set_state(struct udevice *dev, int type, int number, int state)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->set_state)
+               return -EPERM;
+
+       return ops->set_state(dev, type, number, state);
+}
+
+int regulator_get_mode(struct udevice *dev, int type, int number, int *mode)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->get_mode)
+               return -EPERM;
+
+       return ops->get_mode(dev, type, number, mode);
+}
+
+int regulator_set_mode(struct udevice *dev, int type, int number, int mode)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->set_mode)
+               return -EPERM;
+
+       return ops->set_mode(dev, type, number, mode);
+}
+
+int regulator_get(char *name, struct udevice **regulator)
+{
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       ret = uclass_get(UCLASS_PMIC_REGULATOR, &uc);
+       if (ret) {
+               error("Regulator uclass not initialized!");
+               return ret;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev)
+                       continue;
+
+               if (!strncmp(name, dev->name, strlen(name))) {
+                       ret = device_probe(dev);
+                       if (ret)
+                               error("Dev: %s probe failed", dev->name);
+                       *regulator = dev;
+                       return ret;
+               }
+       }
+
+       *regulator = NULL;
+       return -EINVAL;
+}
+
+int regulator_i2c_get(int bus, int addr, struct udevice **regulator)
+{
+       struct udevice *pmic;
+       int ret;
+
+       /* First get parent pmic device */
+       ret = pmic_i2c_get(bus, addr, &pmic);
+       if (ret) {
+               error("Chip: %d on bus %d not found!", addr, bus);
+               return ret;
+       }
+
+       /* Get the first regulator child of pmic device */
+       if (device_get_first_child_by_uclass_id(pmic, UCLASS_PMIC_REGULATOR,
+                                               regulator)) {
+               error("PMIC: %s regulator child not found!", pmic->name);
+               *regulator = NULL;
+               return ret;
+       }
+
+       return 0;
+}
+
+int regulator_spi_get(int bus, int cs, struct udevice **regulator)
+{
+       struct udevice *pmic;
+       int ret;
+
+       /* First get parent pmic device */
+       ret = pmic_spi_get(bus, cs, &pmic);
+       if (ret) {
+               error("Chip: %d on bus %d not found!", cs, bus);
+               return ret;
+       }
+
+       /* Get the first regulator child of pmic device */
+       if (device_get_first_child_by_uclass_id(pmic, UCLASS_PMIC_REGULATOR,
+                                               regulator)) {
+               error("PMIC: %s regulator child not found!", pmic->name);
+               *regulator = NULL;
+               return ret;
+       }
+
+       return 0;
+}
+
+UCLASS_DRIVER(regulator) = {
+       .id             = UCLASS_PMIC_REGULATOR,
+       .name           = "regulator",
+};
include/dm/uclass-id.h +1 -0
index ff360ac..0a1e664 100644
@@ -37,6 +37,7 @@ enum uclass_id {
 
        /* PMIC uclass and PMIC related uclasses */
        UCLASS_PMIC,
+       UCLASS_PMIC_REGULATOR,
 
        UCLASS_COUNT,
        UCLASS_INVALID = -1,
include/power/regulator.h new file +14 -0
new file mode 100644
index 0000000..ee8a280
@@ -0,0 +1,310 @@
+/*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _INCLUDE_REGULATOR_H_
+#define _INCLUDE_REGULATOR_H_
+
+#define DESC_NAME_LEN  20
+
+/* enum regulator_type - used for regulator_*() variant calls */
+enum regulator_type {

include/power/regulator.h line 24

Quoted 10 lines
include/power/regulator.h new file +10 -0
+       REGULATOR_TYPE_LDO = 0,
+       REGULATOR_TYPE_BUCK,
+       REGULATOR_TYPE_DVS,
+};
+
+/* enum regulator_out_state - used for ldo/buck output state setting */
+enum regulator_out_state {
+       REGULATOR_OFF = 0,
+       REGULATOR_ON,
+};

If we will only ever have on/off perhaps we don't need this enum and could just use a bool?

include/power/regulator.h line 33

Quoted 9 lines
include/power/regulator.h new file +9 -0
+
+/**
+ * struct regulator_value_desc - this structure holds an information about
+ * each regulator voltage limits. There is no "step" voltage value - so driver
+ * should take care of this. This is rather platform specific so can be
+ * taken from the device-tree.
+ *
+ * @type   - one of: DESC_TYPE_LDO, DESC_TYPE_BUCK or  DESC_TYPE_DVS
+ * @number - hardware internal regulator number

What is this numbering and where does it come from? It is specified by the PMIC datasheet? Can you expand the comment a bit?

Actually I think this numbering should go away and we should use names instead.

include/power/regulator.h line 44

Quoted 11 lines
include/power/regulator.h new file +11 -0
+ * @min_uV - minimum voltage (micro Volts)
+ * @max_uV - maximum voltage (micro Volts)
+ * @name   - name of regulator - usually will be taken from device tree and
+ *           can describe regulator output connected hardware, e.g. "eMMC"
+*/
+struct regulator_value_desc {
+       int type;
+       int number;
+       int min_uV;
+       int max_uV;
+       char name[DESC_NAME_LEN];

What do you think about making this a const char * and we alloc it with strdup()?

include/power/regulator.h line 50

Quoted 6 lines
include/power/regulator.h new file +6 -0
+};
+
+/**
+ * struct regulator_mode_desc - this structure holds an information about
+ * each regulator operation modes. Probably in most cases - an array.
+ * This will be probably a driver static data since it's device specific.

driver-static (I think you mean)

include/power/regulator.h line 53

Quoted 3 lines
include/power/regulator.h new file +3 -0
+ *
+ * @mode           - a driver specific mode number - used for regulator command
+ * @register_value - a driver specific value for this mode

driver-specific, you are creating an adjective

Quoted 10 lines
include/power/regulator.h new file +10 -0
+ * @name           - the name of mode - used for regulator command
+ */
+struct regulator_mode_desc {
+       int mode;
+       int register_value;
+       char *name;
+};
+
+/* PMIC regulator device operations */
+struct dm_regulator_ops {

include/power/regulator.h line 73

Quoted 10 lines
include/power/regulator.h new file +10 -0
+       /**
+        * get_cnt - get the number of a given regulator 'type' outputs
+        *
+        * @dev    - regulator device
+        * @type   - regulator type, one of enum regulator_type
+        * Gets:
+        * @cnt    - regulator count
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_cnt)(struct udevice *dev, int type, int *cnt);

get_count

Return count in return value instead of a parameter.

Quoted 7 lines
include/power/regulator.h new file +7 -0
+
+       /**
+        * Regulator output value descriptor is specific for each output.
+        * It's usually driver static data, but output value limits are
+        * often defined in the device-tree, so those are platform dependent
+        * attributes.
+        *

include/power/regulator.h line 90

Quoted 10 lines
include/power/regulator.h new file +10 -0
+        * get_value_desc - get value descriptor of the given regulator output
+        * @dev           - regulator device
+        * @type          - regulator type, one of enum regulator_type
+        * @number        - regulator number
+        * Gets:
+        * @desc          - pointer to the descriptor
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_value_desc)(struct udevice *dev, int type, int number,
+                             struct regulator_value_desc **desc);

You could use descp, to indicate it returns a pointer.

include/power/regulator.h line 103

Quoted 13 lines
include/power/regulator.h new file +13 -0
+
+       /**
+        * Regulator output mode descriptors are device specific.
+        * It's usually driver static data.
+        * Each regulator output can support different mode types,
+        * so usually will return an array with a number 'mode_desc_cnt'
+        * of mode descriptors.
+        *
+        * get_mode_desc_array - get mode descriptor array of the given
+        *                       regulator output number
+        * @dev                - regulator device
+        * @type               - regulator type, one of 'enum regulator_type'
+        * @number             - regulator number

regulator_num would be better

include/power/regulator.h line 109

Quoted 6 lines
include/power/regulator.h new file +6 -0
+        * Gets:
+        * @mode_desc_cnt      - descriptor array entry count
+        * @desc               - pointer to the descriptor array
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_mode_desc_array)(struct udevice *dev, int type,

So should type be an enum?

Quoted 6 lines
include/power/regulator.h new file +6 -0
+                                  int number, int *mode_desc_cnt,
+                                  struct regulator_mode_desc **desc);
+
+       /**
+        * The regulator output value function calls operates on a micro Volts,
+        * however the regulator commands operates on a mili Volt unit.

include/power/regulator.h line 125

Quoted 10 lines
include/power/regulator.h new file +10 -0
+        *
+        * get/set_value - get/set output value of the given output number
+        * @dev          - regulator device
+        * @type         - regulator type, one of 'enum regulator_type'
+        * @number       - regulator number
+        * Sets/Gets:
+        * @value        - set or get output value
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_value)(struct udevice *dev, int type, int number, int *value);

Return value in return value instead of a parameter.

include/power/regulator.h line 129

Quoted 4 lines
include/power/regulator.h new file +4 -0
+       int (*set_value)(struct udevice *dev, int type, int number, int value);
+
+       /**
+        * The most basic feature of the regulator output is it's on/off state.

its

include/power/regulator.h line 139

Quoted 10 lines
include/power/regulator.h new file +10 -0
+        *
+        * get/set_state - get/set on/off state of the given output number
+        * @dev          - regulator device
+        * @type         - regulator type, one of 'enum regulator_type'
+        * @number       - regulator number
+        * Sets/Gets:
+        * @state        - set or get one of 'enum regulator_out_state'
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_state)(struct udevice *dev, int type, int number, int *state);

Return state in return value instead of a parameter.

Quoted 6 lines
include/power/regulator.h new file +6 -0
+       int (*set_state)(struct udevice *dev, int type, int number, int state);
+
+       /**
+        * The 'get/set_mode()' function calls should operate on a driver
+        * specific mode definitions, which should be found in:
+        * field 'mode' of struct regulator_mode_desc.

include/power/regulator.h line 155

Quoted 10 lines
include/power/regulator.h new file +10 -0
+        *
+        * get/set_mode - get/set operation mode of the given output number
+        * @dev          - regulator device
+        * @type         - regulator type, one of 'enum regulator_type'
+        * @number       - regulator number
+        * Sets/Gets:
+        * @mode         - set or get output mode
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_mode)(struct udevice *dev, int type, int number, int *mode);

Return mode in return value.

include/power/regulator.h line 160

Quoted 5 lines
include/power/regulator.h new file +5 -0
+       int (*set_mode)(struct udevice *dev, int type, int number, int mode);
+};
+
+/**
+ * regulator_get_cnt: returns the number of driver registered regulators.

driver-registered

include/power/regulator.h line 169

Quoted 9 lines
include/power/regulator.h new file +9 -0
+ * This is used for pmic regulator command purposes.
+ *
+ * @dev     - pointer to the regulator device
+ * @type    - regulator type: device specific
+ * Gets:
+ * @cnt     - regulator count
+ * Returns: 0 on success or negative value of errno.
+ */
+int regulator_get_cnt(struct udevice *dev, int type, int *cnt);

regulator_get_count. Return count in return value instead of a parameter.

Quoted 30 lines
include/power/regulator.h new file +30 -0
+
+/**
+ * regulator_get_value_desc: returns a pointer to the regulator value
+ * descriptor of a given device regulator output number.
+ *
+ * @dev      - pointer to the regulator device
+ * @type     - regulator descriptor type
+ * @number   - descriptor number (regulator output number)
+ * Gets:
+ * @desc     - pointer to the descriptor
+ * Returns: 0 on success or negative value of errno.
+ */
+int regulator_get_value_desc(struct udevice *dev, int type, int number,
+                            struct regulator_value_desc **desc);
+
+/**
+ * regulator_get_mode_desc: returns a pointer to the array of regulator mode
+ * descriptors of a given device regulator type and number.
+ *
+ * @dev        - pointer to the regulator device
+ * @type       - regulator type: device specific
+ * @number     - output number: device specific
+ * Gets:
+ * @mode_cnt   - descriptor array entry count
+ * @desc       - pointer to the descriptor array
+ * Returns: 0 on success or negative value of errno.
+ */
+int regulator_get_mode_desc(struct udevice *dev, int type, int number,
+                           int *mode_cnt, struct regulator_mode_desc **desc);
+

include/power/regulator.h line 209

Quoted 10 lines
include/power/regulator.h new file +10 -0
+/**
+ * regulator_get_value: returns voltage value of a given device
+ * regulator type  number.
+ *
+ * @dev        - pointer to the regulator device
+ * @type       - regulator type: device specific
+ * @number     - output number: device specific
+ * Gets:
+ * @val        - returned voltage - unit: uV (micro Volts)
+ * Returns: 0 on success or negative value of errno.

You may as well return the voltage in the return value, unless it can be -ve (I assume not?)

Quoted 19 lines
include/power/regulator.h new file +19 -0
+ */
+int regulator_get_value(struct udevice *dev, int type, int number, int *val);
+
+/**
+ * regulator_set_value: set the voltage value of a given device regulator type
+ * number to the given one passed by the argument: @val
+ *
+ * @dev    - pointer to the regulator device
+ * @type   - regulator type: device specific
+ * @number - output number: device specific
+ * @val    - voltage value to set - unit: uV (micro Volts)
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_value(struct udevice *dev, int type, int number, int val);
+
+/**
+ * regulator_get_state: returns output state of a given device
+ * regulator type number.
+ *

include/power/regulator.h line 238

Quoted 10 lines
include/power/regulator.h new file +10 -0
+ * @dev        - pointer to the regulator device
+ * @type       - regulator type, device specific
+ * @number     - regulator number, device specific
+ * Gets:
+ * @state      - returned regulator number output state:
+ *               - REGULATOR_OFF (0) - disable
+ *               - REGULATOR_ON  (1) - enable
+ * Returns:    - 0 on success or -errno val if fails
+ */
+int regulator_get_state(struct udevice *dev, int type, int number, int *state);

Return state in return value instead of a parameter.

Quoted 20 lines
include/power/regulator.h new file +20 -0
+
+/**
+ * regulator_set_state: set output state of a given device regulator type
+ * number to the given one passed by the argument: @state
+ *
+ * @dev            - pointer to the regulator device
+ * @type           - regulator type: device specific
+ * @number         - output number: device specific
+ * @state          - output state to set
+ *                   - enum REGULATOR_OFF == 0 - disable
+ *                   - enum REGULATOR_ON  == 1 - enable
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_state(struct udevice *dev, int type, int number, int state);
+
+/**
+ * regulator_get_mode: get mode number of a given device regulator type number
+ *
+ * @dev    - pointer to the regulator device
+ * @type   - output number: device specific

include/power/regulator.h line 268

Quoted 10 lines
include/power/regulator.h new file +10 -0
+ * @number - output number: device specific
+ * Gets:
+ * @mode   - returned mode number
+ * Returns - 0 on success or -errno val if fails
+ * Note:
+ * The regulator driver should return one of defined, mode number rather
+ * than the raw register value. The struct type 'regulator_mode_desc' has
+ * a mode field for this purpose and register_value for a raw register value.
+ */
+int regulator_get_mode(struct udevice *dev, int type, int number, int *mode);

Return mode in return value instead of a parameter.

Quoted 30 lines
include/power/regulator.h new file +30 -0
+
+/**
+ * regulator_set_mode: set given regulator type and number mode to the given
+ * one passed by the argument: @mode
+ *
+ * @dev    - pointer to the regulator device
+ * @type   - output number: device specific
+ * @number - output number: device specific
+ * @mode   - mode type (struct regulator_mode_desc.mode)
+ * Returns - 0 on success or -errno value if fails
+ * Note:
+ * The regulator driver should take one of defined, mode number rather
+ * than a raw register value. The struct type 'regulator_mode_desc' has
+ * a mode field for this purpose and register_value for a raw register value.
+ */
+int regulator_set_mode(struct udevice *dev, int type, int number, int mode);
+
+/**
+ * regulator_..._get: returns the pointer to the pmic regulator device
+ * by specified arguments:
+ *
+ * NAME:
+ *  @name    - device name (useful for specific names)
+ * or SPI/I2C interface:
+ *  @bus     - device I2C/SPI bus number
+ *  @addr_cs - device specific address for I2C or chip select for SPI,
+ *             on the given bus number
+ * Gets:
+ * @regulator - pointer to the pmic device
+ * Returns: 0 on success or negative value of errno.

include/power/regulator.h line 308

Quoted 10 lines
include/power/regulator.h new file +10 -0
+ *
+ * The returned 'regulator' device can be used with:
+ * - regulator_get/set_*
+ * and if pmic_platdata is given, then also with:
+ * - pmic_if_* calls
+ * The last one is used for the pmic command purposes.
+ */
+int regulator_get(char *name, struct udevice **regulator);
+int regulator_i2c_get(int bus, int addr, struct udevice **regulator);
+int regulator_spi_get(int bus, int cs, struct udevice **regulator);

Hoping you don't need these last two!

Quoted 5 lines
include/power/regulator.h new file +2 -1
+
+#endif /* _INCLUDE_REGULATOR_H_ */
--

1.9.1

Regards,
Simon

#69 lore raw

PATCH v2 4/12 dm: pmic: add implementation of driver model regulator uclass

Hello Simon,

Quoted 119 lines · Simon Glass

Hi Przemyslaw,

Quoted 11 lines · Przemyslaw Marczak

This is the implementation of driver model regulator uclass api. To use it, the CONFIG_DM_PMIC is required with driver implementation, since it provides pmic devices basic I/O API.

The regulator framework is based on a 'struct dm_regulator_ops'. It provides a common function calls, for it's basic features:
- regulator_get_cnt() - number of outputs each type
- regulator_get_value_desc() - describe output value limits

- regulator_get_mode_desc()  - describe output operation modes
- regulator_get/set_value()  - output value (uV)
- regulator_get/set_state()  - output on/off state

Would get/set_enable() be better?

Quoted 6 lines

- regulator_get/set_mode() - output operation mode

To get the regulator device:
- regulator_get() - by name only
- regulator_i2c_get() - by i2c bus address (of pmic parent)
- regulator_spi_get() - by spi bus address (of pmic parent)

For the last two, why are we using bus address? This should be by a phandle reference or nane.

Quoted 78 lines

An optional and useful regulator framework features are two descriptors:
- struct regulator_desc - describes the regulator name and output value limits should be defined by device driver for each regulator output.

- struct regulator_mode_desc - (array) describes a number of operation modes supported by each regulator output.

The regulator framework features are described in file:
- include/power/regulator.h

Main files:
- drivers/power/regulator-uclass.c - provides regulator common functions api
- include/power/regulator.h - define all structures required by the regulator

Changes:
- new uclass-id: UCLASS_PMIC_REGULATOR
- new config: CONFIG_DM_REGULATOR

---
Changes V2:
- new operations for regulator uclass:
-- get/set output state - for output on/off setting --- add enum: REGULATOR_OFF, REGULATOR_ON

- regulator uclass code rework and cleanup:
-- change name of:
--- enum 'regulator_desc_type' to 'regulator_type' --- add type DVS
--- struct 'regulator_desc' to 'regulator_value_desc'

-- regulator ops function calls:
--- remove 'ldo/buck' from naming
--- add new argument 'type' for define regulator type

-- regulator.h - update comments

---
  drivers/power/Makefile           |   1 +
  drivers/power/regulator-uclass.c | 227 ++++++++++++++++++++++++++++
  include/dm/uclass-id.h           |   1 +
  include/power/regulator.h        | 310 +++++++++++++++++++++++++++++++++++++++
  4 files changed, 539 insertions(+)

create mode 100644 drivers/power/regulator-uclass.c create mode 100644 include/power/regulator.h

drivers/power/Makefile +1 -0
index 5c9a189..a6b7012 100644
@@ -22,3 +22,4 @@ obj-$(CONFIG_POWER_FSL) += power_fsl.o
  obj-$(CONFIG_POWER_I2C) += power_i2c.o
  obj-$(CONFIG_POWER_SPI) += power_spi.o
  obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
drivers/power/regulator-uclass.c new file +17 -0
new file mode 100644
index 0000000..6b5c678
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <compiler.h>
+#include <dm/device.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <errno.h>

drivers/power/regulator-uclass.c line 27

Quoted 10 lines
drivers/power/regulator-uclass.c new file +10 -0
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int regulator_get_cnt(struct udevice *dev, int type, int *cnt)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;

This is an error so you should be able to just assert(). Failing that let's use -ENXIO as you do below.

Quoted 6 lines

drivers/power/regulator-uclass.c line 30

Quoted 3 lines
drivers/power/regulator-uclass.c new file +3 -0
+
+       if (!ops->get_cnt)
+               return -EPERM;

-ENOSYS for these.

Ah, I missed this one, will fix in V4.

Quoted 234 lines
Quoted 234 lines
drivers/power/regulator-uclass.c new file +197 -0
+
+       return ops->get_cnt(dev, type, cnt);
+}
+
+int regulator_get_value_desc(struct udevice *dev, int type, int number,
+                            struct regulator_value_desc **desc)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENXIO;
+
+       if (!ops->get_value_desc)
+               return -EPERM;
+
+       return ops->get_value_desc(dev, type, number, desc);
+}
+
+int regulator_get_mode_desc(struct udevice *dev, int type, int number,
+                           int *mode_cnt, struct regulator_mode_desc **desc)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENXIO;
+
+       if (!ops->get_mode_desc_array)
+               return -EPERM;
+
+       return ops->get_mode_desc_array(dev, type, number, mode_cnt, desc);
+}
+
+int regulator_get_value(struct udevice *dev, int type, int number, int *value)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->get_value)
+               return -EPERM;
+
+       return ops->get_value(dev, type, number, value);
+}
+
+int regulator_set_value(struct udevice *dev, int type, int number, int value)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->set_value)
+               return -EPERM;
+
+       return ops->set_value(dev, type, number, value);
+}
+
+int regulator_get_state(struct udevice *dev, int type, int number, int *state)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->get_state)
+               return -EPERM;
+
+       return ops->get_state(dev, type, number, state);
+}
+
+int regulator_set_state(struct udevice *dev, int type, int number, int state)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->set_state)
+               return -EPERM;
+
+       return ops->set_state(dev, type, number, state);
+}
+
+int regulator_get_mode(struct udevice *dev, int type, int number, int *mode)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->get_mode)
+               return -EPERM;
+
+       return ops->get_mode(dev, type, number, mode);
+}
+
+int regulator_set_mode(struct udevice *dev, int type, int number, int mode)
+{
+       const struct dm_regulator_ops *ops;
+
+       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+       if (!ops)
+               return -ENODEV;
+
+       if (!ops->set_mode)
+               return -EPERM;
+
+       return ops->set_mode(dev, type, number, mode);
+}
+
+int regulator_get(char *name, struct udevice **regulator)
+{
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+
+       ret = uclass_get(UCLASS_PMIC_REGULATOR, &uc);
+       if (ret) {
+               error("Regulator uclass not initialized!");
+               return ret;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (!dev)
+                       continue;
+
+               if (!strncmp(name, dev->name, strlen(name))) {
+                       ret = device_probe(dev);
+                       if (ret)
+                               error("Dev: %s probe failed", dev->name);
+                       *regulator = dev;
+                       return ret;
+               }
+       }
+
+       *regulator = NULL;
+       return -EINVAL;
+}
+
+int regulator_i2c_get(int bus, int addr, struct udevice **regulator)
+{
+       struct udevice *pmic;
+       int ret;
+
+       /* First get parent pmic device */
+       ret = pmic_i2c_get(bus, addr, &pmic);
+       if (ret) {
+               error("Chip: %d on bus %d not found!", addr, bus);
+               return ret;
+       }
+
+       /* Get the first regulator child of pmic device */
+       if (device_get_first_child_by_uclass_id(pmic, UCLASS_PMIC_REGULATOR,
+                                               regulator)) {
+               error("PMIC: %s regulator child not found!", pmic->name);
+               *regulator = NULL;
+               return ret;
+       }
+
+       return 0;
+}
+
+int regulator_spi_get(int bus, int cs, struct udevice **regulator)
+{
+       struct udevice *pmic;
+       int ret;
+
+       /* First get parent pmic device */
+       ret = pmic_spi_get(bus, cs, &pmic);
+       if (ret) {
+               error("Chip: %d on bus %d not found!", cs, bus);
+               return ret;
+       }
+
+       /* Get the first regulator child of pmic device */
+       if (device_get_first_child_by_uclass_id(pmic, UCLASS_PMIC_REGULATOR,
+                                               regulator)) {
+               error("PMIC: %s regulator child not found!", pmic->name);
+               *regulator = NULL;
+               return ret;
+       }
+
+       return 0;
+}
+
+UCLASS_DRIVER(regulator) = {
+       .id             = UCLASS_PMIC_REGULATOR,
+       .name           = "regulator",
+};
include/dm/uclass-id.h +1 -0
index ff360ac..0a1e664 100644
@@ -37,6 +37,7 @@ enum uclass_id {
 
         /* PMIC uclass and PMIC related uclasses */
         UCLASS_PMIC,
+       UCLASS_PMIC_REGULATOR,
 
         UCLASS_COUNT,
         UCLASS_INVALID = -1,
include/power/regulator.h new file +19 -0
new file mode 100644
index 0000000..ee8a280
@@ -0,0 +1,310 @@
+/*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _INCLUDE_REGULATOR_H_
+#define _INCLUDE_REGULATOR_H_
+
+#define DESC_NAME_LEN  20
+
+/* enum regulator_type - used for regulator_*() variant calls */
+enum regulator_type {
+       REGULATOR_TYPE_LDO = 0,
+       REGULATOR_TYPE_BUCK,
+       REGULATOR_TYPE_DVS,
+};
+
Quoted 9 lines

include/power/regulator.h line 24

Quoted 5 lines
include/power/regulator.h new file +5 -0
+/* enum regulator_out_state - used for ldo/buck output state setting */
+enum regulator_out_state {
+       REGULATOR_OFF = 0,
+       REGULATOR_ON,
+};

If we will only ever have on/off perhaps we don't need this enum and could just use a bool?

Fixed in V3.

Quoted 13 lines

include/power/regulator.h line 33

Quoted 9 lines
include/power/regulator.h new file +9 -0
+
+/**
+ * struct regulator_value_desc - this structure holds an information about
+ * each regulator voltage limits. There is no "step" voltage value - so driver
+ * should take care of this. This is rather platform specific so can be
+ * taken from the device-tree.
+ *
+ * @type   - one of: DESC_TYPE_LDO, DESC_TYPE_BUCK or  DESC_TYPE_DVS
+ * @number - hardware internal regulator number

What is this numbering and where does it come from? It is specified by the PMIC datasheet? Can you expand the comment a bit?

Quoted 2 lines

Actually I think this numbering should go away and we should use names instead.

This suggestion is implemented in V3.

Quoted 6 lines
Quoted 6 lines
include/power/regulator.h new file +6 -0
+ * @min_uV - minimum voltage (micro Volts)
+ * @max_uV - maximum voltage (micro Volts)
+ * @name   - name of regulator - usually will be taken from device tree and
+ *           can describe regulator output connected hardware, e.g. "eMMC"
+*/
+struct regulator_value_desc {
Quoted 9 lines

include/power/regulator.h line 44

Quoted 5 lines
include/power/regulator.h new file +5 -0
+       int type;
+       int number;
+       int min_uV;
+       int max_uV;
+       char name[DESC_NAME_LEN];

What do you think about making this a const char * and we alloc it with strdup()?

Also implemented in V3.

Quoted 315 lines

include/power/regulator.h line 50

Quoted 6 lines
include/power/regulator.h new file +6 -0
+};
+
+/**
+ * struct regulator_mode_desc - this structure holds an information about
+ * each regulator operation modes. Probably in most cases - an array.
+ * This will be probably a driver static data since it's device specific.

driver-static (I think you mean)

include/power/regulator.h line 53

Quoted 3 lines
include/power/regulator.h new file +3 -0
+ *
+ * @mode           - a driver specific mode number - used for regulator command
+ * @register_value - a driver specific value for this mode

driver-specific, you are creating an adjective

Quoted 10 lines
include/power/regulator.h new file +10 -0
+ * @name           - the name of mode - used for regulator command
+ */
+struct regulator_mode_desc {
+       int mode;
+       int register_value;
+       char *name;
+};
+
+/* PMIC regulator device operations */
+struct dm_regulator_ops {

include/power/regulator.h line 73

Quoted 10 lines
include/power/regulator.h new file +10 -0
+       /**
+        * get_cnt - get the number of a given regulator 'type' outputs
+        *
+        * @dev    - regulator device
+        * @type   - regulator type, one of enum regulator_type
+        * Gets:
+        * @cnt    - regulator count
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_cnt)(struct udevice *dev, int type, int *cnt);

get_count

Return count in return value instead of a parameter.

Quoted 7 lines
include/power/regulator.h new file +7 -0
+
+       /**
+        * Regulator output value descriptor is specific for each output.
+        * It's usually driver static data, but output value limits are
+        * often defined in the device-tree, so those are platform dependent
+        * attributes.
+        *

include/power/regulator.h line 90

Quoted 10 lines
include/power/regulator.h new file +10 -0
+        * get_value_desc - get value descriptor of the given regulator output
+        * @dev           - regulator device
+        * @type          - regulator type, one of enum regulator_type
+        * @number        - regulator number
+        * Gets:
+        * @desc          - pointer to the descriptor
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_value_desc)(struct udevice *dev, int type, int number,
+                             struct regulator_value_desc **desc);

You could use descp, to indicate it returns a pointer.

include/power/regulator.h line 103

Quoted 13 lines
include/power/regulator.h new file +13 -0
+
+       /**
+        * Regulator output mode descriptors are device specific.
+        * It's usually driver static data.
+        * Each regulator output can support different mode types,
+        * so usually will return an array with a number 'mode_desc_cnt'
+        * of mode descriptors.
+        *
+        * get_mode_desc_array - get mode descriptor array of the given
+        *                       regulator output number
+        * @dev                - regulator device
+        * @type               - regulator type, one of 'enum regulator_type'
+        * @number             - regulator number

regulator_num would be better

include/power/regulator.h line 109

Quoted 6 lines
include/power/regulator.h new file +6 -0
+        * Gets:
+        * @mode_desc_cnt      - descriptor array entry count
+        * @desc               - pointer to the descriptor array
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_mode_desc_array)(struct udevice *dev, int type,

So should type be an enum?

Quoted 6 lines
include/power/regulator.h new file +6 -0
+                                  int number, int *mode_desc_cnt,
+                                  struct regulator_mode_desc **desc);
+
+       /**
+        * The regulator output value function calls operates on a micro Volts,
+        * however the regulator commands operates on a mili Volt unit.

include/power/regulator.h line 125

Quoted 10 lines
include/power/regulator.h new file +10 -0
+        *
+        * get/set_value - get/set output value of the given output number
+        * @dev          - regulator device
+        * @type         - regulator type, one of 'enum regulator_type'
+        * @number       - regulator number
+        * Sets/Gets:
+        * @value        - set or get output value
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_value)(struct udevice *dev, int type, int number, int *value);

Return value in return value instead of a parameter.

include/power/regulator.h line 129

Quoted 4 lines
include/power/regulator.h new file +4 -0
+       int (*set_value)(struct udevice *dev, int type, int number, int value);
+
+       /**
+        * The most basic feature of the regulator output is it's on/off state.

its

include/power/regulator.h line 139

Quoted 10 lines
include/power/regulator.h new file +10 -0
+        *
+        * get/set_state - get/set on/off state of the given output number
+        * @dev          - regulator device
+        * @type         - regulator type, one of 'enum regulator_type'
+        * @number       - regulator number
+        * Sets/Gets:
+        * @state        - set or get one of 'enum regulator_out_state'
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_state)(struct udevice *dev, int type, int number, int *state);

Return state in return value instead of a parameter.

Quoted 6 lines
include/power/regulator.h new file +6 -0
+       int (*set_state)(struct udevice *dev, int type, int number, int state);
+
+       /**
+        * The 'get/set_mode()' function calls should operate on a driver
+        * specific mode definitions, which should be found in:
+        * field 'mode' of struct regulator_mode_desc.

include/power/regulator.h line 155

Quoted 10 lines
include/power/regulator.h new file +10 -0
+        *
+        * get/set_mode - get/set operation mode of the given output number
+        * @dev          - regulator device
+        * @type         - regulator type, one of 'enum regulator_type'
+        * @number       - regulator number
+        * Sets/Gets:
+        * @mode         - set or get output mode
+        * Returns: 0 on success or negative value of errno.
+        */
+       int (*get_mode)(struct udevice *dev, int type, int number, int *mode);

Return mode in return value.

include/power/regulator.h line 160

Quoted 5 lines
include/power/regulator.h new file +5 -0
+       int (*set_mode)(struct udevice *dev, int type, int number, int mode);
+};
+
+/**
+ * regulator_get_cnt: returns the number of driver registered regulators.

driver-registered

include/power/regulator.h line 169

Quoted 9 lines
include/power/regulator.h new file +9 -0
+ * This is used for pmic regulator command purposes.
+ *
+ * @dev     - pointer to the regulator device
+ * @type    - regulator type: device specific
+ * Gets:
+ * @cnt     - regulator count
+ * Returns: 0 on success or negative value of errno.
+ */
+int regulator_get_cnt(struct udevice *dev, int type, int *cnt);

regulator_get_count. Return count in return value instead of a parameter.

Quoted 30 lines
include/power/regulator.h new file +30 -0
+
+/**
+ * regulator_get_value_desc: returns a pointer to the regulator value
+ * descriptor of a given device regulator output number.
+ *
+ * @dev      - pointer to the regulator device
+ * @type     - regulator descriptor type
+ * @number   - descriptor number (regulator output number)
+ * Gets:
+ * @desc     - pointer to the descriptor
+ * Returns: 0 on success or negative value of errno.
+ */
+int regulator_get_value_desc(struct udevice *dev, int type, int number,
+                            struct regulator_value_desc **desc);
+
+/**
+ * regulator_get_mode_desc: returns a pointer to the array of regulator mode
+ * descriptors of a given device regulator type and number.
+ *
+ * @dev        - pointer to the regulator device
+ * @type       - regulator type: device specific
+ * @number     - output number: device specific
+ * Gets:
+ * @mode_cnt   - descriptor array entry count
+ * @desc       - pointer to the descriptor array
+ * Returns: 0 on success or negative value of errno.
+ */
+int regulator_get_mode_desc(struct udevice *dev, int type, int number,
+                           int *mode_cnt, struct regulator_mode_desc **desc);
+

include/power/regulator.h line 209

Quoted 10 lines
include/power/regulator.h new file +10 -0
+/**
+ * regulator_get_value: returns voltage value of a given device
+ * regulator type  number.
+ *
+ * @dev        - pointer to the regulator device
+ * @type       - regulator type: device specific
+ * @number     - output number: device specific
+ * Gets:
+ * @val        - returned voltage - unit: uV (micro Volts)
+ * Returns: 0 on success or negative value of errno.

You may as well return the voltage in the return value, unless it can be -ve (I assume not?)

Quoted 19 lines
include/power/regulator.h new file +19 -0
+ */
+int regulator_get_value(struct udevice *dev, int type, int number, int *val);
+
+/**
+ * regulator_set_value: set the voltage value of a given device regulator type
+ * number to the given one passed by the argument: @val
+ *
+ * @dev    - pointer to the regulator device
+ * @type   - regulator type: device specific
+ * @number - output number: device specific
+ * @val    - voltage value to set - unit: uV (micro Volts)
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_value(struct udevice *dev, int type, int number, int val);
+
+/**
+ * regulator_get_state: returns output state of a given device
+ * regulator type number.
+ *

include/power/regulator.h line 238

Quoted 10 lines
include/power/regulator.h new file +10 -0
+ * @dev        - pointer to the regulator device
+ * @type       - regulator type, device specific
+ * @number     - regulator number, device specific
+ * Gets:
+ * @state      - returned regulator number output state:
+ *               - REGULATOR_OFF (0) - disable
+ *               - REGULATOR_ON  (1) - enable
+ * Returns:    - 0 on success or -errno val if fails
+ */
+int regulator_get_state(struct udevice *dev, int type, int number, int *state);

Return state in return value instead of a parameter.

Quoted 20 lines
include/power/regulator.h new file +20 -0
+
+/**
+ * regulator_set_state: set output state of a given device regulator type
+ * number to the given one passed by the argument: @state
+ *
+ * @dev            - pointer to the regulator device
+ * @type           - regulator type: device specific
+ * @number         - output number: device specific
+ * @state          - output state to set
+ *                   - enum REGULATOR_OFF == 0 - disable
+ *                   - enum REGULATOR_ON  == 1 - enable
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_state(struct udevice *dev, int type, int number, int state);
+
+/**
+ * regulator_get_mode: get mode number of a given device regulator type number
+ *
+ * @dev    - pointer to the regulator device
+ * @type   - output number: device specific

include/power/regulator.h line 268

Quoted 10 lines
include/power/regulator.h new file +10 -0
+ * @number - output number: device specific
+ * Gets:
+ * @mode   - returned mode number
+ * Returns - 0 on success or -errno val if fails
+ * Note:
+ * The regulator driver should return one of defined, mode number rather
+ * than the raw register value. The struct type 'regulator_mode_desc' has
+ * a mode field for this purpose and register_value for a raw register value.
+ */
+int regulator_get_mode(struct udevice *dev, int type, int number, int *mode);

Return mode in return value instead of a parameter.

Quoted 30 lines
include/power/regulator.h new file +30 -0
+
+/**
+ * regulator_set_mode: set given regulator type and number mode to the given
+ * one passed by the argument: @mode
+ *
+ * @dev    - pointer to the regulator device
+ * @type   - output number: device specific
+ * @number - output number: device specific
+ * @mode   - mode type (struct regulator_mode_desc.mode)
+ * Returns - 0 on success or -errno value if fails
+ * Note:
+ * The regulator driver should take one of defined, mode number rather
+ * than a raw register value. The struct type 'regulator_mode_desc' has
+ * a mode field for this purpose and register_value for a raw register value.
+ */
+int regulator_set_mode(struct udevice *dev, int type, int number, int mode);
+
+/**
+ * regulator_..._get: returns the pointer to the pmic regulator device
+ * by specified arguments:
+ *
+ * NAME:
+ *  @name    - device name (useful for specific names)
+ * or SPI/I2C interface:
+ *  @bus     - device I2C/SPI bus number
+ *  @addr_cs - device specific address for I2C or chip select for SPI,
+ *             on the given bus number
+ * Gets:
+ * @regulator - pointer to the pmic device
+ * Returns: 0 on success or negative value of errno.

include/power/regulator.h line 308

Quoted 10 lines
include/power/regulator.h new file +10 -0
+ *
+ * The returned 'regulator' device can be used with:
+ * - regulator_get/set_*
+ * and if pmic_platdata is given, then also with:
+ * - pmic_if_* calls
+ * The last one is used for the pmic command purposes.
+ */
+int regulator_get(char *name, struct udevice **regulator);
+int regulator_i2c_get(int bus, int addr, struct udevice **regulator);
+int regulator_spi_get(int bus, int cs, struct udevice **regulator);

Hoping you don't need these last two!

Quoted 9 lines
Quoted 5 lines
include/power/regulator.h new file +2 -1
+
+#endif /* _INCLUDE_REGULATOR_H_ */
--

1.9.1

Regards,
Simon

Thank you,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#70 lore raw

PATCH v2 4/12 dm: pmic: add implementation of driver model regulator uclass

In reply to Przemyslaw Marczak

Hi,

Quoted 237 lines · Przemyslaw Marczak

This is the implementation of driver model regulator uclass api. To use it, the CONFIG_DM_PMIC is required with driver implementation, since it provides pmic devices basic I/O API.

The regulator framework is based on a 'struct dm_regulator_ops'. It provides a common function calls, for it's basic features:
- regulator_get_cnt() - number of outputs each type
- regulator_get_value_desc() - describe output value limits

- regulator_get_mode_desc()  - describe output operation modes
- regulator_get/set_value()  - output value (uV)
- regulator_get/set_state()  - output on/off state
- regulator_get/set_mode()   - output operation mode

To get the regulator device:
- regulator_get() - by name only
- regulator_i2c_get() - by i2c bus address (of pmic parent)
- regulator_spi_get() - by spi bus address (of pmic parent)

An optional and useful regulator framework features are two descriptors:
- struct regulator_desc - describes the regulator name and output value limits should be defined by device driver for each regulator output.

- struct regulator_mode_desc - (array) describes a number of operation modes supported by each regulator output.

The regulator framework features are described in file:
- include/power/regulator.h

Main files:
- drivers/power/regulator-uclass.c - provides regulator common functions api
- include/power/regulator.h - define all structures required by the regulator

Changes:
- new uclass-id: UCLASS_PMIC_REGULATOR
- new config: CONFIG_DM_REGULATOR

---
Changes V2:
- new operations for regulator uclass:
-- get/set output state - for output on/off setting --- add enum: REGULATOR_OFF, REGULATOR_ON

- regulator uclass code rework and cleanup:
-- change name of:
--- enum 'regulator_desc_type' to 'regulator_type' --- add type DVS
--- struct 'regulator_desc' to 'regulator_value_desc'

-- regulator ops function calls:
--- remove 'ldo/buck' from naming
--- add new argument 'type' for define regulator type

-- regulator.h - update comments

---
 drivers/power/Makefile           |   1 +
 drivers/power/regulator-uclass.c | 227 ++++++++++++++++++++++++++++
 include/dm/uclass-id.h           |   1 +
 include/power/regulator.h        | 310 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 539 insertions(+)
 create mode 100644 drivers/power/regulator-uclass.c
 create mode 100644 include/power/regulator.h
drivers/power/Makefile +1 -0
index 5c9a189..a6b7012 100644
@@ -22,3 +22,4 @@ obj-$(CONFIG_POWER_FSL) += power_fsl.o
 obj-$(CONFIG_POWER_I2C) += power_i2c.o
 obj-$(CONFIG_POWER_SPI) += power_spi.o
 obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
drivers/power/regulator-uclass.c new file +159 -0
new file mode 100644
index 0000000..6b5c678
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <compiler.h>
+#include <dm/device.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int regulator_get_cnt(struct udevice *dev, int type, int *cnt)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_cnt)
+		return -EPERM;
+
+	return ops->get_cnt(dev, type, cnt);
+}
+
+int regulator_get_value_desc(struct udevice *dev, int type, int number,
+			     struct regulator_value_desc **desc)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENXIO;
+
+	if (!ops->get_value_desc)
+		return -EPERM;
+
+	return ops->get_value_desc(dev, type, number, desc);
+}
+
+int regulator_get_mode_desc(struct udevice *dev, int type, int number,
+			    int *mode_cnt, struct regulator_mode_desc **desc)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENXIO;
+
+	if (!ops->get_mode_desc_array)
+		return -EPERM;
+
+	return ops->get_mode_desc_array(dev, type, number, mode_cnt, desc);
+}
+
+int regulator_get_value(struct udevice *dev, int type, int number, int *value)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_value)
+		return -EPERM;
+
+	return ops->get_value(dev, type, number, value);
+}
+
+int regulator_set_value(struct udevice *dev, int type, int number, int value)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->set_value)
+		return -EPERM;
+
+	return ops->set_value(dev, type, number, value);
+}
+
+int regulator_get_state(struct udevice *dev, int type, int number, int *state)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_state)
+		return -EPERM;
+
+	return ops->get_state(dev, type, number, state);
+}
+
+int regulator_set_state(struct udevice *dev, int type, int number, int state)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->set_state)
+		return -EPERM;
+
+	return ops->set_state(dev, type, number, state);
+}
+
+int regulator_get_mode(struct udevice *dev, int type, int number, int *mode)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_mode)
+		return -EPERM;
+
+	return ops->get_mode(dev, type, number, mode);
+}
+
+int regulator_set_mode(struct udevice *dev, int type, int number, int mode)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->set_mode)
+		return -EPERM;
+
+	return ops->set_mode(dev, type, number, mode);
+}
+
+int regulator_get(char *name, struct udevice **regulator)
+{
+	struct udevice *dev;
+	struct uclass *uc;
+	int ret;
+
+	ret = uclass_get(UCLASS_PMIC_REGULATOR, &uc);
+	if (ret) {
+		error("Regulator uclass not initialized!");
+		return ret;
+	}

drivers/power/regulator-uclass.c line 169

Quoted 10 lines
drivers/power/regulator-uclass.c new file +10 -0
+
+	uclass_foreach_dev(dev, uc) {
+		if (!dev)
+			continue;
+
+		if (!strncmp(name, dev->name, strlen(name))) {
+			ret = device_probe(dev);
+			if (ret)
+				error("Dev: %s probe failed", dev->name);
+			*regulator = dev;

Shouldn't we set regulator=NULL if device_probe() returns an error?

Quoted 377 lines
drivers/power/regulator-uclass.c new file +58 -0
+			return ret;
+		}
+	}
+
+	*regulator = NULL;
+	return -EINVAL;
+}
+
+int regulator_i2c_get(int bus, int addr, struct udevice **regulator)
+{
+	struct udevice *pmic;
+	int ret;
+
+	/* First get parent pmic device */
+	ret = pmic_i2c_get(bus, addr, &pmic);
+	if (ret) {
+		error("Chip: %d on bus %d not found!", addr, bus);
+		return ret;
+	}
+
+	/* Get the first regulator child of pmic device */
+	if (device_get_first_child_by_uclass_id(pmic, UCLASS_PMIC_REGULATOR,
+						regulator)) {
+		error("PMIC: %s regulator child not found!", pmic->name);
+		*regulator = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+int regulator_spi_get(int bus, int cs, struct udevice **regulator)
+{
+	struct udevice *pmic;
+	int ret;
+
+	/* First get parent pmic device */
+	ret = pmic_spi_get(bus, cs, &pmic);
+	if (ret) {
+		error("Chip: %d on bus %d not found!", cs, bus);
+		return ret;
+	}
+
+	/* Get the first regulator child of pmic device */
+	if (device_get_first_child_by_uclass_id(pmic, UCLASS_PMIC_REGULATOR,
+						regulator)) {
+		error("PMIC: %s regulator child not found!", pmic->name);
+		*regulator = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+UCLASS_DRIVER(regulator) = {
+	.id		= UCLASS_PMIC_REGULATOR,
+	.name		= "regulator",
+};
include/dm/uclass-id.h +1 -0
index ff360ac..0a1e664 100644
@@ -37,6 +37,7 @@ enum uclass_id {
 
 	/* PMIC uclass and PMIC related uclasses */
 	UCLASS_PMIC,
+	UCLASS_PMIC_REGULATOR,
 
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
include/power/regulator.h new file +300 -0
new file mode 100644
index 0000000..ee8a280
@@ -0,0 +1,310 @@
+/*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _INCLUDE_REGULATOR_H_
+#define _INCLUDE_REGULATOR_H_
+
+#define DESC_NAME_LEN	20
+
+/* enum regulator_type - used for regulator_*() variant calls */
+enum regulator_type {
+	REGULATOR_TYPE_LDO = 0,
+	REGULATOR_TYPE_BUCK,
+	REGULATOR_TYPE_DVS,
+};
+
+/* enum regulator_out_state - used for ldo/buck output state setting */
+enum regulator_out_state {
+	REGULATOR_OFF = 0,
+	REGULATOR_ON,
+};
+
+/**
+ * struct regulator_value_desc - this structure holds an information about
+ * each regulator voltage limits. There is no "step" voltage value - so driver
+ * should take care of this. This is rather platform specific so can be
+ * taken from the device-tree.
+ *
+ * @type   - one of: DESC_TYPE_LDO, DESC_TYPE_BUCK or  DESC_TYPE_DVS
+ * @number - hardware internal regulator number
+ * @min_uV - minimum voltage (micro Volts)
+ * @max_uV - maximum voltage (micro Volts)
+ * @name   - name of regulator - usually will be taken from device tree and
+ *           can describe regulator output connected hardware, e.g. "eMMC"
+*/
+struct regulator_value_desc {
+	int type;
+	int number;
+	int min_uV;
+	int max_uV;
+	char name[DESC_NAME_LEN];
+};
+
+/**
+ * struct regulator_mode_desc - this structure holds an information about
+ * each regulator operation modes. Probably in most cases - an array.
+ * This will be probably a driver static data since it's device specific.
+ *
+ * @mode           - a driver specific mode number - used for regulator command
+ * @register_value - a driver specific value for this mode
+ * @name           - the name of mode - used for regulator command
+ */
+struct regulator_mode_desc {
+	int mode;
+	int register_value;
+	char *name;
+};
+
+/* PMIC regulator device operations */
+struct dm_regulator_ops {
+	/**
+	 * get_cnt - get the number of a given regulator 'type' outputs
+	 *
+	 * @dev    - regulator device
+	 * @type   - regulator type, one of enum regulator_type
+	 * Gets:
+	 * @cnt    - regulator count
+	 * Returns: 0 on success or negative value of errno.
+	 */
+	int (*get_cnt)(struct udevice *dev, int type, int *cnt);
+
+	/**
+	 * Regulator output value descriptor is specific for each output.
+	 * It's usually driver static data, but output value limits are
+	 * often defined in the device-tree, so those are platform dependent
+	 * attributes.
+	 *
+	 * get_value_desc - get value descriptor of the given regulator output
+	 * @dev           - regulator device
+	 * @type          - regulator type, one of enum regulator_type
+	 * @number        - regulator number
+	 * Gets:
+	 * @desc          - pointer to the descriptor
+	 * Returns: 0 on success or negative value of errno.
+	 */
+	int (*get_value_desc)(struct udevice *dev, int type, int number,
+			      struct regulator_value_desc **desc);
+
+	/**
+	 * Regulator output mode descriptors are device specific.
+	 * It's usually driver static data.
+	 * Each regulator output can support different mode types,
+	 * so usually will return an array with a number 'mode_desc_cnt'
+	 * of mode descriptors.
+	 *
+	 * get_mode_desc_array - get mode descriptor array of the given
+	 *                       regulator output number
+	 * @dev                - regulator device
+	 * @type               - regulator type, one of 'enum regulator_type'
+	 * @number             - regulator number
+	 * Gets:
+	 * @mode_desc_cnt      - descriptor array entry count
+	 * @desc               - pointer to the descriptor array
+	 * Returns: 0 on success or negative value of errno.
+	 */
+	int (*get_mode_desc_array)(struct udevice *dev, int type,
+				   int number, int *mode_desc_cnt,
+				   struct regulator_mode_desc **desc);
+
+	/**
+	 * The regulator output value function calls operates on a micro Volts,
+	 * however the regulator commands operates on a mili Volt unit.
+	 *
+	 * get/set_value - get/set output value of the given output number
+	 * @dev          - regulator device
+	 * @type         - regulator type, one of 'enum regulator_type'
+	 * @number       - regulator number
+	 * Sets/Gets:
+	 * @value        - set or get output value
+	 * Returns: 0 on success or negative value of errno.
+	 */
+	int (*get_value)(struct udevice *dev, int type, int number, int *value);
+	int (*set_value)(struct udevice *dev, int type, int number, int value);
+
+	/**
+	 * The most basic feature of the regulator output is it's on/off state.
+	 *
+	 * get/set_state - get/set on/off state of the given output number
+	 * @dev          - regulator device
+	 * @type         - regulator type, one of 'enum regulator_type'
+	 * @number       - regulator number
+	 * Sets/Gets:
+	 * @state        - set or get one of 'enum regulator_out_state'
+	 * Returns: 0 on success or negative value of errno.
+	 */
+	int (*get_state)(struct udevice *dev, int type, int number, int *state);
+	int (*set_state)(struct udevice *dev, int type, int number, int state);
+
+	/**
+	 * The 'get/set_mode()' function calls should operate on a driver
+	 * specific mode definitions, which should be found in:
+	 * field 'mode' of struct regulator_mode_desc.
+	 *
+	 * get/set_mode - get/set operation mode of the given output number
+	 * @dev          - regulator device
+	 * @type         - regulator type, one of 'enum regulator_type'
+	 * @number       - regulator number
+	 * Sets/Gets:
+	 * @mode         - set or get output mode
+	 * Returns: 0 on success or negative value of errno.
+	 */
+	int (*get_mode)(struct udevice *dev, int type, int number, int *mode);
+	int (*set_mode)(struct udevice *dev, int type, int number, int mode);
+};
+
+/**
+ * regulator_get_cnt: returns the number of driver registered regulators.
+ * This is used for pmic regulator command purposes.
+ *
+ * @dev     - pointer to the regulator device
+ * @type    - regulator type: device specific
+ * Gets:
+ * @cnt     - regulator count
+ * Returns: 0 on success or negative value of errno.
+ */
+int regulator_get_cnt(struct udevice *dev, int type, int *cnt);
+
+/**
+ * regulator_get_value_desc: returns a pointer to the regulator value
+ * descriptor of a given device regulator output number.
+ *
+ * @dev      - pointer to the regulator device
+ * @type     - regulator descriptor type
+ * @number   - descriptor number (regulator output number)
+ * Gets:
+ * @desc     - pointer to the descriptor
+ * Returns: 0 on success or negative value of errno.
+ */
+int regulator_get_value_desc(struct udevice *dev, int type, int number,
+			     struct regulator_value_desc **desc);
+
+/**
+ * regulator_get_mode_desc: returns a pointer to the array of regulator mode
+ * descriptors of a given device regulator type and number.
+ *
+ * @dev        - pointer to the regulator device
+ * @type       - regulator type: device specific
+ * @number     - output number: device specific
+ * Gets:
+ * @mode_cnt   - descriptor array entry count
+ * @desc       - pointer to the descriptor array
+ * Returns: 0 on success or negative value of errno.
+ */
+int regulator_get_mode_desc(struct udevice *dev, int type, int number,
+			    int *mode_cnt, struct regulator_mode_desc **desc);
+
+/**
+ * regulator_get_value: returns voltage value of a given device
+ * regulator type  number.
+ *
+ * @dev        - pointer to the regulator device
+ * @type       - regulator type: device specific
+ * @number     - output number: device specific
+ * Gets:
+ * @val        - returned voltage - unit: uV (micro Volts)
+ * Returns: 0 on success or negative value of errno.
+ */
+int regulator_get_value(struct udevice *dev, int type, int number, int *val);
+
+/**
+ * regulator_set_value: set the voltage value of a given device regulator type
+ * number to the given one passed by the argument: @val
+ *
+ * @dev    - pointer to the regulator device
+ * @type   - regulator type: device specific
+ * @number - output number: device specific
+ * @val    - voltage value to set - unit: uV (micro Volts)
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_value(struct udevice *dev, int type, int number, int val);
+
+/**
+ * regulator_get_state: returns output state of a given device
+ * regulator type number.
+ *
+ * @dev        - pointer to the regulator device
+ * @type       - regulator type, device specific
+ * @number     - regulator number, device specific
+ * Gets:
+ * @state      - returned regulator number output state:
+ *               - REGULATOR_OFF (0) - disable
+ *               - REGULATOR_ON  (1) - enable
+ * Returns:    - 0 on success or -errno val if fails
+ */
+int regulator_get_state(struct udevice *dev, int type, int number, int *state);
+
+/**
+ * regulator_set_state: set output state of a given device regulator type
+ * number to the given one passed by the argument: @state
+ *
+ * @dev            - pointer to the regulator device
+ * @type           - regulator type: device specific
+ * @number         - output number: device specific
+ * @state          - output state to set
+ *                   - enum REGULATOR_OFF == 0 - disable
+ *                   - enum REGULATOR_ON  == 1 - enable
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_state(struct udevice *dev, int type, int number, int state);
+
+/**
+ * regulator_get_mode: get mode number of a given device regulator type number
+ *
+ * @dev    - pointer to the regulator device
+ * @type   - output number: device specific
+ * @number - output number: device specific
+ * Gets:
+ * @mode   - returned mode number
+ * Returns - 0 on success or -errno val if fails
+ * Note:
+ * The regulator driver should return one of defined, mode number rather
+ * than the raw register value. The struct type 'regulator_mode_desc' has
+ * a mode field for this purpose and register_value for a raw register value.
+ */
+int regulator_get_mode(struct udevice *dev, int type, int number, int *mode);
+
+/**
+ * regulator_set_mode: set given regulator type and number mode to the given
+ * one passed by the argument: @mode
+ *
+ * @dev    - pointer to the regulator device
+ * @type   - output number: device specific
+ * @number - output number: device specific
+ * @mode   - mode type (struct regulator_mode_desc.mode)
+ * Returns - 0 on success or -errno value if fails
+ * Note:
+ * The regulator driver should take one of defined, mode number rather
+ * than a raw register value. The struct type 'regulator_mode_desc' has
+ * a mode field for this purpose and register_value for a raw register value.
+ */
+int regulator_set_mode(struct udevice *dev, int type, int number, int mode);
+
+/**
+ * regulator_..._get: returns the pointer to the pmic regulator device
+ * by specified arguments:
+ *
+ * NAME:
+ *  @name    - device name (useful for specific names)
+ * or SPI/I2C interface:
+ *  @bus     - device I2C/SPI bus number
+ *  @addr_cs - device specific address for I2C or chip select for SPI,
+ *             on the given bus number
+ * Gets:
+ * @regulator - pointer to the pmic device
+ * Returns: 0 on success or negative value of errno.
+ *
+ * The returned 'regulator' device can be used with:

include/power/regulator.h line 310

Quoted 10 lines
include/power/regulator.h new file +10 -0
+ * - regulator_get/set_*
+ * and if pmic_platdata is given, then also with:
+ * - pmic_if_* calls
+ * The last one is used for the pmic command purposes.
+ */
+int regulator_get(char *name, struct udevice **regulator);
+int regulator_i2c_get(int bus, int addr, struct udevice **regulator);
+int regulator_spi_get(int bus, int cs, struct udevice **regulator);
+
+#endif /* _INCLUDE_REGULATOR_H_ */

Br,
Robert

#71 lore raw

PATCH v2 4/12 dm: pmic: add implementation of driver model regulator uclass

Hello Robert,

Quoted 244 lines · Robert Baldyga

Hi,

Quoted 241 lines · Przemyslaw Marczak

This is the implementation of driver model regulator uclass api. To use it, the CONFIG_DM_PMIC is required with driver implementation, since it provides pmic devices basic I/O API.

The regulator framework is based on a 'struct dm_regulator_ops'. It provides a common function calls, for it's basic features:
- regulator_get_cnt() - number of outputs each type
- regulator_get_value_desc() - describe output value limits

- regulator_get_mode_desc()  - describe output operation modes
- regulator_get/set_value()  - output value (uV)
- regulator_get/set_state()  - output on/off state
- regulator_get/set_mode()   - output operation mode

To get the regulator device:
- regulator_get() - by name only
- regulator_i2c_get() - by i2c bus address (of pmic parent)
- regulator_spi_get() - by spi bus address (of pmic parent)

An optional and useful regulator framework features are two descriptors:
- struct regulator_desc - describes the regulator name and output value limits should be defined by device driver for each regulator output.

- struct regulator_mode_desc - (array) describes a number of operation modes supported by each regulator output.

The regulator framework features are described in file:
- include/power/regulator.h

Main files:
- drivers/power/regulator-uclass.c - provides regulator common functions api
- include/power/regulator.h - define all structures required by the regulator

Changes:
- new uclass-id: UCLASS_PMIC_REGULATOR
- new config: CONFIG_DM_REGULATOR

---
Changes V2:
- new operations for regulator uclass:
-- get/set output state - for output on/off setting --- add enum: REGULATOR_OFF, REGULATOR_ON

- regulator uclass code rework and cleanup:
-- change name of:
--- enum 'regulator_desc_type' to 'regulator_type' --- add type DVS
--- struct 'regulator_desc' to 'regulator_value_desc'

-- regulator ops function calls:
--- remove 'ldo/buck' from naming
--- add new argument 'type' for define regulator type

-- regulator.h - update comments

---
  drivers/power/Makefile           |   1 +
  drivers/power/regulator-uclass.c | 227 ++++++++++++++++++++++++++++
  include/dm/uclass-id.h           |   1 +
  include/power/regulator.h        | 310 +++++++++++++++++++++++++++++++++++++++
  4 files changed, 539 insertions(+)

create mode 100644 drivers/power/regulator-uclass.c create mode 100644 include/power/regulator.h

drivers/power/Makefile +1 -0
index 5c9a189..a6b7012 100644
@@ -22,3 +22,4 @@ obj-$(CONFIG_POWER_FSL) += power_fsl.o
  obj-$(CONFIG_POWER_I2C) += power_i2c.o
  obj-$(CONFIG_POWER_SPI) += power_spi.o
  obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
drivers/power/regulator-uclass.c new file +163 -0
new file mode 100644
index 0000000..6b5c678
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <compiler.h>
+#include <dm/device.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int regulator_get_cnt(struct udevice *dev, int type, int *cnt)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_cnt)
+		return -EPERM;
+
+	return ops->get_cnt(dev, type, cnt);
+}
+
+int regulator_get_value_desc(struct udevice *dev, int type, int number,
+			     struct regulator_value_desc **desc)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENXIO;
+
+	if (!ops->get_value_desc)
+		return -EPERM;
+
+	return ops->get_value_desc(dev, type, number, desc);
+}
+
+int regulator_get_mode_desc(struct udevice *dev, int type, int number,
+			    int *mode_cnt, struct regulator_mode_desc **desc)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENXIO;
+
+	if (!ops->get_mode_desc_array)
+		return -EPERM;
+
+	return ops->get_mode_desc_array(dev, type, number, mode_cnt, desc);
+}
+
+int regulator_get_value(struct udevice *dev, int type, int number, int *value)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_value)
+		return -EPERM;
+
+	return ops->get_value(dev, type, number, value);
+}
+
+int regulator_set_value(struct udevice *dev, int type, int number, int value)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->set_value)
+		return -EPERM;
+
+	return ops->set_value(dev, type, number, value);
+}
+
+int regulator_get_state(struct udevice *dev, int type, int number, int *state)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_state)
+		return -EPERM;
+
+	return ops->get_state(dev, type, number, state);
+}
+
+int regulator_set_state(struct udevice *dev, int type, int number, int state)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->set_state)
+		return -EPERM;
+
+	return ops->set_state(dev, type, number, state);
+}
+
+int regulator_get_mode(struct udevice *dev, int type, int number, int *mode)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_mode)
+		return -EPERM;
+
+	return ops->get_mode(dev, type, number, mode);
+}
+
+int regulator_set_mode(struct udevice *dev, int type, int number, int mode)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->set_mode)
+		return -EPERM;
+
+	return ops->set_mode(dev, type, number, mode);
+}
+
+int regulator_get(char *name, struct udevice **regulator)
+{
+	struct udevice *dev;
+	struct uclass *uc;
+	int ret;
+
+	ret = uclass_get(UCLASS_PMIC_REGULATOR, &uc);
+	if (ret) {
+		error("Regulator uclass not initialized!");
+		return ret;
+	}
+
+	uclass_foreach_dev(dev, uc) {
+		if (!dev)
+			continue;
Quoted 9 lines

drivers/power/regulator-uclass.c line 169

Quoted 6 lines
drivers/power/regulator-uclass.c new file +6 -0
+
+		if (!strncmp(name, dev->name, strlen(name))) {
+			ret = device_probe(dev);
+			if (ret)
+				error("Dev: %s probe failed", dev->name);
+			*regulator = dev;

Shouldn't we set regulator=NULL if device_probe() returns an error?

Thanks, this is fixed in V3.

Quoted 4 lines

Br,
Robert

Best regards,

Signature · 4 lines
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com
#72 lore raw

PATCH v2 5/12 dm: pmic: new commands: pmic and regulator

In reply to Przemyslaw Marczak

This introduces new commands:
- pmic (new) - CONFIG_DM_PMIC_CMD
- regulator - CONFIG_DM_REGULATOR_CMD
Both uses a common code and dm pmic api.

To avoid code mess the old pmic command is kept without changes.

Command pmic
The new pmic command uses driver model pmic api. The previous pmic I/O functionality is keept. And now read/write is the main pmic command feature. This command can be used only for UCLASS_PMIC devices, since this uclass is designed for pmic I/O operations only and provides pmic platform data.

Command options (pmic [option]):

- list                     - list available PMICs
- dev <id>                 - set id to current pmic device
- pmic dump                - dump registers
- pmic read <reg>          - read register

- pmic write <reg> <value> - write register

The user interface is changed. Before any operation, first the device should be chosen.

Command regulator
It uses the same code, but provides user interface for regulator devices.

This was designed to access the regulator device without it's documentation. It is possible, if driver implements uclass features, e.g. output descriptors.

Available commands:
- list - list UCLASS regulator devices
- dev [id] - show or set current regulator device
- dump - dump registers of current regulator device
- [ldo/buck/dvs][N] [name/state/desc]- print regulator(s) info
- [ldoN/buckN/dvsN] [setval/setmode] [mV/modeN] [-f] - set val (mV) or mode - only if descriptor exists

The regulator descriptor 'min' and 'max' limits prevents setting unsafe value. But sometimes it is useful to change the regulator value for some test - so the force option (-f) is available. This option is not available for change the mode, since this depends on pmic device design.

---
Changes v2:
- remove errno_str() call
- pmic command: move some code to pmic uclass
- pmic command: code cleanup
- fix data types
- add command line, regulator on/off setting feature
- adjust to new pmic api
- cleanup

---
 drivers/power/Makefile   |   2 +
 drivers/power/cmd_pmic.c | 820 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 822 insertions(+)
 create mode 100644 drivers/power/cmd_pmic.c
drivers/power/Makefile +2 -0
index a6b7012..943b38f 100644
@@ -22,4 +22,6 @@ obj-$(CONFIG_POWER_FSL) += power_fsl.o
 obj-$(CONFIG_POWER_I2C) += power_i2c.o
 obj-$(CONFIG_POWER_SPI) += power_spi.o
 obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_PMIC_CMD) += cmd_pmic.o
 obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
+obj-$(CONFIG_DM_REGULATOR_CMD) += cmd_pmic.o
drivers/power/cmd_pmic.c new file +820 -0

820 lines not on this page · raw message

Signature · 1 line
1.9.1
#73 lore raw

PATCH v2 5/12 dm: pmic: new commands: pmic and regulator

Hi Przemyslaw,

Quoted 38 lines · Przemyslaw Marczak

This introduces new commands:
- pmic (new) - CONFIG_DM_PMIC_CMD
- regulator - CONFIG_DM_REGULATOR_CMD
Both uses a common code and dm pmic api.

To avoid code mess the old pmic command is kept without changes.

Command pmic
The new pmic command uses driver model pmic api. The previous pmic I/O functionality is keept. And now read/write is the main pmic command feature. This command can be used only for UCLASS_PMIC devices, since this uclass is designed for pmic I/O operations only and provides pmic platform data.

Command options (pmic [option]):

- list                     - list available PMICs
- dev <id>                 - set id to current pmic device
- pmic dump                - dump registers
- pmic read <reg>          - read register

- pmic write <reg> <value> - write register

The user interface is changed. Before any operation, first the device should be chosen.

Command regulator
It uses the same code, but provides user interface for regulator devices.

This was designed to access the regulator device without it's documentation. It is possible, if driver implements uclass features, e.g. output descriptors.

Available commands:
- list - list UCLASS regulator devices
- dev [id] - show or set current regulator device
- dump - dump registers of current regulator device
- [ldo/buck/dvs][N] [name/state/desc]- print regulator(s) info
- [ldoN/buckN/dvsN] [setval/setmode] [mV/modeN] [-f] - set val (mV) or mode - only if descriptor exists

Quoted 5 lines

The regulator descriptor 'min' and 'max' limits prevents setting unsafe value. But sometimes it is useful to change the regulator value for some test - so the force option (-f) is available. This option is not available for change the mode, since this depends on pmic device design.

This help could go in Kconfig.

Quoted 22 lines

---
Changes v2:
- remove errno_str() call
- pmic command: move some code to pmic uclass
- pmic command: code cleanup
- fix data types
- add command line, regulator on/off setting feature
- adjust to new pmic api
- cleanup

---
 drivers/power/Makefile   |   2 +
 drivers/power/cmd_pmic.c | 820 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 822 insertions(+)
 create mode 100644 drivers/power/cmd_pmic.c
drivers/power/Makefile +0 -0
index a6b7012..943b38f 100644

drivers/power/Makefile obj-$(CONFIG_POWER_FSL) += power_fsl.o · line 27

Quoted 6 lines
drivers/power/Makefile +2 -0
@@ -22,4 +22,6 @@ obj-$(CONFIG_POWER_FSL) += power_fsl.o
 obj-$(CONFIG_POWER_I2C) += power_i2c.o
 obj-$(CONFIG_POWER_SPI) += power_spi.o
 obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_PMIC_CMD) += cmd_pmic.o
 obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
+obj-$(CONFIG_DM_REGULATOR_CMD) += cmd_pmic.o

Can we put this in its own file? Also perhaps it should go in common? Not sure about that though.

Quoted 13 lines
drivers/power/cmd_pmic.c new file +7 -0
new file mode 100644
index 0000000..996bfe7
@@ -0,0 +1,820 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>

drivers/power/cmd_pmic.c line 17

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <fdtdec.h>
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/root.h>
+#include <dm/lists.h>

Please sort includes:

common.h
others in order
<asm/...>
<dm/...>
<linux/...>
"local includes"

Quoted 143 lines
drivers/power/cmd_pmic.c new file +143 -0
+#include <i2c.h>
+#include <compiler.h>
+#include <errno.h>
+
+#define LINE_BUF_LIMIT 80
+#define STR_BUF_LEN    26
+#define ID_STR_LIMIT   4
+#define UC_STR_LIMIT   16
+#define DRV_STR_LIMIT  26
+#define IF_STR_LIMIT   12
+
+static struct udevice *pmic_curr;
+static struct udevice *reg_curr;
+
+#ifdef CONFIG_DM_REGULATOR_CMD
+#define TYPE_INFO(_id, _name) { \
+       .id = _id, \
+       .len = ARRAY_SIZE(_name) - 1, \
+       .name = _name, \
+}
+
+enum display_info {
+       INFO_NAME,
+       INFO_STATE,
+       INFO_DESC,
+       INFO_DESC_MODE,
+       INFO_DESC_VAL,
+};
+
+struct regulator_type_info {
+       int id;
+       int len;
+       char *name;
+};
+
+static struct regulator_type_info type_info[] = {
+       TYPE_INFO(REGULATOR_TYPE_LDO, "ldo"),
+       TYPE_INFO(REGULATOR_TYPE_BUCK, "buck"),
+       TYPE_INFO(REGULATOR_TYPE_DVS, "dvs"),
+};
+
+char *regulator_type_str(int regulator_type)
+{
+       switch (regulator_type) {
+       case REGULATOR_TYPE_LDO:
+       case REGULATOR_TYPE_BUCK:
+       case REGULATOR_TYPE_DVS:
+               return type_info[regulator_type].name;
+       default:
+               return NULL;
+       }
+}
+#endif /* CONFIG_DM_REGULATOR_CMD */
+
+static int set_curr_dev(struct udevice *dev)
+{
+       if (!dev)
+               return -EINVAL;
+
+       if (!dev->driver)
+               return -EINVAL;
+
+       switch (dev->driver->id) {
+       case UCLASS_PMIC:
+               pmic_curr = dev;
+               break;
+       case UCLASS_PMIC_REGULATOR:
+               reg_curr = dev;
+               break;
+       default:
+               error("Bad driver uclass!\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct udevice *get_curr_dev(int uclass_id)
+{
+       switch (uclass_id) {
+       case UCLASS_PMIC:
+               return pmic_curr;
+       case UCLASS_PMIC_REGULATOR:
+               return reg_curr;
+       default:
+               error("Bad uclass ID!\n");
+               return NULL;
+       }
+}
+
+static int curr_dev_name(int uclass_id)
+{
+       struct udevice *dev = get_curr_dev(uclass_id);
+
+       if (dev) {
+               printf("PMIC: %s\n", dev->name);
+               return 0;
+       } else {
+               puts("PMIC dev is not set!\n");
+               return -ENODEV;
+       }
+}
+
+static int pmic_dump(int uclass_id)
+{
+       struct udevice *dev;
+       struct udevice *io_dev;
+       int i, ret, max_offset;
+       unsigned char val;
+
+       dev = get_curr_dev(uclass_id);
+       if (!dev)
+               return -ENODEV;
+
+       if (pmic_io_dev(dev, &io_dev)) {
+               printf("PMIC I/O dev not found\n");
+               return -ENODEV;
+       }
+
+       ret = curr_dev_name(uclass_id);
+       if (ret)
+               return CMD_RET_USAGE;
+
+       max_offset = pmic_if_max_offset(io_dev);
+       printf("Register count: %u\n", max_offset);
+
+       for (i = 0; i < max_offset; i++) {
+               ret = pmic_read(io_dev, i, &val);
+               if (ret) {
+                       printf("PMIC: Registers dump failed: %d\n", ret);
+                       return -EIO;
+               }
+
+               if (!(i % 16))
+                       printf("\n0x%02x: ", i);
+
+               printf("%2.2x ", val);
+       }
+       puts("\n");
+       return 0;
+}
+
+/**

drivers/power/cmd_pmic.c line 170

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+ * display_column - display a string from buffer limited by the column len.
+ *                  Allows display well aligned string columns.
+ *
+ * @buf: a pointer to the buffer with the string to display as a column
+ * @str_len: len of the string
+ * @column_len: a number of characters to be printed, but it is actually
+ *              decremented by 1 for the ending character: '|'
+*/
+static int display_column(char *buf, unsigned int str_len,
+                         unsigned int column_len)

What about using printf("%.*s", str_len, buf) instead of this function?

Quoted 32 lines
drivers/power/cmd_pmic.c new file +32 -0
+{
+       int i = column_len;
+
+       if (str_len < column_len - 1) {
+               for (i = str_len; i < column_len; i++)
+                       buf[i] = ' ';
+       }
+
+       buf[column_len - 1] = '|';
+       buf[column_len] = '\0';
+
+       puts(buf);
+
+       return i;
+}
+
+static int pmic_list_uclass_devices(int uclass_id)
+{
+       ALLOC_CACHE_ALIGN_BUFFER(char, buf, LINE_BUF_LIMIT);
+       struct uclass_driver *uc_drv = NULL;
+       struct udevice *dev = NULL;
+       struct driver *drv = NULL;
+       struct uclass *uc = NULL;
+       int len;
+       int ret;
+
+       puts("| Id | Uclass        | Driver                  | Interface |\n");
+
+       ret = uclass_get(uclass_id, &uc);
+       if (ret) {
+               error("PMIC uclass: %d not found!\n", uclass_id);
+               return -EINVAL;

drivers/power/cmd_pmic.c line 212

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+       }
+
+       uc_drv = uc->uc_drv;
+       uclass_foreach_dev(dev, uc) {
+               if (dev)
+                       device_probe(dev);
+               else
+                       continue;
+
+               printf("| %2.d |", dev->seq);

Why do you need the '.'?

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+
+               len = snprintf(buf, STR_BUF_LEN, " %2.d@%.10s",
+                               uc_drv->id,  uc_drv->name);
+
+               display_column(buf, len, UC_STR_LIMIT);
+
+               drv = dev->driver;
+               if (drv && drv->name)
+                       len = snprintf(buf, STR_BUF_LEN, " %.26s", drv->name);
+               else

drivers/power/cmd_pmic.c line 232

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+                       len = snprintf(buf, STR_BUF_LEN, " - ");
+
+               display_column(buf, len, DRV_STR_LIMIT);
+
+               len = snprintf(buf, STR_BUF_LEN, " %s%d at 0x%x",
+                              pmic_if_str(dev),
+                              pmic_if_bus_num(dev),
+                              pmic_if_addr_cs(dev));
+
+               display_column(buf, len, IF_STR_LIMIT);

Then I hope you might get all this into one printf() and drop buf?

Quoted 22 lines
drivers/power/cmd_pmic.c new file +22 -0
+
+               puts("\n");
+       }
+
+       return 0;
+}
+
+static int pmic_cmd(char *const argv[], int argc, int uclass_id)
+{
+       const char *cmd = argv[0];
+       struct udevice *dev = NULL;
+       int seq;
+
+       if (strcmp(cmd, "list") == 0)
+               return pmic_list_uclass_devices(uclass_id);
+
+       if (strcmp(cmd, "dev") == 0) {
+               switch (argc) {
+               case 2:
+                       seq = simple_strtoul(argv[1], NULL, 0);
+                       uclass_get_device_by_seq(uclass_id, seq, &dev);
+                       if (dev)

drivers/power/cmd_pmic.c line 264

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+                               set_curr_dev(dev);
+                       else
+                               printf("Device: %d not found\n", seq);
+               case 1:
+                       return curr_dev_name(uclass_id);
+               }
+       }
+
+       if (strcmp(cmd, "dump") == 0)
+               return pmic_dump(uclass_id);

Can we use a sub-command table here, like i2c?

Quoted 75 lines
drivers/power/cmd_pmic.c new file +75 -0
+
+       if (!get_curr_dev(uclass_id))
+               return -ENODEV;
+
+       return 1;
+}
+
+#ifdef CONFIG_DM_REGULATOR_CMD
+static char *get_reg_mode_name(int reg, int mode,
+                              struct regulator_mode_desc *mode_desc,
+                              int desc_cnt)
+{
+       int i;
+       char *mode_name = NULL;
+
+       if (!mode_desc)
+               return NULL;
+
+       for (i = 0; i < desc_cnt; i++) {
+               if (mode_desc[i].mode == mode)
+                       mode_name = mode_desc[i].name;
+       }
+
+       return mode_name;
+}
+
+static int set_reg_state(int type, int reg_num, int on_off)
+{
+       struct udevice *dev;
+
+       dev = get_curr_dev(UCLASS_PMIC_REGULATOR);
+       if (!dev)
+               return -ENODEV;
+
+       return regulator_set_state(dev,type, reg_num, on_off);
+}
+
+/**
+ * display_reg_info: list regualtor(s) info
+ * @start_reg; @end_reg - first and last regulator number to list
+ * if start_reg == end_reg - then displays only one regulator info
+ *
+ * @display_info options:
+ *
+ * =?INFO_NAME:
+ * regN: name
+ *
+ * =?INFO_STATE:
+ * regN: name
+ *  - Vout: val mV mode: N (name)
+ *
+ * =?INFO_DESC
+ * regN: name
+ *  - Vout: val mV
+ *  - Vmin: val mV
+ *  - Vmax: val mV
+ *  - mode: 1 (name1) [active]
+ *  - mode: 2 (name2) [active]
+ *  - mode: N (nameN) [active]
+ *
+ * =?INFO_DESC_VAL
+ * regN:
+ *  - Vout: val mV
+ *  - Vmin: val mV
+ *  - Vmax: val mV
+ *
+ * =?INFO_DESC_VAL
+ * regN:
+ *  - mode: 1 (name1) [active]
+ *  - mode: 2 (name2) [active]
+ *  - mode: N (nameN) [active]
+ */
+static int display_reg_info(int type, int start_reg,
+                           int end_reg, int display_info)
+{

drivers/power/cmd_pmic.c line 349

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+       struct udevice *dev;
+       struct regulator_value_desc *desc;
+       struct regulator_mode_desc *mode_desc;
+       char *mode_name;
+       int mode_cnt;
+       int mode;
+       int ret;
+       int state;
+       int reg_val, i, j;
+       int div = 1000;

This should be a #define or const int.

drivers/power/cmd_pmic.c line 360

Quoted 11 lines
drivers/power/cmd_pmic.c new file +11 -0
+
+       if (start_reg > end_reg)
+               return -EINVAL;
+
+       dev = get_curr_dev(UCLASS_PMIC_REGULATOR);
+       if (!dev)
+               return -ENODEV;
+
+       switch (display_info) {
+       case INFO_STATE:
+               puts(" RegN:  val mV  state  mode");

I think we are supposed to use printf() now instead of puts(), unless in SPL for code size reasons.

Quoted 127 lines
drivers/power/cmd_pmic.c new file +127 -0
+               break;
+       case INFO_DESC:
+               puts(" RegN:  @descriptors data");
+               break;
+       case INFO_DESC_VAL:
+       case INFO_DESC_MODE:
+               break;
+       case INFO_NAME:
+               puts(" RegN: name");
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       for (i = start_reg; i <= end_reg; i++) {
+               ret = regulator_get_mode(dev, type, i, &mode);
+               ret |= regulator_get_value(dev, type, i, &reg_val);
+               ret |= regulator_get_value_desc(dev, type, i, &desc);
+               ret |= regulator_get_mode_desc(dev, type, i, &mode_cnt,
+                                              &mode_desc);
+
+               /* Probably no such regulator number */
+               if (ret)
+                       continue;
+
+               /* Display in mV */
+               reg_val /= div;
+
+               printf("\n%s%.2d:", regulator_type_str(type), i);
+
+               switch (display_info) {
+               case INFO_STATE:
+                       printf(" %d mV ",  reg_val);
+
+                       regulator_get_state(dev, type, i, &state);
+
+                       printf("   %s", state ? "ON " : "OFF");
+
+                       mode_name = get_reg_mode_name(i, mode, mode_desc,
+                                                       mode_cnt);
+                       if (mode_name)
+                               printf("  %d@%s", mode, mode_name);
+
+                       continue;
+               case INFO_DESC:
+               case INFO_DESC_VAL:
+                       if (desc)
+                               printf("\n @name: %s", desc->name);
+
+                       /* display voltage range */
+                       printf("\n @Vout: %d mV", reg_val);
+
+                       if (!desc)
+                               puts("\n @no value descriptors");
+                       else
+                               printf("\n @Vmin: %d mV\n @Vmax: %d mV",
+                                                       (desc->min_uV / div),
+                                                       (desc->max_uV / div));
+
+                       if (display_info != INFO_DESC)
+                               continue;
+               case INFO_DESC_MODE:
+                       if (!mode_desc) {
+                               puts("\n @no mode descriptors");
+                               continue;
+                       }
+
+                       /* display operation modes info */
+                       for (j = 0; j < mode_cnt; j++) {
+                               printf("\n @mode: %d (%s)", mode_desc[j].mode,
+                                                         mode_desc[j].name);
+                               if (mode_desc[j].mode == mode)
+                                       puts(" (active)");
+                       }
+                       continue;
+               case INFO_NAME:
+                       if (desc)
+                               printf(" %s", desc->name);
+                       else
+                               puts(" -");
+                       continue;
+               }
+       }
+
+       puts("\n");
+
+       return 0;
+}
+
+/**
+ * check_reg_mode: check if the given regulator supports the given mode
+ *
+ * @dev: device to check
+ * @reg_num: given devices regulator number
+ * @set_mode: mode to check - a mode value of struct regulator_mode_desc
+ * @type: descriptor type: REGULATOR_TYPE_LDO or REGULATOR_TYPE_BUCK
+ */
+static int check_reg_mode(struct udevice *dev, int reg_num, int set_mode,
+                         int type)
+{
+       struct regulator_mode_desc *mode_desc;
+       int i, mode_cnt;
+
+       regulator_get_mode_desc(dev, type, reg_num, &mode_cnt, &mode_desc);
+       if (!mode_desc)
+               goto nodesc;
+
+       for (i = 0; i < mode_cnt; i++) {
+               if (mode_desc[i].mode == set_mode)
+                       return 0;
+       }
+
+       printf("Mode:%d not found in the descriptor:", set_mode);
+
+       display_reg_info(type, reg_num, reg_num, INFO_DESC_MODE);
+
+       return -EINVAL;
+
+nodesc:
+       printf("%s%.2d - no descriptor found\n", regulator_type_str(type),
+                                                reg_num);
+       return -ENODEV;
+}
+
+static int set_reg_mode(int type, int reg_num, int set_mode)
+{
+       int ret;

drivers/power/cmd_pmic.c line 497

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+       struct udevice *dev;
+
+       dev = get_curr_dev(UCLASS_PMIC_REGULATOR);
+       if (!dev)
+               return -ENODEV;
+
+       if (check_reg_mode(dev, reg_num, set_mode, type))
+               return -EPERM;
+
+       printf("Set %s%.2d mode: %d\n", regulator_type_str(type),

Why %2d? Why not just %d?

Quoted 40 lines
drivers/power/cmd_pmic.c new file +40 -0
+                                       reg_num, set_mode);
+
+       ret = regulator_set_mode(dev, type, reg_num, set_mode);
+
+       return ret;
+}
+
+/**
+ * check_reg_val: check if the given regulator value meets
+ *                the descriptor min and max values
+ *
+ * @dev: device to check
+ * @reg_num: given devices regulator number
+ * @set_val: value to check - in uV
+ * @type: descriptor type: REGULATOR_TYPE_LDO or REGULATOR_TYPE_BUCK
+ */
+static int check_reg_val(struct udevice *dev, int reg_num, int set_val,
+                        int type)
+{
+       struct regulator_value_desc *desc;
+
+       regulator_get_value_desc(dev, type, reg_num, &desc);
+       if (!desc)
+               goto nodesc;
+
+       if (set_val >= desc->min_uV && set_val <= desc->max_uV)
+               return 0;
+
+       printf("Value: %d mV exceeds descriptor limits:", (set_val / 1000));
+
+       display_reg_info(type, reg_num, reg_num, INFO_DESC_VAL);
+
+       return -EINVAL;
+nodesc:
+       printf("%s%.2d - no descriptor found\n", regulator_type_str(type),
+                                                reg_num);
+       return -ENODEV;
+}
+
+static int set_reg_val(int type, int reg_num, int set_val, int set_force)

drivers/power/cmd_pmic.c line 547

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+{
+       int ret;
+       struct udevice *dev;
+
+       dev = get_curr_dev(UCLASS_PMIC_REGULATOR);
+       if (!dev)
+               return -ENODEV;
+
+       /* get mV and set as uV */
+       set_val *= 1000;

Perhaps the command should work in uV too? Or maybe a 'mV' suffix on numbers?

Quoted 4 lines
drivers/power/cmd_pmic.c new file +4 -0
+
+       if (!set_force && check_reg_val(dev, reg_num, set_val, type))
+               return -EPERM;
+

drivers/power/cmd_pmic.c line 561

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+       printf("Set %s%.2d val: %d mV", regulator_type_str(type),
+                                       reg_num, (set_val / 1000));
+
+       if (set_force)
+               puts(" (force)\n");
+       else
+               puts("\n");
+
+       ret = regulator_set_value(dev, type, reg_num, set_val);
+       return ret;

You don't need ret.

Quoted 27 lines
drivers/power/cmd_pmic.c new file +27 -0
+}
+
+static int regulator_subcmd(char * const argv[], int argc, int type,
+                     int reg_num, int reg_cnt)
+{
+       int start_reg, end_reg;
+       int set_val, set_force;
+       const char *cmd;
+
+       /* If reg number is given */
+       if (reg_num >= 0) {
+               start_reg = reg_num;
+               end_reg = reg_num;
+       } else {
+               start_reg = 0;
+               end_reg = reg_cnt;
+       }
+
+       cmd = argv[0];
+       if (!strcmp("name", cmd))
+               return display_reg_info(type, start_reg, end_reg, INFO_NAME);
+       else if (!strcmp("state", cmd))
+               return display_reg_info(type, start_reg, end_reg, INFO_STATE);
+       else if (!strcmp("desc", cmd))
+               return display_reg_info(type, start_reg, end_reg, INFO_DESC);
+
+       /* Check if regulator number is given */

drivers/power/cmd_pmic.c line 598

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+       if (reg_num < 0) {
+               puts("reg num?\n");
+               return -EINVAL;
+       }
+
+       if (!strcmp("on", cmd))
+               return set_reg_state(type, reg_num, REGULATOR_ON);
+
+       if (!strcmp("off", cmd))
+               return set_reg_state(type, reg_num, REGULATOR_OFF);

Can we use a sub command table again?

Quoted 104 lines
drivers/power/cmd_pmic.c new file +104 -0
+
+       /* Check argument value */
+       if (argc < 2 || !isdigit(*argv[1])) {
+               puts("Expected positive value!\n");
+               return -EINVAL;
+       }
+
+       set_val = simple_strtoul(argv[1], NULL, 0);
+       set_force = 0;
+
+       if (!strcmp("setval", cmd)) {
+               if (argc == 3 && !strcmp("-f", argv[2]))
+                       set_force = 1;
+
+               return set_reg_val(type, reg_num, set_val, set_force);
+       }
+
+       if (!strcmp("setmode", cmd))
+               return set_reg_mode(type, reg_num, set_val);
+
+       return -EINVAL;
+}
+
+static int do_regulator(cmd_tbl_t *cmdtp, int flag, int argc,
+                       char * const argv[])
+{
+       struct udevice *dev = NULL;
+       int cmd_len;
+       int ret = 0;
+       int reg_num = -1;
+       int reg_cnt;
+       int i;
+       char *cmd;
+
+       if (!argc)
+               return CMD_RET_USAGE;
+
+       /* list, dev, dump */
+       ret = pmic_cmd(argv, argc, UCLASS_PMIC_REGULATOR);
+       if (ret != 1)
+               goto finish;
+
+       cmd = argv[0];
+       cmd_len = strlen(cmd);
+
+       dev = get_curr_dev(UCLASS_PMIC_REGULATOR);
+       if (!dev)
+               return CMD_RET_USAGE;
+
+       for (i = 0; i < ARRAY_SIZE(type_info); i++) {
+               if (!strncmp(cmd, type_info[i].name, type_info[i].len)) {
+                       ret = regulator_get_cnt(dev, type_info[i].id, &reg_cnt);
+                       break;
+               }
+       }
+
+       if (!reg_cnt) {
+               printf("Bad regulator type!\n");
+               goto finish;
+       }
+
+       /* Get regulator number */
+       reg_num = -1;
+       if (cmd_len > type_info[i].len && isdigit(cmd[type_info[i].len]))
+               reg_num = (int)simple_strtoul(cmd + type_info[i].len , NULL, 0);
+
+       if (reg_num > reg_cnt) {
+               printf("Max dev %s number is: %d\n", type_info[i].name,
+                                                    reg_cnt);
+               return CMD_RET_SUCCESS;
+       }
+
+       /* Subcommand missed? */
+       if (argc == 1) {
+               printf("name/state/desc/setval/setmode/on/off ?\n");
+               return CMD_RET_SUCCESS;
+       }
+
+       argv++;
+       argc--;
+       ret = regulator_subcmd(argv, argc, type_info[i].id, reg_num, reg_cnt);
+
+finish:
+       if (ret < 0)
+               return CMD_RET_FAILURE;
+
+       return CMD_RET_SUCCESS;
+}
+#endif /* CONFIG_DM_REGULATOR_CMD */
+
+#ifdef CONFIG_DM_PMIC_CMD
+static int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       struct udevice *dev, *io_dev;
+       unsigned reg;
+       unsigned char val;
+       char *cmd;
+       int ret = 0;
+
+       if (!argc)
+               return CMD_RET_USAGE;
+
+       /* list, dev, dump */
+       ret = pmic_cmd(argv, argc, UCLASS_PMIC);

drivers/power/cmd_pmic.c line 712

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+       if (ret != 1)
+               goto finish;
+
+       dev = get_curr_dev(UCLASS_PMIC);
+       ret = pmic_io_dev(dev, &io_dev);
+       if (ret)
+               goto finish;
+
+       cmd = argv[0];
+       if (strcmp(cmd, "read") == 0) {

Probably not worth a sub-command table here.

Quoted 96 lines
drivers/power/cmd_pmic.c new file +96 -0
+               if (argc != 2) {
+                       ret = -EINVAL;
+                       goto finish;
+               }
+
+               printf("cur dev: %p name: %s\n", dev, dev->name);
+               reg = simple_strtoul(argv[1], NULL, 0);
+
+               printf("read reg: %#x\n", reg);
+               ret = pmic_read(io_dev, reg, &val);
+               if (ret)
+                       puts("PMIC: Register read failed\n");
+               else
+                       printf("0x%02x: 0x%2.2x\n", reg, val);
+
+               goto finish;
+       }
+
+       if (strcmp(cmd, "write") == 0) {
+               if (argc != 3) {
+                       ret = -EINVAL;
+                       goto finish;
+               }
+
+               reg = simple_strtoul(argv[1], NULL, 0);
+               val = simple_strtoul(argv[2], NULL, 0);
+
+               printf("Write reg:%#x  val: %#x\n", reg, val);
+
+               ret = pmic_write(io_dev, reg, val);
+               if (ret)
+                       puts("PMIC: Register write failed\n");
+
+               goto finish;
+       }
+
+finish:
+       if (ret < 0) {
+               printf("Error: %d\n", ret);
+               return CMD_RET_FAILURE;
+       }
+
+       return CMD_RET_SUCCESS;
+}
+#endif /* CONFIG_DM_PMIC_CMD */
+
+static cmd_tbl_t cmd_pmic[] = {
+#ifdef CONFIG_DM_PMIC_CMD
+       U_BOOT_CMD_MKENT(pmic, 5, 1, do_pmic, "", ""),
+#endif
+#ifdef CONFIG_DM_REGULATOR_CMD
+       U_BOOT_CMD_MKENT(regulator, 5, 1, do_regulator, "", ""),
+#endif
+};
+
+static int do_pmicops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       cmd_tbl_t *cmd;
+
+       cmd = find_cmd_tbl(argv[0], cmd_pmic, ARRAY_SIZE(cmd_pmic));
+       if (cmd == NULL || argc > cmd->maxargs)
+               return CMD_RET_USAGE;
+
+       argc--;
+       argv++;
+
+       return cmd->cmd(cmdtp, flag, argc, argv);
+}
+
+#ifdef CONFIG_DM_PMIC_CMD
+U_BOOT_CMD(pmic,       CONFIG_SYS_MAXARGS, 1, do_pmicops,
+       "PMIC",
+       "list                - list available PMICs\n"
+       "pmic dev <id>            - set id to current pmic device\n"
+       "pmic dump                - dump registers\n"
+       "pmic read <reg>          - read register\n"
+       "pmic write <reg> <value> - write register\n"
+);
+#endif /* CONFIG_DM_PMIC_CMD */
+#ifdef CONFIG_DM_REGULATOR_CMD
+U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_pmicops,
+       "PMIC Regulator",
+       "list\n\t- list UCLASS regulator devices\n"
+       "regulator dev [id]\n\t- show or set operating regulator device\n"
+       "regulator dump\n\t- dump registers of current regulator\n"
+       "regulator [ldo/buck/dvs][N] [name/state/desc]"
+       "\n\t- print regulator(s) info\n"
+       "regulator [ldoN/buckN/dvsN] [setval/setmode] [mV/modeN] [-f]"
+       "\n\t- set val (mV) (forcibly) or mode - only if desc available\n\n"
+       "Example of usage:\n"
+       "First get available commands:\n"
+       " # regulator\n"
+       "Look after the regulator device 'Id' number:\n"
+       " # regulator list\n"
+       "Set the operating device:\n"
+       " # regulator dev 'Id'\n"

drivers/power/cmd_pmic.c line 818

Quoted 10 lines
drivers/power/cmd_pmic.c new file +10 -0
+       "List state of device ldo's:\n"
+       " # regulator ldo state\n"
+       "List descriptors of device ldo's:\n"
+       " # regulator ldo desc\n"
+       "Set the voltage of ldo number 1 to 1200mV\n"
+       " # regulator ldo1 setval 1200\n"
+       "Use option: '-f', if given value exceeds the limits(be careful!):\n"
+       " # regulator ldo1 setval 1200 -f\n"
+       "Set the mode of ldo number 1 to '5' (force not available):\n"
+       " # regulator ldo1 setmode 5\n"

So the regulators in the device have numbers. I presume they also have names by virtue of the device tree?

Quoted 5 lines
drivers/power/cmd_pmic.c new file +2 -1
+);
+#endif /* CONFIG_DM_REGULATOR_CMD */
--

1.9.1

Regards,
Simon