All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model
@ 2014-10-08 20:48 Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message Przemyslaw Marczak
                   ` (21 more replies)
  0 siblings, 22 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

-- 
1.9.1

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-09  6:46   ` Joakim Tjernlund
  2014-10-22 15:31   ` Tom Rini
  2014-10-08 20:48 ` [U-Boot] [PATCH 02/19] exynos: config-common: enable errno_str() function Przemyslaw Marczak
                   ` (20 subsequent siblings)
  21 siblings, 2 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 include/errno.h |   3 ++
 lib/Makefile    |   1 +
 lib/errno_str.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 151 insertions(+)
 create mode 100644 lib/errno_str.c

diff --git a/include/errno.h b/include/errno.h
index e24a33b..14ac3cb 100644
--- a/include/errno.h
+++ b/include/errno.h
@@ -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 */
diff --git a/lib/Makefile b/lib/Makefile
index 320197a..6bb2e9b 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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
diff --git a/lib/errno_str.c b/lib/errno_str.c
new file mode 100644
index 0000000..0ba950e
--- /dev/null
+++ b/lib/errno_str.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SDPX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#include <errno.h>
+
+#define ERRNO_MSG(errno, msg)	msg
+#define SAME_AS(x)		(const char *)&errno_message[x]
+
+static const char * const errno_message[] = {
+	ERRNO_MSG(0, "Success"),
+	ERRNO_MSG(EPERM, "Operation not permitted"),
+	ERRNO_MSG(ENOEN, "No such file or directory"),
+	ERRNO_MSG(ESRCH, "No such process"),
+	ERRNO_MSG(EINTR, "Interrupted system call"),
+	ERRNO_MSG(EIO, "I/O error"),
+	ERRNO_MSG(ENXIO, "No such device or address"),
+	ERRNO_MSG(E2BIG, "Argument list too long"),
+	ERRNO_MSG(ENOEXEC, "Exec format error"),
+	ERRNO_MSG(EBADF, "Bad file number"),
+	ERRNO_MSG(ECHILD, "No child processes"),
+	ERRNO_MSG(EAGAIN, "Try again"),
+	ERRNO_MSG(ENOMEM, "Out of memory"),
+	ERRNO_MSG(EACCES, "Permission denied"),
+	ERRNO_MSG(EFAULT, "Bad address"),
+	ERRNO_MSG(ENOTBL, "Block device required"),
+	ERRNO_MSG(EBUSY, "Device or resource busy"),
+	ERRNO_MSG(EEXIST, "File exists"),
+	ERRNO_MSG(EXDEV, "Cross-device link"),
+	ERRNO_MSG(ENODEV, "No such device"),
+	ERRNO_MSG(ENOTDIR, "Not a directory"),
+	ERRNO_MSG(EISDIR, "Is a directory"),
+	ERRNO_MSG(EINVAL, "Invalid argument"),
+	ERRNO_MSG(ENFILE, "File table overflow"),
+	ERRNO_MSG(EMFILE, "Too many open files"),
+	ERRNO_MSG(ENOTTY, "Not a typewriter"),
+	ERRNO_MSG(ETXTBSY, "Text file busy"),
+	ERRNO_MSG(EFBIG, "File too large"),
+	ERRNO_MSG(ENOSPC, "No space left on device"),
+	ERRNO_MSG(ESPIPE, "Illegal seek"),
+	ERRNO_MSG(EROFS, "Read-only file system"),
+	ERRNO_MSG(EMLINK, "Too many links"),
+	ERRNO_MSG(EPIPE, "Broken pipe"),
+	ERRNO_MSG(EDOM, "Math argument out of domain of func"),
+	ERRNO_MSG(ERANGE, "Math result not representable"),
+	ERRNO_MSG(EDEADLK, "Resource deadlock would occur"),
+	ERRNO_MSG(ENAMETOOLONG, "File name too long"),
+	ERRNO_MSG(ENOLCK, "No record locks available"),
+	ERRNO_MSG(ENOSYS, "Function not implemented"),
+	ERRNO_MSG(ENOTEMPTY, "Directory not empty"),
+	ERRNO_MSG(ELOOP, "Too many symbolic links encountered"),
+	ERRNO_MSG(EWOULDBLOCK, SAME_AS(EAGAIN)),
+	ERRNO_MSG(ENOMSG, "No message of desired type"),
+	ERRNO_MSG(EIDRM, "Identifier removed"),
+	ERRNO_MSG(ECHRNG, "Channel number out of range"),
+	ERRNO_MSG(EL2NSYNC, "Level 2 not synchronized"),
+	ERRNO_MSG(EL3HLT, "Level 3 halted"),
+	ERRNO_MSG(EL3RST, "Level 3 reset"),
+	ERRNO_MSG(ELNRNG, "Link number out of range"),
+	ERRNO_MSG(EUNATCH, "Protocol driver not attached"),
+	ERRNO_MSG(ENOCSI, "No CSI structure available"),
+	ERRNO_MSG(EL2HLT, "Level 2 halted"),
+	ERRNO_MSG(EBADE, "Invalid exchange"),
+	ERRNO_MSG(EBADR, "Invalid request descriptor"),
+	ERRNO_MSG(EXFULL, "Exchange full"),
+	ERRNO_MSG(ENOANO, "No anode"),
+	ERRNO_MSG(EBADRQC, "Invalid request code"),
+	ERRNO_MSG(EBADSLT, "Invalid slot"),
+	ERRNO_MSG(EDEADLOCK, SAME_AS(EDEADLK)),
+	ERRNO_MSG(EBFONT, "Bad font file format"),
+	ERRNO_MSG(ENOSTR, "Device not a stream"),
+	ERRNO_MSG(ENODATA, "No data available"),
+	ERRNO_MSG(ETIME, "Timer expired"),
+	ERRNO_MSG(ENOSR, "Out of streams resources"),
+	ERRNO_MSG(ENONET, "Machine is not on the network"),
+	ERRNO_MSG(ENOPKG, "Package not installed"),
+	ERRNO_MSG(EREMOTE, "Object is remote"),
+	ERRNO_MSG(ENOLINK, "Link has been severed"),
+	ERRNO_MSG(EADV, "Advertise error"),
+	ERRNO_MSG(ESRMNT, "Srmount error"),
+	ERRNO_MSG(ECOMM, "Communication error on send"),
+	ERRNO_MSG(EPROTO, "Protocol error"),
+	ERRNO_MSG(EMULTIHOP, "Multihop attempted"),
+	ERRNO_MSG(EDOTDOT, "RFS specific error"),
+	ERRNO_MSG(EBADMSG, "Not a data message"),
+	ERRNO_MSG(EOVERFLOW, "Value too large for defined data type"),
+	ERRNO_MSG(ENOTUNIQ, "Name not unique on network"),
+	ERRNO_MSG(EBADFD, "File descriptor in bad state"),
+	ERRNO_MSG(EREMCHG, "Remote address changed"),
+	ERRNO_MSG(ELIBACC, "Can not access a needed shared library"),
+	ERRNO_MSG(ELIBBAD, "Accessing a corrupted shared library"),
+	ERRNO_MSG(ELIBSCN, ".lib section in a.out corrupted"),
+	ERRNO_MSG(ELIBMAX, "Attempting to link in too many shared libraries"),
+	ERRNO_MSG(ELIBEXEC, "Cannot exec a shared library directly"),
+	ERRNO_MSG(EILSEQ, "Illegal byte sequence"),
+	ERRNO_MSG(ERESTART, "Interrupted system call should be restarted"),
+	ERRNO_MSG(ESTRPIPE, "Streams pipe error"),
+	ERRNO_MSG(EUSERS, "Too many users"),
+	ERRNO_MSG(ENOTSOCK, "Socket operation on non-socket"),
+	ERRNO_MSG(EDESTADDRREQ, "Destination address required"),
+	ERRNO_MSG(EMSGSIZE, "Message too long"),
+	ERRNO_MSG(EPROTOTYPE, "Protocol wrong type for socket"),
+	ERRNO_MSG(ENOPROTOOPT, "Protocol not available"),
+	ERRNO_MSG(EPROTONOSUPPORT, "Protocol not supported"),
+	ERRNO_MSG(ESOCKTNOSUPPORT, "Socket type not supported"),
+	ERRNO_MSG(EOPNOTSUPP, "Operation not supported on transport endpoint"),
+	ERRNO_MSG(EPFNOSUPPORT, "Protocol family not supported"),
+	ERRNO_MSG(AFNOSUPPORT, "Address family not supported by protocol"),
+	ERRNO_MSG(EADDRINUSE, "Address already in use"),
+	ERRNO_MSG(EADDRNOTAVAIL, "Cannot assign requested address"),
+	ERRNO_MSG(ENETDOWN, "Network is down"),
+	ERRNO_MSG(ENETUNREACH, "Network is unreachable"),
+	ERRNO_MSG(ENETRESET, "Network dropped connection because of reset"),
+	ERRNO_MSG(ECONNABORTED, "Software caused connection abort"),
+	ERRNO_MSG(ECONNRESET, "Connection reset by peer"),
+	ERRNO_MSG(ENOBUFS, "No buffer space available"),
+	ERRNO_MSG(EISCONN, "Transport endpoint is already connected"),
+	ERRNO_MSG(ENOTCONN, "Transport endpoint is not connected"),
+	ERRNO_MSG(ESHUTDOWN, "Cannot send after transport endpoint shutdown"),
+	ERRNO_MSG(ETOOMANYREFS, "Too many references: cannot splice"),
+	ERRNO_MSG(ETIMEDOUT, "Connection timed out"),
+	ERRNO_MSG(ECONNREFUSED, "Connection refused"),
+	ERRNO_MSG(EHOSTDOWN, "Host is down"),
+	ERRNO_MSG(EHOSTUNREACH, "No route to host"),
+	ERRNO_MSG(EALREADY, "Operation already in progress"),
+	ERRNO_MSG(EINPROGRESS, "Operation now in progress"),
+	ERRNO_MSG(ESTALE, "Stale NFS file handle"),
+	ERRNO_MSG(EUCLEAN, "Structure needs cleaning"),
+	ERRNO_MSG(ENOTNAM, "Not a XENIX named type file"),
+	ERRNO_MSG(ENAVAIL, "No XENIX semaphores available"),
+	ERRNO_MSG(EISNAM, "Is a named type file"),
+	ERRNO_MSG(EREMOTEIO, "Remote I/O error"),
+	ERRNO_MSG(EDQUOT, "Quota exceeded"),
+	ERRNO_MSG(ENOMEDIUM, "No medium found"),
+	ERRNO_MSG(EMEDIUMTYPE, "Wrong medium type"),
+};
+
+const char *errno_str(int errno)
+{
+	if (errno >= 0)
+		return errno_message[0];
+
+	return errno_message[abs(errno)];
+}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 02/19] exynos: config-common: enable errno_str() function
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 03/19] exynos: config-common: enable generic fs command Przemyslaw Marczak
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 include/configs/exynos-common.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/configs/exynos-common.h b/include/configs/exynos-common.h
index 6ba9bb7..fa02cb2 100644
--- a/include/configs/exynos-common.h
+++ b/include/configs/exynos-common.h
@@ -95,4 +95,6 @@
 #define CONFIG_SYS_NO_FLASH
 #undef CONFIG_CMD_IMLS
 
+#define CONFIG_ERRNO_STR
+
 #endif	/* __CONFIG_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 03/19] exynos: config-common: enable generic fs command
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 02/19] exynos: config-common: enable errno_str() function Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
                   ` (18 subsequent siblings)
  21 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 include/configs/exynos-common.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/configs/exynos-common.h b/include/configs/exynos-common.h
index fa02cb2..3d30a25 100644
--- a/include/configs/exynos-common.h
+++ b/include/configs/exynos-common.h
@@ -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 */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (2 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 03/19] exynos: config-common: enable generic fs command Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-10  3:17   ` Simon Glass
  2014-10-08 20:48 ` [U-Boot] [PATCH 05/19] dm: pmic: add implementation of driver model regulator uclass Przemyslaw Marczak
                   ` (17 subsequent siblings)
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 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

diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index dc64e4d..8def501 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -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
diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
new file mode 100644
index 0000000..5e8494b
--- /dev/null
+++ b/drivers/power/pmic-uclass.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * 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
+		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
+	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;
+	}
+}
+
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int addr_cs)
+{
+	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;
+}
+
+int pmic_init_dm(void)
+{
+	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",
+};
diff --git a/drivers/power/pmic_i2c.c b/drivers/power/pmic_i2c.c
new file mode 100644
index 0000000..350d375
--- /dev/null
+++ b/drivers/power/pmic_i2c.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * (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;
+}
diff --git a/drivers/power/pmic_spi.c b/drivers/power/pmic_spi.c
new file mode 100644
index 0000000..7851adf
--- /dev/null
+++ b/drivers/power/pmic_spi.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * (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;
+}
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index e3e9296..e6d9d39 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -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,
 };
diff --git a/include/power/pmic.h b/include/power/pmic.h
index afbc5aa..7114650 100644
--- a/include/power/pmic.h
+++ b/include/power/pmic.h
@@ -1,4 +1,7 @@
 /*
+ *  Copyright (C) 2014 Samsung Electronics
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
  *  Copyright (C) 2011-2012 Samsung Electronics
  *  Lukasz Majewski <l.majewski@samsung.com>
  *
@@ -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
+*/
+struct pmic_platdata {
+	int bus;
+	int interface;
+	int byte_order;
+	int regs_num;
+	union hw hw;
+};
+
+/* 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);
@@ -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);
+#endif
 
 #define pmic_i2c_addr (p->hw.i2c.addr)
 #define pmic_i2c_tx_num (p->hw.i2c.tx_num)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 05/19] dm: pmic: add implementation of driver model regulator uclass
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (3 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-10  3:10   ` Simon Glass
  2014-10-08 20:48 ` [U-Boot] [PATCH 06/19] dm: common: board_r: add call and weak of power_init_dm() Przemyslaw Marczak
                   ` (16 subsequent siblings)
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 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

diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 8def501..9a0b8c4 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -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
diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
new file mode 100644
index 0000000..4c9614e
--- /dev/null
+++ b/drivers/power/regulator-uclass.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * 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)
+		return -EPERM;
+
+	return ops->get_buck_cnt(dev);
+}
+
+struct regulator_desc *pmic_ldo_desc(struct udevice *dev, int ldo)
+{
+	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))
+		return -EIO;
+
+	return val;
+}
+
+int pmic_set_ldo_val(struct udevice *dev, int ldo, int val)
+{
+	const struct dm_regulator_ops *ops;
+
+	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_SET, ldo, &val))
+		return -EIO;
+
+	return 0;
+}
+
+int pmic_get_ldo_mode(struct udevice *dev, int ldo)
+{
+	const struct dm_regulator_ops *ops;
+	int mode;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->ldo_mode)
+		return -EPERM;
+
+	if (ops->ldo_mode(dev, PMIC_OP_GET, ldo, &mode))
+		return -EIO;
+
+	return mode;
+}
+
+int pmic_set_ldo_mode(struct udevice *dev, int ldo, int mode)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->ldo_mode)
+		return -EPERM;
+
+	if (ops->ldo_mode(dev, PMIC_OP_SET, ldo, &mode))
+		return -EIO;
+
+	return 0;
+}
+
+int pmic_get_buck_val(struct udevice *dev, int buck)
+{
+	const struct dm_regulator_ops *ops;
+	int val;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->buck_val)
+		return -EPERM;
+
+	if (ops->buck_val(dev, PMIC_OP_GET, buck, &val))
+		return -EIO;
+
+	return val;
+}
+
+int pmic_set_buck_val(struct udevice *dev, int buck, int val)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->buck_val)
+		return -EPERM;
+
+	if (ops->buck_val(dev, PMIC_OP_SET, buck, &val))
+		return -EIO;
+
+	return 0;
+}
+
+int pmic_get_buck_mode(struct udevice *dev, int buck)
+{
+	const struct dm_regulator_ops *ops;
+	int mode;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->buck_mode)
+		return -EPERM;
+
+	if (ops->buck_mode(dev, PMIC_OP_GET, buck, &mode))
+		return -EIO;
+
+	return mode;
+}
+
+int pmic_set_buck_mode(struct udevice *dev, int buck, int mode)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->buck_mode)
+		return -EPERM;
+
+	if (ops->buck_mode(dev, PMIC_OP_SET, buck, &mode))
+		return -EIO;
+
+	return 0;
+}
+
+UCLASS_DRIVER(pmic_regulator) = {
+	.id		= UCLASS_PMIC_REGULATOR,
+	.name		= "regulator",
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index e6d9d39..147222c 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -31,6 +31,7 @@ enum uclass_id {
 
 	/* PMIC uclass and PMIC related uclasses */
 	UCLASS_PMIC,
+	UCLASS_PMIC_REGULATOR,
 
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
diff --git a/include/power/pmic.h b/include/power/pmic.h
index 7114650..5a06ba4 100644
--- a/include/power/pmic.h
+++ b/include/power/pmic.h
@@ -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);
diff --git a/include/power/regulator.h b/include/power/regulator.h
new file mode 100644
index 0000000..3e2fea6
--- /dev/null
+++ b/include/power/regulator.h
@@ -0,0 +1,267 @@
+/*
+ *  Copyright (C) 2014 Samsung Electronics
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _INCLUDE_REGULATOR_H_
+#define _INCLUDE_REGULATOR_H_
+
+#define DESC_NAME_LEN	20
+
+/* enum regulator_desc_type - used for lbo/buck descriptor calls */
+enum regulator_desc_type {
+	DESC_TYPE_LDO,
+	DESC_TYPE_BUCK,
+};
+
+/**
+ * struct regulator_desc - this structure holds an information about each
+ * ldo/buck 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 or DESC_TYPE_BUCK
+ * @number - hardware ldo/buck number
+ * @min_uV - minimum voltage (micro Volts)
+ * @max_uV - maximum voltage (micro Volts)
+ * @name   - name of LDO - usually will be taken from device tree and can
+ *           describe ldo/buck really connected hardware
+*/
+struct regulator_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
+ * ldo/buck operation modes. Probably in most cases - an array of modes for
+ * each ldo/buck. This will be probably a driver static data since modes
+ * are device specific more than platform specific.
+ *
+ * @type - one of: DESC_TYPE_LDO or DESC_TYPE_BUCK
+ * @val  - a driver specific value for this mode
+ * @name - the name of mode - for command purposes
+ */
+struct regulator_mode_desc {
+	int mode;
+	int reg_val;
+	char *name;
+};
+
+/* PMIC regulator device operations */
+struct dm_regulator_ops {
+	/* Number of LDOs and BUCKs */
+	int (*get_ldo_cnt)(struct udevice *dev);
+	int (*get_buck_cnt)(struct udevice *dev);
+
+	/**
+	 * LDO and BUCK attribute descriptors can be usually const static data,
+	 * but ldo/buck value limits are often defined in the device-tree,
+	 * so those are platform dependent attributes.
+	 */
+	struct regulator_desc *(*get_val_desc)(struct udevice *dev, int d_type,
+					       int d_num);
+
+	/**
+	 * LDO/BUCK mode descriptors are device specific, so in this case
+	 * we have also the same static data for each driver device.
+	 * But each LDO/BUCK regulator can support different modes,
+	 * so this is the reason why returns mode descriptors array for each
+	 * regulator number.
+	*/
+	struct regulator_mode_desc *(*get_mode_desc_array)(struct udevice *dev,
+							   int desc_type,
+							   int desc_num,
+							   int *desc_mode_cnt);
+
+	/**
+	 * The 'ldo/reg_val()' function calls should operate on a micro Volts
+	 * values, however the regulator commands operates in mili Volts.
+	 *
+	 * The 'ldo/buck_mode()' function calls as a taken/returned mode value
+	 * should always use a 'mode' field value from structure type:
+	 * 'regulator_mode_desc'. So for this purpose each driver should defines
+	 * its own mode types for friendly use with regulator commands.
+	 */
+
+	/* LDO operations */
+	int (*ldo_val)(struct udevice *dev, int operation, int ldo,
+		       int *val);
+	int (*ldo_mode)(struct udevice *dev, int operation, int ldo,
+			int *mode);
+
+	/* Buck converters operations */
+	int (*buck_val)(struct udevice *dev, int operation, int buck,
+			int *val);
+	int (*buck_mode)(struct udevice *dev, int operation, int buck,
+			 int *mode);
+};
+
+/**
+ * pmic_ldo_cnt - returns the number of driver registered linear regulators
+ * and this is used for pmic regulator commands purposes.
+ *
+ * @dev - pointer to regulator device
+ * Returns: a null or positive number of LDO or negative value of errno.
+ */
+int pmic_ldo_cnt(struct udevice *dev);
+
+/**
+ * pmic_buck_cnt - returns the number of driver registered step-down converters
+ * and this is used for pmic regulator commands purposes.
+ *
+ * @dev - pointer to regulator device
+ * Returns: a null or positive number of BUCK or negative value of errno.
+ */
+int pmic_buck_cnt(struct udevice *dev);
+
+/**
+ * pmic_ldo_desc - returns a pointer to the regulator value descriptor
+ * of a given device LDO number.
+ *
+ * @dev   - pointer to regulator device
+ * @ldo   - descriptor number: device specific output number
+ * Returns: pointer to descriptor if found or NULL on error.
+ */
+struct regulator_desc *pmic_ldo_desc(struct udevice *dev, int ldo);
+
+/**
+ * pmic_buck_desc -  returns a pointer to the regulator value descriptor
+ * of a given device BUCK number.
+ *
+ * @dev    - pointer to regulator device
+ * @buck   - descriptor number: device specific output number
+ * Returns: pointer to descriptor if found or NULL on error.
+ */
+struct regulator_desc *pmic_buck_desc(struct udevice *dev, int buck);
+
+/**
+ * pmic_ldo_mode_desc - returns a pointer to the array of regulator mode
+ * descriptors of a given device regulator type and number.
+ *
+ * @dev        - pointer to regulator device
+ * @ldo        - output number: device specific
+ * @mode_cnt   - pointer to the returned descriptor array entries
+ * Returns: pointer to descriptor if found or NULL on error.
+ */
+struct regulator_mode_desc *pmic_ldo_mode_desc(struct udevice *dev, int ldo,
+					       int *mode_cnt);
+
+/**
+ * pmic_buck_mode_desc - returns a pointer to the array of regulator mode
+ * descriptors of a given device regulator type and number.
+ *
+ * @dev        - pointer to regulator device
+ * @ldo        - output number: device specific
+ * @mode_cnt   - pointer to the returned descriptor array entries
+ * Returns: pointer to descriptor if found or NULL on error.
+ */
+struct regulator_mode_desc *pmic_buck_mode_desc(struct udevice *dev, int buck,
+					       int *mode_cnt);
+
+/**
+ * pmic_get_ldo_val: returns LDO voltage value of a given device regulator
+ * number.
+ *
+ * @dev        - pointer to regulator device
+ * @ldo        - output number: device specific
+ * Returns:    - ldo number voltage - unit: uV (micro Volts) or -errno if fails
+ */
+int pmic_get_ldo_val(struct udevice *dev, int ldo);
+
+/**
+ * pmic_set_ldo_val: set LDO voltage value of a given device regulator
+ * number to the given value
+ *
+ * @dev    - pointer to regulator device
+ * @ldo    - output number: device specific
+ * @val    - voltage value to set - unit: uV (micro Volts)
+ * Returns - 0 on success or -errno val if fails
+ */
+int pmic_set_ldo_val(struct udevice *dev, int ldo, int val);
+
+/**
+ * pmic_get_ldo_mode: get LDO mode type of a given device regulator
+ * number
+ *
+ * @dev    - pointer to regulator device
+ * @ldo    - output number: device specific
+ * Returns - mode number on success or -errno val if fails
+ * Note:
+ * The regulator driver should return one of defined, continuous mode number,
+ * rather than a raw register value. The struct type 'regulator_mode_desc' has
+ * a mode field for this purpose and reg_val for a raw register value.
+ */
+int pmic_get_ldo_mode(struct udevice *dev, int ldo);
+
+/**
+ * pmic_set_ldo_mode: set given regulator LDO mode to the given mode type
+ *
+ * @dev    - pointer to regulator device
+ * @ldo    - 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, continuous mode number,
+ * rather than a raw register value. The struct type 'regulator_mode_desc' has
+ * a mode field for this purpose and reg_val for a raw register value.
+ */
+int pmic_set_ldo_mode(struct udevice *dev, int ldo, int mode);
+
+/**
+ * pmic_get_buck_val: returns BUCK voltage value of a given device regulator
+ * number.
+ *
+ * @dev    - pointer to regulator device
+ * @ldo    - output number: device specific
+ * Returns - BUCK number voltage - unit: uV (micro Volts) or -errno if fails
+ */
+int pmic_get_buck_val(struct udevice *dev, int buck);
+
+/**
+ * pmic_set_buck_val: set BUCK voltage value of a given device regulator
+ * number to the given value
+ *
+ * @dev    - pointer to regulator device
+ * @buck    - output number: device specific
+ * @val    - voltage value to set - unit: uV (micro Volts)
+ * Returns - 0 on success or -errno val if fails
+ */
+int pmic_set_buck_val(struct udevice *dev, int buck, int val);
+
+/**
+ * pmic_get_buck_mode: get BUCK mode type of a given device regulator
+ * number
+ *
+ * @dev    - pointer to regulator device
+ * @buck    - output number: device specific
+ * Returns - mode number on success or -errno val if fails
+ * Note:
+ * The regulator driver should return one of defined, continuous mode number,
+ * rather than a raw register value. The struct type 'regulator_mode_desc' has
+ * the 'mode' field for this purpose and the 'reg_val' for a raw register value.
+ */
+int pmic_get_buck_mode(struct udevice *dev, int buck);
+
+/**
+ * pmic_set_buck_mode: set given regulator buck mode to the given mode type
+ *
+ * @dev    - pointer to regulator device
+ * @buck   - 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, continuous mode number,
+ * rather than a raw register value. The struct type 'regulator_mode_desc' has
+ * the 'mode' field for this purpose and the 'reg_val' for a raw register value.
+ */
+int pmic_set_buck_mode(struct udevice *dev, int buck, int mode);
+
+#endif /* _INCLUDE_REGULATOR_H_ */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 06/19] dm: common: board_r: add call and weak of power_init_dm()
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (4 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 05/19] dm: pmic: add implementation of driver model regulator uclass Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-10  3:32   ` Simon Glass
  2014-10-08 20:48 ` [U-Boot] [PATCH 07/19] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
                   ` (15 subsequent siblings)
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 common/board_r.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/common/board_r.c b/common/board_r.c
index cd92288..e574130 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -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,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 07/19] dm: pmic: add max77686 pmic driver
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (5 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 06/19] dm: common: board_r: add call and weak of power_init_dm() Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-22 15:31   ` Tom Rini
  2014-10-08 20:48 ` [U-Boot] [PATCH 08/19] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
                   ` (14 subsequent siblings)
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 drivers/power/pmic/Makefile   |  1 +
 drivers/power/pmic/max77686.c | 89 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 90 insertions(+)
 create mode 100644 drivers/power/pmic/max77686.c

diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index e7b07eb..49beffb 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -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
diff --git a/drivers/power/pmic/max77686.c b/drivers/power/pmic/max77686.c
new file mode 100644
index 0000000..74b10da
--- /dev/null
+++ b/drivers/power/pmic/max77686.c
@@ -0,0 +1,89 @@
+/*
+ *  Copyright (C) 2012 Samsung Electronics
+ *  Rajeshwari Shinde <rajeshwari.s@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/root.h>
+#include <dm/lists.h>
+#include <power/max77686_pmic.h>
+#include <errno.h>
+#include <dm.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int max77686_ofdata_to_platdata(struct udevice *dev)
+{
+	struct pmic_platdata *pl = dev->platdata;
+	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", MAX77686_I2C_ADDR);
+	pl->hw.i2c.tx_num = 1;
+
+	return 0;
+}
+
+static int max77686_probe(struct udevice *parent)
+{
+	struct udevice *reg_dev;
+	struct driver *reg_drv;
+	int ret;
+
+	reg_drv = lists_driver_lookup_name("max77686 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 error only if no parent platdata set */
+	return 0;
+}
+
+static const struct udevice_id max77686_ids[] = {
+	{ .compatible = "maxim,max77686_pmic"},
+	{ }
+};
+
+U_BOOT_DRIVER(pmic_max77686) = {
+	.name = "max77686 pmic",
+	.id = UCLASS_PMIC,
+	.of_match = max77686_ids,
+	.probe = max77686_probe,
+	.ofdata_to_platdata = max77686_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct pmic_platdata),
+};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 08/19] dm: regulator: add max77686 regulator driver
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (6 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 07/19] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-22 15:32   ` Tom Rini
  2014-10-08 20:48 ` [U-Boot] [PATCH 09/19] dm: pmic: new commands: pmic and regulator Przemyslaw Marczak
                   ` (13 subsequent siblings)
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 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

diff --git a/Makefile b/Makefile
index 85b90b7..696b0c0 100644
--- a/Makefile
+++ b/Makefile
@@ -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/
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 9a0b8c4..6a6333f 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -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
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
new file mode 100644
index 0000000..9d282e3
--- /dev/null
+++ b/drivers/power/regulator/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (C) 2014 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c
new file mode 100644
index 0000000..d68785c
--- /dev/null
+++ b/drivers/power/regulator/max77686.c
@@ -0,0 +1,749 @@
+/*
+ *  Copyright (C) 2012 Samsung Electronics
+ *  Rajeshwari Shinde <rajeshwari.s@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/max77686_pmic.h>
+#include <errno.h>
+#include <dm.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct max77686_regulator_info {
+	int ldo_cnt;
+	int buck_cnt;
+	struct regulator_desc ldo_desc[MAX77686_LDO_NUM + 1];
+	struct regulator_desc buck_desc[MAX77686_BUCK_NUM + 1];
+};
+
+#define MODE(_mode, _val, _name) { \
+	.mode = _mode, \
+	.reg_val = _val, \
+	.name = _name, \
+}
+
+/* LDO: 1,3,4,5,9,17,18,19,20,21,22,23,24,26,26,27 */
+static struct regulator_mode_desc max77686_ldo_mode_standby1[] = {
+	MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
+	MODE(OPMODE_LPM, MAX77686_LDO_MODE_LPM, "LPM"),
+	MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
+	MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
+};
+
+/* LDO: 2,6,7,8,10,11,12,14,15,16 */
+static struct regulator_mode_desc max77686_ldo_mode_standby2[] = {
+	MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_LDO_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
+	MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
+};
+
+/* Buck: 1 */
+static struct regulator_mode_desc max77686_buck_mode_standby[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+/* Buck: 2,3,4 */
+static struct regulator_mode_desc max77686_buck_mode_lpm[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_LPM, MAX77686_BUCK_MODE_LPM, "LPM"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+/* Buck: 5,6,7,8,9 */
+static struct regulator_mode_desc max77686_buck_mode_onoff[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+static const char max77686_buck_addr[] = {
+	0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38
+};
+
+static int max77686_buck_volt2hex(int buck, int uV)
+{
+	unsigned int hex = 0;
+	unsigned int hex_max = 0;
+
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		/* hex = (uV - 600000) / 12500; */
+		hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP;
+		hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
+		/**
+		 * This uses voltage scaller - temporary not implemented
+		 * so return just 0
+		 */
+		return 0;
+	default:
+		/* hex = (uV - 750000) / 50000; */
+		hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP;
+		hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
+		break;
+	}
+
+	if (hex >= 0 && hex <= hex_max)
+		return hex;
+
+	error("Value: %d uV is wrong for BUCK%d", uV, buck);
+	return -EINVAL;
+}
+
+static int max77686_buck_hex2volt(int buck, int hex)
+{
+	unsigned uV = 0;
+	unsigned int hex_max = 0;
+
+	if (hex < 0)
+		goto bad_hex;
+
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
+		if (hex > hex_max)
+			goto bad_hex;
+
+		/* uV = hex * 12500 + 600000; */
+		uV = hex * MAX77686_BUCK_UV_LSTEP + MAX77686_BUCK_UV_LMIN;
+		break;
+	default:
+		hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
+		if (hex > hex_max)
+			goto bad_hex;
+
+		/* uV = hex * 50000 + 750000; */
+		uV = hex * MAX77686_BUCK_UV_HSTEP + MAX77686_BUCK_UV_HMIN;
+		break;
+	}
+
+	return uV;
+
+bad_hex:
+	error("Value: %#x is wrong for BUCK%d", hex, buck);
+	return -EINVAL;
+}
+
+static int max77686_ldo_volt2hex(int ldo, int uV)
+{
+	unsigned int hex = 0;
+
+	switch (ldo) {
+	case 1:
+	case 2:
+	case 6:
+	case 7:
+	case 8:
+	case 15:
+		hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_LSTEP;
+		/* hex = (uV - 800000) / 25000; */
+		break;
+	default:
+		hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_HSTEP;
+		/* hex = (uV - 800000) / 50000; */
+	}
+
+	if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX)
+		return hex;
+
+	error("Value: %d uV is wrong for LDO%d", uV, ldo);
+	return -EINVAL;
+}
+
+static int max77686_ldo_hex2volt(int ldo, int hex)
+{
+	unsigned int uV = 0;
+
+	if (hex > MAX77686_LDO_VOLT_MAX_HEX)
+		goto bad_hex;
+
+	switch (ldo) {
+	case 1:
+	case 2:
+	case 6:
+	case 7:
+	case 8:
+	case 15:
+		/* uV = hex * 25000 + 800000; */
+		uV = hex * MAX77686_LDO_UV_LSTEP + MAX77686_LDO_UV_MIN;
+		break;
+	default:
+		/* uV = hex * 50000 + 800000; */
+		uV = hex * MAX77686_LDO_UV_HSTEP + MAX77686_LDO_UV_MIN;
+	}
+
+	return uV;
+
+bad_hex:
+	error("Value: %#x is wrong for ldo%d", hex, ldo);
+	return -EINVAL;
+}
+
+static int max77686_ldo_hex2mode(int ldo, int hex)
+{
+	if (hex > MAX77686_LDO_MODE_MASK)
+		return -EINVAL;
+
+	switch (hex) {
+	case MAX77686_LDO_MODE_OFF:
+		return OPMODE_OFF;
+	case MAX77686_LDO_MODE_LPM: /* == MAX77686_LDO_MODE_STANDBY: */
+		/* The same mode values but different meaning for each ldo */
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			return OPMODE_STANDBY;
+		default:
+			return OPMODE_LPM;
+		}
+	case MAX77686_LDO_MODE_STANDBY_LPM:
+		return OPMODE_STANDBY_LPM;
+	case MAX77686_LDO_MODE_ON:
+		return OPMODE_ON;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int max77686_buck_hex2mode(int buck, int hex)
+{
+	if (hex > MAX77686_BUCK_MODE_MASK)
+		return -EINVAL;
+
+	switch (hex) {
+	case MAX77686_BUCK_MODE_OFF:
+		return OPMODE_OFF;
+	case MAX77686_BUCK_MODE_ON:
+		return OPMODE_ON;
+	case MAX77686_BUCK_MODE_STANDBY:
+		switch (buck) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+			return OPMODE_STANDBY;
+		default:
+			return -EINVAL;
+		}
+	case MAX77686_BUCK_MODE_LPM:
+		switch (buck) {
+		case 2:
+		case 3:
+		case 4:
+			return OPMODE_LPM;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int max77686_get_ldo_cnt(struct udevice *dev)
+{
+	return MAX77686_LDO_NUM;
+}
+
+static int max77686_get_buck_cnt(struct udevice *dev)
+{
+	return MAX77686_BUCK_NUM;
+}
+
+struct regulator_desc *max77686_get_val_desc(struct udevice *dev, int d_type,
+					     int d_num)
+{
+	struct max77686_regulator_info *dev_info;
+
+	if (!dev)
+		return NULL;
+
+	if (!dev->priv)
+		return NULL;
+
+	dev_info = dev->priv;
+
+	switch (d_type) {
+	case DESC_TYPE_LDO:
+		if (d_num < 1 || d_num > 26)
+			return NULL;
+
+		return &dev_info->ldo_desc[d_num];
+	case DESC_TYPE_BUCK:
+		if (d_num < 1 || d_num > 9)
+			return NULL;
+
+		return &dev_info->buck_desc[d_num];
+	default:
+		return NULL;
+	}
+}
+
+static struct regulator_mode_desc *
+max77686_get_mode_desc_array(struct udevice *dev, int d_type, int d_num,
+			     int *d_mode_cnt)
+{
+	struct max77686_regulator_info *dev_info;
+
+	if (!dev)
+		return NULL;
+
+	if (!dev->priv)
+		return NULL;
+
+	dev_info = dev->priv;
+	*d_mode_cnt = 0;
+
+	if (d_type == DESC_TYPE_LDO) {
+		if (d_num < 1 || d_num > dev_info->ldo_cnt)
+			return NULL;
+
+		switch (d_num) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			*d_mode_cnt = ARRAY_SIZE(max77686_ldo_mode_standby2);
+			return max77686_ldo_mode_standby2;
+		default:
+			*d_mode_cnt = ARRAY_SIZE(max77686_ldo_mode_standby1);
+			return max77686_ldo_mode_standby1;
+		}
+	} else if (d_type == DESC_TYPE_BUCK) {
+		if (d_num < 1 || d_num > dev_info->buck_cnt)
+			return NULL;
+
+		switch (d_num) {
+		case 1:
+			*d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_standby);
+			return max77686_buck_mode_standby;
+		case 2:
+		case 3:
+		case 4:
+			*d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_lpm);
+			return max77686_buck_mode_lpm;
+		default:
+			*d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_onoff);
+			return max77686_buck_mode_onoff;
+		}
+	}
+
+	return NULL;
+}
+
+static int max77686_ldo_val(struct udevice *dev, int op,
+				int ldo, int *uV)
+{
+	unsigned int val, ret, hex, adr;
+
+	if (op == PMIC_OP_GET)
+		*uV = 0;
+
+	if (ldo < 1 || ldo > 26) {
+		debug("%s: %d is wrong ldo number\n", __func__, ldo);
+		return -EINVAL;
+	}
+
+	adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
+
+	ret = pmic_reg_read(dev, adr, &val);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= MAX77686_LDO_VOLT_MASK;
+		ret = max77686_ldo_hex2volt(ldo, val);
+		if (ret < 0)
+			return ret;
+		*uV = ret;
+		return 0;
+	}
+
+	hex = max77686_ldo_volt2hex(ldo, *uV);
+	if (hex < 0)
+		return -EINVAL;
+
+	val &= ~MAX77686_LDO_VOLT_MASK;
+	val |= hex;
+	ret |= pmic_reg_write(dev, adr, val);
+
+	return ret;
+}
+
+static int max77686_buck_val(struct udevice *dev, int op,
+				int buck, int *uV)
+{
+	unsigned int val, hex, ret, mask, adr;
+
+	if (buck < 1 || buck > 9) {
+		debug("%s: %d is wrong buck number\n", __func__, buck);
+		return -EINVAL;
+	}
+
+	if (op == PMIC_OP_GET)
+		*uV = 0;
+
+	/* &buck_out = ctrl + 1 */
+	adr = max77686_buck_addr[buck] + 1;
+
+	/* mask */
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		/* Those uses voltage scallers - will support in the future */
+		mask = MAX77686_BUCK234_VOLT_MASK;
+		return -EPERM;
+	default:
+		mask = MAX77686_BUCK_VOLT_MASK;
+	}
+
+	ret = pmic_reg_read(dev, adr, &val);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= mask;
+		ret = max77686_buck_hex2volt(buck, val);
+		if (ret < 0)
+			return ret;
+		*uV = ret;
+		return 0;
+	}
+
+	hex = max77686_buck_volt2hex(buck, *uV);
+	if (hex < 0)
+		return -EINVAL;
+
+	val &= ~mask;
+	val |= hex;
+	ret = pmic_reg_write(dev, adr, val);
+
+	return ret;
+}
+
+static int max77686_ldo_mode(struct udevice *dev, int op, int ldo, int *opmode)
+{
+	unsigned int val, ret, adr, mode;
+
+	if (op == PMIC_OP_GET)
+		*opmode = -EINVAL;
+
+	if (ldo < 1 || ldo > 26) {
+		debug("%s: %d is wrong ldo number\n", __func__, ldo);
+		return -EINVAL;
+	}
+
+	adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
+
+	ret = pmic_reg_read(dev, adr, &val);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= MAX77686_LDO_MODE_MASK;
+		ret = max77686_ldo_hex2mode(ldo, val);
+		if (ret < 0)
+			return ret;
+		*opmode = ret;
+		return 0;
+	}
+
+	/* mode */
+	switch (*opmode) {
+	case OPMODE_OFF:
+		mode = MAX77686_LDO_MODE_OFF;
+		break;
+	case OPMODE_LPM:
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			return -EINVAL;
+		default:
+			mode = MAX77686_LDO_MODE_LPM;
+		}
+		break;
+	case OPMODE_STANDBY:
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			mode = MAX77686_LDO_MODE_STANDBY;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case OPMODE_STANDBY_LPM:
+		mode = MAX77686_LDO_MODE_STANDBY_LPM;
+		break;
+	case OPMODE_ON:
+		mode = MAX77686_LDO_MODE_ON;
+		break;
+	default:
+		mode = 0xff;
+	}
+
+	if (mode == 0xff) {
+		debug("%s: %d is not supported on LDO%d\n", __func__, *opmode,
+							    ldo);
+		return -EINVAL;
+	}
+
+	val &= ~MAX77686_LDO_MODE_MASK;
+	val |= mode;
+	ret |= pmic_reg_write(dev, adr, val);
+
+	return ret;
+}
+
+static int max77686_buck_mode(struct udevice *dev, int op, int buck,
+			      int *opmode)
+{
+	unsigned int val, ret, mask, adr, mode, mode_shift;
+
+	if (buck < 1 || buck > 9) {
+		debug("%s: %d is wrong buck number\n", __func__, buck);
+		return -EINVAL;
+	}
+
+	adr = max77686_buck_addr[buck];
+
+	/* mask */
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		mode_shift = MAX77686_BUCK_MODE_SHIFT_2;
+		break;
+	default:
+		mode_shift = MAX77686_BUCK_MODE_SHIFT_1;
+	}
+
+	mask = MAX77686_BUCK_MODE_MASK << mode_shift;
+
+	ret = pmic_reg_read(dev, adr, &val);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= mask;
+		val >>= mode_shift;
+		ret = max77686_buck_hex2mode(buck, val);
+		if (ret < 0)
+			return ret;
+		*opmode = ret;
+		return 0;
+	}
+
+	/* mode */
+	switch (*opmode) {
+	case OPMODE_OFF:
+		mode = MAX77686_BUCK_MODE_OFF;
+		break;
+	case OPMODE_STANDBY:
+		switch (buck) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+			mode = MAX77686_BUCK_MODE_STANDBY << mode_shift;
+			break;
+		default:
+			mode = 0xff;
+		}
+		break;
+	case OPMODE_LPM:
+		switch (buck) {
+		case 2:
+		case 3:
+		case 4:
+			mode = MAX77686_BUCK_MODE_LPM << mode_shift;
+			break;
+		default:
+			mode = 0xff;
+		}
+		break;
+	case OPMODE_ON:
+		mode = MAX77686_BUCK_MODE_ON << mode_shift;
+		break;
+	default:
+		mode = 0xff;
+	}
+
+	if (mode == 0xff) {
+		debug("%s: %d is not supported on BUCK%d\n", __func__, *opmode,
+							     buck);
+		return -EINVAL;
+	}
+
+	val &= ~mask;
+	val |= mode;
+	ret |= pmic_reg_write(dev, adr, val);
+
+	return ret;
+}
+
+int fill_reg_desc(int offset, struct regulator_desc *desc, int reg_num,
+		  int desc_type)
+{
+	const void *blob = gd->fdt_blob;
+	char *reg_name;
+	int reg_min, reg_max, len;
+
+	desc->type = desc_type;
+	desc->number = reg_num;
+
+	reg_name = (char *)fdt_getprop(blob, offset, "regulator-name", &len);
+	if (reg_name) {
+		strncpy(&desc->name[0], reg_name, DESC_NAME_LEN);
+		desc->name[DESC_NAME_LEN - 1] = '\0';
+	} else {
+		sprintf(&desc->name[0], "-");
+	}
+
+	reg_min = fdtdec_get_int(blob, offset, "regulator-min-microvolt", -1);
+	desc->min_uV = reg_min;
+
+	reg_max = fdtdec_get_int(blob, offset, "regulator-min-microvolt", -1);
+	desc->max_uV = reg_max;
+
+	return 0;
+}
+
+static int get_desc_for_compat(int regulators_node, char *compat, int desc_type,
+			       struct regulator_desc *desc, int desc_num)
+{
+	const void *blob = gd->fdt_blob;
+	const char *reg_compat;
+	int compat_len = strlen(compat);
+	int reg_num;
+	int offset;
+	int cnt = 0;
+
+	/* First subnode of "voltage_regulators" node */
+	offset = fdt_first_subnode(blob, regulators_node);
+
+	while (offset > 0) {
+		/* Get regulator properties */
+		reg_compat = (char *)fdt_getprop(blob, offset,
+			     "regulator-compatible", NULL);
+
+		/* Check compat and compare the number only */
+		reg_num = 0;
+		if (strstr(reg_compat, compat))
+			reg_num = simple_strtoul(reg_compat + compat_len,
+						 NULL, 0);
+		else
+			goto next_offset;
+
+		if (reg_num && reg_num <= desc_num)
+			fill_reg_desc(offset, &desc[reg_num], reg_num,
+				      desc_type);
+
+		cnt++;
+		if (cnt == desc_num)
+			return cnt;
+
+next_offset:
+		offset = fdt_next_subnode(blob, offset);
+	}
+
+	return cnt;
+}
+
+static int reg_max77686_ofdata_to_platdata(struct udevice *dev)
+{
+	struct max77686_regulator_info *dev_info = dev->priv;
+	const void *blob = gd->fdt_blob;
+	int node = dev->of_offset;
+	int offset;
+
+	if (!dev->platdata) {
+		error("Device %s: no platdata", dev->name);
+		return 0;
+	}
+
+	if (node < 0) {
+		error("Device: %s - bad of_offset", dev->name);
+		return 0;
+	}
+
+	offset = fdt_subnode_offset(blob, node,  "voltage-regulators");
+	if (offset < 0) {
+		error("Node %s has no 'voltage-regulators' subnode", dev->name);
+		return 0;
+	}
+
+	dev_info->ldo_cnt = get_desc_for_compat(offset, "LDO", DESC_TYPE_LDO,
+						dev_info->ldo_desc,
+						MAX77686_LDO_NUM);
+
+	dev_info->buck_cnt = get_desc_for_compat(offset, "BUCK", DESC_TYPE_BUCK,
+						 dev_info->buck_desc,
+						 MAX77686_BUCK_NUM);
+
+	/* Even if there is no voltage regulators node in dts,
+	 * always return success and allow the driver to bind.
+	 * Then we can still do read/write pmic registers.
+	 */
+	return 0;
+}
+
+static const struct dm_regulator_ops reg_max77686_ops = {
+	.get_ldo_cnt	= max77686_get_ldo_cnt,
+	.get_buck_cnt	= max77686_get_buck_cnt,
+	.get_val_desc	= max77686_get_val_desc,
+	.get_mode_desc_array	= max77686_get_mode_desc_array,
+	.ldo_val	= max77686_ldo_val,
+	.buck_val	= max77686_buck_val,
+	.ldo_mode	= max77686_ldo_mode,
+	.buck_mode	= max77686_buck_mode,
+};
+
+U_BOOT_DRIVER(max77686_regulator) = {
+	.name = "max77686 regulator",
+	.id = UCLASS_PMIC_REGULATOR,
+	.ops = &reg_max77686_ops,
+	.ofdata_to_platdata = reg_max77686_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct max77686_regulator_info),
+};
diff --git a/include/power/max77686_pmic.h b/include/power/max77686_pmic.h
index c2a772a..10a2cc2 100644
--- a/include/power/max77686_pmic.h
+++ b/include/power/max77686_pmic.h
@@ -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 */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 09/19] dm: pmic: new commands: pmic and regulator
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (7 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 08/19] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-22 15:32   ` Tom Rini
  2014-10-08 20:48 ` [U-Boot] [PATCH 10/19] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
                   ` (12 subsequent siblings)
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 drivers/power/Makefile   |   1 +
 drivers/power/cmd_pmic.c | 845 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 846 insertions(+)
 create mode 100644 drivers/power/cmd_pmic.c

diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 6a6333f..2af63f2 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -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
diff --git a/drivers/power/cmd_pmic.c b/drivers/power/cmd_pmic.c
new file mode 100644
index 0000000..399fd85
--- /dev/null
+++ b/drivers/power/cmd_pmic.c
@@ -0,0 +1,845 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * (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 <linux/ctype.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>
+
+#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
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct udevice *pmic_curr;
+static struct udevice *reg_curr;
+
+enum info_type {
+	INFO_NAME,
+	INFO_STATE,
+	INFO_DESC,
+	INFO_DESC_MODE,
+	INFO_DESC_VAL,
+};
+
+static char * const pmic_interfaces[] = {
+	"I2C",
+	"SPI",
+	"--",
+};
+
+const char *pmic_if_str(int interface)
+{
+	if (interface >= ARRAY_SIZE(pmic_interfaces))
+		return NULL;
+
+	return pmic_interfaces[interface];
+}
+
+char *desc_type_str(int desc_type)
+{
+	static char *type_name[] = {"LDO", "BUCK"};
+
+	switch (desc_type) {
+	case DESC_TYPE_LDO:
+	case DESC_TYPE_BUCK:
+		return type_name[desc_type];
+	default:
+		return NULL;
+	}
+}
+
+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 pmic_platdata *p;
+	int i, ret;
+	u32 val;
+
+	dev = get_curr_dev(uclass_id);
+	if (!dev)
+		return -ENODEV;
+
+	p = dev->platdata;
+	if (!p)
+		return -EPERM;
+
+	ret = curr_dev_name(uclass_id);
+	if (ret)
+		return CMD_RET_USAGE;
+
+	printf("Register count: %u\n", p->regs_num);
+
+	for (i = 0; i < p->regs_num; i++) {
+		ret = pmic_reg_read(dev, i, &val);
+		if (ret) {
+			printf("PMIC: Registers dump failed: %d\n", ret);
+			return -EIO;
+		}
+
+		if (!(i % 8))
+			printf("\n0x%02x: ", i);
+
+		printf("%08x ", val);
+	}
+	puts("\n");
+	return 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)
+{
+	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;
+	struct pmic_platdata *pl;
+	int addr_cs;
+	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;
+	}
+
+	uc_drv = uc->uc_drv;
+	uclass_foreach_dev(dev, uc) {
+		if (dev)
+			device_probe(dev);
+		else
+			continue;
+
+		printf("| %2.d |", dev->seq);
+
+		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
+			len = snprintf(buf, STR_BUF_LEN, " - ");
+
+		display_column(buf, len, DRV_STR_LIMIT);
+
+		pl = dev_get_platdata(dev);
+		if (!pl) {
+			len = snprintf(buf, STR_BUF_LEN, " - ");
+			display_column(buf, len, IF_STR_LIMIT);
+			puts("\n");
+			continue;
+		}
+
+		if (pl->interface == PMIC_I2C)
+			addr_cs = pl->hw.i2c.addr;
+		else
+			addr_cs = pl->hw.spi.cs;
+
+		len = snprintf(buf, STR_BUF_LEN, " %s%d at 0x%x",
+			       pmic_if_str(pl->interface),
+			       pl->bus, addr_cs);
+		display_column(buf, len, IF_STR_LIMIT);
+
+		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)
+				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);
+
+	if (!get_curr_dev(uclass_id))
+		return -ENODEV;
+
+	return 1;
+}
+
+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;
+}
+
+/**
+ * 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
+ *
+ * @info_type 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 desc_type, int start_reg,
+			    int end_reg, int info_type)
+{
+	struct udevice *dev;
+	struct regulator_desc *desc;
+	struct regulator_mode_desc *mode_desc;
+	char *mode_name;
+	int mode_cnt;
+	int mode;
+	int reg_val, i, j;
+	int div = 1000;
+
+	if (start_reg > end_reg)
+		return -EINVAL;
+
+	dev = get_curr_dev(UCLASS_PMIC_REGULATOR);
+	if (!dev)
+		return -ENODEV;
+
+	switch (info_type) {
+	case INFO_STATE:
+		puts(" RegN:  val mV   mode");
+		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++) {
+		switch (desc_type) {
+		case DESC_TYPE_LDO:
+			desc = pmic_ldo_desc(dev, i);
+			mode_desc = pmic_ldo_mode_desc(dev, i, &mode_cnt);
+			mode = pmic_get_ldo_mode(dev, i);
+			break;
+		case DESC_TYPE_BUCK:
+			desc = pmic_buck_desc(dev, i);
+			mode_desc = pmic_buck_mode_desc(dev, i, &mode_cnt);
+			mode = pmic_get_buck_mode(dev, i);
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		if (desc_type == DESC_TYPE_LDO)
+			reg_val = pmic_get_ldo_val(dev, i);
+		else
+			reg_val = pmic_get_buck_val(dev, i);
+
+		/* no such regulator number */
+		if (reg_val < 0)
+			continue;
+
+		/* Display in mV */
+		reg_val /= div;
+
+		printf("\n%s%.2d:", desc_type_str(desc_type), i);
+
+		switch (info_type) {
+		case INFO_STATE:
+			printf(" %d mV ",  reg_val);
+
+			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 (info_type != 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
+ * @desc_type: descriptor type: DESC_TYPE_LDO or DESC_TYPE_BUCK
+ */
+static int check_reg_mode(struct udevice *dev, int reg_num, int set_mode,
+			  int desc_type)
+{
+	struct regulator_mode_desc *mode_desc;
+	int i, mode_cnt;
+
+	switch (desc_type) {
+	case DESC_TYPE_LDO:
+		mode_desc = pmic_ldo_mode_desc(dev, reg_num, &mode_cnt);
+		if (!mode_desc)
+			goto nodesc;
+
+		break;
+	case DESC_TYPE_BUCK:
+		mode_desc = pmic_buck_mode_desc(dev, reg_num, &mode_cnt);
+		if (!mode_desc)
+			goto nodesc;
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	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(desc_type, reg_num, reg_num, INFO_DESC_MODE);
+
+	return -EINVAL;
+
+nodesc:
+	printf("%s%.2d - no descriptor found\n", desc_type_str(desc_type),
+						 reg_num);
+	return -ENODEV;
+}
+
+static int set_reg_mode(int desc_type, int reg_num, int set_mode)
+{
+	int ret;
+	struct udevice *dev;
+
+	dev = get_curr_dev(UCLASS_PMIC_REGULATOR);
+	if (!dev)
+		return -ENODEV;
+
+	if (check_reg_mode(dev, reg_num, set_mode, desc_type))
+		return -EPERM;
+
+	printf("Set %s%.2d mode: %d\n", desc_type_str(desc_type),
+					     reg_num, set_mode);
+
+	if (desc_type == DESC_TYPE_LDO)
+		ret = pmic_set_ldo_mode(dev, reg_num, set_mode);
+	else
+		ret = pmic_set_buck_mode(dev, 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
+ * @desc_type: descriptor type: DESC_TYPE_LDO or DESC_TYPE_BUCK
+ */
+static int check_reg_val(struct udevice *dev, int reg_num, int set_val,
+			 int desc_type)
+{
+	struct regulator_desc *desc;
+
+	switch (desc_type) {
+	case DESC_TYPE_LDO:
+		desc = pmic_ldo_desc(dev, reg_num);
+		if (!desc)
+			goto nodesc;
+
+		break;
+	case DESC_TYPE_BUCK:
+		desc = pmic_buck_desc(dev, reg_num);
+		if (!desc)
+			goto nodesc;
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	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(desc_type, reg_num, reg_num, INFO_DESC_VAL);
+
+	return -EINVAL;
+nodesc:
+	printf("%s%.2d - no descriptor found\n", desc_type_str(desc_type),
+						 reg_num);
+	return -ENODEV;
+}
+
+static int set_reg_val(int desc_type, int reg_num, int set_val, int set_force)
+{
+	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;
+
+	if (!set_force && check_reg_val(dev, reg_num, set_val, desc_type))
+		return -EPERM;
+
+	printf("Set %s%.2d val: %d mV", desc_type_str(desc_type),
+					reg_num, (set_val / 1000));
+
+	if (set_force)
+		puts(" (force)\n");
+	else
+		puts("\n");
+
+	if (desc_type == DESC_TYPE_LDO)
+		ret = pmic_set_ldo_val(dev, reg_num, set_val);
+	else
+		ret = pmic_set_buck_val(dev, reg_num, set_val);
+
+	return ret;
+}
+
+static int reg_subcmd(char * const argv[], int argc, int desc_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(desc_type, start_reg, end_reg,
+					INFO_NAME);
+	else if (!strcmp("state", cmd))
+		return display_reg_info(desc_type, start_reg, end_reg,
+					INFO_STATE);
+	else if (!strcmp("desc", cmd))
+		return display_reg_info(desc_type, start_reg, end_reg,
+					INFO_DESC);
+
+	/* Check if regulator number is given */
+	if (reg_num < 0) {
+		puts("reg num?\n");
+		return -EINVAL;
+	}
+
+	/* 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(desc_type, reg_num, set_val, set_force);
+	}
+
+	if (!strcmp("setmode", cmd))
+		return set_reg_mode(desc_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 desc_type;
+	int cmd_len;
+	char *cmd;
+	int ret = 0;
+	int cmd_type_len = 0;
+	int reg_num = -1;
+	int reg_cnt;
+	char cmd_type_str[5];
+
+	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;
+
+	/* cmd_type: ldo, buck, ldoN, buckN */
+	if (!strncmp(cmd, "ldo", 3)) {
+		reg_cnt = pmic_ldo_cnt(dev);
+		desc_type = DESC_TYPE_LDO;
+		cmd_type_len = 3;
+		strncpy(cmd_type_str, cmd, cmd_type_len);
+		cmd_type_str[cmd_type_len] = '\0';
+	} else if (!strncmp(cmd, "buck", 4)) {
+		reg_cnt = pmic_buck_cnt(dev);
+		desc_type = DESC_TYPE_BUCK;
+		cmd_type_len = 4;
+		strncpy(cmd_type_str, cmd, cmd_type_len);
+		cmd_type_str[cmd_type_len] = '\0';
+	} else {
+		ret = -EINVAL;
+		goto finish;
+	}
+
+	/* Get regulator number */
+	reg_num = -1;
+	if (cmd_len > cmd_type_len && isdigit(cmd[cmd_type_len]))
+		reg_num = (int)simple_strtoul(cmd + cmd_type_len, NULL, 0);
+
+	/* Subcommand missed? */
+	if (argc == 1) {
+		printf("name/state/desc/setval/setmode ?\n");
+		return CMD_RET_SUCCESS;
+	}
+
+	argv++;
+	argc--;
+	ret = reg_subcmd(argv, argc, desc_type, reg_num, reg_cnt);
+
+finish:
+	if (ret < 0) {
+		printf("Error: %s\n", errno_str(ret));
+		return CMD_RET_FAILURE;
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	static struct udevice *dev;
+	unsigned reg, val;
+	char *cmd;
+	int ret = 0;
+
+	if (!argc)
+		return CMD_RET_USAGE;
+
+	/* list, dev, dump */
+	ret = pmic_cmd(argv, argc, UCLASS_PMIC);
+	if (ret != 1)
+		goto finish;
+
+	dev = get_curr_dev(UCLASS_PMIC);
+
+	cmd = argv[0];
+	if (strcmp(cmd, "read") == 0) {
+		if (argc != 2) {
+			ret = -EINVAL;
+			goto finish;
+		}
+
+		reg = simple_strtoul(argv[1], NULL, 0);
+		ret = pmic_reg_read(dev, reg, &val);
+		if (ret)
+			puts("PMIC: Register read failed\n");
+		else
+			printf("0x%02x: 0x%08x\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_reg_write(dev, reg, val);
+		if (ret)
+			puts("PMIC: Register write failed\n");
+
+		goto finish;
+	}
+
+finish:
+	if (ret < 0) {
+		printf("Error: %s\n", errno_str(ret));
+		return CMD_RET_FAILURE;
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t cmd_pmic[] = {
+	U_BOOT_CMD_MKENT(pmic, 5, 1, do_pmic, "", ""),
+	U_BOOT_CMD_MKENT(regulator, 5, 1, do_regulator, "", ""),
+};
+
+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);
+}
+
+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"
+);
+
+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][N] [name/state/desc]"
+	"\n\t- print regulator(s) info\n"
+	"regulator [ldoN/buckN] [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"
+	"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"
+);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 10/19] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (8 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 09/19] dm: pmic: new commands: pmic and regulator Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 11/19] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 board/samsung/common/board.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/board/samsung/common/board.c b/board/samsung/common/board.c
index e1fc123..e4cddc2 100644
--- a/board/samsung/common/board.c
+++ b/board/samsung/common/board.c
@@ -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();
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 11/19] doc: driver-model: pmic and regulator uclass documentation
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (9 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 10/19] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-10  3:36   ` Simon Glass
  2014-10-08 20:48 ` [U-Boot] [PATCH 12/19] samsung: board: lcd menu: check if any power framework is enabled Przemyslaw Marczak
                   ` (10 subsequent siblings)
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 doc/driver-model/dm-pmic-framework.txt | 450 +++++++++++++++++++++++++++++++++
 1 file changed, 450 insertions(+)
 create mode 100644 doc/driver-model/dm-pmic-framework.txt

diff --git a/doc/driver-model/dm-pmic-framework.txt b/doc/driver-model/dm-pmic-framework.txt
new file mode 100644
index 0000000..1b69eee
--- /dev/null
+++ b/doc/driver-model/dm-pmic-framework.txt
@@ -0,0 +1,450 @@
+#
+# (C) Copyright 2014 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# 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.
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 12/19] samsung: board: lcd menu: check if any power framework is enabled
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (10 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 11/19] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 13/19] samsung: misc: power_key_pressed: add support to dm pmic framework Przemyslaw Marczak
                   ` (9 subsequent siblings)
  21 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 board/samsung/common/board.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/board/samsung/common/board.c b/board/samsung/common/board.c
index e4cddc2..450740a 100644
--- a/board/samsung/common/board.c
+++ b/board/samsung/common/board.c
@@ -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
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 13/19] samsung: misc: power_key_pressed: add support to dm pmic framework
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (11 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 12/19] samsung: board: lcd menu: check if any power framework is enabled Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-10  3:37   ` Simon Glass
  2014-10-08 20:48 ` [U-Boot] [PATCH 14/19] trats2: board: add support to dm pmic api Przemyslaw Marczak
                   ` (8 subsequent siblings)
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 board/samsung/common/misc.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
index 4538ac7..94308a9 100644
--- a/board/samsung/common/misc.c
+++ b/board/samsung/common/misc.c
@@ -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
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 14/19] trats2: board: add support to dm pmic api
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (12 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 13/19] samsung: misc: power_key_pressed: add support to dm pmic framework Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-10  3:39   ` Simon Glass
  2014-10-08 20:48 ` [U-Boot] [PATCH 15/19] trats2: dts: max77686: add pmic alias and names cleanup Przemyslaw Marczak
                   ` (7 subsequent siblings)
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 board/samsung/trats2/trats2.c | 208 +++++++++++-------------------------------
 1 file changed, 52 insertions(+), 156 deletions(-)

diff --git a/board/samsung/trats2/trats2.c b/board/samsung/trats2/trats2.c
index a737749..b107ba5 100644
--- a/board/samsung/trats2/trats2.c
+++ b/board/samsung/trats2/trats2.c
@@ -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)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 15/19] trats2: dts: max77686: add pmic alias and names cleanup
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (13 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 14/19] trats2: board: add support to dm pmic api Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 16/19] trats2: config: enable dm pmic, dm regulator api, dm max77686 Przemyslaw Marczak
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 arch/arm/dts/exynos4412-trats2.dts | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/arch/arm/dts/exynos4412-trats2.dts b/arch/arm/dts/exynos4412-trats2.dts
index 3b1e458..fac57d7 100644
--- a/arch/arm/dts/exynos4412-trats2.dts
+++ b/arch/arm/dts/exynos4412-trats2.dts
@@ -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;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 16/19] trats2: config: enable dm pmic, dm regulator api, dm max77686
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (14 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 15/19] trats2: dts: max77686: add pmic alias and names cleanup Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-10  3:40   ` Simon Glass
  2014-10-08 20:48 ` [U-Boot] [PATCH 17/19] odroid: board: add support to dm pmic api Przemyslaw Marczak
                   ` (5 subsequent siblings)
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 include/configs/trats2.h | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/include/configs/trats2.h b/include/configs/trats2.h
index 42481ab..6d04498 100644
--- a/include/configs/trats2.h
+++ b/include/configs/trats2.h
@@ -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
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 17/19] odroid: board: add support to dm pmic api
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (15 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 16/19] trats2: config: enable dm pmic, dm regulator api, dm max77686 Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 18/19] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
                   ` (4 subsequent siblings)
  21 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 board/samsung/odroid/odroid.c | 31 +++++++++++++++++--------------
 1 file changed, 17 insertions(+), 14 deletions(-)

diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
index fd5d2d2..eac578f 100644
--- a/board/samsung/odroid/odroid.c
+++ b/board/samsung/odroid/odroid.c
@@ -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 = {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 18/19] odroid: dts: add 'voltage-regulators' description to max77686 node
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (16 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 17/19] odroid: board: add support to dm pmic api Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-08 20:48 ` [U-Boot] [PATCH 19/19] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
                   ` (3 subsequent siblings)
  21 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 arch/arm/dts/exynos4412-odroid.dts | 250 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 249 insertions(+), 1 deletion(-)

diff --git a/arch/arm/dts/exynos4412-odroid.dts b/arch/arm/dts/exynos4412-odroid.dts
index 4c5e2b3..b6abd51 100644
--- a/arch/arm/dts/exynos4412-odroid.dts
+++ b/arch/arm/dts/exynos4412-odroid.dts
@@ -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>;
+				};
+			};
 		};
 	};
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 19/19] odroid: config: enable dm pmic, dm regulator and max77686 driver
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (17 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 18/19] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
@ 2014-10-08 20:48 ` Przemyslaw Marczak
  2014-10-08 20:55 ` [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (2 subsequent siblings)
  21 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:48 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 include/configs/odroid.h | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/include/configs/odroid.h b/include/configs/odroid.h
index b928af8..ccc602d 100644
--- a/include/configs/odroid.h
+++ b/include/configs/odroid.h
@@ -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
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (18 preceding siblings ...)
  2014-10-08 20:48 ` [U-Boot] [PATCH 19/19] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
@ 2014-10-08 20:55 ` Przemyslaw Marczak
  2014-10-09  6:05   ` Simon Glass
  2014-10-22 15:31 ` Tom Rini
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
  21 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-08 20:55 UTC (permalink / raw)
  To: u-boot

Hello all,

On 10/08/2014 10:48 PM, Przemyslaw Marczak wrote:
> 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
>

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,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model
  2014-10-08 20:55 ` [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
@ 2014-10-09  6:05   ` Simon Glass
  2014-10-09 15:04     ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-10-09  6:05 UTC (permalink / raw)
  To: u-boot

Hi,

On 8 October 2014 14:55, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello all,
>
>
> On 10/08/2014 10:48 PM, Przemyslaw Marczak wrote:

[snip]

> 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

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message
  2014-10-08 20:48 ` [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message Przemyslaw Marczak
@ 2014-10-09  6:46   ` Joakim Tjernlund
  2014-10-09 16:23     ` Przemyslaw Marczak
  2014-10-22 15:31   ` Tom Rini
  1 sibling, 1 reply; 218+ messages in thread
From: Joakim Tjernlund @ 2014-10-09  6:46 UTC (permalink / raw)
  To: u-boot

> From: Przemyslaw Marczak <p.marczak@samsung.com>
> 
> 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

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model
  2014-10-09  6:05   ` Simon Glass
@ 2014-10-09 15:04     ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-09 15:04 UTC (permalink / raw)
  To: u-boot

Hello,

On 10/09/2014 08:05 AM, Simon Glass wrote:
> Hi,
>
> On 8 October 2014 14:55, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Hello all,
>>
>>
>> On 10/08/2014 10:48 PM, Przemyslaw Marczak wrote:
>
> [snip]
>
>> 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
>

No problem, it is available here:

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

branch: dm-working-pmic

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message
  2014-10-09  6:46   ` Joakim Tjernlund
@ 2014-10-09 16:23     ` Przemyslaw Marczak
  2014-10-09 22:53       ` Simon Glass
  2014-10-10  5:03       ` Joakim Tjernlund
  0 siblings, 2 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-09 16:23 UTC (permalink / raw)
  To: u-boot

Hello Joakim,

On 10/09/2014 08:46 AM, Joakim Tjernlund wrote:
>> From: Przemyslaw Marczak <p.marczak@samsung.com>
>>
>> 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
>

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,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message
  2014-10-09 16:23     ` Przemyslaw Marczak
@ 2014-10-09 22:53       ` Simon Glass
  2014-10-10  5:03       ` Joakim Tjernlund
  1 sibling, 0 replies; 218+ messages in thread
From: Simon Glass @ 2014-10-09 22:53 UTC (permalink / raw)
  To: u-boot

On 9 October 2014 10:23, Przemyslaw Marczak <p.marczak@samsung.com> wrote:

> Hello Joakim,
>
> On 10/09/2014 08:46 AM, Joakim Tjernlund wrote:
>
>> From: Przemyslaw Marczak <p.marczak@samsung.com>
>>>
>>> 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
>>
>>
> 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.
>

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

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 05/19] dm: pmic: add implementation of driver model regulator uclass
  2014-10-08 20:48 ` [U-Boot] [PATCH 05/19] dm: pmic: add implementation of driver model regulator uclass Przemyslaw Marczak
@ 2014-10-10  3:10   ` Simon Glass
  2014-10-10 13:41     ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-10-10  3:10 UTC (permalink / raw)
  To: u-boot

Hi,

On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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(...)

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

>
> 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
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  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
>
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index 8def501..9a0b8c4 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -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
> diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
> new file mode 100644
> index 0000000..4c9614e
> --- /dev/null
> +++ b/drivers/power/regulator-uclass.c
> @@ -0,0 +1,250 @@
> +/*
> + * Copyright (C) 2014 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * 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)
> +               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.

> +{
> +       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))
> +               return -EIO;
> +
> +       return val;
> +}
> +

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass
  2014-10-08 20:48 ` [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
@ 2014-10-10  3:17   ` Simon Glass
  2014-10-10 13:32     ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-10-10  3:17 UTC (permalink / raw)
  To: u-boot

Hi,

On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  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
>
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index dc64e4d..8def501 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -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
> diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
> new file mode 100644
> index 0000000..5e8494b
> --- /dev/null
> +++ b/drivers/power/pmic-uclass.c
> @@ -0,0 +1,255 @@
> +/*
> + * Copyright (C) 2014 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * 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
> +               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.

> +       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;
> +       }
> +}
> +
> +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.

> +{
> +       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;
> +}
> +
> +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.

> +{
> +       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",
> +};
> diff --git a/drivers/power/pmic_i2c.c b/drivers/power/pmic_i2c.c
> new file mode 100644
> index 0000000..350d375
> --- /dev/null
> +++ b/drivers/power/pmic_i2c.c
> @@ -0,0 +1,136 @@
> +/*
> + * Copyright (C) 2014 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * Copyright (C) 2011 Samsung Electronics
> + * Lukasz Majewski <l.majewski@samsung.com>
> + *
> + * (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;
> +}
> diff --git a/drivers/power/pmic_spi.c b/drivers/power/pmic_spi.c
> new file mode 100644
> index 0000000..7851adf
> --- /dev/null
> +++ b/drivers/power/pmic_spi.c
> @@ -0,0 +1,137 @@
> +/*
> + * Copyright (C) 2014 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * Copyright (C) 2011 Samsung Electronics
> + * Lukasz Majewski <l.majewski@samsung.com>
> + *
> + * (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;
> +}
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index e3e9296..e6d9d39 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -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,
>  };
> diff --git a/include/power/pmic.h b/include/power/pmic.h
> index afbc5aa..7114650 100644
> --- a/include/power/pmic.h
> +++ b/include/power/pmic.h
> @@ -1,4 +1,7 @@
>  /*
> + *  Copyright (C) 2014 Samsung Electronics
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
>   *  Copyright (C) 2011-2012 Samsung Electronics
>   *  Lukasz Majewski <l.majewski@samsung.com>
>   *
> @@ -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
> +*/
> +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.

> +};
> +
> +/* 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);
> @@ -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.

> +#endif
>
>  #define pmic_i2c_addr (p->hw.i2c.addr)
>  #define pmic_i2c_tx_num (p->hw.i2c.tx_num)
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 06/19] dm: common: board_r: add call and weak of power_init_dm()
  2014-10-08 20:48 ` [U-Boot] [PATCH 06/19] dm: common: board_r: add call and weak of power_init_dm() Przemyslaw Marczak
@ 2014-10-10  3:32   ` Simon Glass
  2014-10-20 15:45     ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-10-10  3:32 UTC (permalink / raw)
  To: u-boot

Hi,

On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This function call is required to init dm pmic framework
> and drivers before call to power_init_board().
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  common/board_r.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
>
> diff --git a/common/board_r.c b/common/board_r.c
> index cd92288..e574130 100644
> --- a/common/board_r.c
> +++ b/common/board_r.c
> @@ -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

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

>         power_init_board,
>  #ifndef CONFIG_SYS_NO_FLASH
>         initr_flash,
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 11/19] doc: driver-model: pmic and regulator uclass documentation
  2014-10-08 20:48 ` [U-Boot] [PATCH 11/19] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
@ 2014-10-10  3:36   ` Simon Glass
  2014-10-10 13:45     ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-10-10  3:36 UTC (permalink / raw)
  To: u-boot

Hi,

On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  doc/driver-model/dm-pmic-framework.txt | 450 +++++++++++++++++++++++++++++++++
>  1 file changed, 450 insertions(+)
>  create mode 100644 doc/driver-model/dm-pmic-framework.txt
>
> diff --git a/doc/driver-model/dm-pmic-framework.txt b/doc/driver-model/dm-pmic-framework.txt
> new file mode 100644
> index 0000000..1b69eee
> --- /dev/null
> +++ b/doc/driver-model/dm-pmic-framework.txt
> @@ -0,0 +1,450 @@
> +#
> +# (C) Copyright 2014 Samsung Electronics
> +# Przemyslaw Marczak <p.marczak@samsung.com>
> +#
> +# 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);

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.

> +       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.
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 13/19] samsung: misc: power_key_pressed: add support to dm pmic framework
  2014-10-08 20:48 ` [U-Boot] [PATCH 13/19] samsung: misc: power_key_pressed: add support to dm pmic framework Przemyslaw Marczak
@ 2014-10-10  3:37   ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2014-10-10  3:37 UTC (permalink / raw)
  To: u-boot

Hi,

On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  board/samsung/common/misc.c | 21 ++++++++++++++++++---
>  1 file changed, 18 insertions(+), 3 deletions(-)
>
> diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
> index 4538ac7..94308a9 100644
> --- a/board/samsung/common/misc.c
> +++ b/board/samsung/common/misc.c
> @@ -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 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.

> +
> +       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
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 14/19] trats2: board: add support to dm pmic api
  2014-10-08 20:48 ` [U-Boot] [PATCH 14/19] trats2: board: add support to dm pmic api Przemyslaw Marczak
@ 2014-10-10  3:39   ` Simon Glass
  2014-10-10 13:46     ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-10-10  3:39 UTC (permalink / raw)
  To: u-boot

Hi,

On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  board/samsung/trats2/trats2.c | 208 +++++++++++-------------------------------
>  1 file changed, 52 insertions(+), 156 deletions(-)
>
> diff --git a/board/samsung/trats2/trats2.c b/board/samsung/trats2/trats2.c
> index a737749..b107ba5 100644
> --- a/board/samsung/trats2/trats2.c
> +++ b/board/samsung/trats2/trats2.c
> @@ -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);

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

> +#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)
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 16/19] trats2: config: enable dm pmic, dm regulator api, dm max77686
  2014-10-08 20:48 ` [U-Boot] [PATCH 16/19] trats2: config: enable dm pmic, dm regulator api, dm max77686 Przemyslaw Marczak
@ 2014-10-10  3:40   ` Simon Glass
  2014-10-10 13:50     ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-10-10  3:40 UTC (permalink / raw)
  To: u-boot

Hi,

On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  include/configs/trats2.h | 14 ++++++--------
>  1 file changed, 6 insertions(+), 8 deletions(-)
>
> diff --git a/include/configs/trats2.h b/include/configs/trats2.h
> index 42481ab..6d04498 100644
> --- a/include/configs/trats2.h
> +++ b/include/configs/trats2.h
> @@ -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
> --
> 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

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message
  2014-10-09 16:23     ` Przemyslaw Marczak
  2014-10-09 22:53       ` Simon Glass
@ 2014-10-10  5:03       ` Joakim Tjernlund
  2014-10-10 11:49         ` Przemyslaw Marczak
  1 sibling, 1 reply; 218+ messages in thread
From: Joakim Tjernlund @ 2014-10-10  5:03 UTC (permalink / raw)
  To: u-boot

Przemyslaw Marczak <p.marczak@samsung.com> wrote on 2014/10/09 18:23:54:
> 
> Hello Joakim,
> 
> On 10/09/2014 08:46 AM, Joakim Tjernlund wrote:
> >> From: Przemyslaw Marczak <p.marczak@samsung.com>
> >>
> >> 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
> >
> 
> 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

> 
> 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.

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message
  2014-10-10  5:03       ` Joakim Tjernlund
@ 2014-10-10 11:49         ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-10 11:49 UTC (permalink / raw)
  To: u-boot

Hello Joakim,

On 10/10/2014 07:03 AM, Joakim Tjernlund wrote:
> Przemyslaw Marczak <p.marczak@samsung.com> wrote on 2014/10/09 18:23:54:
>>
>> Hello Joakim,
>>
>> On 10/09/2014 08:46 AM, Joakim Tjernlund wrote:
>>>> From: Przemyslaw Marczak <p.marczak@samsung.com>
>>>>
>>>> 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
>>>
>>
>> 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.

>>
>> 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.
>
>
>

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,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass
  2014-10-10  3:17   ` Simon Glass
@ 2014-10-10 13:32     ` Przemyslaw Marczak
  2014-10-10 23:18       ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-10 13:32 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 10/10/2014 05:17 AM, Simon Glass wrote:
> Hi,
>
> On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>   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
>>
>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> index dc64e4d..8def501 100644
>> --- a/drivers/power/Makefile
>> +++ b/drivers/power/Makefile
>> @@ -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
>> diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
>> new file mode 100644
>> index 0000000..5e8494b
>> --- /dev/null
>> +++ b/drivers/power/pmic-uclass.c
>> @@ -0,0 +1,255 @@
>> +/*
>> + * Copyright (C) 2014 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * 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
>> +               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.
>

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?

>> +       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;
>> +       }
>> +}
>> +
>> +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.

>> +{
>> +       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;
>> +}
>> +
>> +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?

>> +{
>> +       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",
>> +};
>> diff --git a/drivers/power/pmic_i2c.c b/drivers/power/pmic_i2c.c
>> new file mode 100644
>> index 0000000..350d375
>> --- /dev/null
>> +++ b/drivers/power/pmic_i2c.c
>> @@ -0,0 +1,136 @@
>> +/*
>> + * Copyright (C) 2014 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * Copyright (C) 2011 Samsung Electronics
>> + * Lukasz Majewski <l.majewski@samsung.com>
>> + *
>> + * (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;
>> +}
>> diff --git a/drivers/power/pmic_spi.c b/drivers/power/pmic_spi.c
>> new file mode 100644
>> index 0000000..7851adf
>> --- /dev/null
>> +++ b/drivers/power/pmic_spi.c
>> @@ -0,0 +1,137 @@
>> +/*
>> + * Copyright (C) 2014 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * Copyright (C) 2011 Samsung Electronics
>> + * Lukasz Majewski <l.majewski@samsung.com>
>> + *
>> + * (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;
>> +}
>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>> index e3e9296..e6d9d39 100644
>> --- a/include/dm/uclass-id.h
>> +++ b/include/dm/uclass-id.h
>> @@ -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,
>>   };
>> diff --git a/include/power/pmic.h b/include/power/pmic.h
>> index afbc5aa..7114650 100644
>> --- a/include/power/pmic.h
>> +++ b/include/power/pmic.h
>> @@ -1,4 +1,7 @@
>>   /*
>> + *  Copyright (C) 2014 Samsung Electronics
>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>>    *  Copyright (C) 2011-2012 Samsung Electronics
>>    *  Lukasz Majewski <l.majewski@samsung.com>
>>    *
>> @@ -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
>> +*/
>> +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.
>
>> +};
>> +
>> +/* 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);
>> @@ -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.

>> +#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,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 05/19] dm: pmic: add implementation of driver model regulator uclass
  2014-10-10  3:10   ` Simon Glass
@ 2014-10-10 13:41     ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-10 13:41 UTC (permalink / raw)
  To: u-boot

On 10/10/2014 05:10 AM, Simon Glass wrote:
> Hi,
>
> On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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(...)
>
> I've just got a few high-level comment on this series so will respond
> to each patch.
>
Ok
>>
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>   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
>>
>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> index 8def501..9a0b8c4 100644
>> --- a/drivers/power/Makefile
>> +++ b/drivers/power/Makefile
>> @@ -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
>> diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
>> new file mode 100644
>> index 0000000..4c9614e
>> --- /dev/null
>> +++ b/drivers/power/regulator-uclass.c
>> @@ -0,0 +1,250 @@
>> +/*
>> + * Copyright (C) 2014 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * 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)
>> +               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.
>> +{
>> +       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))
>> +               return -EIO;
>> +
>> +       return val;
>> +}
>> +
>
> Regards,
> Simon
>

Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 11/19] doc: driver-model: pmic and regulator uclass documentation
  2014-10-10  3:36   ` Simon Glass
@ 2014-10-10 13:45     ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-10 13:45 UTC (permalink / raw)
  To: u-boot

Hello,

On 10/10/2014 05:36 AM, Simon Glass wrote:
> Hi,
>
> On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>   doc/driver-model/dm-pmic-framework.txt | 450 +++++++++++++++++++++++++++++++++
>>   1 file changed, 450 insertions(+)
>>   create mode 100644 doc/driver-model/dm-pmic-framework.txt
>>
>> diff --git a/doc/driver-model/dm-pmic-framework.txt b/doc/driver-model/dm-pmic-framework.txt
>> new file mode 100644
>> index 0000000..1b69eee
>> --- /dev/null
>> +++ b/doc/driver-model/dm-pmic-framework.txt
>> @@ -0,0 +1,450 @@
>> +#
>> +# (C) Copyright 2014 Samsung Electronics
>> +# Przemyslaw Marczak <p.marczak@samsung.com>
>> +#
>> +# 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);
>
> 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.

>> +       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.
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thank you,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 14/19] trats2: board: add support to dm pmic api
  2014-10-10  3:39   ` Simon Glass
@ 2014-10-10 13:46     ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-10 13:46 UTC (permalink / raw)
  To: u-boot

Hello,

On 10/10/2014 05:39 AM, Simon Glass wrote:
> Hi,
>
> On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>   board/samsung/trats2/trats2.c | 208 +++++++++++-------------------------------
>>   1 file changed, 52 insertions(+), 156 deletions(-)
>>
>> diff --git a/board/samsung/trats2/trats2.c b/board/samsung/trats2/trats2.c
>> index a737749..b107ba5 100644
>> --- a/board/samsung/trats2/trats2.c
>> +++ b/board/samsung/trats2/trats2.c
>> @@ -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);
>
> If you just ensure that CONFIG_DM_PMIC is defined by the board then
> you can avoid this.
>

Ok

>> +#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)
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>
Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 16/19] trats2: config: enable dm pmic, dm regulator api, dm max77686
  2014-10-10  3:40   ` Simon Glass
@ 2014-10-10 13:50     ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-10 13:50 UTC (permalink / raw)
  To: u-boot

Hello,

On 10/10/2014 05:40 AM, Simon Glass wrote:
> Hi,
>
> On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>   include/configs/trats2.h | 14 ++++++--------
>>   1 file changed, 6 insertions(+), 8 deletions(-)
>>
>> diff --git a/include/configs/trats2.h b/include/configs/trats2.h
>> index 42481ab..6d04498 100644
>> --- a/include/configs/trats2.h
>> +++ b/include/configs/trats2.h
>> @@ -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
>> --
>> 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
>
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,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass
  2014-10-10 13:32     ` Przemyslaw Marczak
@ 2014-10-10 23:18       ` Simon Glass
  2014-10-20 15:44         ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-10-10 23:18 UTC (permalink / raw)
  To: u-boot

Hi,

On 10 October 2014 07:32, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 10/10/2014 05:17 AM, Simon Glass wrote:
>>
>> Hi,
>>
>> On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>>>
>>> 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
>>>
>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>> ---
>>>   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
>>>
>>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>>> index dc64e4d..8def501 100644
>>> --- a/drivers/power/Makefile
>>> +++ b/drivers/power/Makefile
>>> @@ -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
>>> diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
>>> new file mode 100644
>>> index 0000000..5e8494b
>>> --- /dev/null
>>> +++ b/drivers/power/pmic-uclass.c
>>> @@ -0,0 +1,255 @@
>>> +/*
>>> + * Copyright (C) 2014 Samsung Electronics
>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>> + *
>>> + * 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
>>> +               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.
>>
>
> 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.

>
>
>>> +       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;
>>> +       }
>>> +}
>>> +
>>> +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.

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);

>
>
>>> +{
>>> +       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;
>>> +}
>>> +
>>> +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?

No there is no such check at present.

>
>
>>> +{
>>> +       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",
>>> +};
>>> diff --git a/drivers/power/pmic_i2c.c b/drivers/power/pmic_i2c.c
>>> new file mode 100644
>>> index 0000000..350d375
>>> --- /dev/null
>>> +++ b/drivers/power/pmic_i2c.c
>>> @@ -0,0 +1,136 @@
>>> +/*
>>> + * Copyright (C) 2014 Samsung Electronics
>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>> + *
>>> + * Copyright (C) 2011 Samsung Electronics
>>> + * Lukasz Majewski <l.majewski@samsung.com>
>>> + *
>>> + * (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;
>>> +}
>>> diff --git a/drivers/power/pmic_spi.c b/drivers/power/pmic_spi.c
>>> new file mode 100644
>>> index 0000000..7851adf
>>> --- /dev/null
>>> +++ b/drivers/power/pmic_spi.c
>>> @@ -0,0 +1,137 @@
>>> +/*
>>> + * Copyright (C) 2014 Samsung Electronics
>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>> + *
>>> + * Copyright (C) 2011 Samsung Electronics
>>> + * Lukasz Majewski <l.majewski@samsung.com>
>>> + *
>>> + * (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;
>>> +}
>>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>>> index e3e9296..e6d9d39 100644
>>> --- a/include/dm/uclass-id.h
>>> +++ b/include/dm/uclass-id.h
>>> @@ -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,
>>>   };
>>> diff --git a/include/power/pmic.h b/include/power/pmic.h
>>> index afbc5aa..7114650 100644
>>> --- a/include/power/pmic.h
>>> +++ b/include/power/pmic.h
>>> @@ -1,4 +1,7 @@
>>>   /*
>>> + *  Copyright (C) 2014 Samsung Electronics
>>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>>> + *
>>>    *  Copyright (C) 2011-2012 Samsung Electronics
>>>    *  Lukasz Majewski <l.majewski@samsung.com>
>>>    *
>>> @@ -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
>>> +*/
>>> +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.
>>
>>> +};
>>> +
>>> +/* 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);
>>> @@ -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.
>
>>> +#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 :)

Very interested to see this development.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass
  2014-10-10 23:18       ` Simon Glass
@ 2014-10-20 15:44         ` Przemyslaw Marczak
  2014-10-20 15:46           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-20 15:44 UTC (permalink / raw)
  To: u-boot

Hello Simon,

I missed some of your comments.

On 10/11/2014 01:18 AM, Simon Glass wrote:
> Hi,
>
> On 10 October 2014 07:32, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Hello Simon,
>>
>>
>> On 10/10/2014 05:17 AM, Simon Glass wrote:
>>>
>>> Hi,
>>>
>>> On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>>>>
>>>> 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
>>>>
>>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>>> ---
>>>>    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
>>>>
>>>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>>>> index dc64e4d..8def501 100644
>>>> --- a/drivers/power/Makefile
>>>> +++ b/drivers/power/Makefile
>>>> @@ -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
>>>> diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
>>>> new file mode 100644
>>>> index 0000000..5e8494b
>>>> --- /dev/null
>>>> +++ b/drivers/power/pmic-uclass.c
>>>> @@ -0,0 +1,255 @@
>>>> +/*
>>>> + * Copyright (C) 2014 Samsung Electronics
>>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>>> + *
>>>> + * 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
>>>> +               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.
>>>
>>
>> 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.
>

Ok, now I see.

>>
>>
>>>> +       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;
>>>> +       }
>>>> +}
>>>> +
>>>> +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.
>
> 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.

>>
>>
>>>> +{
>>>> +       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;
>>>> +}
>>>> +
>>>> +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?
>
> No there is no such check at present.
>
>>
>>
>>>> +{
>>>> +       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",
>>>> +};
>>>> diff --git a/drivers/power/pmic_i2c.c b/drivers/power/pmic_i2c.c
>>>> new file mode 100644
>>>> index 0000000..350d375
>>>> --- /dev/null
>>>> +++ b/drivers/power/pmic_i2c.c
>>>> @@ -0,0 +1,136 @@
>>>> +/*
>>>> + * Copyright (C) 2014 Samsung Electronics
>>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>>> + *
>>>> + * Copyright (C) 2011 Samsung Electronics
>>>> + * Lukasz Majewski <l.majewski@samsung.com>
>>>> + *
>>>> + * (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;
>>>> +}
>>>> diff --git a/drivers/power/pmic_spi.c b/drivers/power/pmic_spi.c
>>>> new file mode 100644
>>>> index 0000000..7851adf
>>>> --- /dev/null
>>>> +++ b/drivers/power/pmic_spi.c
>>>> @@ -0,0 +1,137 @@
>>>> +/*
>>>> + * Copyright (C) 2014 Samsung Electronics
>>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>>> + *
>>>> + * Copyright (C) 2011 Samsung Electronics
>>>> + * Lukasz Majewski <l.majewski@samsung.com>
>>>> + *
>>>> + * (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;
>>>> +}
>>>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>>>> index e3e9296..e6d9d39 100644
>>>> --- a/include/dm/uclass-id.h
>>>> +++ b/include/dm/uclass-id.h
>>>> @@ -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,
>>>>    };
>>>> diff --git a/include/power/pmic.h b/include/power/pmic.h
>>>> index afbc5aa..7114650 100644
>>>> --- a/include/power/pmic.h
>>>> +++ b/include/power/pmic.h
>>>> @@ -1,4 +1,7 @@
>>>>    /*
>>>> + *  Copyright (C) 2014 Samsung Electronics
>>>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>>>> + *
>>>>     *  Copyright (C) 2011-2012 Samsung Electronics
>>>>     *  Lukasz Majewski <l.majewski@samsung.com>
>>>>     *
>>>> @@ -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
>>>> +*/
>>>> +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.
>>>
>>>> +};
>>>> +
>>>> +/* 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);
>>>> @@ -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.
>>
>>>> +#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 :)
>
> 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,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 06/19] dm: common: board_r: add call and weak of power_init_dm()
  2014-10-10  3:32   ` Simon Glass
@ 2014-10-20 15:45     ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-20 15:45 UTC (permalink / raw)
  To: u-boot

Hello Simon,
On 10/10/2014 05:32 AM, Simon Glass wrote:
> Hi,
>
> On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This function call is required to init dm pmic framework
>> and drivers before call to power_init_board().
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>   common/board_r.c | 8 ++++++++
>>   1 file changed, 8 insertions(+)
>>
>> diff --git a/common/board_r.c b/common/board_r.c
>> index cd92288..e574130 100644
>> --- a/common/board_r.c
>> +++ b/common/board_r.c
>> @@ -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
>
> 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.

>>          power_init_board,
>>   #ifndef CONFIG_SYS_NO_FLASH
>>          initr_flash,
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass
  2014-10-20 15:44         ` Przemyslaw Marczak
@ 2014-10-20 15:46           ` Simon Glass
  2014-10-20 15:51             ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-10-20 15:46 UTC (permalink / raw)
  To: u-boot

Hi,

On 20 October 2014 09:44, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
> I missed some of your comments.
>
>
> On 10/11/2014 01:18 AM, Simon Glass wrote:
>>
>> Hi,
>>
>> On 10 October 2014 07:32, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>>> Hello Simon,
>>>
>>>
>>> On 10/10/2014 05:17 AM, Simon Glass wrote:
>>>>
>>>>
>>>> Hi,
>>>>
>>>> On 8 October 2014 14:48, Przemyslaw Marczak <p.marczak@samsung.com>
>>>> wrote:
>>>>>
>>>>>
>>>>> 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
>>>>>
>>>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>>>> ---
>>>>>    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
>>>>>
>>>>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>>>>> index dc64e4d..8def501 100644
>>>>> --- a/drivers/power/Makefile
>>>>> +++ b/drivers/power/Makefile
>>>>> @@ -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
>>>>> diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
>>>>> new file mode 100644
>>>>> index 0000000..5e8494b
>>>>> --- /dev/null
>>>>> +++ b/drivers/power/pmic-uclass.c
>>>>> @@ -0,0 +1,255 @@
>>>>> +/*
>>>>> + * Copyright (C) 2014 Samsung Electronics
>>>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>>>> + *
>>>>> + * 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
>>>>> +               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.
>>>>
>>>
>>> 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.
>>
>
> Ok, now I see.
>
>
>>>
>>>
>>>>> +       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;
>>>>> +       }
>>>>> +}
>>>>> +
>>>>> +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.
>>
>>
>> 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.
>
>
>>>
>>>
>>>>> +{
>>>>> +       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;
>>>>> +}
>>>>> +
>>>>> +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?
>>
>>
>> No there is no such check at present.
>>
>>>
>>>
>>>>> +{
>>>>> +       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",
>>>>> +};
>>>>> diff --git a/drivers/power/pmic_i2c.c b/drivers/power/pmic_i2c.c
>>>>> new file mode 100644
>>>>> index 0000000..350d375
>>>>> --- /dev/null
>>>>> +++ b/drivers/power/pmic_i2c.c
>>>>> @@ -0,0 +1,136 @@
>>>>> +/*
>>>>> + * Copyright (C) 2014 Samsung Electronics
>>>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>>>> + *
>>>>> + * Copyright (C) 2011 Samsung Electronics
>>>>> + * Lukasz Majewski <l.majewski@samsung.com>
>>>>> + *
>>>>> + * (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;
>>>>> +}
>>>>> diff --git a/drivers/power/pmic_spi.c b/drivers/power/pmic_spi.c
>>>>> new file mode 100644
>>>>> index 0000000..7851adf
>>>>> --- /dev/null
>>>>> +++ b/drivers/power/pmic_spi.c
>>>>> @@ -0,0 +1,137 @@
>>>>> +/*
>>>>> + * Copyright (C) 2014 Samsung Electronics
>>>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>>>> + *
>>>>> + * Copyright (C) 2011 Samsung Electronics
>>>>> + * Lukasz Majewski <l.majewski@samsung.com>
>>>>> + *
>>>>> + * (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;
>>>>> +}
>>>>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>>>>> index e3e9296..e6d9d39 100644
>>>>> --- a/include/dm/uclass-id.h
>>>>> +++ b/include/dm/uclass-id.h
>>>>> @@ -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,
>>>>>    };
>>>>> diff --git a/include/power/pmic.h b/include/power/pmic.h
>>>>> index afbc5aa..7114650 100644
>>>>> --- a/include/power/pmic.h
>>>>> +++ b/include/power/pmic.h
>>>>> @@ -1,4 +1,7 @@
>>>>>    /*
>>>>> + *  Copyright (C) 2014 Samsung Electronics
>>>>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>>>>> + *
>>>>>     *  Copyright (C) 2011-2012 Samsung Electronics
>>>>>     *  Lukasz Majewski <l.majewski@samsung.com>
>>>>>     *
>>>>> @@ -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
>>>>> +*/
>>>>> +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.
>>>>
>>>>> +};
>>>>> +
>>>>> +/* 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);
>>>>> @@ -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.
>>>
>>>>> +#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 :)
>>
>>
>> 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?

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

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass
  2014-10-20 15:46           ` Simon Glass
@ 2014-10-20 15:51             ` Przemyslaw Marczak
  2014-11-06 22:34               ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-20 15:51 UTC (permalink / raw)
  To: u-boot

Hello,

... snip ...
>>
>> 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
>

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

Regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (19 preceding siblings ...)
  2014-10-08 20:55 ` [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
@ 2014-10-22 15:31 ` Tom Rini
  2014-10-24 15:50   ` Przemyslaw Marczak
  2014-10-27 12:41   ` Przemyslaw Marczak
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
  21 siblings, 2 replies; 218+ messages in thread
From: Tom Rini @ 2014-10-22 15:31 UTC (permalink / raw)
  To: u-boot

On Wed, Oct 08, 2014 at 10:48:36PM +0200, Przemyslaw Marczak wrote:

> 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.

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
-------------- 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>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message
  2014-10-08 20:48 ` [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message Przemyslaw Marczak
  2014-10-09  6:46   ` Joakim Tjernlund
@ 2014-10-22 15:31   ` Tom Rini
  2014-12-11  3:25     ` Simon Glass
  1 sibling, 1 reply; 218+ messages in thread
From: Tom Rini @ 2014-10-22 15:31 UTC (permalink / raw)
  To: u-boot

On Wed, Oct 08, 2014 at 10:48:37PM +0200, Przemyslaw Marczak wrote:

> 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
> 
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>

In and of itself,

Reviewed-by: Tom Rini <trini@ti.com>

-- 
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>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 07/19] dm: pmic: add max77686 pmic driver
  2014-10-08 20:48 ` [U-Boot] [PATCH 07/19] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
@ 2014-10-22 15:31   ` Tom Rini
  0 siblings, 0 replies; 218+ messages in thread
From: Tom Rini @ 2014-10-22 15:31 UTC (permalink / raw)
  To: u-boot

On Wed, Oct 08, 2014 at 10:48:43PM +0200, Przemyslaw Marczak wrote:

> 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.
> 
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
[snip]
> +	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:

Reviewed-by: Tom Rini <trini@ti.com>

-- 
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>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 08/19] dm: regulator: add max77686 regulator driver
  2014-10-08 20:48 ` [U-Boot] [PATCH 08/19] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
@ 2014-10-22 15:32   ` Tom Rini
  0 siblings, 0 replies; 218+ messages in thread
From: Tom Rini @ 2014-10-22 15:32 UTC (permalink / raw)
  To: u-boot

On Wed, Oct 08, 2014 at 10:48:44PM +0200, Przemyslaw Marczak wrote:

> 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
> 
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
[snip]
> +	if (ldo < 1 || ldo > 26) {
> +		debug("%s: %d is wrong ldo number\n", __func__, ldo);
> +		return -EINVAL;
> +	}
[snip]
> +	if (buck < 1 || buck > 9) {
> +		debug("%s: %d is wrong buck number\n", __func__, buck);
> +		return -EINVAL;
> +	}
[snip]
> +	if (ldo < 1 || ldo > 26) {
> +		debug("%s: %d is wrong ldo number\n", __func__, ldo);
> +		return -EINVAL;
> +	}
[snip]
> +	if (mode == 0xff) {
> +		debug("%s: %d is not supported on LDO%d\n", __func__, *opmode,
> +							    ldo);
> +		return -EINVAL;
> +	}
[snip]
> +	if (buck < 1 || buck > 9) {
> +		debug("%s: %d is wrong buck number\n", __func__, buck);
> +		return -EINVAL;
> +	}
[snip]
> +	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.

-- 
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>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 09/19] dm: pmic: new commands: pmic and regulator
  2014-10-08 20:48 ` [U-Boot] [PATCH 09/19] dm: pmic: new commands: pmic and regulator Przemyslaw Marczak
@ 2014-10-22 15:32   ` Tom Rini
  0 siblings, 0 replies; 218+ messages in thread
From: Tom Rini @ 2014-10-22 15:32 UTC (permalink / raw)
  To: u-boot

On Wed, Oct 08, 2014 at 10:48:45PM +0200, Przemyslaw Marczak wrote:

> 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.
> 
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
[snip]
> +	if (ret < 0) {
> +		printf("Error: %s\n", errno_str(ret));
> +		return CMD_RET_FAILURE;
> +	}
[snip]
> +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!

-- 
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>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model
  2014-10-22 15:31 ` Tom Rini
@ 2014-10-24 15:50   ` Przemyslaw Marczak
  2014-10-27 12:41   ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-24 15:50 UTC (permalink / raw)
  To: u-boot

Hello,

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

... snip ...

>
> 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.

-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model
  2014-10-22 15:31 ` Tom Rini
  2014-10-24 15:50   ` Przemyslaw Marczak
@ 2014-10-27 12:41   ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-10-27 12:41 UTC (permalink / raw)
  To: u-boot

Hello Tom,

On 10/22/2014 05:31 PM, Tom Rini wrote:
> On Wed, Oct 08, 2014 at 10:48:36PM +0200, Przemyslaw Marczak wrote:
>
>> 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.
>
> 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.
>

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,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass
  2014-10-20 15:51             ` Przemyslaw Marczak
@ 2014-11-06 22:34               ` Simon Glass
  2014-11-12 10:29                 ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-11-06 22:34 UTC (permalink / raw)
  To: u-boot

Hi,


On 20 October 2014 09:51, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>
> Hello,
>
> ... snip ...
>>>
>>>
>>> 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
>>
>
> 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

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass
  2014-11-06 22:34               ` Simon Glass
@ 2014-11-12 10:29                 ` Przemyslaw Marczak
  2014-11-12 15:26                   ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-11-12 10:29 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 11/06/2014 11:34 PM, Simon Glass wrote:
> Hi,
>
>
> On 20 October 2014 09:51, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>>
>> Hello,
>>
>> ... snip ...
>>>>
>>>>
>>>> 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
>>>
>>
>> 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,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass
  2014-11-12 10:29                 ` Przemyslaw Marczak
@ 2014-11-12 15:26                   ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2014-11-12 15:26 UTC (permalink / raw)
  To: u-boot

Hi Prezemyslaw,

On 12 November 2014 03:29, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 11/06/2014 11:34 PM, Simon Glass wrote:
>>
>> Hi,
>>
>>
>> On 20 October 2014 09:51, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>>>
>>> Hello,
>>>
>>> ... snip ...
>>>>>
>>>>>
>>>>>
>>>>> 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
>>>>
>>>
>>> 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.

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

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message
  2014-10-22 15:31   ` Tom Rini
@ 2014-12-11  3:25     ` Simon Glass
  2014-12-11 10:11       ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2014-12-11  3:25 UTC (permalink / raw)
  To: u-boot

On 22 October 2014 at 09:31, Tom Rini <trini@ti.com> wrote:
> On Wed, Oct 08, 2014 at 10:48:37PM +0200, Przemyslaw Marczak wrote:
>
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>
> In and of itself,
>
> Reviewed-by: Tom Rini <trini@ti.com>

Applied to u-boot-dm, thanks!

- Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message
  2014-12-11  3:25     ` Simon Glass
@ 2014-12-11 10:11       ` Przemyslaw Marczak
  2014-12-11 13:24         ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2014-12-11 10:11 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 12/11/2014 04:25 AM, Simon Glass wrote:
> On 22 October 2014 at 09:31, Tom Rini <trini@ti.com> wrote:
>> On Wed, Oct 08, 2014 at 10:48:37PM +0200, Przemyslaw Marczak wrote:
>>
>>> 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
>>>
>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>
>> In and of itself,
>>
>> Reviewed-by: Tom Rini <trini@ti.com>
>
> 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,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message
  2014-12-11 10:11       ` Przemyslaw Marczak
@ 2014-12-11 13:24         ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2014-12-11 13:24 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 11 December 2014 at 03:11, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 12/11/2014 04:25 AM, Simon Glass wrote:
>>
>> On 22 October 2014 at 09:31, Tom Rini <trini@ti.com> wrote:
>>>
>>> On Wed, Oct 08, 2014 at 10:48:37PM +0200, Przemyslaw Marczak wrote:
>>>
>>>> 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
>>>>
>>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>>
>>>
>>> In and of itself,
>>>
>>> Reviewed-by: Tom Rini <trini@ti.com>
>>
>>
>> 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

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model
  2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
                   ` (20 preceding siblings ...)
  2014-10-22 15:31 ` Tom Rini
@ 2015-03-03 16:24 ` Przemyslaw Marczak
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 01/12] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
                     ` (15 more replies)
  21 siblings, 16 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

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

-- 
1.9.1

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 01/12] exynos5: fix build break by adding CONFIG_POWER
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-04 12:19     ` Minkyu Kang
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 02/12] dm: device: add function device_get_first_child_by_uclass_id() Przemyslaw Marczak
                     ` (14 subsequent siblings)
  15 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 include/configs/exynos5-common.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/configs/exynos5-common.h b/include/configs/exynos5-common.h
index 3ab8d55..3ee9482 100644
--- a/include/configs/exynos5-common.h
+++ b/include/configs/exynos5-common.h
@@ -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
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 02/12] dm: device: add function device_get_first_child_by_uclass_id()
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 01/12] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-06 14:11     ` Simon Glass
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 03/12] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
                     ` (13 subsequent siblings)
  15 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- new commit
---
 drivers/core/device.c | 15 +++++++++++++++
 include/dm/device.h   | 16 ++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/drivers/core/device.c b/drivers/core/device.c
index 73c3e07..76b22cf 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -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)
 {
diff --git a/include/dm/device.h b/include/dm/device.h
index 7a48eb8..9f0d6ce 100644
--- a/include/dm/device.h
+++ b/include/dm/device.h
@@ -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
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 03/12] dm: pmic: add implementation of driver model pmic uclass
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 01/12] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 02/12] dm: device: add function device_get_first_child_by_uclass_id() Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-06 14:11     ` Simon Glass
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 04/12] dm: pmic: add implementation of driver model regulator uclass Przemyslaw Marczak
                     ` (12 subsequent siblings)
  15 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
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

diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 2145652..5c9a189 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -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
diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
new file mode 100644
index 0000000..309463e
--- /dev/null
+++ b/drivers/power/pmic-uclass.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * 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",
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 91bb90d..ff360ac 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -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,
 };
diff --git a/include/power/pmic.h b/include/power/pmic.h
index afbc5aa..94171ac 100644
--- a/include/power/pmic.h
+++ b/include/power/pmic.h
@@ -1,4 +1,7 @@
 /*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
  *  Copyright (C) 2011-2012 Samsung Electronics
  *  Lukasz Majewski <l.majewski@samsung.com>
  *
@@ -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],
+ *                                           my_pmic->name, my_pmic->platdata,
+ *                                           my_pmic->of_offset,
+ *                                           my_pmic_child_dev[i]);
+ *                 ...
+ *         }
+ * ...
+ * }
+ *
+ * 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
+ * @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;
+};
+
+/**
+ * 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);
+	int (*write)(struct udevice *pmic, unsigned reg, unsigned char val);
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,
+ * 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,
+};
+
+/**
+ * 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)
+{
+	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:
+ * @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);
+
+/**
+ * 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"/"--"
+ *
+ * @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);
+
+/**
+ * pmic_read/write: read/write to the UCLASS_PMIC device
+ *
+ * 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.
+ */
+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)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 04/12] dm: pmic: add implementation of driver model regulator uclass
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (2 preceding siblings ...)
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 03/12] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-06 14:12     ` Simon Glass
  2015-03-10 11:41     ` Robert Baldyga
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 05/12] dm: pmic: new commands: pmic and regulator Przemyslaw Marczak
                     ` (11 subsequent siblings)
  15 siblings, 2 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
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

diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 5c9a189..a6b7012 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -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
diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
new file mode 100644
index 0000000..6b5c678
--- /dev/null
+++ b/drivers/power/regulator-uclass.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * 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;
+
+		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",
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index ff360ac..0a1e664 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -37,6 +37,7 @@ enum uclass_id {
 
 	/* PMIC uclass and PMIC related uclasses */
 	UCLASS_PMIC,
+	UCLASS_PMIC_REGULATOR,
 
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
diff --git a/include/power/regulator.h b/include/power/regulator.h
new file mode 100644
index 0000000..ee8a280
--- /dev/null
+++ b/include/power/regulator.h
@@ -0,0 +1,310 @@
+/*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * 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:
+ * - 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_ */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 05/12] dm: pmic: new commands: pmic and regulator
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (3 preceding siblings ...)
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 04/12] dm: pmic: add implementation of driver model regulator uclass Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-06 14:13     ` Simon Glass
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 06/12] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
                     ` (10 subsequent siblings)
  15 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
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

diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index a6b7012..943b38f 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -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
diff --git a/drivers/power/cmd_pmic.c b/drivers/power/cmd_pmic.c
new file mode 100644
index 0000000..996bfe7
--- /dev/null
+++ b/drivers/power/cmd_pmic.c
@@ -0,0 +1,820 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#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>
+#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;
+}
+
+/**
+ * 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)
+{
+	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;
+	}
+
+	uc_drv = uc->uc_drv;
+	uclass_foreach_dev(dev, uc) {
+		if (dev)
+			device_probe(dev);
+		else
+			continue;
+
+		printf("| %2.d |", dev->seq);
+
+		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
+			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);
+
+		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)
+				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);
+
+	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)
+{
+	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;
+
+	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");
+		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;
+	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),
+					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)
+{
+	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;
+
+	if (!set_force && check_reg_val(dev, reg_num, set_val, type))
+		return -EPERM;
+
+	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;
+}
+
+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 */
+	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);
+
+	/* 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);
+	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) {
+		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"
+	"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"
+);
+#endif /* CONFIG_DM_REGULATOR_CMD */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 06/12] dm: pmic: add max77686 pmic driver
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (4 preceding siblings ...)
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 05/12] dm: pmic: new commands: pmic and regulator Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 07/12] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
                     ` (9 subsequent siblings)
  15 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

This is the implementation of driver model uclass pmic driver.
The max77686 pmic driver implements read/write operations and driver
bind method - to bind other pmic uclass devices as a parent pmic device.
This driver provides pmic_platdata for also for child regulator.

This driver will try to bind the regulator device with regulator driver.
This should succeed if regulator driver is compiled.

If no regulator driver found, then the pmic can still provide read/write
operations, and can be used with pmic framework function calls.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- add implementation of pmic read/write
- max77686: add new operations
- max77686: header: change PMIC_NUM_OF_REGS to MAX77686_NUM_OF_REGS
---
 drivers/power/pmic/Makefile        |   1 +
 drivers/power/pmic/max77686.c      | 102 +++++++++++++++++++++++++++++++++++++
 drivers/power/pmic/pmic_max77686.c |   2 +-
 include/power/max77686_pmic.h      |   2 +-
 4 files changed, 105 insertions(+), 2 deletions(-)
 create mode 100644 drivers/power/pmic/max77686.c

diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 985cfdb..242c767 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -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
diff --git a/drivers/power/pmic/max77686.c b/drivers/power/pmic/max77686.c
new file mode 100644
index 0000000..01867ac
--- /dev/null
+++ b/drivers/power/pmic/max77686.c
@@ -0,0 +1,102 @@
+/*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak  <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <i2c.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 <power/max77686_pmic.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int max77686_write(struct udevice *pmic, unsigned reg, unsigned char val)
+{
+	unsigned char buf[4] = { 0 };
+
+	buf[0] = cpu_to_le32(val) & 0xff;
+
+	if (dm_i2c_write(pmic, reg, buf, 1)) {
+		error("write error to device: %p register: %#x!", pmic, reg);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int max77686_read(struct udevice *pmic, unsigned reg, unsigned char *val)
+{
+	unsigned char buf[4] = { 0 };
+
+	if (dm_i2c_read(pmic, reg, buf, 1)) {
+		error("read error from device: %p register: %#x!", pmic, reg);
+		return -EIO;
+	}
+
+	*val = le32_to_cpu(buf[0]);
+
+	return 0;
+}
+
+static int max77686_bind(struct udevice *pmic)
+{
+	struct dm_i2c_chip *i2c_chip = dev_get_parent_platdata(pmic);
+	struct pmic_platdata *pl = pmic->platdata;
+	struct udevice *reg_dev;
+	struct driver *reg_drv;
+	int ret;
+
+	/* The same platdata is used for the regulator driver */
+	pl->if_type = PMIC_I2C;
+	pl->if_bus_num = pmic->parent->req_seq;
+	pl->if_addr_cs = i2c_chip->chip_addr;
+	pl->if_max_offset = MAX77686_NUM_OF_REGS;
+
+	reg_drv = lists_driver_lookup_name("max77686 regulator");
+	if (!reg_drv) {
+		error("PMIC: %s regulator driver not found!\n", pmic->name);
+		return 0;
+	}
+
+	/**
+	 * Try bind the child regulator driver
+	 * |-- I2C bus
+	 *     '---max77686 pmic I/O driver
+	 *         '--max77686 regulator driver
+	 */
+	ret = device_bind(pmic, reg_drv, pmic->name, pmic->platdata,
+			  pmic->of_offset, &reg_dev);
+	if (ret)
+		error("%s regulator bind failed", pmic->name);
+
+	/* Always return success for this device */
+	return 0;
+}
+
+static struct dm_pmic_ops max77686_ops = {
+	.read = max77686_read,
+	.write = max77686_write,
+};
+
+static const struct udevice_id max77686_ids[] = {
+	{ .compatible = "maxim,max77686_pmic"},
+	{ }
+};
+
+U_BOOT_DRIVER(pmic_max77686) = {
+	.name = "max77686 pmic",
+	.id = UCLASS_PMIC,
+	.of_match = max77686_ids,
+	.bind = max77686_bind,
+	.ops = &max77686_ops,
+	.platdata_auto_alloc_size = sizeof(struct pmic_platdata),
+};
diff --git a/drivers/power/pmic/pmic_max77686.c b/drivers/power/pmic/pmic_max77686.c
index 95b1a57..1ad810a 100644
--- a/drivers/power/pmic/pmic_max77686.c
+++ b/drivers/power/pmic/pmic_max77686.c
@@ -295,7 +295,7 @@ int pmic_init(unsigned char bus)
 
 	p->name = name;
 	p->interface = PMIC_I2C;
-	p->number_of_regs = PMIC_NUM_OF_REGS;
+	p->number_of_regs = MAX77686_NUM_OF_REGS;
 	p->hw.i2c.tx_num = 1;
 
 	puts("Board PMIC init\n");
diff --git a/include/power/max77686_pmic.h b/include/power/max77686_pmic.h
index b0e4255..fe26d13 100644
--- a/include/power/max77686_pmic.h
+++ b/include/power/max77686_pmic.h
@@ -122,7 +122,7 @@ enum {
 	MAX77686_REG_PMIC_BBAT		= 0x7e,
 	MAX77686_REG_PMIC_32KHZ,
 
-	PMIC_NUM_OF_REGS,
+	MAX77686_NUM_OF_REGS,
 };
 
 /* I2C device address for pmic max77686 */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 07/12] dm: regulator: add max77686 regulator driver
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (5 preceding siblings ...)
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 06/12] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-06 14:14     ` Simon Glass
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 08/12] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
                     ` (8 subsequent siblings)
  15 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- change debug() to error()
- code cleanup
- fix data types
- ldo/buck state implementation
- adjust to new uclass api
---
 Makefile                           |   1 +
 drivers/power/Makefile             |   1 -
 drivers/power/regulator/Makefile   |   8 +
 drivers/power/regulator/max77686.c | 926 +++++++++++++++++++++++++++++++++++++
 include/power/max77686_pmic.h      |  24 +-
 5 files changed, 956 insertions(+), 4 deletions(-)
 create mode 100644 drivers/power/regulator/Makefile
 create mode 100644 drivers/power/regulator/max77686.c

diff --git a/Makefile b/Makefile
index 6da4215..fcb37ae 100644
--- a/Makefile
+++ b/Makefile
@@ -627,6 +627,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/
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 943b38f..7ff1baa 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -15,7 +15,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
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
new file mode 100644
index 0000000..9d282e3
--- /dev/null
+++ b/drivers/power/regulator/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (C) 2014 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c
new file mode 100644
index 0000000..711c19c
--- /dev/null
+++ b/drivers/power/regulator/max77686.c
@@ -0,0 +1,926 @@
+/*
+ *  Copyright (C) 2012-2015 Samsung Electronics
+ *
+ *  Rajeshwari Shinde <rajeshwari.s@samsung.com>
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/max77686_pmic.h>
+#include <errno.h>
+#include <dm.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct max77686_regulator_info {
+	int ldo_cnt;
+	int buck_cnt;
+	struct regulator_value_desc ldo_desc[MAX77686_LDO_NUM + 1];
+	struct regulator_value_desc buck_desc[MAX77686_BUCK_NUM + 1];
+};
+
+#define MODE(_mode, _val, _name) { \
+	.mode = _mode, \
+	.register_value = _val, \
+	.name = _name, \
+}
+
+/* LDO: 1,3,4,5,9,17,18,19,20,21,22,23,24,26,26,27 */
+static struct regulator_mode_desc max77686_ldo_mode_standby1[] = {
+	MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
+	MODE(OPMODE_LPM, MAX77686_LDO_MODE_LPM, "LPM"),
+	MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
+	MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
+};
+
+/* LDO: 2,6,7,8,10,11,12,14,15,16 */
+static struct regulator_mode_desc max77686_ldo_mode_standby2[] = {
+	MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_LDO_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
+	MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
+};
+
+/* Buck: 1 */
+static struct regulator_mode_desc max77686_buck_mode_standby[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+/* Buck: 2,3,4 */
+static struct regulator_mode_desc max77686_buck_mode_lpm[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_LPM, MAX77686_BUCK_MODE_LPM, "LPM"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+/* Buck: 5,6,7,8,9 */
+static struct regulator_mode_desc max77686_buck_mode_onoff[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+static const char max77686_buck_addr[] = {
+	0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38
+};
+
+static int max77686_buck_volt2hex(int buck, int uV)
+{
+	unsigned int hex = 0;
+	unsigned int hex_max = 0;
+
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		/* hex = (uV - 600000) / 12500; */
+		hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP;
+		hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
+		/**
+		 * This uses voltage scaller - temporary not implemented
+		 * so return just 0
+		 */
+		return 0;
+	default:
+		/* hex = (uV - 750000) / 50000; */
+		hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP;
+		hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
+		break;
+	}
+
+	if (hex >= 0 && hex <= hex_max)
+		return hex;
+
+	error("Value: %d uV is wrong for BUCK%d", uV, buck);
+	return -EINVAL;
+}
+
+static int max77686_buck_hex2volt(int buck, int hex)
+{
+	unsigned uV = 0;
+	unsigned int hex_max = 0;
+
+	if (hex < 0)
+		goto bad_hex;
+
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
+		if (hex > hex_max)
+			goto bad_hex;
+
+		/* uV = hex * 12500 + 600000; */
+		uV = hex * MAX77686_BUCK_UV_LSTEP + MAX77686_BUCK_UV_LMIN;
+		break;
+	default:
+		hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
+		if (hex > hex_max)
+			goto bad_hex;
+
+		/* uV = hex * 50000 + 750000; */
+		uV = hex * MAX77686_BUCK_UV_HSTEP + MAX77686_BUCK_UV_HMIN;
+		break;
+	}
+
+	return uV;
+
+bad_hex:
+	error("Value: %#x is wrong for BUCK%d", hex, buck);
+	return -EINVAL;
+}
+
+static int max77686_ldo_volt2hex(int ldo, int uV)
+{
+	unsigned int hex = 0;
+
+	switch (ldo) {
+	case 1:
+	case 2:
+	case 6:
+	case 7:
+	case 8:
+	case 15:
+		hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_LSTEP;
+		/* hex = (uV - 800000) / 25000; */
+		break;
+	default:
+		hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_HSTEP;
+		/* hex = (uV - 800000) / 50000; */
+	}
+
+	if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX)
+		return hex;
+
+	error("Value: %d uV is wrong for LDO%d", uV, ldo);
+	return -EINVAL;
+}
+
+static int max77686_ldo_hex2volt(int ldo, int hex)
+{
+	unsigned int uV = 0;
+
+	if (hex > MAX77686_LDO_VOLT_MAX_HEX)
+		goto bad_hex;
+
+	switch (ldo) {
+	case 1:
+	case 2:
+	case 6:
+	case 7:
+	case 8:
+	case 15:
+		/* uV = hex * 25000 + 800000; */
+		uV = hex * MAX77686_LDO_UV_LSTEP + MAX77686_LDO_UV_MIN;
+		break;
+	default:
+		/* uV = hex * 50000 + 800000; */
+		uV = hex * MAX77686_LDO_UV_HSTEP + MAX77686_LDO_UV_MIN;
+	}
+
+	return uV;
+
+bad_hex:
+	error("Value: %#x is wrong for ldo%d", hex, ldo);
+	return -EINVAL;
+}
+
+static int max77686_ldo_hex2mode(int ldo, int hex)
+{
+	if (hex > MAX77686_LDO_MODE_MASK)
+		return -EINVAL;
+
+	switch (hex) {
+	case MAX77686_LDO_MODE_OFF:
+		return OPMODE_OFF;
+	case MAX77686_LDO_MODE_LPM: /* == MAX77686_LDO_MODE_STANDBY: */
+		/* The same mode values but different meaning for each ldo */
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			return OPMODE_STANDBY;
+		default:
+			return OPMODE_LPM;
+		}
+	case MAX77686_LDO_MODE_STANDBY_LPM:
+		return OPMODE_STANDBY_LPM;
+	case MAX77686_LDO_MODE_ON:
+		return OPMODE_ON;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int max77686_buck_hex2mode(int buck, int hex)
+{
+	if (hex > MAX77686_BUCK_MODE_MASK)
+		return -EINVAL;
+
+	switch (hex) {
+	case MAX77686_BUCK_MODE_OFF:
+		return OPMODE_OFF;
+	case MAX77686_BUCK_MODE_ON:
+		return OPMODE_ON;
+	case MAX77686_BUCK_MODE_STANDBY:
+		switch (buck) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+			return OPMODE_STANDBY;
+		default:
+			return -EINVAL;
+		}
+	case MAX77686_BUCK_MODE_LPM:
+		switch (buck) {
+		case 2:
+		case 3:
+		case 4:
+			return OPMODE_LPM;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int max77686_get_value_desc(struct udevice *dev, int type, int d_num,
+				   struct regulator_value_desc **desc)
+{
+	struct max77686_regulator_info *dev_info;
+
+	if (!dev)
+		return -ENODEV;
+
+	if (!dev->priv)
+		return -ENXIO;
+
+	dev_info = dev->priv;
+
+	switch (type) {
+	case REGULATOR_TYPE_LDO:
+		if (d_num < 1 || d_num > 26)
+			return -EINVAL;
+
+		*desc = &dev_info->ldo_desc[d_num];
+		return 0;
+	case REGULATOR_TYPE_BUCK:
+		if (d_num < 1 || d_num > 9)
+			return -EINVAL;
+
+		*desc = &dev_info->buck_desc[d_num];
+		return 0;
+	default:
+		return -EPERM;
+	}
+}
+
+static int max77686_get_mode_desc_array(struct udevice *dev, int type,
+					int d_num, int *d_mode_cnt,
+					struct regulator_mode_desc **desc)
+{
+	struct max77686_regulator_info *dev_info;
+
+	if (!dev)
+		return -ENODEV;
+
+	if (!dev->priv)
+		return -ENXIO;
+
+	dev_info = dev->priv;
+	*d_mode_cnt = 0;
+
+	if (type == REGULATOR_TYPE_LDO) {
+		if (d_num < 1 || d_num > dev_info->ldo_cnt)
+			return -EINVAL;
+
+		switch (d_num) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			*d_mode_cnt = ARRAY_SIZE(max77686_ldo_mode_standby2);
+			*desc = max77686_ldo_mode_standby2;
+			return 0;
+		default:
+			*d_mode_cnt = ARRAY_SIZE(max77686_ldo_mode_standby1);
+			*desc = max77686_ldo_mode_standby1;
+			return 0;
+		}
+	} else if (type == REGULATOR_TYPE_BUCK) {
+		if (d_num < 1 || d_num > dev_info->buck_cnt)
+			return -EINVAL;
+
+		switch (d_num) {
+		case 1:
+			*d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_standby);
+			*desc = max77686_buck_mode_standby;
+			return 0;
+		case 2:
+		case 3:
+		case 4:
+			*d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_lpm);
+			*desc = max77686_buck_mode_lpm;
+			return 0;
+		default:
+			*d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_onoff);
+			*desc = max77686_buck_mode_onoff;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int max77686_ldo_val(struct udevice *dev, int op,
+				int ldo, int *uV)
+{
+	unsigned int ret, hex, adr;
+	unsigned char val;
+
+	if (op == PMIC_OP_GET)
+		*uV = 0;
+
+	if (ldo < 1 || ldo > 26) {
+		error("Wrong ldo number: %d", ldo);
+		return -EINVAL;
+	}
+
+	adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
+
+	ret = pmic_read(dev->parent, adr, &val);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= MAX77686_LDO_VOLT_MASK;
+		ret = max77686_ldo_hex2volt(ldo, val);
+		if (ret < 0)
+			return ret;
+		*uV = ret;
+		return 0;
+	}
+
+	hex = max77686_ldo_volt2hex(ldo, *uV);
+	if (hex < 0)
+		return -EINVAL;
+
+	val &= ~MAX77686_LDO_VOLT_MASK;
+	val |= hex;
+	ret |= pmic_write(dev->parent, adr, val);
+
+	return ret;
+}
+
+static int max77686_buck_val(struct udevice *dev, int op,
+				int buck, int *uV)
+{
+	unsigned int hex, ret, mask, adr;
+	unsigned char val;
+
+	if (buck < 1 || buck > 9) {
+		error("Wrong buck number: %d", buck);
+		return -EINVAL;
+	}
+
+	if (op == PMIC_OP_GET)
+		*uV = 0;
+
+	/* &buck_out = ctrl + 1 */
+	adr = max77686_buck_addr[buck] + 1;
+
+	/* mask */
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		/* Those uses voltage scallers - will support in the future */
+		mask = MAX77686_BUCK234_VOLT_MASK;
+		return -EPERM;
+	default:
+		mask = MAX77686_BUCK_VOLT_MASK;
+	}
+
+	ret = pmic_read(dev->parent, adr, &val);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= mask;
+		ret = max77686_buck_hex2volt(buck, val);
+		if (ret < 0)
+			return ret;
+		*uV = ret;
+		return 0;
+	}
+
+	hex = max77686_buck_volt2hex(buck, *uV);
+	if (hex < 0)
+		return -EINVAL;
+
+	val &= ~mask;
+	val |= hex;
+	ret = pmic_write(dev->parent, adr, val);
+
+	return ret;
+}
+
+static int max77686_ldo_mode(struct udevice *dev, int op, int ldo, int *opmode)
+{
+	unsigned int ret, adr, mode;
+	unsigned char val;
+
+	if (op == PMIC_OP_GET)
+		*opmode = -EINVAL;
+
+	if (ldo < 1 || ldo > 26) {
+		error("Wrong ldo number: %d", ldo);
+		return -EINVAL;
+	}
+
+	adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
+
+	ret = pmic_read(dev->parent, adr, &val);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= MAX77686_LDO_MODE_MASK;
+		ret = max77686_ldo_hex2mode(ldo, val);
+		if (ret < 0)
+			return ret;
+		*opmode = ret;
+		return 0;
+	}
+
+	/* mode */
+	switch (*opmode) {
+	case OPMODE_OFF:
+		mode = MAX77686_LDO_MODE_OFF;
+		break;
+	case OPMODE_LPM:
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			return -EINVAL;
+		default:
+			mode = MAX77686_LDO_MODE_LPM;
+		}
+		break;
+	case OPMODE_STANDBY:
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			mode = MAX77686_LDO_MODE_STANDBY;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case OPMODE_STANDBY_LPM:
+		mode = MAX77686_LDO_MODE_STANDBY_LPM;
+		break;
+	case OPMODE_ON:
+		mode = MAX77686_LDO_MODE_ON;
+		break;
+	default:
+		mode = 0xff;
+	}
+
+	if (mode == 0xff) {
+		error("Wrong mode: %d for ldo%d", *opmode, ldo);
+		return -EINVAL;
+	}
+
+	val &= ~MAX77686_LDO_MODE_MASK;
+	val |= mode;
+	ret |= pmic_write(dev->parent, adr, val);
+
+	return ret;
+}
+
+static int max77686_ldo_state(struct udevice *dev, int op, int ldo, int *state)
+{
+	int ret, on_off;
+
+	if (op == PMIC_OP_GET) {
+		ret = max77686_ldo_mode(dev, op, ldo, &on_off);
+		if (ret)
+			return ret;
+
+		switch (on_off) {
+		case OPMODE_OFF:
+			*state = 0;
+			break;
+		case OPMODE_ON:
+			*state = 1;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else if (op == PMIC_OP_SET) {
+		switch (*state) {
+		case 0:
+			on_off = OPMODE_OFF;
+			break;
+		case 1:
+			on_off = OPMODE_ON;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = max77686_ldo_mode(dev, op, ldo, &on_off);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int max77686_buck_mode(struct udevice *dev, int op, int buck,
+			      int *opmode)
+{
+	unsigned int ret, mask, adr, mode, mode_shift;
+	unsigned char val;
+
+	if (buck < 1 || buck > 9) {
+		error("Wrong buck number: %d", buck);
+		return -EINVAL;
+	}
+
+	adr = max77686_buck_addr[buck];
+
+	/* mask */
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		mode_shift = MAX77686_BUCK_MODE_SHIFT_2;
+		break;
+	default:
+		mode_shift = MAX77686_BUCK_MODE_SHIFT_1;
+	}
+
+	mask = MAX77686_BUCK_MODE_MASK << mode_shift;
+
+	ret = pmic_read(dev->parent, adr, &val);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= mask;
+		val >>= mode_shift;
+		ret = max77686_buck_hex2mode(buck, val);
+		if (ret < 0)
+			return ret;
+		*opmode = ret;
+		return 0;
+	}
+
+	/* mode */
+	switch (*opmode) {
+	case OPMODE_OFF:
+		mode = MAX77686_BUCK_MODE_OFF;
+		break;
+	case OPMODE_STANDBY:
+		switch (buck) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+			mode = MAX77686_BUCK_MODE_STANDBY << mode_shift;
+			break;
+		default:
+			mode = 0xff;
+		}
+		break;
+	case OPMODE_LPM:
+		switch (buck) {
+		case 2:
+		case 3:
+		case 4:
+			mode = MAX77686_BUCK_MODE_LPM << mode_shift;
+			break;
+		default:
+			mode = 0xff;
+		}
+		break;
+	case OPMODE_ON:
+		mode = MAX77686_BUCK_MODE_ON << mode_shift;
+		break;
+	default:
+		mode = 0xff;
+	}
+
+	if (mode == 0xff) {
+		error("Wrong mode:%d for buck%d\n", *opmode, buck);
+		return -EINVAL;
+	}
+
+	val &= ~mask;
+	val |= mode;
+	ret |= pmic_write(dev->parent, adr, val);
+
+	return ret;
+}
+
+static int max77686_buck_state(struct udevice *dev, int op,
+			       int buck, int *state)
+{
+	int ret, on_off;
+
+	if (op == PMIC_OP_GET) {
+		ret = max77686_buck_mode(dev, op, buck, &on_off);
+		if (ret)
+			return ret;
+
+		switch (on_off) {
+		case OPMODE_OFF:
+			*state = 0;
+			break;
+		case OPMODE_ON:
+			*state = 1;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else if (op == PMIC_OP_SET) {
+		switch (*state) {
+		case 0:
+			on_off = OPMODE_OFF;
+			break;
+		case 1:
+			on_off = OPMODE_ON;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = max77686_buck_mode(dev, op, buck, &on_off);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int fill_reg_desc(int offset, struct regulator_value_desc *desc, int reg_num,
+		  int desc_type)
+{
+	const void *blob = gd->fdt_blob;
+	char *reg_name;
+	int reg_min, reg_max, len;
+
+	desc->type = desc_type;
+	desc->number = reg_num;
+
+	reg_name = (char *)fdt_getprop(blob, offset, "regulator-name", &len);
+	if (reg_name) {
+		strncpy(&desc->name[0], reg_name, DESC_NAME_LEN);
+		desc->name[DESC_NAME_LEN - 1] = '\0';
+	} else {
+		sprintf(&desc->name[0], "-");
+	}
+
+	reg_min = fdtdec_get_int(blob, offset, "regulator-min-microvolt", -1);
+	desc->min_uV = reg_min;
+
+	reg_max = fdtdec_get_int(blob, offset, "regulator-min-microvolt", -1);
+	desc->max_uV = reg_max;
+
+	return 0;
+}
+
+static int get_desc_for_compat(int regulators_node, char *compat, int desc_type,
+			       struct regulator_value_desc *desc, int desc_num)
+{
+	const void *blob = gd->fdt_blob;
+	const char *reg_compat;
+	int compat_len = strlen(compat);
+	int reg_num;
+	int offset;
+	int cnt = 0;
+
+	/* First subnode of "voltage_regulators" node */
+	offset = fdt_first_subnode(blob, regulators_node);
+
+	while (offset > 0) {
+		/* Get regulator properties */
+		reg_compat = (char *)fdt_getprop(blob, offset,
+			     "regulator-compatible", NULL);
+
+		/* Check compat and compare the number only */
+		reg_num = 0;
+		if (strstr(reg_compat, compat))
+			reg_num = simple_strtoul(reg_compat + compat_len,
+						 NULL, 0);
+		else
+			goto next_offset;
+
+		if (reg_num && reg_num <= desc_num)
+			fill_reg_desc(offset, &desc[reg_num], reg_num,
+				      desc_type);
+
+		cnt++;
+		if (cnt == desc_num)
+			return cnt;
+
+next_offset:
+		offset = fdt_next_subnode(blob, offset);
+	}
+
+	return cnt;
+}
+
+static int reg_max77686_ofdata_to_platdata(struct udevice *dev)
+{
+	struct max77686_regulator_info *dev_info = dev->priv;
+	const void *blob = gd->fdt_blob;
+	int node = dev->of_offset;
+	int offset;
+
+	if (node < 0) {
+		error("Device: %s - bad of_offset", dev->name);
+		return 0;
+	}
+
+	offset = fdt_subnode_offset(blob, node,  "voltage-regulators");
+	if (offset < 0) {
+		error("Node %s has no 'voltage-regulators' subnode", dev->name);
+		return 0;
+	}
+
+	dev_info->ldo_cnt = get_desc_for_compat(offset, "LDO", REGULATOR_TYPE_LDO,
+						dev_info->ldo_desc,
+						MAX77686_LDO_NUM);
+
+	dev_info->buck_cnt = get_desc_for_compat(offset, "BUCK", REGULATOR_TYPE_BUCK,
+						 dev_info->buck_desc,
+						 MAX77686_BUCK_NUM);
+
+	/* Even if there is no voltage regulators node in dts,
+	 * always return success and allow the driver to bind.
+	 * Then we can still do read/write pmic registers.
+	 */
+	return 0;
+
+}
+
+static int max77686_get_cnt(struct udevice *dev, int type, int *cnt)
+{
+	switch (type) {
+	case REGULATOR_TYPE_LDO:
+		*cnt = MAX77686_LDO_NUM;
+		break;
+	case REGULATOR_TYPE_BUCK:
+		*cnt = MAX77686_BUCK_NUM;
+		break;
+	default:
+		*cnt = 0;
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+
+static int max77686_get_value(struct udevice *dev, int type, int number,
+			      int *value)
+{
+	switch (type) {
+	case REGULATOR_TYPE_LDO:
+		return max77686_ldo_val(dev, PMIC_OP_GET, number, value);
+	case REGULATOR_TYPE_BUCK:
+		return max77686_buck_val(dev, PMIC_OP_GET, number, value);
+	default:
+		return -EPERM;
+	}
+}
+
+static int max77686_set_value(struct udevice *dev, int type, int number,
+			      int value)
+{
+	switch (type) {
+	case REGULATOR_TYPE_LDO:
+		return max77686_ldo_val(dev, PMIC_OP_SET, number, &value);
+	case REGULATOR_TYPE_BUCK:
+		return max77686_buck_val(dev, PMIC_OP_SET, number, &value);
+	default:
+		return -EPERM;
+	}
+}
+
+static int max77686_get_state(struct udevice *dev, int type, int number,
+			      int *state)
+{
+	switch (type) {
+	case REGULATOR_TYPE_LDO:
+		return max77686_ldo_state(dev, PMIC_OP_GET, number, state);
+	case REGULATOR_TYPE_BUCK:
+		return max77686_buck_state(dev, PMIC_OP_GET, number, state);
+	default:
+		*state = -1;
+		return -EPERM;
+	}
+}
+
+static int max77686_set_state(struct udevice *dev, int type, int number,
+			      int state)
+{
+	switch (type) {
+	case REGULATOR_TYPE_LDO:
+		return max77686_ldo_state(dev, PMIC_OP_SET, number, &state);
+	case REGULATOR_TYPE_BUCK:
+		return max77686_buck_state(dev, PMIC_OP_SET, number, &state);
+	default:
+		return -EPERM;
+	}
+}
+
+static int max77686_get_mode(struct udevice *dev, int type, int number,
+			     int *mode)
+{
+	switch (type) {
+	case REGULATOR_TYPE_LDO:
+		return max77686_ldo_mode(dev, PMIC_OP_GET, number, mode);
+	case REGULATOR_TYPE_BUCK:
+		return max77686_buck_mode(dev, PMIC_OP_GET, number, mode);
+	default:
+		return -EPERM;
+	}
+}
+
+static int max77686_set_mode(struct udevice *dev, int type, int number,
+			     int mode)
+{
+	switch (type) {
+	case REGULATOR_TYPE_LDO:
+		return max77686_ldo_mode(dev, PMIC_OP_SET, number, &mode);
+	case REGULATOR_TYPE_BUCK:
+		return max77686_buck_mode(dev, PMIC_OP_SET, number, &mode);
+	default:
+		return -EPERM;
+	}
+}
+
+static const struct dm_regulator_ops reg_max77686_ops = {
+	.get_cnt		= max77686_get_cnt,
+	.get_value_desc		= max77686_get_value_desc,
+	.get_mode_desc_array	= max77686_get_mode_desc_array,
+	.get_value		= max77686_get_value,
+	.set_value		= max77686_set_value,
+	.get_state		= max77686_get_state,
+	.set_state		= max77686_set_state,
+	.get_mode		= max77686_get_mode,
+	.set_mode		= max77686_set_mode,
+};
+
+U_BOOT_DRIVER(max77686_regulator) = {
+	.name = "max77686 regulator",
+	.id = UCLASS_PMIC_REGULATOR,
+	.ops = &reg_max77686_ops,
+	.ofdata_to_platdata = reg_max77686_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct max77686_regulator_info),
+};
diff --git a/include/power/max77686_pmic.h b/include/power/max77686_pmic.h
index fe26d13..81c27d8 100644
--- a/include/power/max77686_pmic.h
+++ b/include/power/max77686_pmic.h
@@ -126,7 +126,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,23 +146,29 @@ 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_voltage(struct pmic *p, int buck, ulong uV);
 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
@@ -170,6 +179,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 */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 08/12] doc: driver-model: pmic and regulator uclass documentation
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (6 preceding siblings ...)
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 07/12] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-06 14:14     ` Simon Glass
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 09/12] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
                     ` (7 subsequent siblings)
  15 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes v2:
- update documentation with the framework api changes
---
 doc/driver-model/dm-pmic-framework.txt | 367 +++++++++++++++++++++++++++++++++
 1 file changed, 367 insertions(+)
 create mode 100644 doc/driver-model/dm-pmic-framework.txt

diff --git a/doc/driver-model/dm-pmic-framework.txt b/doc/driver-model/dm-pmic-framework.txt
new file mode 100644
index 0000000..f2fb4ac
--- /dev/null
+++ b/doc/driver-model/dm-pmic-framework.txt
@@ -0,0 +1,367 @@
+#
+# (C) Copyright 2014-2015 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+PMIC framework based on Driver Model
+====================================
+TOC:
+1. Introduction
+2. How does it work
+3. Pmic driver api
+4. Pmic driver
+5. Pmic command
+6. Regulator driver api
+7. Design limitations
+8. Regulator driver
+9. Regulator command
+
+1. Introduction
+===============
+This is an introduction to driver-model multi uclass PMIC devices support.
+At present it is based on two uclass types:
+
+- UCLASS_PMIC - basic uclass type for PMIC I/O, which provides common read/write
+                interface.
+
+- UCLASS_PMIC_REGULATOR - additional uclass type for specific PMIC features,
+                          which are various voltage regulators.
+
+New files:
+UCLASS_PMIC:
+- drivers/power/pmic-uclass.c
+- include/power/pmic.h
+UCLASS_PMIC_REGULATOR:
+- drivers/power/regulator-uclass.c
+- include/power/regulator.h
+
+Commands:
+- drivers/power/cmd_pmic.c (pmic; regulator)
+
+2. How doees it work
+====================
+The Power Management Integrated Circuits (PMIC) are used in embedded systems
+to provide stable, precise and specific voltage power source with over-voltage
+and thermal protection circuits.
+
+The single PMIC can provide various functionalities with single or multiple
+interfaces, like in the example below.
+
+-- SoC
+ |
+ |            ______________________________________
+ | BUS 0     |       Multi interface PMIC IC        |--> LDO out 1
+ | e.g.I2C0  |                                      |--> LDO out N
+ |-----------|---- PMIC device 0 (READ/WRITE ops)   |
+ | or SPI0   |    |_ REGULATOR device (ldo/... ops) |--> BUCK out 1
+ |           |    |_ CHARGER device (charger ops)   |--> BUCK out M
+ |           |    |_ MUIC device (microUSB con ops) |
+ | BUS 1     |    |_ ...                            |---> BATTERY
+ | e.g.I2C1  |                                      |
+ |-----------|---- PMIC device 1 (READ/WRITE ops)   |---> USB in 1
+ . or SPI1   |    |_ RTC device (rtc ops)           |---> USB in 2
+ .           |______________________________________|---> USB out
+ .
+
+Since U-Boot provides driver model features for I2C and SPI bus drivers,
+the PMIC devices should also support this. With the new basic uclass types
+for PMIC I/O and regulator features, PMIC drivers can simply provide common
+features, with multiple interface and instance support.
+
+Basic design assumptions:
+
+- Common I/O api - UCLASS_PMIC
+The main assumption is to use UCLASS_PMIC device to provide I/O interface,
+for devices other uclass types. It is no matter what is the type of device
+physical I/O interface. Usually PMIC devices are using SPI or I2C interface,
+but use of any other interface (e.g. when PMIC is not directly connected
+to the SoC) - is now possible. Drivers can use the same read/write api.
+
+- Common regulator api - UCLASS_REGULATOR
+For setting the attributes of verious types of regulators with common api,
+this uclass can be implemented. This allows to drive the each regulator output
+value, on/off state and custom defined operation modes. It also provides the
+user interface for all operations.
+For the very simple implementation, the regulator drivers are not required,
+so the code could base on pmic read/write only.
+
+When board device-tree file includes pmic subnode and the U_Boot compatible
+driver exists, then the pmic device bind should looks like this:
+
+|_ root - will bind the device for I2C/SPI bus node
+  |_ i2c/spi - should bind a device for pmic node
+    |_ pmic (parent) - should bind child devices for its features
+      |_ regulator (child)
+      |_ charger   (child)
+      |_ other     (child)
+
+Usually PMIC design provides:
+ - single I/O interface (single UCLASS_PMIC driver)
+   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 (UCLASS_PMIC driver for each)
+   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.
+
+3. Pmic driver api
+===================
+To use the pmic API, config: CONFIG_DM_PMIC is required.
+The new driver API is very simple and is based on 'struct dm_pmic_ops',
+which define two basic operations: device read and write.
+
+The platform data is introduced as 'struct pmic_platdata', to keep an info
+about the device interface.
+
+The api is described in file: 'include/power/pmic.h'
+
+3.1 Getting the device:
+- By name only - usefull if the name is unique, or only one pmic device exists:
+  int pmic_get(char *name, struct udevice **pmic);
+
+- By I2C or SPI bus number and chip address - when few pmic devices exists:
+  int pmic_i2c_get(int bus, int addr, struct udevice **pmic)
+  int pmic_spi_get(int bus, int cs, struct udevice **pmic);
+
+- To get the given PMIC/REGULATOR I/O dev by pmic platdata:
+  int pmic_io_dev(struct udevice *pmic, struct udevice **pmic_io);
+
+3.2 Pmic platdata function calls - useful for pmic/regulator commands:
+- Get the device interface type:
+  int pmic_if_type(struct udevice *pmic);
+
+- Get the device interface bus number:
+  int pmic_if_bus_num(struct udevice *pmic);
+
+- Get the device addres(I2C) or cs(SPI) on bus:
+  int pmic_if_addr_cs(struct udevice *pmic);
+
+- Get the device max internal address offset:
+  int pmic_if_max_offset(struct udevice *pmic);
+
+- Get the device interface name string:
+  const char *pmic_if_str(struct udevice *pmic);
+
+3.3. Device I/O interface
+Can be used with UCLASS_PMIC devices:
+- Read the device 'reg':
+  int pmic_read(struct udevice *pmic, unsigned reg, unsigned char *val);
+
+- Write to the device 'reg':
+  int pmic_write(struct udevice *pmic, unsigned reg, unsigned char val);
+
+4. Pmic driver
+============================
+As an example of the pmic driver, please take a look into the MAX77686 driver
+'drivers/power/pmic/max77686.c' and the description in 'include/power/pmic.h'
+
+The pmic driver can be defined by U_BOOT_DRIVER() macro:
+
+U_BOOT_DRIVER(pmic_max77686) = {
+	.name = "max77686 pmic",
+	.id = UCLASS_PMIC,
+	.of_match = max77686_ids,     - Allows to bind by compatible
+	.bind = max77686_bind,        - Function called at device bind
+	.ops = &max77686_ops,         - Pmic api function calls
+	.platdata_auto_alloc_size = sizeof(struct pmic_platdata),
+};
+
+To bind the pmic device, field '.of_match' is required with proper compatible.
+To make the device useful with the pmic command, the driver must define
+the size of platform data allocation size.
+
+Driver ops:
+.read = max77686_read() - allows to use pmic_read()
+.write = max77686_write() - allows to use pmic_write()
+
+Driver bind:
+- max77686_bind(): is called on device bind and:
+  - fills the platform data structure
+  - bind MAX77686 regulator child device
+
+Note:
+For the simply case, when regulator driver doesn't exists, the driver bind
+method should only fill the platform data structure.
+
+5. Pmic command
+===============
+To use the pmic command, config: CONFIG_DM_PMIC_CMD is required.
+The new pmic command allows to:
+- list pmic devices
+- choose the current device (like the mmc command)
+- read or write the pmic register
+- dump all pmic registers
+
+The pmic platform data is mandatory to use this command with devices.
+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 0          - set dev 0 as current device
+# pmic dump           - dump the registers of the current pmic dev
+# pmic read 0x0       - read the register at address 0x0
+# pmic write 0x0 0x1  - write 0x1 to the register at addres 0x0
+
+6. Regulator driver API
+===================================
+To use the regulator API, config: CONFIG_DM_REGULATOR is required.
+The same as for the pmic devices, regulator drivers should alloc platform data.
+It's the same type as for uclass pmic drivers: 'struct pmic_platdata', and can
+be shared with the parent pmic device.
+
+The api is described in file: 'include/power/regulator.h'
+
+6.1 Getting the device:
+- By name - usefull for unique names, or if only one regulator device exists:
+  int regulator_get(char *name, struct udevice **regulator);
+
+- By I2C or SPI bus number and chip address of pmic parent - when few regulator
+  devices exists:
+  int regulator_i2c_get(int bus, int addr, struct udevice **regulator);
+  int regulator_spi_get(int bus, int cs, struct udevice **regulator);
+
+6.2 Pmic platdata function calls - useful for pmic/regulator commands
+It's the same as for Point 3.2.
+
+6.3 Device I/O interface
+According to the framework assumptions, where uclass pmic device is a parent
+of pmic devices other uclass types, the regulator devices should use uclass
+pmic interface, with the parent as the device, like this:
+- pmic_read(regulator->parent, some_reg, some_val);
+- pmic_write(regulator->parent, some_reg, &some_val);
+
+6.4 Device regulator operations
+The regulator function calls are based on few data types:
+- enum regulator_type {...} - standard types: LDO, BUCK and DVS
+- enum regulator_out_state {...} - ON/OFF states
+- struct regulator_value_desc {...} - output name and value limits
+- struct regulator_mode_desc {...} - output operation mode value and name
+- struct dm_regulator_ops {...} - regulator driver function calls
+
+The first argument is always device. And the device uclass id must be always:
+- 'UCLASS_PMIC_REGULATOR'
+
+Function details are described in regulator header. The basic features are:
+- Get the number of the given regulator type outputs
+  int regulator_get_cnt(struct udevice *dev, int type, int *cnt);
+
+- Get the given output value descriptor
+  int regulator_get_value_desc(struct udevice *dev, int type, int number,
+                               struct regulator_value_desc **desc);
+
+- Get the given output mode descriptor array (usually more then one mode)
+  int regulator_get_mode_desc(struct udevice *dev, int type, int number,
+                              int *mode_cnt, struct regulator_mode_desc **desc);
+
+- Get or set the given output voltage value (micro Volts unit)
+  int regulator_get_value(struct udevice *dev, int type, int number, int *val);
+  int regulator_set_value(struct udevice *dev, int type, int number, int val);
+
+- Get or set the given output on/off state
+  int regulator_get_state(struct udevice *dev, int type, int number, int *state);
+  int regulator_set_state(struct udevice *dev, int type, int number, int state);
+
+- Get or set the given output operation mode (mode defined by the driver)
+  int regulator_get_mode(struct udevice *dev, int type, int number, int *mode);
+  int regulator_set_mode(struct udevice *dev, int type, int number, int mode);
+
+7. Design limitations:
+The child devices of the single parent device (pmic, interface), should not
+provide it's features by more then one device each uclass types.
+It's because, e.g. regulator_i2c_get() function will:
+- look for the pmic device at address given by args,
+- return it's first child device, which uclass type is 'UCLASS_PMIC_REGULATOR'
+So, if more then one pmic child device exists, each the same uclass type, then
+the regulator_i2c_get(), will return only first device. But usually there is no
+need to implement the same functionality with more than one instance of each
+uclass type device.
+
+8. Regulator driver
+======================================
+As an example of the regulator driver, please take a look into the MAX77686
+regulator driver (drivers/power/regulator/max77686.c). The driver structure:
+
+U_BOOT_DRIVER(max77686_regulator) = {
+        .name = "max77686 regulator",
+        .id = UCLASS_PMIC_REGULATOR,
+        .ops = &reg_max77686_ops,
+        .ofdata_to_platdata = reg_max77686_ofdata_to_platdata,
+        .priv_auto_alloc_size = sizeof(struct max77686_regulator_info),
+};
+
+This driver structure is different than for the pmic driver.
+
+It doesn't provide:
+.of_match
+.platdata_auto_alloc_size
+
+Both are not required in this case, because .of_match is known at the bind call,
+and the platdata pointer is the same as for the parent device (pmic).
+
+But instead of the pmic driver, this driver provides:
+.of_data_to_platdata
+
+And this call above is responsible for fill the regulator outputs descriptors,
+which are described in device-tree, e.g. 'arch/arm/dts/exynos4412-odroid.dts'
+
+The bind for this driver is called in pmic/max77686.c driver bind.
+After the pmic driver successful bind, there are two max77686 devices:
+- max77686 pmic (parent)         - UCLASS_PMIC
+ '- max77686 regulator (child)   - UCLASS_PMIC_REGULATOR
+
+For the I/O, this driver uses pmic_read/write calls, with the parent device
+as the first argument, e.g.: pmic_read(dev->parent, adr, &val);
+
+9. Regulator command
+====================
+To use the pmic command, config: CONFIG_DM_REGULATOR_CMD is required.
+
+The extension for the 'pmic' command is 'regulator' command.
+This actually uses the same code, but provides the 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 the pmic device,
+without the pmic documentation. If driver provide each regulator
+output - value and mode descriptor - then user can operate on it.
+
+Usage:
+regulator list     - list UCLASS regulator devices
+regulator dev [id] - show or set operating regulator device
+regulator dump     - dump registers of current regulator
+regulator [ldo/buck/dvs][N] [name/state/desc] - print regulator(s) info
+regulator [ldoN/buckN/dvsN] [setval/setmode] [mV/modeN] [-f] - set val (mV)
+
+The -f option (forcibly) or mode - only if descriptor is 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'
+
+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.
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 09/12] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (7 preceding siblings ...)
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 08/12] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 10/12] odroid: board: add support to dm pmic api Przemyslaw Marczak
                     ` (6 subsequent siblings)
  15 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 board/samsung/common/board.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/board/samsung/common/board.c b/board/samsung/common/board.c
index da2245f..c16e847 100644
--- a/board/samsung/common/board.c
+++ b/board/samsung/common/board.c
@@ -21,9 +21,9 @@
 #include <asm/arch/pinmux.h>
 #include <asm/arch/power.h>
 #include <asm/arch/system.h>
-#include <power/pmic.h>
 #include <asm/arch/sromc.h>
 #include <lcd.h>
+#include <i2c.h>
 #include <samsung/misc.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -162,7 +162,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();
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 10/12] odroid: board: add support to dm pmic api
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (8 preceding siblings ...)
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 09/12] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-06 14:14     ` Simon Glass
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 11/12] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
                     ` (5 subsequent siblings)
  15 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

This commit change the old pmic framework calls with the new ones.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes v2:
- remove board_init_i2c() call
- update regulator calls
- update headers
- samsung/misc.c: include required header
---
 board/samsung/common/misc.c   |  1 +
 board/samsung/odroid/odroid.c | 52 ++++++++++++++++++++++++++-----------------
 2 files changed, 33 insertions(+), 20 deletions(-)

diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
index 4538ac7..18d71e8 100644
--- a/board/samsung/common/misc.c
+++ b/board/samsung/common/misc.c
@@ -16,6 +16,7 @@
 #include <asm/arch/cpu.h>
 #include <asm/gpio.h>
 #include <linux/input.h>
+#include <dm.h>
 #include <power/pmic.h>
 #include <mmc.h>
 
diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
index bff6ac9..2448cde 100644
--- a/board/samsung/odroid/odroid.c
+++ b/board/samsung/odroid/odroid.c
@@ -12,7 +12,9 @@
 #include <asm/arch/gpio.h>
 #include <asm/gpio.h>
 #include <asm/arch/cpu.h>
+#include <dm.h>
 #include <power/pmic.h>
+#include <power/regulator.h>
 #include <power/max77686_pmic.h>
 #include <errno.h>
 #include <usb.h>
@@ -402,15 +404,23 @@ static void board_gpio_init(void)
 
 static int pmic_init_max77686(void)
 {
-	struct pmic *p = pmic_get("MAX77686_PMIC");
+	struct udevice *reg;
+	int type;
 
-	if (pmic_probe(p))
+	if (regulator_get("max77686", &reg)) {
+		error("Regulator get error\n");
 		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 */
+	type = REGULATOR_TYPE_LDO;
+	regulator_set_value(reg, type, 20, 1800000);	/* LDO20 eMMC */
+	regulator_set_value(reg, type, 21, 2800000);	/* LDO21 SD */
+	regulator_set_value(reg, type, 22, 2800000);	/* LDO22 eMMC */
+
+	regulator_set_mode(reg, type, 20, OPMODE_ON);
+	regulator_set_mode(reg, type, 21, OPMODE_ON);
+	regulator_set_mode(reg, type, 22, OPMODE_ON);
 
 	return 0;
 }
@@ -435,7 +445,6 @@ int exynos_init(void)
 
 int exynos_power_init(void)
 {
-	pmic_init(0);
 	pmic_init_max77686();
 
 	return 0;
@@ -444,19 +453,21 @@ int exynos_power_init(void)
 #ifdef CONFIG_USB_GADGET
 static int s5pc210_phy_control(int on)
 {
-	struct pmic *p_pmic;
+	struct udevice *reg;
+	int type;
 
-	p_pmic = pmic_get("MAX77686_PMIC");
-	if (!p_pmic)
+	if (regulator_get("max77686", &reg)) {
+		error("Regulator get error\n");
 		return -ENODEV;
+	}
 
-	if (pmic_probe(p_pmic))
-		return -1;
+	type = REGULATOR_TYPE_LDO;
 
 	if (on)
-		return max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
+		return regulator_set_mode(reg, type, 12, OPMODE_ON);
 	else
-		return max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
+		return regulator_set_mode(reg, type, 12, OPMODE_LPM);
+
 }
 
 struct s3c_plat_otg_data s5pc210_otg_data = {
@@ -473,7 +484,8 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
 int board_usb_init(int index, enum usb_init_type init)
 {
 #ifdef CONFIG_CMD_USB
-	struct pmic *p_pmic;
+	struct udevice *reg;
+	int type, ret;
 
 	/* Set Ref freq 0 => 24MHz, 1 => 26MHz*/
 	/* Odroid Us have it at 24MHz, Odroid Xs at 26MHz */
@@ -491,14 +503,14 @@ int board_usb_init(int index, enum usb_init_type init)
 	/* Power off and on BUCK8 for LAN9730 */
 	debug("LAN9730 - Turning power buck 8 OFF and ON.\n");
 
-	p_pmic = pmic_get("MAX77686_PMIC");
-	if (p_pmic && !pmic_probe(p_pmic)) {
-		max77686_set_buck_voltage(p_pmic, 8, 750000);
-		max77686_set_buck_voltage(p_pmic, 8, 3300000);
+	if (regulator_get("max77686", &reg)) {
+		type = REGULATOR_TYPE_BUCK;
+		ret = regulator_set_value(reg, type, 8, 750000);
+		ret |= regulator_set_value(reg, type, 8, 3300000);
+		if (ret)
+			error("Can't set regulator\n");
 	}
-
 #endif
-
 	debug("USB_udc_probe\n");
 	return s3c_udc_probe(&s5pc210_otg_data);
 }
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 11/12] odroid: dts: add 'voltage-regulators' description to max77686 node
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (9 preceding siblings ...)
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 10/12] odroid: board: add support to dm pmic api Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 12/12] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
                     ` (4 subsequent siblings)
  15 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- odroid: dts: remove pmic alias
---
 arch/arm/dts/exynos4412-odroid.dts | 249 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 248 insertions(+), 1 deletion(-)

diff --git a/arch/arm/dts/exynos4412-odroid.dts b/arch/arm/dts/exynos4412-odroid.dts
index 582f6e5..6408c6f 100644
--- a/arch/arm/dts/exynos4412-odroid.dts
+++ b/arch/arm/dts/exynos4412-odroid.dts
@@ -35,11 +35,258 @@
 		samsung,i2c-max-bus-freq = <100000>;
 		status = "okay";
 
-		max77686_pmic at 09 {
+		max77686 {
 			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>;
+				};
+			};
 		};
 	};
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 12/12] odroid: config: enable dm pmic, dm regulator and max77686 driver
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (10 preceding siblings ...)
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 11/12] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
@ 2015-03-03 16:24   ` Przemyslaw Marczak
  2015-03-06 14:15     ` Simon Glass
  2015-03-03 16:30   ` [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model Przemyslaw Marczak
                     ` (3 subsequent siblings)
  15 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:24 UTC (permalink / raw)
  To: u-boot

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_I2C
- CONFIG_DM_PMIC_MAX77686
- CONFIG_DM_REGULATOR
- CONFIG_DM_REGULATOR_MAX77686

And removes the unused:
- CONFIG_DM_I2C_COMPAT
- CONFIG_POWER
- CONFIG_POWER_I2C
- CONFIG_POWER_MAX77686

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- config: enable dm i2c; cleanup
- remove CONFIG_DM_I2C_COMPAT
- enable regulator command
---
 configs/odroid_defconfig | 1 -
 include/configs/odroid.h | 9 ++++++---
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/configs/odroid_defconfig b/configs/odroid_defconfig
index 816a3fa..4116137 100644
--- a/configs/odroid_defconfig
+++ b/configs/odroid_defconfig
@@ -4,4 +4,3 @@ CONFIG_TARGET_ODROID=y
 CONFIG_OF_CONTROL=y
 CONFIG_DEFAULT_DEVICE_TREE="exynos4412-odroid"
 CONFIG_DM_I2C=y
-CONFIG_DM_I2C_COMPAT=y
diff --git a/include/configs/odroid.h b/include/configs/odroid.h
index 8b47537..d2bc7ee 100644
--- a/include/configs/odroid.h
+++ b/include/configs/odroid.h
@@ -182,9 +182,12 @@
 #define CONFIG_SYS_I2C_S3C24X0_SLAVE	0
 
 /* POWER */
-#define CONFIG_POWER
-#define CONFIG_POWER_I2C
-#define CONFIG_POWER_MAX77686
+#define CONFIG_DM_PMIC
+#define CONFIG_DM_PMIC_CMD
+#define CONFIG_DM_PMIC_MAX77686
+#define CONFIG_DM_REGULATOR
+#define CONFIG_DM_REGULATOR_CMD
+#define CONFIG_DM_REGULATOR_MAX77686
 
 /* GPT */
 #define CONFIG_RANDOM_UUID
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (11 preceding siblings ...)
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 12/12] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
@ 2015-03-03 16:30   ` Przemyslaw Marczak
  2015-03-03 16:40   ` Przemyslaw Marczak
                     ` (2 subsequent siblings)
  15 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:30 UTC (permalink / raw)
  To: u-boot

Hello,

On 03/03/2015 05:24 PM, Przemyslaw Marczak wrote:
> 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
>

Missed one e-mail:
+cc: trini at konsulko.com

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (12 preceding siblings ...)
  2015-03-03 16:30   ` [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model Przemyslaw Marczak
@ 2015-03-03 16:40   ` Przemyslaw Marczak
  2015-03-06 14:10   ` Simon Glass
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
  15 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-03 16:40 UTC (permalink / raw)
  To: u-boot

And the patchset on github: 
https://github.com/bobenstein/u-boot/tree/dm-pmic-v2

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 01/12] exynos5: fix build break by adding CONFIG_POWER
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 01/12] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
@ 2015-03-04 12:19     ` Minkyu Kang
  0 siblings, 0 replies; 218+ messages in thread
From: Minkyu Kang @ 2015-03-04 12:19 UTC (permalink / raw)
  To: u-boot

On 04/03/15 01:24, Przemyslaw Marczak wrote:
> 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.
> 
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  include/configs/exynos5-common.h | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/include/configs/exynos5-common.h b/include/configs/exynos5-common.h
> index 3ab8d55..3ee9482 100644
> --- a/include/configs/exynos5-common.h
> +++ b/include/configs/exynos5-common.h
> @@ -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
> 

Acked-by: Minkyu Kang <mk7.kang@samsung.com>

Thanks,
Minkyu Kang.

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (13 preceding siblings ...)
  2015-03-03 16:40   ` Przemyslaw Marczak
@ 2015-03-06 14:10   ` Simon Glass
  2015-03-06 15:08     ` Przemyslaw Marczak
  2015-03-10  2:12     ` Simon Glass
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
  15 siblings, 2 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-06 14:10 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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

This is an impressive pieces of work! It will be great to get these in.

Here are some high-level comments on this series:

1. I think regulator LDOs and bucks should be proper devices (struct
udevice), bound by the pmic when it is probed. The advantages are

a. You can see them in the device list - the pmic will end up with a
lot more children
b. You can use the same regulator uclass for each, but have different
operations for each driver (e.g. max77686 might provide two different
drivers, one for LDO, one for buck).
c. Things like your 'switch (type)' in max7786_get_state() etc. will go away
d. You can perform operations on them without having to specify their
parent and number - e.g. regulator_set_mode(struct udevice *ldo, int
mode) which is much more natural for users
e. You avoid needing your own list of regulators and bucks - struct
max7786_regulator_info. After all, keeping track of child devices is
something that driver model can do

2. I see device tree support, but the Linux device tree bindings are
not fully supported, e.g. the regulators sub-node uses
regulator-compatible instead of regulator-name. I think it should be
exactly the same (and we should copy the device tree files, only
leaving out what we don't support)

3. The I2C/SPI difference is a bit clunky. We should try to hide this
away. The most obvious problem is in getting the device. Instead of
pmic_i2c_get() we should use the "power-supply" property in the device
tree, so we need a function which can find the regulator given the
device node (a bit like gpio_request_by_name() but for PMICs). The
pmic_get() function is OK and will be needed also, as I am sure we
will use names in some places. We should remove any mention of the bus
type from the API I think. Also regulator number seems and odd concept
- better to use the name/device tree link to find the right device.
One way to avoid I2C/SPI problems is to create a helper file which
allows you to read and write registers given a struct udevice. It
could look at whether the device is I2C or SPI and do the right thing.
This could be generally useful, not just for PMICs.

4. Should use Kconfig now.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 02/12] dm: device: add function device_get_first_child_by_uclass_id()
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 02/12] dm: device: add function device_get_first_child_by_uclass_id() Przemyslaw Marczak
@ 2015-03-06 14:11     ` Simon Glass
  2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-06 14:11 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>
> 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.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes V2:
> - new commit
> ---
>  drivers/core/device.c | 15 +++++++++++++++
>  include/dm/device.h   | 16 ++++++++++++++++
>  2 files changed, 31 insertions(+)
>
> diff --git a/drivers/core/device.c b/drivers/core/device.c
> index 73c3e07..76b22cf 100644
> --- a/drivers/core/device.c
> +++ b/drivers/core/device.c
> @@ -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?

>
> +                                       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)
>  {
> diff --git a/include/dm/device.h b/include/dm/device.h
> index 7a48eb8..9f0d6ce 100644
> --- a/include/dm/device.h
> +++ b/include/dm/device.h
> @@ -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
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 03/12] dm: pmic: add implementation of driver model pmic uclass
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 03/12] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
@ 2015-03-06 14:11     ` Simon Glass
  2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-06 14:11 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> 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

This should have a Kconfig file.

>
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index 2145652..5c9a189 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -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
> diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
> new file mode 100644
> index 0000000..309463e
> --- /dev/null
> +++ b/drivers/power/pmic-uclass.c
> @@ -0,0 +1,191 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * 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",
> +};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index 91bb90d..ff360ac 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -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

> +       UCLASS_PMIC,
> +
>         UCLASS_COUNT,
>         UCLASS_INVALID = -1,
>  };
> diff --git a/include/power/pmic.h b/include/power/pmic.h
> index afbc5aa..94171ac 100644
> --- a/include/power/pmic.h
> +++ b/include/power/pmic.h
> @@ -1,4 +1,7 @@
>  /*
> + *  Copyright (C) 2014-2015 Samsung Electronics
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
>   *  Copyright (C) 2011-2012 Samsung Electronics
>   *  Lukasz Majewski <l.majewski@samsung.com>
>   *
> @@ -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],
> + *                                           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.?

> + * ...
> + * }
> + *
> + * 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
> + * @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.

> +
> +/**
> + * 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?

> +       int (*write)(struct udevice *pmic, unsigned reg, unsigned char val);
> +};
> +
> +/* enum pmic_op_type - used for various pmic devices operation calls,

Comment style

> + * 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,
> +};
> +
> +/**
> + * 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

> +{
> +       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:
> + * @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.

> +
> +/**
> + * 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"/"--"
> + *
> + * @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.

> +
> +/**
> + * pmic_read/write: read/write to the UCLASS_PMIC device
> + *
> + * 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/ ?

> + */
> +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)
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 04/12] dm: pmic: add implementation of driver model regulator uclass
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 04/12] dm: pmic: add implementation of driver model regulator uclass Przemyslaw Marczak
@ 2015-03-06 14:12     ` Simon Glass
  2015-03-25 16:08       ` Przemyslaw Marczak
  2015-03-10 11:41     ` Robert Baldyga
  1 sibling, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-06 14:12 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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?

> - 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.

>
> 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
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> 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
>
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index 5c9a189..a6b7012 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -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
> diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
> new file mode 100644
> index 0000000..6b5c678
> --- /dev/null
> +++ b/drivers/power/regulator-uclass.c
> @@ -0,0 +1,227 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * 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;

This is an error so you should be able to just assert(). Failing that
let's use -ENXIO as you do below.

> +
> +       if (!ops->get_cnt)
> +               return -EPERM;

-ENOSYS for these.

> +
> +       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",
> +};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index ff360ac..0a1e664 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -37,6 +37,7 @@ enum uclass_id {
>
>         /* PMIC uclass and PMIC related uclasses */
>         UCLASS_PMIC,
> +       UCLASS_PMIC_REGULATOR,
>
>         UCLASS_COUNT,
>         UCLASS_INVALID = -1,
> diff --git a/include/power/regulator.h b/include/power/regulator.h
> new file mode 100644
> index 0000000..ee8a280
> --- /dev/null
> +++ b/include/power/regulator.h
> @@ -0,0 +1,310 @@
> +/*
> + *  Copyright (C) 2014-2015 Samsung Electronics
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * 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,
> +};

If we will only ever have on/off perhaps we don't need this enum and
could just use a bool?

> +
> +/**
> + * 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.

> + * @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()?

> +};
> +
> +/**
> + * 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)

> + *
> + * @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

> + * @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);

get_count

Return count in return value instead of a parameter.

> +
> +       /**
> +        * 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);

You could use descp, to indicate it returns a pointer.

> +
> +       /**
> +        * 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

> +        * 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?

> +                                  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);

Return value in return value instead of a parameter.

> +       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

> +        *
> +        * 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.

> +       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);

Return mode in return value.

> +       int (*set_mode)(struct udevice *dev, int type, int number, int mode);
> +};
> +
> +/**
> + * regulator_get_cnt: returns the number of driver registered regulators.

driver-registered

> + * 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.

> +
> +/**
> + * 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.

You may as well return the voltage in the return value, unless it can
be -ve (I assume not?)

> + */
> +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);

Return state in return value instead of a parameter.

> +
> +/**
> + * 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);

Return mode in return value instead of a parameter.

> +
> +/**
> + * 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:
> + * - 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!

> +
> +#endif /* _INCLUDE_REGULATOR_H_ */
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 05/12] dm: pmic: new commands: pmic and regulator
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 05/12] dm: pmic: new commands: pmic and regulator Przemyslaw Marczak
@ 2015-03-06 14:13     ` Simon Glass
  2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-06 14:13 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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.

This help could go in Kconfig.

>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> 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
>
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index a6b7012..943b38f 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -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.

> diff --git a/drivers/power/cmd_pmic.c b/drivers/power/cmd_pmic.c
> new file mode 100644
> index 0000000..996bfe7
> --- /dev/null
> +++ b/drivers/power/cmd_pmic.c
> @@ -0,0 +1,820 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +#include <common.h>
> +#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"

> +#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;
> +}
> +
> +/**
> + * 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?

> +{
> +       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;
> +       }
> +
> +       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 '.'?

> +
> +               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
> +                       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?

> +
> +               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)
> +                               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?

> +
> +       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)
> +{
> +       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.

> +
> +       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.

> +               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;
> +       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?

> +                                       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)
> +{
> +       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?

> +
> +       if (!set_force && check_reg_val(dev, reg_num, set_val, type))
> +               return -EPERM;
> +
> +       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.

> +}
> +
> +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 */
> +       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?

> +
> +       /* 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);
> +       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.

> +               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"
> +       "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?

> +);
> +#endif /* CONFIG_DM_REGULATOR_CMD */
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 08/12] doc: driver-model: pmic and regulator uclass documentation
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 08/12] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
@ 2015-03-06 14:14     ` Simon Glass
  2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-06 14:14 UTC (permalink / raw)
  To: u-boot

Hi,

On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes v2:
> - update documentation with the framework api changes
> ---
>  doc/driver-model/dm-pmic-framework.txt | 367 +++++++++++++++++++++++++++++++++
>  1 file changed, 367 insertions(+)
>  create mode 100644 doc/driver-model/dm-pmic-framework.txt
>
> diff --git a/doc/driver-model/dm-pmic-framework.txt b/doc/driver-model/dm-pmic-framework.txt
> new file mode 100644
> index 0000000..f2fb4ac
> --- /dev/null
> +++ b/doc/driver-model/dm-pmic-framework.txt
> @@ -0,0 +1,367 @@
> +#
> +# (C) Copyright 2014-2015 Samsung Electronics
> +# Przemyslaw Marczak <p.marczak@samsung.com>
> +#
> +# SPDX-License-Identifier:      GPL-2.0+
> +#
> +
> +PMIC framework based on Driver Model
> +====================================
> +TOC:
> +1. Introduction
> +2. How does it work
> +3. Pmic driver api
> +4. Pmic driver
> +5. Pmic command
> +6. Regulator driver api
> +7. Design limitations
> +8. Regulator driver
> +9. Regulator command
> +
> +1. Introduction
> +===============
> +This is an introduction to driver-model multi uclass PMIC devices support.
> +At present it is based on two uclass types:
> +
> +- UCLASS_PMIC - basic uclass type for PMIC I/O, which provides common read/write
> +                interface.
> +
> +- UCLASS_PMIC_REGULATOR - additional uclass type for specific PMIC features,
> +                          which are various voltage regulators.
> +
> +New files:
> +UCLASS_PMIC:
> +- drivers/power/pmic-uclass.c
> +- include/power/pmic.h
> +UCLASS_PMIC_REGULATOR:
> +- drivers/power/regulator-uclass.c
> +- include/power/regulator.h
> +
> +Commands:
> +- drivers/power/cmd_pmic.c (pmic; regulator)
> +
> +2. How doees it work
> +====================
> +The Power Management Integrated Circuits (PMIC) are used in embedded systems
> +to provide stable, precise and specific voltage power source with over-voltage
> +and thermal protection circuits.
> +
> +The single PMIC can provide various functionalities with single or multiple
> +interfaces, like in the example below.
> +
> +-- SoC
> + |
> + |            ______________________________________
> + | BUS 0     |       Multi interface PMIC IC        |--> LDO out 1
> + | e.g.I2C0  |                                      |--> LDO out N
> + |-----------|---- PMIC device 0 (READ/WRITE ops)   |
> + | or SPI0   |    |_ REGULATOR device (ldo/... ops) |--> BUCK out 1
> + |           |    |_ CHARGER device (charger ops)   |--> BUCK out M
> + |           |    |_ MUIC device (microUSB con ops) |
> + | BUS 1     |    |_ ...                            |---> BATTERY
> + | e.g.I2C1  |                                      |
> + |-----------|---- PMIC device 1 (READ/WRITE ops)   |---> USB in 1
> + . or SPI1   |    |_ RTC device (rtc ops)           |---> USB in 2
> + .           |______________________________________|---> USB out
> + .
> +
> +Since U-Boot provides driver model features for I2C and SPI bus drivers,
> +the PMIC devices should also support this. With the new basic uclass types
> +for PMIC I/O and regulator features, PMIC drivers can simply provide common
> +features, with multiple interface and instance support.
> +
> +Basic design assumptions:
> +
> +- Common I/O api - UCLASS_PMIC
> +The main assumption is to use UCLASS_PMIC device to provide I/O interface,
> +for devices other uclass types. It is no matter what is the type of device
> +physical I/O interface. Usually PMIC devices are using SPI or I2C interface,
> +but use of any other interface (e.g. when PMIC is not directly connected
> +to the SoC) - is now possible. Drivers can use the same read/write api.
> +
> +- Common regulator api - UCLASS_REGULATOR
> +For setting the attributes of verious types of regulators with common api,
> +this uclass can be implemented. This allows to drive the each regulator output
> +value, on/off state and custom defined operation modes. It also provides the
> +user interface for all operations.
> +For the very simple implementation, the regulator drivers are not required,
> +so the code could base on pmic read/write only.
> +
> +When board device-tree file includes pmic subnode and the U_Boot compatible
> +driver exists, then the pmic device bind should looks like this:
> +
> +|_ root - will bind the device for I2C/SPI bus node
> +  |_ i2c/spi - should bind a device for pmic node
> +    |_ pmic (parent) - should bind child devices for its features
> +      |_ regulator (child)
> +      |_ charger   (child)
> +      |_ other     (child)
> +
> +Usually PMIC design provides:
> + - single I/O interface (single UCLASS_PMIC driver)
> +   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 (UCLASS_PMIC driver for each)
> +   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.
> +
> +3. Pmic driver api
> +===================
> +To use the pmic API, config: CONFIG_DM_PMIC is required.
> +The new driver API is very simple and is based on 'struct dm_pmic_ops',
> +which define two basic operations: device read and write.
> +
> +The platform data is introduced as 'struct pmic_platdata', to keep an info
> +about the device interface.
> +
> +The api is described in file: 'include/power/pmic.h'
> +
> +3.1 Getting the device:
> +- By name only - usefull if the name is unique, or only one pmic device exists:
> +  int pmic_get(char *name, struct udevice **pmic);
> +
> +- By I2C or SPI bus number and chip address - when few pmic devices exists:
> +  int pmic_i2c_get(int bus, int addr, struct udevice **pmic)
> +  int pmic_spi_get(int bus, int cs, struct udevice **pmic);
> +
> +- To get the given PMIC/REGULATOR I/O dev by pmic platdata:
> +  int pmic_io_dev(struct udevice *pmic, struct udevice **pmic_io);
> +
> +3.2 Pmic platdata function calls - useful for pmic/regulator commands:
> +- Get the device interface type:
> +  int pmic_if_type(struct udevice *pmic);
> +
> +- Get the device interface bus number:
> +  int pmic_if_bus_num(struct udevice *pmic);
> +
> +- Get the device addres(I2C) or cs(SPI) on bus:
> +  int pmic_if_addr_cs(struct udevice *pmic);
> +
> +- Get the device max internal address offset:
> +  int pmic_if_max_offset(struct udevice *pmic);
> +
> +- Get the device interface name string:
> +  const char *pmic_if_str(struct udevice *pmic);
> +
> +3.3. Device I/O interface
> +Can be used with UCLASS_PMIC devices:
> +- Read the device 'reg':
> +  int pmic_read(struct udevice *pmic, unsigned reg, unsigned char *val);
> +
> +- Write to the device 'reg':
> +  int pmic_write(struct udevice *pmic, unsigned reg, unsigned char val);
> +
> +4. Pmic driver
> +============================
> +As an example of the pmic driver, please take a look into the MAX77686 driver
> +'drivers/power/pmic/max77686.c' and the description in 'include/power/pmic.h'
> +
> +The pmic driver can be defined by U_BOOT_DRIVER() macro:
> +
> +U_BOOT_DRIVER(pmic_max77686) = {
> +       .name = "max77686 pmic",
> +       .id = UCLASS_PMIC,
> +       .of_match = max77686_ids,     - Allows to bind by compatible
> +       .bind = max77686_bind,        - Function called at device bind
> +       .ops = &max77686_ops,         - Pmic api function calls
> +       .platdata_auto_alloc_size = sizeof(struct pmic_platdata),
> +};
> +
> +To bind the pmic device, field '.of_match' is required with proper compatible.
> +To make the device useful with the pmic command, the driver must define
> +the size of platform data allocation size.
> +
> +Driver ops:
> +.read = max77686_read() - allows to use pmic_read()
> +.write = max77686_write() - allows to use pmic_write()
> +
> +Driver bind:
> +- max77686_bind(): is called on device bind and:
> +  - fills the platform data structure
> +  - bind MAX77686 regulator child device
> +
> +Note:
> +For the simply case, when regulator driver doesn't exists, the driver bind
> +method should only fill the platform data structure.
> +
> +5. Pmic command
> +===============
> +To use the pmic command, config: CONFIG_DM_PMIC_CMD is required.
> +The new pmic command allows to:
> +- list pmic devices
> +- choose the current device (like the mmc command)
> +- read or write the pmic register
> +- dump all pmic registers
> +
> +The pmic platform data is mandatory to use this command with devices.
> +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 0          - set dev 0 as current device
> +# pmic dump           - dump the registers of the current pmic dev
> +# pmic read 0x0       - read the register at address 0x0
> +# pmic write 0x0 0x1  - write 0x1 to the register at addres 0x0
> +
> +6. Regulator driver API
> +===================================
> +To use the regulator API, config: CONFIG_DM_REGULATOR is required.
> +The same as for the pmic devices, regulator drivers should alloc platform data.
> +It's the same type as for uclass pmic drivers: 'struct pmic_platdata', and can
> +be shared with the parent pmic device.
> +
> +The api is described in file: 'include/power/regulator.h'
> +
> +6.1 Getting the device:
> +- By name - usefull for unique names, or if only one regulator device exists:
> +  int regulator_get(char *name, struct udevice **regulator);
> +
> +- By I2C or SPI bus number and chip address of pmic parent - when few regulator
> +  devices exists:
> +  int regulator_i2c_get(int bus, int addr, struct udevice **regulator);
> +  int regulator_spi_get(int bus, int cs, struct udevice **regulator);
> +
> +6.2 Pmic platdata function calls - useful for pmic/regulator commands
> +It's the same as for Point 3.2.
> +
> +6.3 Device I/O interface
> +According to the framework assumptions, where uclass pmic device is a parent
> +of pmic devices other uclass types, the regulator devices should use uclass
> +pmic interface, with the parent as the device, like this:
> +- pmic_read(regulator->parent, some_reg, some_val);
> +- pmic_write(regulator->parent, some_reg, &some_val);
> +
> +6.4 Device regulator operations
> +The regulator function calls are based on few data types:
> +- enum regulator_type {...} - standard types: LDO, BUCK and DVS
> +- enum regulator_out_state {...} - ON/OFF states
> +- struct regulator_value_desc {...} - output name and value limits
> +- struct regulator_mode_desc {...} - output operation mode value and name
> +- struct dm_regulator_ops {...} - regulator driver function calls
> +
> +The first argument is always device. And the device uclass id must be always:
> +- 'UCLASS_PMIC_REGULATOR'
> +
> +Function details are described in regulator header. The basic features are:
> +- Get the number of the given regulator type outputs
> +  int regulator_get_cnt(struct udevice *dev, int type, int *cnt);
> +
> +- Get the given output value descriptor
> +  int regulator_get_value_desc(struct udevice *dev, int type, int number,
> +                               struct regulator_value_desc **desc);
> +
> +- Get the given output mode descriptor array (usually more then one mode)
> +  int regulator_get_mode_desc(struct udevice *dev, int type, int number,
> +                              int *mode_cnt, struct regulator_mode_desc **desc);
> +
> +- Get or set the given output voltage value (micro Volts unit)
> +  int regulator_get_value(struct udevice *dev, int type, int number, int *val);
> +  int regulator_set_value(struct udevice *dev, int type, int number, int val);
> +
> +- Get or set the given output on/off state
> +  int regulator_get_state(struct udevice *dev, int type, int number, int *state);
> +  int regulator_set_state(struct udevice *dev, int type, int number, int state);
> +
> +- Get or set the given output operation mode (mode defined by the driver)
> +  int regulator_get_mode(struct udevice *dev, int type, int number, int *mode);
> +  int regulator_set_mode(struct udevice *dev, int type, int number, int mode);
> +
> +7. Design limitations:
> +The child devices of the single parent device (pmic, interface), should not
> +provide it's features by more then one device each uclass types.
> +It's because, e.g. regulator_i2c_get() function will:
> +- look for the pmic device at address given by args,
> +- return it's first child device, which uclass type is 'UCLASS_PMIC_REGULATOR'
> +So, if more then one pmic child device exists, each the same uclass type, then
> +the regulator_i2c_get(), will return only first device. But usually there is no
> +need to implement the same functionality with more than one instance of each
> +uclass type device.
> +
> +8. Regulator driver
> +======================================
> +As an example of the regulator driver, please take a look into the MAX77686
> +regulator driver (drivers/power/regulator/max77686.c). The driver structure:
> +
> +U_BOOT_DRIVER(max77686_regulator) = {
> +        .name = "max77686 regulator",
> +        .id = UCLASS_PMIC_REGULATOR,
> +        .ops = &reg_max77686_ops,
> +        .ofdata_to_platdata = reg_max77686_ofdata_to_platdata,
> +        .priv_auto_alloc_size = sizeof(struct max77686_regulator_info),
> +};
> +
> +This driver structure is different than for the pmic driver.
> +
> +It doesn't provide:
> +.of_match
> +.platdata_auto_alloc_size
> +
> +Both are not required in this case, because .of_match is known at the bind call,
> +and the platdata pointer is the same as for the parent device (pmic).
> +
> +But instead of the pmic driver, this driver provides:
> +.of_data_to_platdata
> +
> +And this call above is responsible for fill the regulator outputs descriptors,
> +which are described in device-tree, e.g. 'arch/arm/dts/exynos4412-odroid.dts'
> +
> +The bind for this driver is called in pmic/max77686.c driver bind.
> +After the pmic driver successful bind, there are two max77686 devices:
> +- max77686 pmic (parent)         - UCLASS_PMIC
> + '- max77686 regulator (child)   - UCLASS_PMIC_REGULATOR
> +
> +For the I/O, this driver uses pmic_read/write calls, with the parent device
> +as the first argument, e.g.: pmic_read(dev->parent, adr, &val);
> +
> +9. Regulator command
> +====================
> +To use the pmic command, config: CONFIG_DM_REGULATOR_CMD is required.
> +
> +The extension for the 'pmic' command is 'regulator' command.
> +This actually uses the same code, but provides the 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 the pmic device,
> +without the pmic documentation. If driver provide each regulator
> +output - value and mode descriptor - then user can operate on it.
> +
> +Usage:
> +regulator list     - list UCLASS regulator devices
> +regulator dev [id] - show or set operating regulator device
> +regulator dump     - dump registers of current regulator
> +regulator [ldo/buck/dvs][N] [name/state/desc] - print regulator(s) info
> +regulator [ldoN/buckN/dvsN] [setval/setmode] [mV/modeN] [-f] - set val (mV)
> +
> +The -f option (forcibly) or mode - only if descriptor is 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'
> +
> +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.
> --
> 1.9.1
>

This is a good and thorough overview. Can we drop the 'dm-' prefix on
the filename and word-wrap it a bit more consistently towards the end
(lots of short lines at present). Some of the phrasing is a bit hard
to parse, but we can figure out that later.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 07/12] dm: regulator: add max77686 regulator driver
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 07/12] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
@ 2015-03-06 14:14     ` Simon Glass
  2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-06 14:14 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes V2:
> - change debug() to error()
> - code cleanup
> - fix data types
> - ldo/buck state implementation
> - adjust to new uclass api
> ---
>  Makefile                           |   1 +
>  drivers/power/Makefile             |   1 -
>  drivers/power/regulator/Makefile   |   8 +
>  drivers/power/regulator/max77686.c | 926 +++++++++++++++++++++++++++++++++++++
>  include/power/max77686_pmic.h      |  24 +-
>  5 files changed, 956 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/power/regulator/Makefile
>  create mode 100644 drivers/power/regulator/max77686.c
>
> diff --git a/Makefile b/Makefile
> index 6da4215..fcb37ae 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -627,6 +627,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/
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index 943b38f..7ff1baa 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -15,7 +15,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
> diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
> new file mode 100644
> index 0000000..9d282e3
> --- /dev/null
> +++ b/drivers/power/regulator/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# Copyright (C) 2014 Samsung Electronics
> +# Przemyslaw Marczak <p.marczak@samsung.com>
> +#
> +# SPDX-License-Identifier:     GPL-2.0+
> +#
> +
> +obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
> diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c
> new file mode 100644
> index 0000000..711c19c
> --- /dev/null
> +++ b/drivers/power/regulator/max77686.c
> @@ -0,0 +1,926 @@
> +/*
> + *  Copyright (C) 2012-2015 Samsung Electronics
> + *
> + *  Rajeshwari Shinde <rajeshwari.s@samsung.com>
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <fdtdec.h>
> +#include <i2c.h>
> +#include <dm.h>
> +#include <power/pmic.h>
> +#include <power/regulator.h>
> +#include <power/max77686_pmic.h>
> +#include <errno.h>
> +#include <dm.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +struct max77686_regulator_info {
> +       int ldo_cnt;
> +       int buck_cnt;
> +       struct regulator_value_desc ldo_desc[MAX77686_LDO_NUM + 1];

Why +1? Then let's use MAX77686_LDO_COUNT for the number of LDOs.

> +       struct regulator_value_desc buck_desc[MAX77686_BUCK_NUM + 1];
> +};
> +
> +#define MODE(_mode, _val, _name) { \
> +       .mode = _mode, \
> +       .register_value = _val, \
> +       .name = _name, \
> +}
> +
> +/* LDO: 1,3,4,5,9,17,18,19,20,21,22,23,24,26,26,27 */
> +static struct regulator_mode_desc max77686_ldo_mode_standby1[] = {
> +       MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
> +       MODE(OPMODE_LPM, MAX77686_LDO_MODE_LPM, "LPM"),
> +       MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
> +       MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
> +};
> +
> +/* LDO: 2,6,7,8,10,11,12,14,15,16 */
> +static struct regulator_mode_desc max77686_ldo_mode_standby2[] = {
> +       MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
> +       MODE(OPMODE_STANDBY, MAX77686_LDO_MODE_STANDBY, "ON/OFF"),
> +       MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
> +       MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
> +};
> +
> +/* Buck: 1 */
> +static struct regulator_mode_desc max77686_buck_mode_standby[] = {
> +       MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
> +       MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
> +       MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
> +};
> +
> +/* Buck: 2,3,4 */
> +static struct regulator_mode_desc max77686_buck_mode_lpm[] = {
> +       MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
> +       MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
> +       MODE(OPMODE_LPM, MAX77686_BUCK_MODE_LPM, "LPM"),
> +       MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
> +};
> +
> +/* Buck: 5,6,7,8,9 */
> +static struct regulator_mode_desc max77686_buck_mode_onoff[] = {
> +       MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
> +       MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
> +};
> +
> +static const char max77686_buck_addr[] = {
> +       0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38
> +};
> +
> +static int max77686_buck_volt2hex(int buck, int uV)
> +{
> +       unsigned int hex = 0;
> +       unsigned int hex_max = 0;
> +
> +       switch (buck) {
> +       case 2:
> +       case 3:
> +       case 4:
> +               /* hex = (uV - 600000) / 12500; */
> +               hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP;
> +               hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
> +               /**
> +                * This uses voltage scaller - temporary not implemented
> +                * so return just 0
> +                */
> +               return 0;
> +       default:
> +               /* hex = (uV - 750000) / 50000; */
> +               hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP;
> +               hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
> +               break;
> +       }
> +
> +       if (hex >= 0 && hex <= hex_max)
> +               return hex;
> +
> +       error("Value: %d uV is wrong for BUCK%d", uV, buck);
> +       return -EINVAL;
> +}
> +
> +static int max77686_buck_hex2volt(int buck, int hex)
> +{
> +       unsigned uV = 0;
> +       unsigned int hex_max = 0;
> +
> +       if (hex < 0)
> +               goto bad_hex;
> +
> +       switch (buck) {
> +       case 2:
> +       case 3:
> +       case 4:
> +               hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
> +               if (hex > hex_max)
> +                       goto bad_hex;
> +
> +               /* uV = hex * 12500 + 600000; */
> +               uV = hex * MAX77686_BUCK_UV_LSTEP + MAX77686_BUCK_UV_LMIN;
> +               break;
> +       default:
> +               hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
> +               if (hex > hex_max)
> +                       goto bad_hex;
> +
> +               /* uV = hex * 50000 + 750000; */
> +               uV = hex * MAX77686_BUCK_UV_HSTEP + MAX77686_BUCK_UV_HMIN;
> +               break;
> +       }
> +
> +       return uV;
> +
> +bad_hex:
> +       error("Value: %#x is wrong for BUCK%d", hex, buck);
> +       return -EINVAL;
> +}
> +
> +static int max77686_ldo_volt2hex(int ldo, int uV)
> +{
> +       unsigned int hex = 0;
> +
> +       switch (ldo) {
> +       case 1:
> +       case 2:
> +       case 6:
> +       case 7:
> +       case 8:
> +       case 15:
> +               hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_LSTEP;
> +               /* hex = (uV - 800000) / 25000; */
> +               break;
> +       default:
> +               hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_HSTEP;
> +               /* hex = (uV - 800000) / 50000; */
> +       }
> +
> +       if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX)
> +               return hex;
> +
> +       error("Value: %d uV is wrong for LDO%d", uV, ldo);
> +       return -EINVAL;
> +}
> +
> +static int max77686_ldo_hex2volt(int ldo, int hex)
> +{
> +       unsigned int uV = 0;
> +
> +       if (hex > MAX77686_LDO_VOLT_MAX_HEX)
> +               goto bad_hex;
> +
> +       switch (ldo) {
> +       case 1:
> +       case 2:
> +       case 6:
> +       case 7:
> +       case 8:
> +       case 15:
> +               /* uV = hex * 25000 + 800000; */
> +               uV = hex * MAX77686_LDO_UV_LSTEP + MAX77686_LDO_UV_MIN;
> +               break;
> +       default:
> +               /* uV = hex * 50000 + 800000; */
> +               uV = hex * MAX77686_LDO_UV_HSTEP + MAX77686_LDO_UV_MIN;
> +       }
> +
> +       return uV;
> +
> +bad_hex:
> +       error("Value: %#x is wrong for ldo%d", hex, ldo);
> +       return -EINVAL;
> +}
> +
> +static int max77686_ldo_hex2mode(int ldo, int hex)
> +{
> +       if (hex > MAX77686_LDO_MODE_MASK)
> +               return -EINVAL;
> +
> +       switch (hex) {
> +       case MAX77686_LDO_MODE_OFF:
> +               return OPMODE_OFF;
> +       case MAX77686_LDO_MODE_LPM: /* == MAX77686_LDO_MODE_STANDBY: */
> +               /* The same mode values but different meaning for each ldo */
> +               switch (ldo) {
> +               case 2:
> +               case 6:
> +               case 7:
> +               case 8:
> +               case 10:
> +               case 11:
> +               case 12:
> +               case 14:
> +               case 15:
> +               case 16:
> +                       return OPMODE_STANDBY;
> +               default:
> +                       return OPMODE_LPM;
> +               }
> +       case MAX77686_LDO_MODE_STANDBY_LPM:
> +               return OPMODE_STANDBY_LPM;
> +       case MAX77686_LDO_MODE_ON:
> +               return OPMODE_ON;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int max77686_buck_hex2mode(int buck, int hex)
> +{
> +       if (hex > MAX77686_BUCK_MODE_MASK)
> +               return -EINVAL;
> +
> +       switch (hex) {
> +       case MAX77686_BUCK_MODE_OFF:
> +               return OPMODE_OFF;
> +       case MAX77686_BUCK_MODE_ON:
> +               return OPMODE_ON;
> +       case MAX77686_BUCK_MODE_STANDBY:
> +               switch (buck) {
> +               case 1:
> +               case 2:
> +               case 3:
> +               case 4:
> +                       return OPMODE_STANDBY;
> +               default:
> +                       return -EINVAL;
> +               }
> +       case MAX77686_BUCK_MODE_LPM:
> +               switch (buck) {
> +               case 2:
> +               case 3:
> +               case 4:
> +                       return OPMODE_LPM;
> +               default:
> +                       return -EINVAL;
> +               }
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int max77686_get_value_desc(struct udevice *dev, int type, int d_num,
> +                                  struct regulator_value_desc **desc)
> +{
> +       struct max77686_regulator_info *dev_info;

Can we just pass struct max77686_regulator_info into this function
avoid all this checking? Similar elsewhere it applied.

> +
> +       if (!dev)
> +               return -ENODEV;
> +
> +       if (!dev->priv)
> +               return -ENXIO;
> +
> +       dev_info = dev->priv;
> +
> +       switch (type) {
> +       case REGULATOR_TYPE_LDO:
> +               if (d_num < 1 || d_num > 26)
> +                       return -EINVAL;
> +
> +               *desc = &dev_info->ldo_desc[d_num];
> +               return 0;
> +       case REGULATOR_TYPE_BUCK:
> +               if (d_num < 1 || d_num > 9)
> +                       return -EINVAL;
> +
> +               *desc = &dev_info->buck_desc[d_num];
> +               return 0;
> +       default:
> +               return -EPERM;
> +       }
> +}
> +
> +static int max77686_get_mode_desc_array(struct udevice *dev, int type,
> +                                       int d_num, int *d_mode_cnt,
> +                                       struct regulator_mode_desc **desc)
> +{
> +       struct max77686_regulator_info *dev_info;
> +
> +       if (!dev)
> +               return -ENODEV;
> +
> +       if (!dev->priv)
> +               return -ENXIO;
> +
> +       dev_info = dev->priv;
> +       *d_mode_cnt = 0;
> +
> +       if (type == REGULATOR_TYPE_LDO) {
> +               if (d_num < 1 || d_num > dev_info->ldo_cnt)
> +                       return -EINVAL;
> +
> +               switch (d_num) {
> +               case 2:
> +               case 6:
> +               case 7:
> +               case 8:
> +               case 10:
> +               case 11:
> +               case 12:
> +               case 14:
> +               case 15:
> +               case 16:
> +                       *d_mode_cnt = ARRAY_SIZE(max77686_ldo_mode_standby2);
> +                       *desc = max77686_ldo_mode_standby2;
> +                       return 0;
> +               default:
> +                       *d_mode_cnt = ARRAY_SIZE(max77686_ldo_mode_standby1);
> +                       *desc = max77686_ldo_mode_standby1;
> +                       return 0;
> +               }
> +       } else if (type == REGULATOR_TYPE_BUCK) {
> +               if (d_num < 1 || d_num > dev_info->buck_cnt)
> +                       return -EINVAL;
> +
> +               switch (d_num) {
> +               case 1:
> +                       *d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_standby);
> +                       *desc = max77686_buck_mode_standby;
> +                       return 0;
> +               case 2:
> +               case 3:
> +               case 4:
> +                       *d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_lpm);
> +                       *desc = max77686_buck_mode_lpm;
> +                       return 0;
> +               default:
> +                       *d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_onoff);
> +                       *desc = max77686_buck_mode_onoff;
> +                       return 0;
> +               }
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static int max77686_ldo_val(struct udevice *dev, int op,
> +                               int ldo, int *uV)
> +{
> +       unsigned int ret, hex, adr;
> +       unsigned char val;
> +
> +       if (op == PMIC_OP_GET)
> +               *uV = 0;
> +
> +       if (ldo < 1 || ldo > 26) {
> +               error("Wrong ldo number: %d", ldo);
> +               return -EINVAL;
> +       }
> +
> +       adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
> +
> +       ret = pmic_read(dev->parent, adr, &val);
> +       if (ret)
> +               return ret;
> +
> +       if (op == PMIC_OP_GET) {
> +               val &= MAX77686_LDO_VOLT_MASK;
> +               ret = max77686_ldo_hex2volt(ldo, val);
> +               if (ret < 0)
> +                       return ret;
> +               *uV = ret;
> +               return 0;
> +       }
> +
> +       hex = max77686_ldo_volt2hex(ldo, *uV);
> +       if (hex < 0)
> +               return -EINVAL;
> +
> +       val &= ~MAX77686_LDO_VOLT_MASK;
> +       val |= hex;
> +       ret |= pmic_write(dev->parent, adr, val);

You should not | error values together as you will get nonsense. Try:

ret = pmic_write()
if (ret)
  return ret

or

if (ret)
   goto err:

/* more code *.

err:
  return ret;

> +
> +       return ret;
> +}
> +
> +static int max77686_buck_val(struct udevice *dev, int op,
> +                               int buck, int *uV)
> +{
> +       unsigned int hex, ret, mask, adr;
> +       unsigned char val;
> +
> +       if (buck < 1 || buck > 9) {
> +               error("Wrong buck number: %d", buck);
> +               return -EINVAL;
> +       }
> +
> +       if (op == PMIC_OP_GET)
> +               *uV = 0;
> +
> +       /* &buck_out = ctrl + 1 */
> +       adr = max77686_buck_addr[buck] + 1;
> +
> +       /* mask */
> +       switch (buck) {
> +       case 2:
> +       case 3:
> +       case 4:
> +               /* Those uses voltage scallers - will support in the future */
> +               mask = MAX77686_BUCK234_VOLT_MASK;
> +               return -EPERM;
> +       default:
> +               mask = MAX77686_BUCK_VOLT_MASK;
> +       }
> +
> +       ret = pmic_read(dev->parent, adr, &val);
> +       if (ret)
> +               return ret;
> +
> +       if (op == PMIC_OP_GET) {
> +               val &= mask;
> +               ret = max77686_buck_hex2volt(buck, val);
> +               if (ret < 0)
> +                       return ret;
> +               *uV = ret;
> +               return 0;
> +       }
> +
> +       hex = max77686_buck_volt2hex(buck, *uV);
> +       if (hex < 0)
> +               return -EINVAL;
> +
> +       val &= ~mask;
> +       val |= hex;
> +       ret = pmic_write(dev->parent, adr, val);
> +
> +       return ret;
> +}
> +
> +static int max77686_ldo_mode(struct udevice *dev, int op, int ldo, int *opmode)
> +{
> +       unsigned int ret, adr, mode;
> +       unsigned char val;
> +
> +       if (op == PMIC_OP_GET)
> +               *opmode = -EINVAL;
> +
> +       if (ldo < 1 || ldo > 26) {
> +               error("Wrong ldo number: %d", ldo);
> +               return -EINVAL;
> +       }
> +
> +       adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
> +
> +       ret = pmic_read(dev->parent, adr, &val);
> +       if (ret)
> +               return ret;
> +
> +       if (op == PMIC_OP_GET) {
> +               val &= MAX77686_LDO_MODE_MASK;
> +               ret = max77686_ldo_hex2mode(ldo, val);
> +               if (ret < 0)
> +                       return ret;
> +               *opmode = ret;
> +               return 0;
> +       }
> +
> +       /* mode */
> +       switch (*opmode) {
> +       case OPMODE_OFF:
> +               mode = MAX77686_LDO_MODE_OFF;
> +               break;
> +       case OPMODE_LPM:
> +               switch (ldo) {
> +               case 2:
> +               case 6:
> +               case 7:
> +               case 8:
> +               case 10:
> +               case 11:
> +               case 12:
> +               case 14:
> +               case 15:
> +               case 16:
> +                       return -EINVAL;
> +               default:
> +                       mode = MAX77686_LDO_MODE_LPM;
> +               }
> +               break;
> +       case OPMODE_STANDBY:
> +               switch (ldo) {
> +               case 2:
> +               case 6:
> +               case 7:
> +               case 8:
> +               case 10:
> +               case 11:
> +               case 12:
> +               case 14:
> +               case 15:
> +               case 16:
> +                       mode = MAX77686_LDO_MODE_STANDBY;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +               break;
> +       case OPMODE_STANDBY_LPM:
> +               mode = MAX77686_LDO_MODE_STANDBY_LPM;
> +               break;
> +       case OPMODE_ON:
> +               mode = MAX77686_LDO_MODE_ON;
> +               break;
> +       default:
> +               mode = 0xff;
> +       }
> +
> +       if (mode == 0xff) {
> +               error("Wrong mode: %d for ldo%d", *opmode, ldo);
> +               return -EINVAL;
> +       }
> +
> +       val &= ~MAX77686_LDO_MODE_MASK;
> +       val |= mode;
> +       ret |= pmic_write(dev->parent, adr, val);
> +
> +       return ret;
> +}
> +
> +static int max77686_ldo_state(struct udevice *dev, int op, int ldo, int *state)
> +{
> +       int ret, on_off;
> +
> +       if (op == PMIC_OP_GET) {
> +               ret = max77686_ldo_mode(dev, op, ldo, &on_off);
> +               if (ret)
> +                       return ret;
> +
> +               switch (on_off) {
> +               case OPMODE_OFF:
> +                       *state = 0;
> +                       break;
> +               case OPMODE_ON:
> +                       *state = 1;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +       } else if (op == PMIC_OP_SET) {
> +               switch (*state) {
> +               case 0:
> +                       on_off = OPMODE_OFF;
> +                       break;
> +               case 1:
> +                       on_off = OPMODE_ON;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +
> +               ret = max77686_ldo_mode(dev, op, ldo, &on_off);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int max77686_buck_mode(struct udevice *dev, int op, int buck,
> +                             int *opmode)
> +{
> +       unsigned int ret, mask, adr, mode, mode_shift;
> +       unsigned char val;
> +
> +       if (buck < 1 || buck > 9) {
> +               error("Wrong buck number: %d", buck);
> +               return -EINVAL;
> +       }
> +
> +       adr = max77686_buck_addr[buck];
> +
> +       /* mask */
> +       switch (buck) {
> +       case 2:
> +       case 3:
> +       case 4:
> +               mode_shift = MAX77686_BUCK_MODE_SHIFT_2;
> +               break;
> +       default:
> +               mode_shift = MAX77686_BUCK_MODE_SHIFT_1;
> +       }
> +
> +       mask = MAX77686_BUCK_MODE_MASK << mode_shift;
> +
> +       ret = pmic_read(dev->parent, adr, &val);
> +       if (ret)
> +               return ret;
> +
> +       if (op == PMIC_OP_GET) {
> +               val &= mask;
> +               val >>= mode_shift;
> +               ret = max77686_buck_hex2mode(buck, val);
> +               if (ret < 0)
> +                       return ret;
> +               *opmode = ret;
> +               return 0;
> +       }
> +
> +       /* mode */
> +       switch (*opmode) {
> +       case OPMODE_OFF:
> +               mode = MAX77686_BUCK_MODE_OFF;
> +               break;
> +       case OPMODE_STANDBY:
> +               switch (buck) {
> +               case 1:
> +               case 2:
> +               case 3:
> +               case 4:
> +                       mode = MAX77686_BUCK_MODE_STANDBY << mode_shift;
> +                       break;
> +               default:
> +                       mode = 0xff;
> +               }
> +               break;
> +       case OPMODE_LPM:
> +               switch (buck) {
> +               case 2:
> +               case 3:
> +               case 4:
> +                       mode = MAX77686_BUCK_MODE_LPM << mode_shift;
> +                       break;
> +               default:
> +                       mode = 0xff;
> +               }
> +               break;
> +       case OPMODE_ON:
> +               mode = MAX77686_BUCK_MODE_ON << mode_shift;
> +               break;
> +       default:
> +               mode = 0xff;
> +       }
> +
> +       if (mode == 0xff) {
> +               error("Wrong mode:%d for buck%d\n", *opmode, buck);
> +               return -EINVAL;
> +       }
> +
> +       val &= ~mask;
> +       val |= mode;
> +       ret |= pmic_write(dev->parent, adr, val);
> +
> +       return ret;
> +}
> +
> +static int max77686_buck_state(struct udevice *dev, int op,
> +                              int buck, int *state)
> +{
> +       int ret, on_off;
> +
> +       if (op == PMIC_OP_GET) {
> +               ret = max77686_buck_mode(dev, op, buck, &on_off);
> +               if (ret)
> +                       return ret;
> +
> +               switch (on_off) {
> +               case OPMODE_OFF:
> +                       *state = 0;
> +                       break;
> +               case OPMODE_ON:
> +                       *state = 1;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +       } else if (op == PMIC_OP_SET) {
> +               switch (*state) {
> +               case 0:
> +                       on_off = OPMODE_OFF;
> +                       break;
> +               case 1:
> +                       on_off = OPMODE_ON;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +
> +               ret = max77686_buck_mode(dev, op, buck, &on_off);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +int fill_reg_desc(int offset, struct regulator_value_desc *desc, int reg_num,
> +                 int desc_type)
> +{
> +       const void *blob = gd->fdt_blob;
> +       char *reg_name;
> +       int reg_min, reg_max, len;
> +
> +       desc->type = desc_type;
> +       desc->number = reg_num;
> +
> +       reg_name = (char *)fdt_getprop(blob, offset, "regulator-name", &len);

You can drop the cast.

> +       if (reg_name) {
> +               strncpy(&desc->name[0], reg_name, DESC_NAME_LEN);
> +               desc->name[DESC_NAME_LEN - 1] = '\0';
> +       } else {
> +               sprintf(&desc->name[0], "-");
> +       }
> +
> +       reg_min = fdtdec_get_int(blob, offset, "regulator-min-microvolt", -1);
> +       desc->min_uV = reg_min;
> +
> +       reg_max = fdtdec_get_int(blob, offset, "regulator-min-microvolt", -1);
> +       desc->max_uV = reg_max;
> +
> +       return 0;
> +}
> +
> +static int get_desc_for_compat(int regulators_node, char *compat, int desc_type,
> +                              struct regulator_value_desc *desc, int desc_num)
> +{
> +       const void *blob = gd->fdt_blob;
> +       const char *reg_compat;
> +       int compat_len = strlen(compat);
> +       int reg_num;
> +       int offset;
> +       int cnt = 0;
> +
> +       /* First subnode of "voltage_regulators" node */
> +       offset = fdt_first_subnode(blob, regulators_node);
> +
> +       while (offset > 0) {
> +               /* Get regulator properties */
> +               reg_compat = (char *)fdt_getprop(blob, offset,
> +                            "regulator-compatible", NULL);
> +
> +               /* Check compat and compare the number only */
> +               reg_num = 0;
> +               if (strstr(reg_compat, compat))
> +                       reg_num = simple_strtoul(reg_compat + compat_len,
> +                                                NULL, 0);
> +               else
> +                       goto next_offset;
> +
> +               if (reg_num && reg_num <= desc_num)
> +                       fill_reg_desc(offset, &desc[reg_num], reg_num,
> +                                     desc_type);
> +
> +               cnt++;
> +               if (cnt == desc_num)
> +                       return cnt;
> +
> +next_offset:
> +               offset = fdt_next_subnode(blob, offset);
> +       }
> +
> +       return cnt;
> +}
> +
> +static int reg_max77686_ofdata_to_platdata(struct udevice *dev)
> +{
> +       struct max77686_regulator_info *dev_info = dev->priv;
> +       const void *blob = gd->fdt_blob;
> +       int node = dev->of_offset;
> +       int offset;
> +
> +       if (node < 0) {
> +               error("Device: %s - bad of_offset", dev->name);
> +               return 0;
> +       }
> +
> +       offset = fdt_subnode_offset(blob, node,  "voltage-regulators");
> +       if (offset < 0) {
> +               error("Node %s has no 'voltage-regulators' subnode", dev->name);
> +               return 0;
> +       }
> +
> +       dev_info->ldo_cnt = get_desc_for_compat(offset, "LDO", REGULATOR_TYPE_LDO,
> +                                               dev_info->ldo_desc,
> +                                               MAX77686_LDO_NUM);
> +
> +       dev_info->buck_cnt = get_desc_for_compat(offset, "BUCK", REGULATOR_TYPE_BUCK,
> +                                                dev_info->buck_desc,
> +                                                MAX77686_BUCK_NUM);

Do you think these should be child devices?

> +
> +       /* Even if there is no voltage regulators node in dts,

Comment style

> +        * always return success and allow the driver to bind.
> +        * Then we can still do read/write pmic registers.
> +        */
> +       return 0;
> +
> +}
> +
> +static int max77686_get_cnt(struct udevice *dev, int type, int *cnt)
> +{
> +       switch (type) {
> +       case REGULATOR_TYPE_LDO:
> +               *cnt = MAX77686_LDO_NUM;
> +               break;
> +       case REGULATOR_TYPE_BUCK:
> +               *cnt = MAX77686_BUCK_NUM;
> +               break;
> +       default:
> +               *cnt = 0;
> +               return -EPERM;
> +       }
> +
> +       return 0;
> +}
> +
> +
> +static int max77686_get_value(struct udevice *dev, int type, int number,
> +                             int *value)
> +{
> +       switch (type) {
> +       case REGULATOR_TYPE_LDO:
> +               return max77686_ldo_val(dev, PMIC_OP_GET, number, value);
> +       case REGULATOR_TYPE_BUCK:
> +               return max77686_buck_val(dev, PMIC_OP_GET, number, value);
> +       default:
> +               return -EPERM;
> +       }
> +}
> +
> +static int max77686_set_value(struct udevice *dev, int type, int number,
> +                             int value)
> +{
> +       switch (type) {
> +       case REGULATOR_TYPE_LDO:
> +               return max77686_ldo_val(dev, PMIC_OP_SET, number, &value);
> +       case REGULATOR_TYPE_BUCK:
> +               return max77686_buck_val(dev, PMIC_OP_SET, number, &value);
> +       default:
> +               return -EPERM;
> +       }
> +}
> +
> +static int max77686_get_state(struct udevice *dev, int type, int number,
> +                             int *state)
> +{
> +       switch (type) {
> +       case REGULATOR_TYPE_LDO:
> +               return max77686_ldo_state(dev, PMIC_OP_GET, number, state);
> +       case REGULATOR_TYPE_BUCK:
> +               return max77686_buck_state(dev, PMIC_OP_GET, number, state);
> +       default:
> +               *state = -1;
> +               return -EPERM;
> +       }
> +}
> +
> +static int max77686_set_state(struct udevice *dev, int type, int number,
> +                             int state)
> +{
> +       switch (type) {
> +       case REGULATOR_TYPE_LDO:
> +               return max77686_ldo_state(dev, PMIC_OP_SET, number, &state);
> +       case REGULATOR_TYPE_BUCK:
> +               return max77686_buck_state(dev, PMIC_OP_SET, number, &state);
> +       default:
> +               return -EPERM;
> +       }
> +}
> +
> +static int max77686_get_mode(struct udevice *dev, int type, int number,
> +                            int *mode)
> +{
> +       switch (type) {
> +       case REGULATOR_TYPE_LDO:
> +               return max77686_ldo_mode(dev, PMIC_OP_GET, number, mode);
> +       case REGULATOR_TYPE_BUCK:
> +               return max77686_buck_mode(dev, PMIC_OP_GET, number, mode);
> +       default:
> +               return -EPERM;
> +       }
> +}
> +
> +static int max77686_set_mode(struct udevice *dev, int type, int number,
> +                            int mode)
> +{
> +       switch (type) {
> +       case REGULATOR_TYPE_LDO:
> +               return max77686_ldo_mode(dev, PMIC_OP_SET, number, &mode);
> +       case REGULATOR_TYPE_BUCK:
> +               return max77686_buck_mode(dev, PMIC_OP_SET, number, &mode);
> +       default:
> +               return -EPERM;
> +       }
> +}
> +
> +static const struct dm_regulator_ops reg_max77686_ops = {
> +       .get_cnt                = max77686_get_cnt,
> +       .get_value_desc         = max77686_get_value_desc,
> +       .get_mode_desc_array    = max77686_get_mode_desc_array,
> +       .get_value              = max77686_get_value,
> +       .set_value              = max77686_set_value,
> +       .get_state              = max77686_get_state,
> +       .set_state              = max77686_set_state,
> +       .get_mode               = max77686_get_mode,
> +       .set_mode               = max77686_set_mode,
> +};
> +
> +U_BOOT_DRIVER(max77686_regulator) = {
> +       .name = "max77686 regulator",
> +       .id = UCLASS_PMIC_REGULATOR,
> +       .ops = &reg_max77686_ops,
> +       .ofdata_to_platdata = reg_max77686_ofdata_to_platdata,
> +       .priv_auto_alloc_size = sizeof(struct max77686_regulator_info),
> +};
> diff --git a/include/power/max77686_pmic.h b/include/power/max77686_pmic.h
> index fe26d13..81c27d8 100644
> --- a/include/power/max77686_pmic.h
> +++ b/include/power/max77686_pmic.h
> @@ -126,7 +126,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,23 +146,29 @@ 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_voltage(struct pmic *p, int buck, ulong uV);
>  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
> @@ -170,6 +179,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 */
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 10/12] odroid: board: add support to dm pmic api
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 10/12] odroid: board: add support to dm pmic api Przemyslaw Marczak
@ 2015-03-06 14:14     ` Simon Glass
  2015-03-25 16:09       ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-06 14:14 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This commit change the old pmic framework calls with the new ones.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes v2:
> - remove board_init_i2c() call
> - update regulator calls
> - update headers
> - samsung/misc.c: include required header
> ---
>  board/samsung/common/misc.c   |  1 +
>  board/samsung/odroid/odroid.c | 52 ++++++++++++++++++++++++++-----------------
>  2 files changed, 33 insertions(+), 20 deletions(-)
>
> diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
> index 4538ac7..18d71e8 100644
> --- a/board/samsung/common/misc.c
> +++ b/board/samsung/common/misc.c
> @@ -16,6 +16,7 @@
>  #include <asm/arch/cpu.h>
>  #include <asm/gpio.h>
>  #include <linux/input.h>
> +#include <dm.h>
>  #include <power/pmic.h>
>  #include <mmc.h>
>
> diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
> index bff6ac9..2448cde 100644
> --- a/board/samsung/odroid/odroid.c
> +++ b/board/samsung/odroid/odroid.c
> @@ -12,7 +12,9 @@
>  #include <asm/arch/gpio.h>
>  #include <asm/gpio.h>
>  #include <asm/arch/cpu.h>
> +#include <dm.h>
>  #include <power/pmic.h>
> +#include <power/regulator.h>
>  #include <power/max77686_pmic.h>
>  #include <errno.h>
>  #include <usb.h>
> @@ -402,15 +404,23 @@ static void board_gpio_init(void)
>
>  static int pmic_init_max77686(void)
>  {
> -       struct pmic *p = pmic_get("MAX77686_PMIC");
> +       struct udevice *reg;
> +       int type;
>
> -       if (pmic_probe(p))
> +       if (regulator_get("max77686", &reg)) {
> +               error("Regulator get error\n");
>                 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 */
> +       type = REGULATOR_TYPE_LDO;
> +       regulator_set_value(reg, type, 20, 1800000);    /* LDO20 eMMC */
> +       regulator_set_value(reg, type, 21, 2800000);    /* LDO21 SD */
> +       regulator_set_value(reg, type, 22, 2800000);    /* LDO22 eMMC */
> +
> +       regulator_set_mode(reg, type, 20, OPMODE_ON);
> +       regulator_set_mode(reg, type, 21, OPMODE_ON);
> +       regulator_set_mode(reg, type, 22, OPMODE_ON);

These 20, 21, 22 values seem bad to me. Do we not have names? Also I
see in the device tree that these regulators have the same min and max
voltage, so perhaps you can use that voltage automatically?

I think when you change the LDOs to devices you might end up with:

struct udevice *ldo;

ret = regulator_get("VCCQ_MMC2_2.8V", &ldo);   // use the regulator
device name which comes from regulator-name property.
if (ret) {
   debug(...)
   goto err;
}
ret = regulator_set_voltage(ldo, 1800000);
...error check
ret = regulator_set_mode(ldo, OPMODE_ON);
...error check

>
>         return 0;
>  }
> @@ -435,7 +445,6 @@ int exynos_init(void)
>
>  int exynos_power_init(void)
>  {
> -       pmic_init(0);
>         pmic_init_max77686();
>
>         return 0;
> @@ -444,19 +453,21 @@ int exynos_power_init(void)
>  #ifdef CONFIG_USB_GADGET
>  static int s5pc210_phy_control(int on)
>  {
> -       struct pmic *p_pmic;
> +       struct udevice *reg;
> +       int type;
>
> -       p_pmic = pmic_get("MAX77686_PMIC");
> -       if (!p_pmic)
> +       if (regulator_get("max77686", &reg)) {
> +               error("Regulator get error\n");
>                 return -ENODEV;
> +       }
>
> -       if (pmic_probe(p_pmic))
> -               return -1;
> +       type = REGULATOR_TYPE_LDO;
>
>         if (on)
> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
> +               return regulator_set_mode(reg, type, 12, OPMODE_ON);
>         else
> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
> +               return regulator_set_mode(reg, type, 12, OPMODE_LPM);
> +
>  }
>
>  struct s3c_plat_otg_data s5pc210_otg_data = {
> @@ -473,7 +484,8 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
>  int board_usb_init(int index, enum usb_init_type init)
>  {
>  #ifdef CONFIG_CMD_USB
> -       struct pmic *p_pmic;
> +       struct udevice *reg;
> +       int type, ret;
>
>         /* Set Ref freq 0 => 24MHz, 1 => 26MHz*/
>         /* Odroid Us have it at 24MHz, Odroid Xs at 26MHz */
> @@ -491,14 +503,14 @@ int board_usb_init(int index, enum usb_init_type init)
>         /* Power off and on BUCK8 for LAN9730 */
>         debug("LAN9730 - Turning power buck 8 OFF and ON.\n");
>
> -       p_pmic = pmic_get("MAX77686_PMIC");
> -       if (p_pmic && !pmic_probe(p_pmic)) {
> -               max77686_set_buck_voltage(p_pmic, 8, 750000);
> -               max77686_set_buck_voltage(p_pmic, 8, 3300000);
> +       if (regulator_get("max77686", &reg)) {
> +               type = REGULATOR_TYPE_BUCK;
> +               ret = regulator_set_value(reg, type, 8, 750000);
> +               ret |= regulator_set_value(reg, type, 8, 3300000);
> +               if (ret)
> +                       error("Can't set regulator\n");

We should return a proper error number. Also print it here as it might
provide clues.

>         }
> -
>  #endif
> -
>         debug("USB_udc_probe\n");
>         return s3c_udc_probe(&s5pc210_otg_data);
>  }
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 12/12] odroid: config: enable dm pmic, dm regulator and max77686 driver
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 12/12] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
@ 2015-03-06 14:15     ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-06 14:15 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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_I2C
> - CONFIG_DM_PMIC_MAX77686
> - CONFIG_DM_REGULATOR
> - CONFIG_DM_REGULATOR_MAX77686
>
> And removes the unused:
> - CONFIG_DM_I2C_COMPAT
> - CONFIG_POWER
> - CONFIG_POWER_I2C
> - CONFIG_POWER_MAX77686
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes V2:
> - config: enable dm i2c; cleanup
> - remove CONFIG_DM_I2C_COMPAT
> - enable regulator command
> ---
>  configs/odroid_defconfig | 1 -
>  include/configs/odroid.h | 9 ++++++---
>  2 files changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/configs/odroid_defconfig b/configs/odroid_defconfig
> index 816a3fa..4116137 100644
> --- a/configs/odroid_defconfig
> +++ b/configs/odroid_defconfig
> @@ -4,4 +4,3 @@ CONFIG_TARGET_ODROID=y
>  CONFIG_OF_CONTROL=y
>  CONFIG_DEFAULT_DEVICE_TREE="exynos4412-odroid"
>  CONFIG_DM_I2C=y
> -CONFIG_DM_I2C_COMPAT=y
> diff --git a/include/configs/odroid.h b/include/configs/odroid.h
> index 8b47537..d2bc7ee 100644
> --- a/include/configs/odroid.h
> +++ b/include/configs/odroid.h
> @@ -182,9 +182,12 @@
>  #define CONFIG_SYS_I2C_S3C24X0_SLAVE   0
>
>  /* POWER */
> -#define CONFIG_POWER
> -#define CONFIG_POWER_I2C
> -#define CONFIG_POWER_MAX77686
> +#define CONFIG_DM_PMIC
> +#define CONFIG_DM_PMIC_CMD
> +#define CONFIG_DM_PMIC_MAX77686
> +#define CONFIG_DM_REGULATOR
> +#define CONFIG_DM_REGULATOR_CMD
> +#define CONFIG_DM_REGULATOR_MAX77686

These should move to Kconfig.

>
>  /* GPT */
>  #define CONFIG_RANDOM_UUID
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model
  2015-03-06 14:10   ` Simon Glass
@ 2015-03-06 15:08     ` Przemyslaw Marczak
  2015-03-06 19:58       ` Simon Glass
  2015-03-10  2:12     ` Simon Glass
  1 sibling, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-06 15:08 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/06/2015 03:10 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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
>
> This is an impressive pieces of work! It will be great to get these in.

Thank you, and also thank you for the review.
I hope to finish this all successfully.

>
> Here are some high-level comments on this series:
>
> 1. I think regulator LDOs and bucks should be proper devices (struct
> udevice), bound by the pmic when it is probed. The advantages are
>
> a. You can see them in the device list - the pmic will end up with a
> lot more children
> b. You can use the same regulator uclass for each, but have different
> operations for each driver (e.g. max77686 might provide two different
> drivers, one for LDO, one for buck).
> c. Things like your 'switch (type)' in max7786_get_state() etc. will go away
> d. You can perform operations on them without having to specify their
> parent and number - e.g. regulator_set_mode(struct udevice *ldo, int
> mode) which is much more natural for users
> e. You avoid needing your own list of regulators and bucks - struct
> max7786_regulator_info. After all, keeping track of child devices is
> something that driver model can do
>
> 2. I see device tree support, but the Linux device tree bindings are
> not fully supported, e.g. the regulators sub-node uses
> regulator-compatible instead of regulator-name. I think it should be
> exactly the same (and we should copy the device tree files, only
> leaving out what we don't support)
>
> 3. The I2C/SPI difference is a bit clunky. We should try to hide this
> away. The most obvious problem is in getting the device. Instead of
> pmic_i2c_get() we should use the "power-supply" property in the device
> tree, so we need a function which can find the regulator given the
> device node (a bit like gpio_request_by_name() but for PMICs). The
> pmic_get() function is OK and will be needed also, as I am sure we
> will use names in some places. We should remove any mention of the bus
> type from the API I think. Also regulator number seems and odd concept
> - better to use the name/device tree link to find the right device.
> One way to avoid I2C/SPI problems is to create a helper file which
> allows you to read and write registers given a struct udevice. It
> could look at whether the device is I2C or SPI and do the right thing.
> This could be generally useful, not just for PMICs.
>
> 4. Should use Kconfig now.
>
> Regards,
> Simon
>

I quickly read your all comments, and all are very helpful for me, I 
will reply to each in the next week.

I see that this piece of code should be done as e.g. the gpio uclass is.

My previous assumption was, to use the regulator numbers rather than 
names - as it is easy and faster - cause we have one driver instance for 
few regulators - but I agree, need change.

Usually there are numbers in the pmic documentation, and the names are 
in the board schematics which uses both names and numbers.
So I added getting the names from dts just as some useful info for the 
regulator command.

I agree with you, that using the names instead of the numbers allow to 
make some things more easy, e.g. getting the 'udevice' by name.

I made some rework of the soft i2c with dm support, it looks that it is 
working fine. I will need this for work on next pmic devices in my 
Trats2 (charger, muic and fuelgauge).

Probably, I will send the dm soft i2c as independent patch set, and then 
get back to this framework code.

Thank you and best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model
  2015-03-06 15:08     ` Przemyslaw Marczak
@ 2015-03-06 19:58       ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-06 19:58 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 6 March 2015 at 08:08, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 03/06/2015 03:10 PM, Simon Glass wrote:
>>
>> Hi Przemyslaw,
>>
>> On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>>> 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
>>
>>
>> This is an impressive pieces of work! It will be great to get these in.
>
>
> Thank you, and also thank you for the review.
> I hope to finish this all successfully.
>
>
>>
>> Here are some high-level comments on this series:
>>
>> 1. I think regulator LDOs and bucks should be proper devices (struct
>> udevice), bound by the pmic when it is probed. The advantages are
>>
>> a. You can see them in the device list - the pmic will end up with a
>> lot more children
>> b. You can use the same regulator uclass for each, but have different
>> operations for each driver (e.g. max77686 might provide two different
>> drivers, one for LDO, one for buck).
>> c. Things like your 'switch (type)' in max7786_get_state() etc. will go
>> away
>> d. You can perform operations on them without having to specify their
>> parent and number - e.g. regulator_set_mode(struct udevice *ldo, int
>> mode) which is much more natural for users
>> e. You avoid needing your own list of regulators and bucks - struct
>> max7786_regulator_info. After all, keeping track of child devices is
>> something that driver model can do
>>
>> 2. I see device tree support, but the Linux device tree bindings are
>> not fully supported, e.g. the regulators sub-node uses
>> regulator-compatible instead of regulator-name. I think it should be
>> exactly the same (and we should copy the device tree files, only
>> leaving out what we don't support)
>>
>> 3. The I2C/SPI difference is a bit clunky. We should try to hide this
>> away. The most obvious problem is in getting the device. Instead of
>> pmic_i2c_get() we should use the "power-supply" property in the device
>> tree, so we need a function which can find the regulator given the
>> device node (a bit like gpio_request_by_name() but for PMICs). The
>> pmic_get() function is OK and will be needed also, as I am sure we
>> will use names in some places. We should remove any mention of the bus
>> type from the API I think. Also regulator number seems and odd concept
>> - better to use the name/device tree link to find the right device.
>> One way to avoid I2C/SPI problems is to create a helper file which
>> allows you to read and write registers given a struct udevice. It
>> could look at whether the device is I2C or SPI and do the right thing.
>> This could be generally useful, not just for PMICs.
>>
>> 4. Should use Kconfig now.
>>
>> Regards,
>> Simon
>>
>
> I quickly read your all comments, and all are very helpful for me, I will
> reply to each in the next week.
>
> I see that this piece of code should be done as e.g. the gpio uclass is.
>
> My previous assumption was, to use the regulator numbers rather than names -
> as it is easy and faster - cause we have one driver instance for few
> regulators - but I agree, need change.
>
> Usually there are numbers in the pmic documentation, and the names are in
> the board schematics which uses both names and numbers.
> So I added getting the names from dts just as some useful info for the
> regulator command.
>
> I agree with you, that using the names instead of the numbers allow to make
> some things more easy, e.g. getting the 'udevice' by name.
>
> I made some rework of the soft i2c with dm support, it looks that it is
> working fine. I will need this for work on next pmic devices in my Trats2
> (charger, muic and fuelgauge).
>
> Probably, I will send the dm soft i2c as independent patch set, and then get
> back to this framework code.

Sounds good! It's a lot of work but it will be nice to have all this done.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model
  2015-03-06 14:10   ` Simon Glass
  2015-03-06 15:08     ` Przemyslaw Marczak
@ 2015-03-10  2:12     ` Simon Glass
  2015-03-25 16:09       ` Przemyslaw Marczak
  1 sibling, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-10  2:12 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 6 March 2015 at 07:10, Simon Glass <sjg@chromium.org> wrote:
> Hi Przemyslaw,
>
> On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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
>
> This is an impressive pieces of work! It will be great to get these in.
>
> Here are some high-level comments on this series:
>
> 1. I think regulator LDOs and bucks should be proper devices (struct
> udevice), bound by the pmic when it is probed. The advantages are
>
> a. You can see them in the device list - the pmic will end up with a
> lot more children
> b. You can use the same regulator uclass for each, but have different
> operations for each driver (e.g. max77686 might provide two different
> drivers, one for LDO, one for buck).
> c. Things like your 'switch (type)' in max7786_get_state() etc. will go away
> d. You can perform operations on them without having to specify their
> parent and number - e.g. regulator_set_mode(struct udevice *ldo, int
> mode) which is much more natural for users
> e. You avoid needing your own list of regulators and bucks - struct
> max7786_regulator_info. After all, keeping track of child devices is
> something that driver model can do
>
> 2. I see device tree support, but the Linux device tree bindings are
> not fully supported, e.g. the regulators sub-node uses
> regulator-compatible instead of regulator-name. I think it should be
> exactly the same (and we should copy the device tree files, only
> leaving out what we don't support)
>
> 3. The I2C/SPI difference is a bit clunky. We should try to hide this
> away. The most obvious problem is in getting the device. Instead of
> pmic_i2c_get() we should use the "power-supply" property in the device
> tree, so we need a function which can find the regulator given the
> device node (a bit like gpio_request_by_name() but for PMICs). The
> pmic_get() function is OK and will be needed also, as I am sure we
> will use names in some places. We should remove any mention of the bus
> type from the API I think. Also regulator number seems and odd concept
> - better to use the name/device tree link to find the right device.
> One way to avoid I2C/SPI problems is to create a helper file which
> allows you to read and write registers given a struct udevice. It
> could look at whether the device is I2C or SPI and do the right thing.
> This could be generally useful, not just for PMICs.
>
> 4. Should use Kconfig now.

Sorry I don't know how I forgot to mention this:

5. A test PMIC device for sandbox so that the tests can run (test/dm/pmic.c).

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 04/12] dm: pmic: add implementation of driver model regulator uclass
  2015-03-03 16:24   ` [U-Boot] [PATCH v2 04/12] dm: pmic: add implementation of driver model regulator uclass Przemyslaw Marczak
  2015-03-06 14:12     ` Simon Glass
@ 2015-03-10 11:41     ` Robert Baldyga
  2015-03-25 16:08       ` Przemyslaw Marczak
  1 sibling, 1 reply; 218+ messages in thread
From: Robert Baldyga @ 2015-03-10 11:41 UTC (permalink / raw)
  To: u-boot

Hi,

On 03/03/2015 05:24 PM, Przemyslaw Marczak wrote:
> 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
> 
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> 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
> 
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index 5c9a189..a6b7012 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -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
> diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
> new file mode 100644
> index 0000000..6b5c678
> --- /dev/null
> +++ b/drivers/power/regulator-uclass.c
> @@ -0,0 +1,227 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * 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;
> +
> +		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?

> +			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",
> +};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index ff360ac..0a1e664 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -37,6 +37,7 @@ enum uclass_id {
>  
>  	/* PMIC uclass and PMIC related uclasses */
>  	UCLASS_PMIC,
> +	UCLASS_PMIC_REGULATOR,
>  
>  	UCLASS_COUNT,
>  	UCLASS_INVALID = -1,
> diff --git a/include/power/regulator.h b/include/power/regulator.h
> new file mode 100644
> index 0000000..ee8a280
> --- /dev/null
> +++ b/include/power/regulator.h
> @@ -0,0 +1,310 @@
> +/*
> + *  Copyright (C) 2014-2015 Samsung Electronics
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * 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:
> + * - 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

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 00/17] Power(full) framework based on Driver Model
  2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
                     ` (14 preceding siblings ...)
  2015-03-06 14:10   ` Simon Glass
@ 2015-03-24 20:30   ` Przemyslaw Marczak
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 01/17] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
                       ` (19 more replies)
  15 siblings, 20 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

Hello,
Here is the third RFC version of the new PMIC framework.Big thanks to
Simon Glass, your comments were really helpful, and I think, that this
version is much more better to discuss, than the previous. The changes
made in this version are described below each commit. Sorry that I didn't
reply to each patch, I agreed with most and just started the work.

Best regards

Przemyslaw Marczak (17):
  exynos5: fix build break by adding CONFIG_POWER
  fdt_ro.c: add new function: fdt_node_check_prop_compatible()
  dm: core: lists.c: add new function lists_bind_fdt_by_prop()
  lib: Kconfig: add entry for errno_str() function
  dm: pmic: add implementation of driver model pmic uclass
  dm: regulator: add implementation of driver model regulator uclass
  dm: pmic: add pmic command
  dm: regulator: add regulator command
  pmic: max77686 set the same compatible as in the kernel
  dm: pmic: add max77686 pmic driver
  dm: regulator: add max77686 regulator driver
  dm: regulator: add fixed voltage 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 +++++++++-
 arch/arm/dts/exynos4412-trats2.dts   |   2 +-
 arch/arm/dts/exynos5250-smdk5250.dts |   2 +-
 arch/arm/dts/exynos5250-snow.dts     |   2 +-
 board/samsung/common/board.c         |   4 +-
 board/samsung/common/misc.c          |   1 +
 board/samsung/odroid/odroid.c        | 113 ++++-
 common/Kconfig                       |  36 ++
 common/Makefile                      |   4 +
 common/cmd_pmic.c                    | 210 +++++++++
 common/cmd_regulator.c               | 385 +++++++++++++++
 configs/odroid_defconfig             |   8 +-
 doc/driver-model/pmic-framework.txt  | 350 ++++++++++++++
 drivers/core/lists.c                 |  28 +-
 drivers/power/Kconfig                | 124 ++++-
 drivers/power/Makefile               |   3 +-
 drivers/power/pmic-uclass.c          | 130 ++++++
 drivers/power/pmic/Makefile          |   1 +
 drivers/power/pmic/max77686.c        |  76 +++
 drivers/power/pmic/pmic_max77686.c   |   2 +-
 drivers/power/regulator-uclass.c     | 219 +++++++++
 drivers/power/regulator/Makefile     |   9 +
 drivers/power/regulator/fixed.c      | 124 +++++
 drivers/power/regulator/max77686.c   | 876 +++++++++++++++++++++++++++++++++++
 include/configs/exynos5-common.h     |   4 +
 include/configs/odroid.h             |   5 -
 include/dm/lists.h                   |  18 +
 include/dm/uclass-id.h               |   4 +
 include/libfdt.h                     |  27 ++
 include/power/max77686_pmic.h        |  26 +-
 include/power/pmic.h                 | 210 +++++++++
 include/power/regulator.h            | 259 +++++++++++
 lib/Kconfig                          |   8 +
 lib/fdtdec.c                         |   2 +-
 lib/libfdt/fdt_ro.c                  |  14 +-
 36 files changed, 3481 insertions(+), 55 deletions(-)
 create mode 100644 common/cmd_pmic.c
 create mode 100644 common/cmd_regulator.c
 create mode 100644 doc/driver-model/pmic-framework.txt
 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/fixed.c
 create mode 100644 drivers/power/regulator/max77686.c
 create mode 100644 include/power/regulator.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 01/17] exynos5: fix build break by adding CONFIG_POWER
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:05       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 02/17] fdt_ro.c: add new function: fdt_node_check_prop_compatible() Przemyslaw Marczak
                       ` (18 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 include/configs/exynos5-common.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/configs/exynos5-common.h b/include/configs/exynos5-common.h
index 3ab8d55..3ee9482 100644
--- a/include/configs/exynos5-common.h
+++ b/include/configs/exynos5-common.h
@@ -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
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 02/17] fdt_ro.c: add new function: fdt_node_check_prop_compatible()
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 01/17] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:07       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 03/17] dm: core: lists.c: add new function lists_bind_fdt_by_prop() Przemyslaw Marczak
                       ` (17 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

This commit:
- moves fdt_node_check_compatible() code to fdt_node_check_prop_compatible()
- adds call to fdt_node_check_prop_compatible() in fdt_node_check_compatible()
  with 'compatible' as the name of compatible property.

The function: fdt_node_check_compatible() - works the same as previous.
The function fdt_node_check_prop_compatible() - allows for checking compatible
string in given property name.

If some fdt node uses different name for compatible property, than 'compatible',
then the function fdt_node_check_prop_compatible() can be used with the custom
compatible property name as an argument.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 include/libfdt.h    | 27 +++++++++++++++++++++++++++
 lib/libfdt/fdt_ro.c | 14 +++++++++++---
 2 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/include/libfdt.h b/include/libfdt.h
index f3cbb63..0ff91b6 100644
--- a/include/libfdt.h
+++ b/include/libfdt.h
@@ -807,6 +807,33 @@ int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
 
 /**
+ * fdt_node_check_prop_compatible: check a node's 'property_name' for compatible
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property_name: name of property within looking for the 'compatible' string
+ * @compatible: string to match against
+ *
+ *
+ * fdt_node_check_prop_compatible() returns 0 if the given node contains a
+ * 'compatible' property with the given string as one of its elements,
+ * it returns non-zero otherwise, or on error.
+ *
+ * returns:
+ *	0, if the node has a 'compatible' property listing the given string
+ *	1, if the node has a 'compatible' property, but it does not list
+ *		the given string
+ *	-FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
+ *	-FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_check_prop_compatible(const void *fdt, int nodeoffset,
+				   const char *property_name,
+				   const char *compatible);
+
+/**
  * fdt_node_check_compatible: check a node's compatible property
  * @fdt: pointer to the device tree blob
  * @nodeoffset: offset of a tree node
diff --git a/lib/libfdt/fdt_ro.c b/lib/libfdt/fdt_ro.c
index 03733e5..a3f4f8a 100644
--- a/lib/libfdt/fdt_ro.c
+++ b/lib/libfdt/fdt_ro.c
@@ -567,13 +567,14 @@ int fdt_get_string(const void *fdt, int node, const char *property,
 	return fdt_get_string_index(fdt, node, property, 0, output);
 }
 
-int fdt_node_check_compatible(const void *fdt, int nodeoffset,
-			      const char *compatible)
+int fdt_node_check_prop_compatible(const void *fdt, int nodeoffset,
+				   const char *prop_name,
+				   const char *compatible)
 {
 	const void *prop;
 	int len;
 
-	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
+	prop = fdt_getprop(fdt, nodeoffset, prop_name, &len);
 	if (!prop)
 		return len;
 	if (fdt_stringlist_contains(prop, len, compatible))
@@ -582,6 +583,13 @@ int fdt_node_check_compatible(const void *fdt, int nodeoffset,
 		return 1;
 }
 
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+			      const char *compatible)
+{
+	return fdt_node_check_prop_compatible(fdt, nodeoffset, "compatible",
+					      compatible);
+}
+
 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
 				  const char *compatible)
 {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 03/17] dm: core: lists.c: add new function lists_bind_fdt_by_prop()
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 01/17] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 02/17] fdt_ro.c: add new function: fdt_node_check_prop_compatible() Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:07       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 04/17] lib: Kconfig: add entry for errno_str() function Przemyslaw Marczak
                       ` (16 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

This change adds new function: lists_bind_fdt_by_prop(), which can be used
for bind the devices by custom property name for the compatible string.

The function lists_bind_fdt() works the same as previous.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 drivers/core/lists.c | 28 +++++++++++++++++++---------
 include/dm/lists.h   | 18 ++++++++++++++++++
 2 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/drivers/core/lists.c b/drivers/core/lists.c
index ff115c4..d96040a 100644
--- a/drivers/core/lists.c
+++ b/drivers/core/lists.c
@@ -99,14 +99,17 @@ int device_bind_driver(struct udevice *parent, const char *drv_name,
  * @param blob:		Device tree pointer
  * @param offset:	Offset of node in device tree
  * @param of_match:	List of compatible strings to match
+ * @param prop:		Name of compatible string property
  * @param of_idp:	Returns the match that was found
  * @return 0 if there is a match, -ENOENT if no match, -ENODEV if the node
  * does not have a compatible string, other error <0 if there is a device
  * tree error
  */
-static int driver_check_compatible(const void *blob, int offset,
-				   const struct udevice_id *of_match,
-				   const struct udevice_id **of_idp)
+static int driver_check_prop_compatible(const void *blob,
+					int offset,
+					const char *prop,
+					const struct udevice_id *of_match,
+					const struct udevice_id **of_idp)
 {
 	int ret;
 
@@ -115,8 +118,8 @@ static int driver_check_compatible(const void *blob, int offset,
 		return -ENOENT;
 
 	while (of_match->compatible) {
-		ret = fdt_node_check_compatible(blob, offset,
-						of_match->compatible);
+		ret = fdt_node_check_prop_compatible(blob, offset, prop,
+						     of_match->compatible);
 		if (!ret) {
 			*of_idp = of_match;
 			return 0;
@@ -131,8 +134,8 @@ static int driver_check_compatible(const void *blob, int offset,
 	return -ENOENT;
 }
 
-int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
-		   struct udevice **devp)
+int lists_bind_fdt_by_prop(struct udevice *parent, const void *blob, int offset,
+			   const char *prop, struct udevice **devp)
 {
 	struct driver *driver = ll_entry_start(struct driver, driver);
 	const int n_ents = ll_entry_count(struct driver, driver);
@@ -148,8 +151,8 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
 	if (devp)
 		*devp = NULL;
 	for (entry = driver; entry != driver + n_ents; entry++) {
-		ret = driver_check_compatible(blob, offset, entry->of_match,
-					      &id);
+		ret = driver_check_prop_compatible(blob, offset, prop,
+						   entry->of_match, &id);
 		name = fdt_get_name(blob, offset, NULL);
 		if (ret == -ENOENT) {
 			continue;
@@ -183,4 +186,11 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
 
 	return result;
 }
+
+int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
+		   struct udevice **devp)
+{
+	return lists_bind_fdt_by_prop(parent, blob, offset, "compatible", devp);
+
+}
 #endif
diff --git a/include/dm/lists.h b/include/dm/lists.h
index 1b50af9..c63757b 100644
--- a/include/dm/lists.h
+++ b/include/dm/lists.h
@@ -45,6 +45,24 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id);
 int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only);
 
 /**
+ * lists_bind_fdt_by_prop() - bind a device tree node by the given compatible
+ * property name.
+ *
+ * This creates a new device bound to the given device tree node, with
+ * @parent as its parent.
+ *
+ * @parent: parent device (root)
+ * @blob: device tree blob
+ * @offset: offset of this device tree node
+ * @prop: fdt property name within looking for the driver compatible string
+ * @devp: if non-NULL, returns a pointer to the bound device
+ * @return 0 if device was bound, -EINVAL if the device tree is invalid,
+ * other -ve value on error
+ */
+int lists_bind_fdt_by_prop(struct udevice *parent, const void *blob, int offset,
+			   const char *prop, struct udevice **devp);
+
+/**
  * lists_bind_fdt() - bind a device tree node
  *
  * This creates a new device bound to the given device tree node, with
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 04/17] lib: Kconfig: add entry for errno_str() function
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (2 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 03/17] dm: core: lists.c: add new function lists_bind_fdt_by_prop() Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:07       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 05/17] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
                       ` (15 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 lib/Kconfig | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/lib/Kconfig b/lib/Kconfig
index c9d2767..06d0b28 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -65,4 +65,12 @@ config SHA_PROG_HW_ACCEL
 	  is performed in hardware.
 endmenu
 
+config ERRNO_STR
+	bool "Enable function for getting errno-related string message"
+	help
+	  The function errno_str(int errno), 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
+
 endmenu
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 05/17] dm: pmic: add implementation of driver model pmic uclass
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (3 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 04/17] lib: Kconfig: add entry for errno_str() function Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:07       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass Przemyslaw Marczak
                       ` (14 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

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/driver-model/pmic-framework.txt

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
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

Changes V3:
- pmic-uclass.c and pmic.h:
  -- remove  pmic_if_* functions
  -- add new function pmic_child_node_scan()
- add Kconfig entry
---
 drivers/power/Kconfig       |  68 ++++++++++++++
 drivers/power/Makefile      |   1 +
 drivers/power/pmic-uclass.c | 130 +++++++++++++++++++++++++++
 include/dm/uclass-id.h      |   3 +
 include/power/pmic.h        | 210 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 412 insertions(+)
 create mode 100644 drivers/power/pmic-uclass.c

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index f8f0239..3513b46 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -1,3 +1,71 @@
+config DM_PMIC
+	bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC)"
+	depends on DM
+	---help---
+	This config enables the driver-model multi uclass PMIC support.
+	Its basic uclass type is: UCLASS_PMIC, which is designed to provide
+	a common I/O interface for pmic child devices of various uclass types.
+
+	Usually PMIC IC's provides more than one functionality, which means
+	that we should implement new uclass operations for each one. Usually
+	PMIC's 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
+	|   |  (pmic sub-devices)
+	|   |_ REGULATOR device (ldo/buck/... ops)  - UCLASS_REGULATOR
+	|   |_ CHARGER device (charger ops)         - UCLASS_CHARGER (future)
+	|   |_ MUIC device (microUSB connector ops) - UCLASS_MUIC    (future)
+	|   |_ ...
+	|
+	|_ BUS 1 device (e.g. I2C1)                 - UCLASS_I2C/SPI/...
+	   |_ PMIC device (READ/WRITE ops)          - UCLASS_PMIC
+	     |  (pmic sub-devices)
+	     |_ RTC device (rtc ops)                - UCLASS_MUIC (future)
+
+	From the I/O interface point of view, there can be found two PMIC types:
+	- single I/O interface - then UCLASS_PMIC device should be a parent of
+	  all pmic sub-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:
+	      |  (pmic sub-devices)
+	      |_ regulator (child)
+	      |_ charger   (child)
+	      |_ other     (child)
+
+	The same for other I/O bus nodes, if pmic uses more then one interface.
+
+	Note:
+	Each PMIC interface driver should use different compatible string.
+
+	There are few basic functions in the UCLASS_PMIC driver API, declared
+	in the file 'include/power/pmic.h':
+	 - int pmic_get(...);
+	 - int pmic_read(struct udevice *pmic, ...);
+	 - int pmic_write(struct udevice *pmic, ...);
+	For the simple implementation, in some cases the pmic uclass device,
+	can be self-sufficient to drive the PMIC functionality. In other case,
+	if each pmic sub-device(child) driver need access to the pmic specified
+	registers, it need to know only the register address and then 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)     (parent) - UCLASS_PMIC
+	|   |_ dev: my_regulator (set value/etc.) (child)  - UCLASS_REGULATOR
+	So the call will looks like below:
+	'pmic_write(regulator->parent, addr, value, len);'
+
 config AXP221_POWER
 	boolean "axp221 / axp223 pmic support"
 	depends on MACH_SUN6I || MACH_SUN8I
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 2145652..5c9a189 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -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
diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
new file mode 100644
index 0000000..df49a4b
--- /dev/null
+++ b/drivers/power/pmic-uclass.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * 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;
+
+int pmic_get(char *name, struct udevice **devp)
+{
+	struct udevice *dev;
+	int ret;
+
+	*devp = NULL;
+
+	for (ret = uclass_first_device(UCLASS_PMIC, &dev);
+	     dev;
+	     ret = uclass_next_device(&dev)) {
+		if (!strcmp(name, dev->name)) {
+			*devp = dev;
+			return ret;
+		}
+	}
+
+	return -ENODEV;
+}
+
+int pmic_reg_count(struct udevice *dev)
+{
+	const struct dm_pmic_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC);
+	if (!ops)
+		return -ENODEV;
+
+	return ops->reg_count;
+}
+
+int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len)
+{
+	const struct dm_pmic_ops *ops;
+
+	if (!buffer)
+		return -EFAULT;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->read)
+		return -EPERM;
+
+	if (ops->read(dev, reg, buffer, len))
+		return -EIO;
+
+	return 0;
+}
+
+int pmic_write(struct udevice *dev, uint reg, uint8_t *buffer, int len)
+{
+	const struct dm_pmic_ops *ops;
+
+	if (!buffer)
+		return -EFAULT;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_PMIC);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->write)
+		return -EPERM;
+
+	if (ops->write(dev, reg, buffer, len))
+		return -EIO;
+
+	return 0;
+}
+
+int pmic_child_node_scan(struct udevice *parent,
+			 const char *optional_subnode,
+			 const char *compatible_property_name)
+{
+	const void *blob = gd->fdt_blob;
+	struct udevice *childp;
+	int offset = parent->of_offset;
+	int childs = 0;
+	int ret;
+
+	debug("%s: bind child for %s\n", __func__, parent->name);
+
+	if (optional_subnode) {
+		debug("Looking for %s optional subnode: %s \n", parent->name,
+		      optional_subnode);
+		offset = fdt_subnode_offset(blob, offset, optional_subnode);
+		if (offset <= 0) {
+			debug("Pmic: %s subnode: %s not found!",
+			      parent->name, optional_subnode);
+			return -ENXIO;
+		}
+	}
+
+	for (offset = fdt_first_subnode(blob, offset); offset > 0;
+	     offset = fdt_next_subnode(blob, offset)) {
+		ret = lists_bind_fdt_by_prop(parent, blob, offset,
+					     compatible_property_name, &childp);
+		if (ret)
+			continue;
+
+		childs++;
+	}
+
+	return childs;
+}
+
+UCLASS_DRIVER(pmic) = {
+	.id		= UCLASS_PMIC,
+	.name		= "pmic",
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 91bb90d..3ecfa23 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -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 uclass types */
+	UCLASS_PMIC,
+
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
 };
diff --git a/include/power/pmic.h b/include/power/pmic.h
index afbc5aa..b55304f 100644
--- a/include/power/pmic.h
+++ b/include/power/pmic.h
@@ -1,4 +1,7 @@
 /*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
  *  Copyright (C) 2011-2012 Samsung Electronics
  *  Lukasz Majewski <l.majewski@samsung.com>
  *
@@ -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,210 @@ 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 the example below. It should be by the call
+ * to 'pmic_child_node_scan()' - it allows bind in optional subnode and with
+ * optional compatible property, e.g. "regulator-compatible" or "regulator".
+ *
+ * my_pmic.c - pmic driver:
+ *
+ * int my_pmic_bind(struct udevice *my_pmic)
+ * {
+ * ...
+ *      ret = pmic_child_node_scan(my_pmic, NULL, "compatible");
+ *                 ...
+ * ...
+ * }
+ *
+ * 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.
+ */
+
+/**
+ * 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.
+ *
+ * @reg_count: devices register count
+ * @read:      read 'len' bytes at "reg" and store it into the 'buffer'
+ * @write:     write 'len' bytes from the 'buffer' to the register at 'reg' address
+ */
+struct dm_pmic_ops {
+	int reg_count;
+	int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int len);
+	int (*write)(struct udevice *dev, uint reg, uint8_t *buffer, int len);
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,
+ * 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,
+};
+
+/**
+ * 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)
+{
+	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_child_node_scan - scan the pmic node or its 'optional_subnode' for its
+ * childs, by the compatible property name given by arg 'compatible_prop_name'.
+ *
+ * Few dts files with a different pmic regulators, uses different property
+ * name for its driver 'compatible' string, like:
+ * - 'compatible'
+ * 'regulator-compatible'
+ * The pmic driver should should bind its devices by proper compatible property
+ * name.
+ *
+ * @parent                   - pmic parent device (usually UCLASS_PMIC)
+ * @optional_subnode         - optional pmic subnode with the childs within it
+ * @compatible_property_name - e.g.: 'compatible'; 'regulator-compatible', ...
+ */
+int pmic_child_node_scan(struct udevice *parent,
+			 const char *optional_subnode,
+			 const char *compatible_property_name);
+
+/**
+ * pmic_get: get the pmic device using its name
+ *
+ * @name - device name
+ * @devp - returned pointer to the pmic device
+ * Returns: 0 on success or negative value of errno.
+ *
+ * The returned devp device can be used with pmic_read/write calls
+ */
+int pmic_get(char *name, struct udevice **devp);
+
+/**
+ * pmic_reg_count: get the pmic register count
+ *
+ * The required pmic device can be obtained by 'pmic_get()'
+ *
+ * @dev - pointer to the UCLASS_PMIC device
+ *
+ * Returns: register count value on success or negative value of errno.
+ */
+int pmic_reg_count(struct udevice *dev);
+
+/**
+ * pmic_read/write: read/write to the UCLASS_PMIC device
+ *
+ * The required pmic device can be obtained by 'pmic_get()'
+ *
+ * @pmic - pointer to the UCLASS_PMIC device
+ * @reg  - device register offset
+ * @val  - value for write or its pointer to read
+ *
+ * Returns: 0 on success or negative value of errno.
+ */
+int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len);
+int pmic_write(struct udevice *dev, uint reg, uint8_t *buffer, int len);
+#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 +297,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)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (4 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 05/17] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:07       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 07/17] dm: pmic: add pmic command Przemyslaw Marczak
                       ` (13 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

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.

To get the regulator device:
- regulator_get()             - get the regulator device

The regulator framework is based on a 'struct dm_regulator_ops'.
It provides a common function calls, for it's basic features:
- regulator_info()            - get the regulator info structure
- regulator_mode()            - get the regulator mode info structure
- regulator_get/set_value()   - get/set the regulator output voltage
- regulator_get/set_current() - get/set the regulator output current
- regulator_get/set_enable()  - get/set the regulator output enable state
- regulator_get/set_mode()    - get/set the regulator output operation mode

An optional and useful regulator framework features are two descriptors:
- struct dm_regulator_info- describes the regulator name and output value limits

- struct dm_regulator_mode - (array) describes the regulators operation modes

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
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

Changes V3:
- regulator-uclass.c and regulator.h:
  -- api cleanup
  -- new function regulator_ofdata_to_platdata()
  -- update of comments
  -- add Kconfig
---
 drivers/power/Kconfig            |  33 ++++-
 drivers/power/Makefile           |   1 +
 drivers/power/regulator-uclass.c | 219 +++++++++++++++++++++++++++++++++
 include/dm/uclass-id.h           |   1 +
 include/power/regulator.h        | 259 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 512 insertions(+), 1 deletion(-)
 create mode 100644 drivers/power/regulator-uclass.c
 create mode 100644 include/power/regulator.h

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 3513b46..1e73c7a 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -66,7 +66,38 @@ config DM_PMIC
 	So the call will looks like below:
 	'pmic_write(regulator->parent, addr, value, len);'
 
-config AXP221_POWER
+config DM_REGULATOR
+	bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)"
+	depends on DM
+	---help---
+	This config enables the driver-model regulator uclass support, which
+	provides implementation of driver model regulator uclass api.
+
+	Regulator uclass API calls:
+	To get the regulator device:
+	- regulator_get()             - get the regulator device
+
+	The regulator framework is based on a 'struct dm_regulator_ops'.
+	It provides a common function calls, for it's basic features:
+	- regulator_info()            - get the regulator info structure
+	- regulator_mode()            - get the regulator mode info structure
+	- regulator_get/set_value()   - operate on output voltage value
+	- regulator_get/set_current() - operate on output current value
+	- regulator_get/set_enable()  - operate on output enable state
+	- regulator_get/set_mode()    - operate on output operation mode
+
+	An optional and useful regulator framework features are two descriptors:
+	- struct dm_regulator_info - describes the regulator name and output limits
+	- struct dm_regulator_mode - describes the regulators operation mode
+
+	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 regulato
+
+	config AXP221_POWER
 	boolean "axp221 / axp223 pmic support"
 	depends on MACH_SUN6I || MACH_SUN8I
 	default y
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 5c9a189..a6b7012 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -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
diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
new file mode 100644
index 0000000..b6a00c6
--- /dev/null
+++ b/drivers/power/regulator-uclass.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * 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_info(struct udevice *dev, struct dm_regulator_info **infop)
+{
+	if (!dev || !dev->uclass_priv)
+		return -ENODEV;
+
+	*infop = dev->uclass_priv;
+
+	return 0;
+}
+
+int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep)
+{
+	struct dm_regulator_info *info;
+	int ret;
+
+	ret = regulator_info(dev, &info);
+	if (ret)
+		return ret;
+
+	*modep = info->mode;
+	return info->mode_count;
+}
+
+int regulator_get_value(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_value)
+		return -EPERM;
+
+	return ops->get_value(dev);
+}
+
+int regulator_set_value(struct udevice *dev, int uV)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->set_value)
+		return -EPERM;
+
+	return ops->set_value(dev, uV);
+}
+
+int regulator_get_current(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_current)
+		return -EPERM;
+
+	return ops->get_current(dev);
+}
+
+int regulator_set_current(struct udevice *dev, int uA)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->set_current)
+		return -EPERM;
+
+	return ops->set_current(dev, uA);
+}
+
+bool regulator_get_enable(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_enable)
+		return -EPERM;
+
+	return ops->get_enable(dev);
+}
+
+int regulator_set_enable(struct udevice *dev, bool enable)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->set_enable)
+		return -EPERM;
+
+	return ops->set_enable(dev, enable);
+}
+
+int regulator_get_mode(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->get_mode)
+		return -EPERM;
+
+	return ops->get_mode(dev);
+}
+
+int regulator_set_mode(struct udevice *dev, int mode)
+{
+	const struct dm_regulator_ops *ops;
+
+	ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
+	if (!ops)
+		return -ENODEV;
+
+	if (!ops->set_mode)
+		return -EPERM;
+
+	return ops->set_mode(dev, mode);
+}
+
+int regulator_ofdata_to_platdata(struct udevice *dev)
+{
+	struct dm_regulator_info *info = dev->uclass_priv;
+	int offset = dev->of_offset;
+	int len;
+
+	/* Mandatory constraints */
+	info->name = strdup(fdt_getprop(gd->fdt_blob, offset,
+					"regulator-name", &len));
+	if (!info->name)
+		return -ENXIO;
+
+	info->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
+				      "regulator-min-microvolt", -1);
+	if (info->min_uV < 0)
+		return -ENXIO;
+
+	info->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
+				      "regulator-max-microvolt", -1);
+	if (info->max_uV < 0)
+		return -ENXIO;
+
+	/* Optional constraints */
+	info->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
+				      "regulator-min-microamp", -1);
+	info->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
+				      "regulator-max-microamp", -1);
+	info->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
+					 "regulator-always-on");
+	info->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
+					 "regulator-boot-on");
+
+	return 0;
+}
+
+int regulator_get(char *name, struct udevice **devp)
+{
+	struct dm_regulator_info *info;
+	struct udevice *dev;
+	int ret;
+
+	*devp = NULL;
+
+	for (ret = uclass_first_device(UCLASS_REGULATOR, &dev);
+	     dev;
+	     ret = uclass_next_device(&dev)) {
+		info = dev->uclass_priv;
+		if (!info)
+			continue;
+
+		if (!strcmp(name, info->name)) {
+			*devp = dev;
+			return ret;
+		}
+	}
+
+	return -ENODEV;
+}
+
+UCLASS_DRIVER(regulator) = {
+	.id		= UCLASS_REGULATOR,
+	.name		= "regulator",
+	.per_device_auto_alloc_size = sizeof(struct dm_regulator_info),
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 3ecfa23..23356f3 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -37,6 +37,7 @@ enum uclass_id {
 
 	/* PMIC uclass and PMIC-related uclass types */
 	UCLASS_PMIC,
+	UCLASS_REGULATOR,
 
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
diff --git a/include/power/regulator.h b/include/power/regulator.h
new file mode 100644
index 0000000..cf083c5
--- /dev/null
+++ b/include/power/regulator.h
@@ -0,0 +1,259 @@
+/*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _INCLUDE_REGULATOR_H_
+#define _INCLUDE_REGULATOR_H_
+
+/* enum regulator_type - used for regulator_*() variant calls */
+enum regulator_type {
+	REGULATOR_TYPE_LDO = 0,
+	REGULATOR_TYPE_BUCK,
+	REGULATOR_TYPE_DVS,
+	REGULATOR_TYPE_FIXED,
+	REGULATOR_TYPE_OTHER,
+};
+
+/**
+ * struct dm_regulator_mode - this structure holds an information about
+ * each regulator operation mode. Probably in most cases - an array.
+ * This will be probably a driver-static data, since it is device-specific.
+ *
+ * @id             - a driver-specific mode id
+ * @register_value - a driver-specific value for its mode id
+ * @name           - the name of mode - used for regulator command
+ * Note:
+ * The field 'id', should be always a positive number, since the negative values
+ * are reserved for the errno numbers when returns the mode id.
+ */
+struct dm_regulator_mode {
+	int id; /* Set only as >= 0 (negative value is reserved for errno) */
+	int register_value;
+	const char *name;
+};
+
+/**
+ * struct dm_regulator_info - this structure holds an information about
+ * each regulator constraints and supported operation modes. There is no "step"
+ * voltage value - so driver should take care of this.
+ *
+ * @type       - one of 'enum regulator_type'
+ * @mode       - pointer to the regulator mode (array if more than one)
+ * @mode_count - number of '.mode' entries
+ * @min_uV*    - minimum voltage (micro Volts)
+ * @max_uV*    - maximum voltage (micro Volts)
+ * @min_uA*    - minimum amperage (micro Amps)
+ * @max_uA*    - maximum amperage (micro Amps)
+ * @always_on* - bool type, true or false
+ * @boot_on*   - bool type, true or false
+ * @name*      - fdt regulator name - should be taken from the device tree 
+ *
+ * Note: for attributes signed with '*'
+ * These platform-specific constraints can be taken by regulator api function,
+ * which is 'regulator_ofdata_to_platdata()'. Please read the description, which
+ * can be found near the declaration of the mentioned function.
+*/
+struct dm_regulator_info {
+	enum regulator_type type;
+	struct dm_regulator_mode *mode;
+	int mode_count;
+	int min_uV;
+	int max_uV;
+	int min_uA;
+	int max_uA;
+	bool always_on;
+	bool boot_on;
+	const char *name;
+};
+
+/* PMIC regulator device operations */
+struct dm_regulator_ops {
+	/**
+	 * The regulator output value function calls operates on a micro Volts.
+	 *
+	 * get/set_value - get/set output value of the given output number
+	 * @dev          - regulator device
+	 * Sets:
+	 * @uV           - set the output value [micro Volts]
+	 * Returns: output value [uV] on success or negative errno if fail.
+	 */
+	int (*get_value)(struct udevice *dev);
+	int (*set_value)(struct udevice *dev, int uV);
+
+	/**
+	 * The regulator output current function calls operates on a micro Amps.
+	 *
+	 * get/set_current - get/set output current of the given output number
+	 * @dev            - regulator device
+	 * Sets:
+	 * @uA           - set the output current [micro Amps]
+	 * Returns: output value [uA] on success or negative errno if fail.
+	 */
+	int (*get_current)(struct udevice *dev);
+	int (*set_current)(struct udevice *dev, int uA);
+
+	/**
+	 * The most basic feature of the regulator output is its enable state.
+	 *
+	 * get/set_enable - get/set enable state of the given output number
+	 * @dev           - regulator device
+	 * Sets:
+	 * @enable         - set true - enable or false - disable
+	 * Returns: true/false for get; or 0 / -errno for set.
+	 */
+	bool (*get_enable)(struct udevice *dev);
+	int (*set_enable)(struct udevice *dev, bool enable);
+
+	/**
+	 * The 'get/set_mode()' function calls should operate on a driver
+	 * specific mode definitions, which should be found in:
+	 * field 'mode' of struct mode_desc.
+	 *
+	 * get/set_mode - get/set operation mode of the given output number
+	 * @dev         - regulator device
+	 * Sets
+	 * @mode_id     - set output mode id (struct dm_regulator_mode->id)
+	 * Returns: id/0 for get/set on success or negative errno if fail.
+	 * Note:
+	 * The field 'id' of struct type 'dm_regulator_mode', should be always
+	 * positive number, since the negative is reserved for the error.
+	 */
+	int (*get_mode)(struct udevice *dev);
+	int (*set_mode)(struct udevice *dev, int mode_id);
+};
+
+/**
+ * regulator_info: returns a pointer to the devices regulator info structure
+ *
+ * @dev    - pointer to the regulator device
+ * @infop  - pointer to the returned regulator info
+ * Returns - 0 on success or negative value of errno.
+ */
+int regulator_info(struct udevice *dev, struct dm_regulator_info **infop);
+
+/**
+ * regulator_mode: returns a pointer to the array of regulator mode info
+ *
+ * @dev        - pointer to the regulator device
+ * @modep      - pointer to the returned mode info array
+ * Returns     - count of modep entries on success or negative errno if fail.
+ */
+int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep);
+
+/**
+ * regulator_get_value: get microvoltage voltage value of a given regulator
+ *
+ * @dev    - pointer to the regulator device
+ * Returns - positive output value [uV] on success or negative errno if fail.
+ */
+int regulator_get_value(struct udevice *dev);
+
+/**
+ * regulator_set_value: set the microvoltage value of a given regulator.
+ *
+ * @dev    - pointer to the regulator device
+ * @uV     - the output value to set [micro Volts]
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_value(struct udevice *dev, int uV);
+
+/**
+ * regulator_get_current: get microampere value of a given regulator
+ *
+ * @dev    - pointer to the regulator device
+ * Returns - positive output current [uA] on success or negative errno if fail.
+ */
+int regulator_get_current(struct udevice *dev);
+
+/**
+ * regulator_set_current: set the microampere value of a given regulator.
+ *
+ * @dev    - pointer to the regulator device
+ * @uA     - set the output current [micro Amps]
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_current(struct udevice *dev, int uA);
+
+/**
+ * regulator_get_enable: get regulator device enable state.
+ *
+ * @dev    - pointer to the regulator device
+ * Returns - true/false of enable state
+ */
+bool regulator_get_enable(struct udevice *dev);
+
+/**
+ * regulator_set_enable: set regulator enable state
+ *
+ * @dev    - pointer to the regulator device
+ * @enable - set true or false
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_enable(struct udevice *dev, bool enable);
+
+/**
+ * regulator_get_mode: get mode of a given device regulator
+ *
+ * @dev    - pointer to the regulator device
+ * Returns - positive  mode number 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 'mode_desc' provides a field 'mode'
+ * for this purpose and register_value for a raw register value.
+ */
+int regulator_get_mode(struct udevice *dev);
+
+/**
+ * regulator_set_mode: set given regulator mode
+ *
+ * @dev    - pointer to the regulator device
+ * @mode   - mode type (field 'mode' of struct mode_desc)
+ * 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 mode);
+
+/**
+ * regulator_ofdata_to_platdata: get the regulator constraints from its fdt node
+ * and put it into the regulator 'dm_regulator_info' (dev->uclass_priv).
+ *
+ * An example of required regulator fdt node constraints:
+ * ldo1 {
+ *      regulator-compatible = "LDO1"; (not used here, but required for bind)
+ *      regulator-name = "VDD_MMC_1.8V"; (mandatory)
+ *      regulator-min-microvolt = <1000000>; (mandatory)
+ *      regulator-max-microvolt = <1000000>; (mandatory)
+ *      regulator-min-microamp = <1000>; (optional)
+ *      regulator-max-microamp = <1000>; (optional)
+ *      regulator-always-on; (optional)
+ *      regulator-boot-on; (optional)
+ * };
+ *
+ * @dev    - pointer to the regulator device
+ * Returns - 0 on success or -errno value if fails
+ * Note2:
+ * This function can be called at stage in which 'dev->uclass_priv' is non-NULL.
+ * It is possible at two driver call stages: '.ofdata_to_platdata' or '.probe'.
+ */
+int regulator_ofdata_to_platdata(struct udevice *dev);
+
+/**
+ * regulator_get: returns the pointer to the pmic regulator device based on
+ * regulator fdt node data
+ *
+ * @fdt_name - property 'regulator-name' value of regulator fdt node
+ * @devp     - returned pointer to the regulator device
+ * Returns: 0 on success or negative value of errno.
+ *
+ * The returned 'regulator' device can be used with:
+ * - regulator_get/set_*
+ */
+int regulator_get(char *fdt_name, struct udevice **devp);
+
+#endif /* _INCLUDE_REGULATOR_H_ */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 07/17] dm: pmic: add pmic command
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (5 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:07       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 08/17] dm: regulator: add regulator command Przemyslaw Marczak
                       ` (12 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

This is new command for the pmic devices based on driver model pmic api.
Command features are unchanged:
- list          - list UCLASS pmic devices
- pmic dev [id]      - show or [set] operating pmic device (NEW)
- pmic dump          - dump registers
- pmic read address  - read byte of register at address
- pmic write address - write byte to register at address

The only one change for this command is 'dev' subcommand.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>

Changes v3:
- new file
- add Kconfig
---
 common/Kconfig    |  14 ++++
 common/Makefile   |   3 +
 common/cmd_pmic.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 227 insertions(+)
 create mode 100644 common/cmd_pmic.c

diff --git a/common/Kconfig b/common/Kconfig
index e662774..1125e6d 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -335,4 +335,18 @@ config CMD_SETGETDCR
 
 endmenu
 
+menu "Power commands"
+config DM_PMIC_CMD
+	bool "Enable Driver Model PMIC command"
+	depends on DM_PMIC
+	help
+	  This is new command for the pmic devices based on driver model pmic api.
+	  Command features are unchanged:
+	  - list               - list UCLASS pmic devices
+	  - pmic dev [id]      - show or [set] operating pmic device (NEW)
+	  - pmic dump          - dump registers
+	  - pmic read address  - read byte of register at address
+	  - pmic write address - write byte to register at address
+	  The only one change for this command is 'dev' subcommand.
+endmenu
 endmenu
diff --git a/common/Makefile b/common/Makefile
index 7216a13..d908851 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -208,6 +208,9 @@ obj-$(CONFIG_UPDATE_TFTP) += update.o
 obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
 obj-$(CONFIG_CMD_DFU) += cmd_dfu.o
 obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
+
+# Power
+obj-$(CONFIG_DM_PMIC_CMD) += cmd_pmic.o
 endif
 
 ifdef CONFIG_SPL_BUILD
diff --git a/common/cmd_pmic.c b/common/cmd_pmic.c
new file mode 100644
index 0000000..978a94a
--- /dev/null
+++ b/common/cmd_pmic.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#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>
+#include <i2c.h>
+#include <compiler.h>
+#include <errno.h>
+
+#define LIMIT_SEQ	3
+#define LIMIT_DEVNAME	20
+
+static struct udevice *pmic_curr;
+
+static int failed(const char *getset, const char *thing,
+		  const char *for_dev, int ret)
+{
+	printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing, for_dev,
+						    ret, errno_str(ret));
+	return CMD_RET_FAILURE;
+}
+
+static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	int seq, ret = -ENODEV;
+
+	switch (argc) {
+	case 2:
+		seq = simple_strtoul(argv[1], NULL, 0);
+		ret = uclass_get_device_by_seq(UCLASS_PMIC, seq, &pmic_curr);
+	case 1:
+		if (!pmic_curr)
+			return failed("get", "the", "device", ret);
+
+		printf("dev: %d @ %s\n", pmic_curr->seq, pmic_curr->name);
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	const char *parent_uc;
+	int ret;
+
+	printf("|%*s | %-*.*s| %-*.*s| %s @ %s\n",
+	       LIMIT_SEQ, "Seq",
+	       LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
+	       LIMIT_DEVNAME, LIMIT_DEVNAME, "Parent name",
+	       "Parent uclass", "seq");
+
+	for (ret = uclass_first_device(UCLASS_PMIC, &dev); dev;
+	     ret = uclass_next_device(&dev)) {
+		if (!dev)
+			continue;
+
+		/* Parent uclass name*/
+		parent_uc = dev->parent->uclass->uc_drv->name;
+
+		printf("|%*d | %-*.*s| %-*.*s| %s @ %d\n",
+		       LIMIT_SEQ, dev->seq,
+		       LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
+		       LIMIT_DEVNAME, LIMIT_DEVNAME, dev->parent->name,
+		       parent_uc, dev->parent->seq);
+	}
+
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_dump(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	int regs, ret;
+	uint8_t value;
+	uint reg;
+
+	if (!pmic_curr)
+		return failed("get", "current", "device", -ENODEV);
+
+	dev = pmic_curr;
+
+	printf("Dump pmic: %s registers\n", dev->name);
+
+	regs = pmic_reg_count(dev);
+	reg = 0;
+	while (reg < regs) {
+		ret = pmic_read(dev, reg, &value, 1);
+		if (ret)
+			return failed("read", dev->name, "register", ret);
+
+		if (!(reg % 16))
+			printf("\n0x%02x: ", reg);
+
+		printf("%2.2x ", value);
+		reg++;
+	}
+	printf("\n");
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_read(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	int regs, ret;
+	uint8_t value;
+	uint reg;
+
+	if (!pmic_curr)
+		return failed("get", "current", "device", -ENODEV);
+
+	dev = pmic_curr;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	reg = simple_strtoul(argv[1], NULL, 0);
+	regs = pmic_reg_count(dev);
+	if (reg > regs) {
+		printf("Pmic max reg: %d\n", regs);
+		return failed("read", "given", "address", -EFAULT);
+	}
+
+	ret = pmic_read(dev, reg, &value, 1);
+	if (ret)
+		return failed("read", dev->name, "register", ret);
+
+	printf("0x%02x: 0x%2.2x\n", reg, value);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	int regs, ret;
+	uint8_t value;
+	uint reg;
+
+	if (!pmic_curr)
+		return failed("get", "current", "device", -ENODEV);
+
+	dev = pmic_curr;
+
+	if (argc != 3)
+		return CMD_RET_USAGE;
+
+	reg = simple_strtoul(argv[1], NULL, 0);
+	regs = pmic_reg_count(dev);
+	if (reg > regs) {
+		printf("Pmic max reg: %d\n", regs);
+		return failed("write", "given", "address", -EFAULT);
+	}
+
+	value = simple_strtoul(argv[2], NULL, 0);
+
+	ret = pmic_write(dev, reg, &value, 1);
+	if (ret)
+		return failed("write", dev->name, "register", ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t subcmd[] = {
+	U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
+	U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
+	U_BOOT_CMD_MKENT(dump, 1, 1, do_dump, "", ""),
+	U_BOOT_CMD_MKENT(read, 2, 1, do_read, "", ""),
+	U_BOOT_CMD_MKENT(write, 3, 1, do_write, "", ""),
+};
+
+static int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc,
+			char * const argv[])
+{
+	cmd_tbl_t *cmd;
+
+	argc--;
+	argv++;
+
+	cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
+	if (cmd == NULL || argc > cmd->maxargs)
+		return CMD_RET_USAGE;
+
+	return cmd->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(pmic, CONFIG_SYS_MAXARGS, 1, do_pmic,
+	"uclass operations",
+	"list          - list UCLASS pmic devices\n"
+	"pmic dev [id]      - show or [set] operating pmic device\n"
+	"pmic dump          - dump registers\n"
+	"pmic read address  - read byte of register at address\n"
+	"pmic write address - write byte to register at address\n"
+);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 08/17] dm: regulator: add regulator command
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (6 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 07/17] dm: pmic: add pmic command Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:07       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 09/17] pmic: max77686 set the same compatible as in the kernel Przemyslaw Marczak
                       ` (11 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

This command is based on driver model regulator api.
User interface features:
- list                   - list UCLASS regulator devices
- regulator dev [id]     - show or [set] operating regulator device
- regulator [info]       - print constraints info
- regulator [status]     - print operating status
- regulator [value] [-f] - print/[set] voltage value [uV] (force)
- regulator [current]    - print/[set] current value [uA]
- regulator [mode_id]    - print/[set] operating mode id
- regulator [enable]     - enable the regulator output
- regulator [disable]    - disable the regulator output

The 'force' option can be used for setting the value which exceeds the limits,
which are found in device-tree and are keept in regulators info structure.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>

---
Changes v3:
- new file
- Kconfig entry
---
 common/Kconfig         |  22 +++
 common/Makefile        |   1 +
 common/cmd_regulator.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 408 insertions(+)
 create mode 100644 common/cmd_regulator.c

diff --git a/common/Kconfig b/common/Kconfig
index 1125e6d..48f360f 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -348,5 +348,27 @@ config DM_PMIC_CMD
 	  - pmic read address  - read byte of register at address
 	  - pmic write address - write byte to register at address
 	  The only one change for this command is 'dev' subcommand.
+
+config DM_REGULATOR_CMD
+	bool "Enable Driver Model REGULATOR command"
+	depends on DM_REGULATOR
+	help
+	  This command is based on driver model regulator api.
+	  User interface features:
+	  - list                   - list UCLASS regulator devices
+	  - regulator dev [id]     - show or [set] operating regulator device
+	  - regulator [info]       - print constraints info
+	  - regulator [status]     - print operating status
+	  - regulator [value] [-f] - print/[set] voltage value [uV] (force)
+	  - regulator [current]    - print/[set] current value [uA]
+	  - regulator [mode_id]    - print/[set] operating mode id
+	  - regulator [enable]     - enable the regulator output
+	  - regulator [disable]    - disable the regulator output
+
+	  The 'force' option can be used for setting the value which exceeds
+	  the limit which are found in device-tree and are keept in regulators
+	  info structure.
+
 endmenu
+
 endmenu
diff --git a/common/Makefile b/common/Makefile
index d908851..d63fe12 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -211,6 +211,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
 
 # Power
 obj-$(CONFIG_DM_PMIC_CMD) += cmd_pmic.o
+obj-$(CONFIG_DM_REGULATOR_CMD) += cmd_regulator.o
 endif
 
 ifdef CONFIG_SPL_BUILD
diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
new file mode 100644
index 0000000..d388b14
--- /dev/null
+++ b/common/cmd_regulator.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#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>
+#include <i2c.h>
+#include <compiler.h>
+#include <errno.h>
+
+#define LIMIT_SEQ	3
+#define LIMIT_DEVNAME	20
+#define LIMIT_OFNAME	20
+#define LIMIT_INFO	12
+
+static struct udevice *reg_curr;
+
+static int failed(const char *getset, const char *thing,
+		  const char *for_dev, int ret)
+{
+	printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing, for_dev,
+						    ret, errno_str(ret));
+	return CMD_RET_FAILURE;
+}
+
+static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct dm_regulator_info *info;
+	int seq, ret = CMD_RET_FAILURE;
+
+	switch (argc) {
+	case 2:
+		seq = simple_strtoul(argv[1], NULL, 0);
+		uclass_get_device_by_seq(UCLASS_REGULATOR, seq, &reg_curr);
+	case 1:
+		ret = regulator_info(reg_curr, &info);
+		if (ret)
+			return failed("get", "the", "device", ret);
+
+		printf("dev: %d @ %s\n", reg_curr->seq, info->name);
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int get_curr_dev_and_info(struct udevice **devp,
+				 struct dm_regulator_info **infop)
+{
+	int ret = -ENODEV;
+
+	*devp = reg_curr;
+	if (!*devp)
+		return failed("get", "current", "device", ret);
+
+	ret = regulator_info(*devp, infop);
+	if (ret)
+		return failed("get", reg_curr->name, "info", ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct dm_regulator_info *info;
+	const char *parent_uc;
+	struct udevice *dev;
+	int ret;
+
+	printf("|%*s | %*.*s @ %-*.*s| %s @ %s\n",
+	       LIMIT_SEQ, "Seq",
+	       LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
+	       LIMIT_OFNAME, LIMIT_OFNAME, "fdtname",
+	       "Parent", "uclass");
+
+	for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
+	     ret = uclass_next_device(&dev)) {
+		if (regulator_info(dev, &info)) {
+			printf("(null) info for: %s\n", dev->name);
+			continue;
+		}
+
+		/* Parent uclass name*/
+		parent_uc = dev->parent->uclass->uc_drv->name;
+
+		printf("|%*d | %*.*s @ %-*.*s| %s @ %s\n",
+		       LIMIT_SEQ, dev->seq,
+		       LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
+		       LIMIT_OFNAME, LIMIT_OFNAME, info->name,
+		       dev->parent->name, parent_uc);
+	}
+
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_info(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_info *info;
+	struct dm_regulator_mode *modes;
+	const char *parent_uc;
+	int mode_count;
+	int ret;
+	int i;
+
+	ret = get_curr_dev_and_info(&dev, &info);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	parent_uc = dev->parent->uclass->uc_drv->name;
+
+	printf("Uclass regulator dev %d info:\n", dev->seq);
+	printf("%-*s %s @ %s\n%-*s %s\n%-*s %s\n",
+	       LIMIT_INFO, "* parent:", dev->parent->name, parent_uc,
+	       LIMIT_INFO, "* dev name:", dev->name,
+	       LIMIT_INFO, "* fdt name:", info->name);
+
+	printf("%-*s %d\n%-*s %d\n%-*s %d\n%-*s %d\n%-*s %s\n%-*s %s\n",
+	       LIMIT_INFO, "* min uV:", info->min_uV,
+	       LIMIT_INFO, "* max uV:", info->max_uV,
+	       LIMIT_INFO, "* min uA:", info->min_uA,
+	       LIMIT_INFO, "* max uA:", info->max_uA,
+	       LIMIT_INFO, "* always on:", info->always_on ? "true" : "false",
+	       LIMIT_INFO, "* boot on:", info->boot_on ? "true" : "false");
+
+	mode_count = regulator_mode(dev, &modes);
+	if (!mode_count)
+		return failed("get mode", "for mode", "null count", -EPERM);
+
+	if (mode_count < 0)
+		return failed("get", info->name, "mode count", mode_count);
+
+	printf("* operation modes:\n");
+	for (i = 0; i < mode_count; i++, modes++)
+		printf("  - mode %d (%s)\n", modes->id, modes->name);
+
+	return CMD_RET_SUCCESS;
+}
+
+static const char *get_mode_name(struct dm_regulator_mode *mode,
+				 int mode_count,
+				 int mode_id)
+{
+	while (mode_count--) {
+		if (mode->id == mode_id)
+			return mode->name;
+		mode++;
+	}
+
+	return NULL;
+}
+
+static int do_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_info *info;
+	const char *mode_name;
+	int value, mode, ret;
+	bool enabled;
+
+	ret = get_curr_dev_and_info(&dev, &info);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	/* Value is mandatory */
+	value = regulator_get_value(dev);
+	if (value < 0)
+		return failed("get", info->name, "voltage value", value);
+
+	mode = regulator_get_mode(dev);
+	mode_name = get_mode_name(info->mode, info->mode_count, mode);
+	enabled = regulator_get_enable(dev);
+
+	printf("Regulator seq %d status:\n", dev->seq);
+	printf("%-*s %d\n%-*s %d (%s)\n%-*s %s\n",
+	       LIMIT_INFO, " * value:", value,
+	       LIMIT_INFO, " * mode:", mode, mode_name,
+	       LIMIT_INFO, " * enabled:", enabled ? "true" : "false");
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_value(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_info *info;
+	int value;
+	int force;
+	int ret;
+
+	ret = get_curr_dev_and_info(&dev, &info);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	if (argc == 1) {
+		value = regulator_get_value(dev);
+		if (value < 0)
+			return failed("get", info->name, "voltage", value);
+
+		printf("%d uV\n", value);
+		return CMD_RET_SUCCESS;
+	}
+
+	if (info->type == REGULATOR_TYPE_FIXED) {
+		printf("Fixed regulator value change not allowed.\n");
+		return CMD_RET_SUCCESS;
+	}
+
+	if (argc == 3)
+		force = !strcmp("-f", argv[2]);
+	else
+		force = 0;
+
+	value = simple_strtoul(argv[1], NULL, 0);
+	if ((value < info->min_uV || value > info->max_uV) && !force) {
+		printf("Value exceeds regulator constraint limits\n");
+		return CMD_RET_FAILURE;
+	}
+
+	ret = regulator_set_value(dev, value);
+	if (ret)
+		return failed("set", info->name, "voltage value", ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_current(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_info *info;
+	int current;
+	int ret;
+
+	ret = get_curr_dev_and_info(&dev, &info);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	if (argc == 1) {
+		current = regulator_get_current(dev);
+		if (current < 0)
+			return failed("get", info->name, "current", current);
+
+		printf("%d uA\n", current);
+		return CMD_RET_SUCCESS;
+	}
+
+	if (info->type == REGULATOR_TYPE_FIXED) {
+		printf("Fixed regulator current change not allowed.\n");
+		return CMD_RET_SUCCESS;
+	}
+
+	current = simple_strtoul(argv[1], NULL, 0);
+	if (current < info->min_uA || current > info->max_uA) {
+		printf("Current exceeds regulator constraint limits\n");
+		return CMD_RET_FAILURE;
+	}
+
+	ret = regulator_set_current(dev, current);
+	if (ret)
+		return failed("set", info->name, "current value", ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_info *info;
+	int new_mode;
+	int mode;
+	int ret;
+
+	ret = get_curr_dev_and_info(&dev, &info);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	if (info->type == REGULATOR_TYPE_FIXED) {
+		printf("Fixed regulator mode option not allowed.\n");
+		return CMD_RET_SUCCESS;
+	}
+
+	if (argc == 1) {
+		mode = regulator_get_mode(dev);
+		if (mode < 0)
+			return failed("get", info->name, "mode", ret);
+
+		printf("mode id: %d\n", mode);
+		return CMD_RET_SUCCESS;
+	}
+
+	new_mode = simple_strtoul(argv[1], NULL, 0);
+
+	ret = regulator_set_mode(dev, new_mode);
+	if (ret)
+		return failed("set", info->name, "mode", ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_enable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_info *info;
+	int ret;
+
+	ret = get_curr_dev_and_info(&dev, &info);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	ret = regulator_set_enable(dev, true);
+	if (ret)
+		return failed("enable", "regulator", info->name, ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_disable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_info *info;
+	int ret;
+
+	ret = get_curr_dev_and_info(&dev, &info);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	ret = regulator_set_enable(dev, false);
+	if (ret)
+		return failed("disable", "regulator", info->name, ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t subcmd[] = {
+	U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
+	U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
+	U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""),
+	U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
+	U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""),
+	U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""),
+	U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""),
+	U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""),
+	U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""),
+};
+
+static int do_regulator(cmd_tbl_t *cmdtp, int flag, int argc,
+			char * const argv[])
+{
+	cmd_tbl_t *cmd;
+
+	argc--;
+	argv++;
+
+	cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
+	if (cmd == NULL || argc > cmd->maxargs)
+		return CMD_RET_USAGE;
+
+	return cmd->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator,
+	"uclass operations",
+	"list         - list UCLASS regulator devices\n"
+	"regulator dev [id]     - show or [set] operating regulator device\n"
+	"regulator [info]       - print constraints info\n"
+	"regulator [status]     - print operating status\n"
+	"regulator [value] [-f] - print/[set] voltage value [uV] (force)\n"
+	"regulator [current]    - print/[set] current value [uA]\n"
+	"regulator [mode_id]    - print/[set] operating mode id\n"
+	"regulator [enable]     - enable the regulator output\n"
+	"regulator [disable]    - disable the regulator output\n"
+);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 09/17] pmic: max77686 set the same compatible as in the kernel
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (7 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 08/17] dm: regulator: add regulator command Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:08       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 10/17] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
                       ` (10 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

This commit also updates the proper dts files.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 arch/arm/dts/exynos4412-odroid.dts   | 2 +-
 arch/arm/dts/exynos4412-trats2.dts   | 2 +-
 arch/arm/dts/exynos5250-smdk5250.dts | 2 +-
 arch/arm/dts/exynos5250-snow.dts     | 2 +-
 lib/fdtdec.c                         | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/arm/dts/exynos4412-odroid.dts b/arch/arm/dts/exynos4412-odroid.dts
index 582f6e5..5507d9a 100644
--- a/arch/arm/dts/exynos4412-odroid.dts
+++ b/arch/arm/dts/exynos4412-odroid.dts
@@ -36,7 +36,7 @@
 		status = "okay";
 
 		max77686_pmic at 09 {
-			compatible = "maxim,max77686_pmic";
+			compatible = "maxim,max77686";
 			interrupts = <7 0>;
 			reg = <0x09 0 0>;
 			#clock-cells = <1>;
diff --git a/arch/arm/dts/exynos4412-trats2.dts b/arch/arm/dts/exynos4412-trats2.dts
index dd238df..5c0bb91 100644
--- a/arch/arm/dts/exynos4412-trats2.dts
+++ b/arch/arm/dts/exynos4412-trats2.dts
@@ -41,7 +41,7 @@
 		status = "okay";
 
 		max77686_pmic at 09 {
-			compatible = "maxim,max77686_pmic";
+			compatible = "maxim,max77686";
 			interrupts = <7 0>;
 			reg = <0x09 0 0>;
 			#clock-cells = <1>;
diff --git a/arch/arm/dts/exynos5250-smdk5250.dts b/arch/arm/dts/exynos5250-smdk5250.dts
index 9273562..3cebfc2 100644
--- a/arch/arm/dts/exynos5250-smdk5250.dts
+++ b/arch/arm/dts/exynos5250-smdk5250.dts
@@ -68,7 +68,7 @@
 	i2c at 12c60000 {
 		pmic at 9 {
 			reg = <0x9>;
-			compatible = "maxim,max77686_pmic";
+			compatible = "maxim,max77686";
 		};
 	};
 
diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts
index 7d8be69..dda6b34 100644
--- a/arch/arm/dts/exynos5250-snow.dts
+++ b/arch/arm/dts/exynos5250-snow.dts
@@ -107,7 +107,7 @@
 	i2c at 12c60000 {
 		pmic at 9 {
 			reg = <0x9>;
-			compatible = "maxim,max77686_pmic";
+			compatible = "maxim,max77686";
 		};
 	};
 
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 1a0268a..261ec90 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -55,7 +55,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
 	COMPAT(SAMSUNG_EXYNOS_DWMMC, "samsung,exynos-dwmmc"),
 	COMPAT(SAMSUNG_EXYNOS_MMC, "samsung,exynos-mmc"),
 	COMPAT(SAMSUNG_EXYNOS_SERIAL, "samsung,exynos4210-uart"),
-	COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686_pmic"),
+	COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686"),
 	COMPAT(GENERIC_SPI_FLASH, "spi-flash"),
 	COMPAT(MAXIM_98095_CODEC, "maxim,max98095-codec"),
 	COMPAT(INFINEON_SLB9635_TPM, "infineon,slb9635-tpm"),
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 10/17] dm: pmic: add max77686 pmic driver
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (8 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 09/17] pmic: max77686 set the same compatible as in the kernel Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:08       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 11/17] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
                       ` (9 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

This is the implementation of driver model uclass pmic driver.
The max77686 pmic driver implements read/write operations and driver
bind method - to bind other pmic uclass devices as a parent pmic device.
This driver provides pmic_platdata for also for child regulator.

This driver will try to bind the regulator device with regulator driver.
This should succeed if regulator driver is compiled.

If no regulator driver found, then the pmic can still provide read/write
operations, and can be used with pmic framework function calls.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- add implementation of pmic read/write
- max77686: add new operations
- max77686: header: change PMIC_NUM_OF_REGS to MAX77686_NUM_OF_REGS

Changes V3:
- pmic/max77686.c: call pmic_child_node_scan() to bind regulator device
- remove use of pmic platdata
- remove unused endian conversions
- Kconfig: add max77686 pmic entry
---
 drivers/power/Kconfig              |  7 ++++
 drivers/power/pmic/Makefile        |  1 +
 drivers/power/pmic/max77686.c      | 76 ++++++++++++++++++++++++++++++++++++++
 drivers/power/pmic/pmic_max77686.c |  2 +-
 include/power/max77686_pmic.h      |  2 +-
 5 files changed, 86 insertions(+), 2 deletions(-)
 create mode 100644 drivers/power/pmic/max77686.c

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 1e73c7a..c4d4c72 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -66,6 +66,13 @@ config DM_PMIC
 	So the call will looks like below:
 	'pmic_write(regulator->parent, addr, value, len);'
 
+config DM_PMIC_MAX77686
+	bool "Enable Driver Model for PMIC MAX77686"
+	depends on DM_PMIC
+	---help---
+	This config enables implementation of driver-model pmic uclass features
+	for PMIC MAX77686. The driver implements read/write operations/
+
 config DM_REGULATOR
 	bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)"
 	depends on DM
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 985cfdb..242c767 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -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
diff --git a/drivers/power/pmic/max77686.c b/drivers/power/pmic/max77686.c
new file mode 100644
index 0000000..3193c73
--- /dev/null
+++ b/drivers/power/pmic/max77686.c
@@ -0,0 +1,76 @@
+/*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak  <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <i2c.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 <power/max77686_pmic.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int max77686_write(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+	if (dm_i2c_write(dev, reg, buff, len)) {
+		error("write error to device: %p register: %#x!", dev, reg);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int max77686_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+	if (dm_i2c_read(dev, reg, buff, len)) {
+		error("read error from device: %p register: %#x!", dev, reg);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int max77686_bind(struct udevice *pmic)
+{
+	int ret;
+
+	ret = pmic_child_node_scan(pmic, "voltage-regulators",
+					 "regulator-compatible");
+	if (ret < 0)
+		debug("%s: %s subnode scan error: %d.\n", __func__, pmic->name,
+							  ret);
+	else
+		debug("%s: %s subnode found %d childs.\n", __func__, pmic->name,
+							   ret);
+
+	/* Always return success for this device */
+	return 0;
+}
+
+static struct dm_pmic_ops max77686_ops = {
+	.reg_count = MAX77686_NUM_OF_REGS,
+	.read = max77686_read,
+	.write = max77686_write,
+};
+
+static const struct udevice_id max77686_ids[] = {
+	{ .compatible = "maxim,max77686"},
+	{ }
+};
+
+U_BOOT_DRIVER(pmic_max77686) = {
+	.name = "max77686 pmic",
+	.id = UCLASS_PMIC,
+	.of_match = max77686_ids,
+	.bind = max77686_bind,
+	.ops = &max77686_ops,
+};
diff --git a/drivers/power/pmic/pmic_max77686.c b/drivers/power/pmic/pmic_max77686.c
index 95b1a57..1ad810a 100644
--- a/drivers/power/pmic/pmic_max77686.c
+++ b/drivers/power/pmic/pmic_max77686.c
@@ -295,7 +295,7 @@ int pmic_init(unsigned char bus)
 
 	p->name = name;
 	p->interface = PMIC_I2C;
-	p->number_of_regs = PMIC_NUM_OF_REGS;
+	p->number_of_regs = MAX77686_NUM_OF_REGS;
 	p->hw.i2c.tx_num = 1;
 
 	puts("Board PMIC init\n");
diff --git a/include/power/max77686_pmic.h b/include/power/max77686_pmic.h
index b0e4255..fe26d13 100644
--- a/include/power/max77686_pmic.h
+++ b/include/power/max77686_pmic.h
@@ -122,7 +122,7 @@ enum {
 	MAX77686_REG_PMIC_BBAT		= 0x7e,
 	MAX77686_REG_PMIC_32KHZ,
 
-	PMIC_NUM_OF_REGS,
+	MAX77686_NUM_OF_REGS,
 };
 
 /* I2C device address for pmic max77686 */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 11/17] dm: regulator: add max77686 regulator driver
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (9 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 10/17] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:08       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 12/17] dm: regulator: add fixed voltage " Przemyslaw Marczak
                       ` (8 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- change debug() to error()
- code cleanup
- fix data types
- ldo/buck state implementation
- adjust to new uclass api

Changes V3:
- regulator/max77686.c:
  -- adjust to api changes
  -- add separeted drivers for buck and ldo
  -- bind regulators by its compatibles
- Kconfig: add regulator max77686 entry
---
 Makefile                           |   1 +
 drivers/power/Kconfig              |   8 +
 drivers/power/Makefile             |   1 -
 drivers/power/regulator/Makefile   |   8 +
 drivers/power/regulator/max77686.c | 876 +++++++++++++++++++++++++++++++++++++
 include/power/max77686_pmic.h      |  24 +-
 6 files changed, 914 insertions(+), 4 deletions(-)
 create mode 100644 drivers/power/regulator/Makefile
 create mode 100644 drivers/power/regulator/max77686.c

diff --git a/Makefile b/Makefile
index 1b3ebe7..9ecf3bb 100644
--- a/Makefile
+++ b/Makefile
@@ -632,6 +632,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/
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index c4d4c72..97abbf0 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -112,6 +112,14 @@ config DM_REGULATOR
 	Say y here to enable support for the axp221 / axp223 pmic found on most
 	sun6i (A31) / sun8i (A23) boards.
 
+config DM_REGULATOR_MAX77686
+	bool "Enable Driver Model for REGULATOR MAX77686"
+	depends on DM_REGULATOR && DM_PMIC_MAX77686
+	---help---
+	This config enables implementation of driver-model regulator uclass
+	features for REGULATOR MAX77686. The driver implements get/set api for:
+	value, enable and mode.
+
 config AXP221_DCDC1_VOLT
 	int "axp221 dcdc1 voltage"
 	depends on AXP221_POWER
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index a6b7012..f206bdd 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -15,7 +15,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
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
new file mode 100644
index 0000000..9d282e3
--- /dev/null
+++ b/drivers/power/regulator/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (C) 2014 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c
new file mode 100644
index 0000000..496c70a
--- /dev/null
+++ b/drivers/power/regulator/max77686.c
@@ -0,0 +1,876 @@
+/*
+ *  Copyright (C) 2012-2015 Samsung Electronics
+ *
+ *  Rajeshwari Shinde <rajeshwari.s@samsung.com>
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/max77686_pmic.h>
+#include <errno.h>
+#include <dm.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define MODE(_id, _val, _name) { \
+	.id = _id, \
+	.register_value = _val, \
+	.name = _name, \
+}
+
+/* LDO: 1,3,4,5,9,17,18,19,20,21,22,23,24,26,26,27 */
+static struct dm_regulator_mode max77686_ldo_mode_standby1[] = {
+	MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
+	MODE(OPMODE_LPM, MAX77686_LDO_MODE_LPM, "LPM"),
+	MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
+	MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
+};
+
+/* LDO: 2,6,7,8,10,11,12,14,15,16 */
+static struct dm_regulator_mode max77686_ldo_mode_standby2[] = {
+	MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_LDO_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
+	MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
+};
+
+/* Buck: 1 */
+static struct dm_regulator_mode max77686_buck_mode_standby[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+/* Buck: 2,3,4 */
+static struct dm_regulator_mode max77686_buck_mode_lpm[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_LPM, MAX77686_BUCK_MODE_LPM, "LPM"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+/* Buck: 5,6,7,8,9 */
+static struct dm_regulator_mode max77686_buck_mode_onoff[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+static const char max77686_buck_addr[] = {
+	0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38
+};
+
+static int max77686_buck_volt2hex(int buck, int uV)
+{
+	unsigned int hex = 0;
+	unsigned int hex_max = 0;
+
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		/* hex = (uV - 600000) / 12500; */
+		hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP;
+		hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
+		/**
+		 * This uses voltage scaller - temporary not implemented
+		 * so return just 0
+		 */
+		return 0;
+	default:
+		/* hex = (uV - 750000) / 50000; */
+		hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP;
+		hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
+		break;
+	}
+
+	if (hex >= 0 && hex <= hex_max)
+		return hex;
+
+	error("Value: %d uV is wrong for BUCK%d", uV, buck);
+	return -EINVAL;
+}
+
+static int max77686_buck_hex2volt(int buck, int hex)
+{
+	unsigned uV = 0;
+	unsigned int hex_max = 0;
+
+	if (hex < 0)
+		goto bad_hex;
+
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
+		if (hex > hex_max)
+			goto bad_hex;
+
+		/* uV = hex * 12500 + 600000; */
+		uV = hex * MAX77686_BUCK_UV_LSTEP + MAX77686_BUCK_UV_LMIN;
+		break;
+	default:
+		hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
+		if (hex > hex_max)
+			goto bad_hex;
+
+		/* uV = hex * 50000 + 750000; */
+		uV = hex * MAX77686_BUCK_UV_HSTEP + MAX77686_BUCK_UV_HMIN;
+		break;
+	}
+
+	return uV;
+
+bad_hex:
+	error("Value: %#x is wrong for BUCK%d", hex, buck);
+	return -EINVAL;
+}
+
+static int max77686_ldo_volt2hex(int ldo, int uV)
+{
+	unsigned int hex = 0;
+
+	switch (ldo) {
+	case 1:
+	case 2:
+	case 6:
+	case 7:
+	case 8:
+	case 15:
+		hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_LSTEP;
+		/* hex = (uV - 800000) / 25000; */
+		break;
+	default:
+		hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_HSTEP;
+		/* hex = (uV - 800000) / 50000; */
+	}
+
+	if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX)
+		return hex;
+
+	error("Value: %d uV is wrong for LDO%d", uV, ldo);
+	return -EINVAL;
+}
+
+static int max77686_ldo_hex2volt(int ldo, int hex)
+{
+	unsigned int uV = 0;
+
+	if (hex > MAX77686_LDO_VOLT_MAX_HEX)
+		goto bad_hex;
+
+	switch (ldo) {
+	case 1:
+	case 2:
+	case 6:
+	case 7:
+	case 8:
+	case 15:
+		/* uV = hex * 25000 + 800000; */
+		uV = hex * MAX77686_LDO_UV_LSTEP + MAX77686_LDO_UV_MIN;
+		break;
+	default:
+		/* uV = hex * 50000 + 800000; */
+		uV = hex * MAX77686_LDO_UV_HSTEP + MAX77686_LDO_UV_MIN;
+	}
+
+	return uV;
+
+bad_hex:
+	error("Value: %#x is wrong for ldo%d", hex, ldo);
+	return -EINVAL;
+}
+
+static int max77686_ldo_hex2mode(int ldo, int hex)
+{
+	if (hex > MAX77686_LDO_MODE_MASK)
+		return -EINVAL;
+
+	switch (hex) {
+	case MAX77686_LDO_MODE_OFF:
+		return OPMODE_OFF;
+	case MAX77686_LDO_MODE_LPM: /* == MAX77686_LDO_MODE_STANDBY: */
+		/* The same mode values but different meaning for each ldo */
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			return OPMODE_STANDBY;
+		default:
+			return OPMODE_LPM;
+		}
+	case MAX77686_LDO_MODE_STANDBY_LPM:
+		return OPMODE_STANDBY_LPM;
+	case MAX77686_LDO_MODE_ON:
+		return OPMODE_ON;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int max77686_buck_hex2mode(int buck, int hex)
+{
+	if (hex > MAX77686_BUCK_MODE_MASK)
+		return -EINVAL;
+
+	switch (hex) {
+	case MAX77686_BUCK_MODE_OFF:
+		return OPMODE_OFF;
+	case MAX77686_BUCK_MODE_ON:
+		return OPMODE_ON;
+	case MAX77686_BUCK_MODE_STANDBY:
+		switch (buck) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+			return OPMODE_STANDBY;
+		default:
+			return -EINVAL;
+		}
+	case MAX77686_BUCK_MODE_LPM:
+		switch (buck) {
+		case 2:
+		case 3:
+		case 4:
+			return OPMODE_LPM;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int max77686_buck_modes(int buck, struct dm_regulator_mode **modesp)
+{
+	int ret = -EINVAL;
+
+	if (buck < 1 || buck > MAX77686_BUCK_NUM)
+		return ret;
+
+	switch (buck) {
+	case 1:
+		*modesp = max77686_buck_mode_standby;
+		ret = ARRAY_SIZE(max77686_buck_mode_standby);
+	case 2:
+	case 3:
+	case 4:
+		*modesp = max77686_buck_mode_lpm;
+		ret = ARRAY_SIZE(max77686_buck_mode_lpm);
+	default:
+		*modesp = max77686_buck_mode_onoff;
+		ret = ARRAY_SIZE(max77686_buck_mode_onoff);
+	}
+
+	return ret;
+}
+
+static int max77686_ldo_modes(int ldo, struct dm_regulator_mode **modesp)
+{
+	int ret = -EINVAL;
+
+	if (ldo < 1 || ldo > MAX77686_LDO_NUM)
+		return ret;
+
+	switch (ldo) {
+	case 2:
+	case 6:
+	case 7:
+	case 8:
+	case 10:
+	case 11:
+	case 12:
+	case 14:
+	case 15:
+	case 16:
+		*modesp = max77686_ldo_mode_standby2;
+		ret = ARRAY_SIZE(max77686_ldo_mode_standby2);
+	default:
+		*modesp = max77686_ldo_mode_standby1;
+		ret = ARRAY_SIZE(max77686_ldo_mode_standby1);
+	}
+
+	return ret;
+}
+
+static int dev2num(struct udevice *dev)
+{
+	return dev->of_id->data;
+}
+
+static int max77686_ldo_val(struct udevice *dev, int op, int *uV)
+{
+	unsigned int ret, hex, adr;
+	unsigned char val;
+	int ldo;
+
+	if (op == PMIC_OP_GET)
+		*uV = 0;
+
+	ldo = dev2num(dev);
+	if (ldo < 1 || ldo > MAX77686_LDO_NUM) {
+		error("Wrong ldo number: %d", ldo);
+		return -EINVAL;
+	}
+
+	adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
+
+	ret = pmic_read(dev->parent, adr, &val, 1);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= MAX77686_LDO_VOLT_MASK;
+		ret = max77686_ldo_hex2volt(ldo, val);
+		if (ret < 0)
+			return ret;
+		*uV = ret;
+		return 0;
+	}
+
+	hex = max77686_ldo_volt2hex(ldo, *uV);
+	if (hex < 0)
+		return -EINVAL;
+
+	val &= ~MAX77686_LDO_VOLT_MASK;
+	val |= hex;
+	ret = pmic_write(dev->parent, adr, &val, 1);
+
+	return ret;
+}
+
+static int max77686_buck_val(struct udevice *dev, int op, int *uV)
+{
+	unsigned int hex, ret, mask, adr;
+	unsigned char val;
+	int buck;
+
+	buck = dev2num(dev);
+	if (buck < 1 || buck > MAX77686_BUCK_NUM) {
+		error("Wrong buck number: %d", buck);
+		return -EINVAL;
+	}
+
+	if (op == PMIC_OP_GET)
+		*uV = 0;
+
+	/* &buck_out = ctrl + 1 */
+	adr = max77686_buck_addr[buck] + 1;
+
+	/* mask */
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		/* Those uses voltage scallers - will support in the future */
+		mask = MAX77686_BUCK234_VOLT_MASK;
+		return -EPERM;
+	default:
+		mask = MAX77686_BUCK_VOLT_MASK;
+	}
+
+	ret = pmic_read(dev->parent, adr, &val, 1);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= mask;
+		ret = max77686_buck_hex2volt(buck, val);
+		if (ret < 0)
+			return ret;
+		*uV = ret;
+		return 0;
+	}
+
+	hex = max77686_buck_volt2hex(buck, *uV);
+	if (hex < 0)
+		return -EINVAL;
+
+	val &= ~mask;
+	val |= hex;
+	ret = pmic_write(dev->parent, adr, &val, 1);
+
+	return ret;
+}
+
+static int max77686_ldo_mode(struct udevice *dev, int op, int *opmode)
+{
+	unsigned int ret, adr, mode;
+	unsigned char val;
+	int ldo;
+
+	if (op == PMIC_OP_GET)
+		*opmode = -EINVAL;
+
+	ldo = dev2num(dev);
+	if (ldo < 1 || ldo > MAX77686_LDO_NUM) {
+		error("Wrong ldo number: %d", ldo);
+		return -EINVAL;
+	}
+
+	adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
+
+	ret = pmic_read(dev->parent, adr, &val, 1);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= MAX77686_LDO_MODE_MASK;
+		ret = max77686_ldo_hex2mode(ldo, val);
+		if (ret < 0)
+			return ret;
+		*opmode = ret;
+		return 0;
+	}
+
+	/* mode */
+	switch (*opmode) {
+	case OPMODE_OFF:
+		mode = MAX77686_LDO_MODE_OFF;
+		break;
+	case OPMODE_LPM:
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			return -EINVAL;
+		default:
+			mode = MAX77686_LDO_MODE_LPM;
+		}
+		break;
+	case OPMODE_STANDBY:
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			mode = MAX77686_LDO_MODE_STANDBY;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case OPMODE_STANDBY_LPM:
+		mode = MAX77686_LDO_MODE_STANDBY_LPM;
+		break;
+	case OPMODE_ON:
+		mode = MAX77686_LDO_MODE_ON;
+		break;
+	default:
+		mode = 0xff;
+	}
+
+	if (mode == 0xff) {
+		error("Wrong mode: %d for ldo%d", *opmode, ldo);
+		return -EINVAL;
+	}
+
+	val &= ~MAX77686_LDO_MODE_MASK;
+	val |= mode;
+	ret = pmic_write(dev->parent, adr, &val, 1);
+
+	return ret;
+}
+
+static int max77686_ldo_enable(struct udevice *dev, int op, bool *enable)
+{
+	int ret, on_off;
+
+	if (op == PMIC_OP_GET) {
+		ret = max77686_ldo_mode(dev, op, &on_off);
+		if (ret)
+			return ret;
+
+		switch (on_off) {
+		case OPMODE_OFF:
+			*enable = 0;
+			break;
+		case OPMODE_ON:
+			*enable = 1;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else if (op == PMIC_OP_SET) {
+		switch (*enable) {
+		case 0:
+			on_off = OPMODE_OFF;
+			break;
+		case 1:
+			on_off = OPMODE_ON;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = max77686_ldo_mode(dev, op, &on_off);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int max77686_buck_mode(struct udevice *dev, int op, int *opmode)
+{
+	unsigned int ret, mask, adr, mode, mode_shift;
+	unsigned char val;
+	int buck;
+
+	buck = dev2num(dev);
+	if (buck < 1 || buck > MAX77686_BUCK_NUM) {
+		error("Wrong buck number: %d", buck);
+		return -EINVAL;
+	}
+
+	adr = max77686_buck_addr[buck];
+
+	/* mask */
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		mode_shift = MAX77686_BUCK_MODE_SHIFT_2;
+		break;
+	default:
+		mode_shift = MAX77686_BUCK_MODE_SHIFT_1;
+	}
+
+	mask = MAX77686_BUCK_MODE_MASK << mode_shift;
+
+	ret = pmic_read(dev->parent, adr, &val, 1);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= mask;
+		val >>= mode_shift;
+		ret = max77686_buck_hex2mode(buck, val);
+		if (ret < 0)
+			return ret;
+		*opmode = ret;
+		return 0;
+	}
+
+	/* mode */
+	switch (*opmode) {
+	case OPMODE_OFF:
+		mode = MAX77686_BUCK_MODE_OFF;
+		break;
+	case OPMODE_STANDBY:
+		switch (buck) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+			mode = MAX77686_BUCK_MODE_STANDBY << mode_shift;
+			break;
+		default:
+			mode = 0xff;
+		}
+		break;
+	case OPMODE_LPM:
+		switch (buck) {
+		case 2:
+		case 3:
+		case 4:
+			mode = MAX77686_BUCK_MODE_LPM << mode_shift;
+			break;
+		default:
+			mode = 0xff;
+		}
+		break;
+	case OPMODE_ON:
+		mode = MAX77686_BUCK_MODE_ON << mode_shift;
+		break;
+	default:
+		mode = 0xff;
+	}
+
+	if (mode == 0xff) {
+		error("Wrong mode: %d for buck: %d\n", *opmode, buck);
+		return -EINVAL;
+	}
+
+	val &= ~mask;
+	val |= mode;
+	ret = pmic_write(dev->parent, adr, &val, 1);
+
+	return ret;
+}
+
+static int max77686_buck_enable(struct udevice *dev, int op, bool *enable)
+{
+	int ret, on_off;
+
+	if (op == PMIC_OP_GET) {
+		ret = max77686_buck_mode(dev, op, &on_off);
+		if (ret)
+			return ret;
+
+		switch (on_off) {
+		case OPMODE_OFF:
+			*enable = false;
+			break;
+		case OPMODE_ON:
+			*enable = true;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else if (op == PMIC_OP_SET) {
+		switch (*enable) {
+		case 0:
+			on_off = OPMODE_OFF;
+			break;
+		case 1:
+			on_off = OPMODE_ON;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = max77686_buck_mode(dev, op, &on_off);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int max77686_ldo_ofdata_to_platdata(struct udevice *dev)
+{
+	struct dm_regulator_info *info = dev->uclass_priv;
+	int ret;
+
+	ret = regulator_ofdata_to_platdata(dev);
+	if (ret)
+		return ret;
+
+	info->type = REGULATOR_TYPE_LDO;
+	info->mode_count = max77686_ldo_modes(dev2num(dev), &info->mode);
+
+	return 0;
+}
+
+static int ldo_get_value(struct udevice *dev)
+{
+	int uV;
+	int ret;
+
+	ret = max77686_ldo_val(dev, PMIC_OP_GET, &uV);
+	if (ret)
+		return ret;
+
+	return uV;
+}
+
+static int ldo_set_value(struct udevice *dev, int uV)
+{
+	return max77686_ldo_val(dev, PMIC_OP_SET, &uV);
+}
+
+static bool ldo_get_enable(struct udevice *dev)
+{
+	bool enable = false;
+	int ret;
+
+	ret = max77686_ldo_enable(dev, PMIC_OP_GET, &enable);
+	if (ret)
+		return ret;
+
+	return enable;
+}
+
+static int ldo_set_enable(struct udevice *dev, bool enable)
+{
+	return max77686_ldo_enable(dev, PMIC_OP_SET, &enable);
+}
+
+static int ldo_get_mode(struct udevice *dev)
+{
+	int mode;
+	int ret;
+
+	ret = max77686_ldo_mode(dev, PMIC_OP_GET, &mode);
+	if (ret)
+		return ret;
+
+	return mode;
+}
+
+static int ldo_set_mode(struct udevice *dev, int mode)
+{
+	return max77686_ldo_mode(dev, PMIC_OP_SET, &mode);
+}
+
+static int max77686_buck_ofdata_to_platdata(struct udevice *dev)
+{
+	struct dm_regulator_info *info = dev->uclass_priv;
+	int ret;
+
+	ret = regulator_ofdata_to_platdata(dev);
+	if (ret)
+		return ret;
+
+	info->type = REGULATOR_TYPE_BUCK;
+	info->mode_count = max77686_buck_modes(dev2num(dev), &info->mode);
+
+	return 0;
+}
+
+static int buck_get_value(struct udevice *dev)
+{
+	int uV;
+	int ret;
+
+	ret = max77686_buck_val(dev, PMIC_OP_GET, &uV);
+	if (ret)
+		return ret;
+
+	return uV;
+}
+
+static int buck_set_value(struct udevice *dev, int uV)
+{
+	return max77686_buck_val(dev, PMIC_OP_SET, &uV);
+}
+
+static bool buck_get_enable(struct udevice *dev)
+{
+	bool enable = false;
+	int ret;
+
+	ret = max77686_buck_enable(dev, PMIC_OP_GET, &enable);
+	if (ret)
+		return ret;
+
+	return enable;
+}
+
+static int buck_set_enable(struct udevice *dev, bool enable)
+{
+	return max77686_buck_enable(dev, PMIC_OP_SET, &enable);
+}
+
+static int buck_get_mode(struct udevice *dev)
+{
+	int mode;
+	int ret;
+
+	ret = max77686_buck_mode(dev, PMIC_OP_GET, &mode);
+	if (ret)
+		return ret;
+
+	return mode;
+}
+
+static int buck_set_mode(struct udevice *dev, int mode)
+{
+	return max77686_buck_mode(dev, PMIC_OP_SET, &mode);
+}
+
+static const struct dm_regulator_ops max77686_ldo_ops = {
+	.get_value  = ldo_get_value,
+	.set_value  = ldo_set_value,
+	.get_enable = ldo_get_enable,
+	.set_enable = ldo_set_enable,
+	.get_mode   = ldo_get_mode,
+	.set_mode   = ldo_set_mode,
+};
+
+static const struct udevice_id max77686_ldo_ids[] = {
+	{ .compatible = "LDO1", .data = 1 },
+	{ .compatible = "LDO2", .data = 2 },
+	{ .compatible = "LDO3", .data = 3 },
+	{ .compatible = "LDO4", .data = 4 },
+	{ .compatible = "LDO5", .data = 5 },
+	{ .compatible = "LDO6", .data = 6 },
+	{ .compatible = "LDO7", .data = 7 },
+	{ .compatible = "LDO8", .data = 8 },
+	{ .compatible = "LDO9", .data = 9 },
+	{ .compatible = "LDO10", .data = 10 },
+	{ .compatible = "LDO11", .data = 11 },
+	{ .compatible = "LDO12", .data = 12 },
+	{ .compatible = "LDO13", .data = 13 },
+	{ .compatible = "LDO14", .data = 14 },
+	{ .compatible = "LDO15", .data = 15 },
+	{ .compatible = "LDO16", .data = 16 },
+	{ .compatible = "LDO17", .data = 17 },
+	{ .compatible = "LDO18", .data = 18 },
+	{ .compatible = "LDO19", .data = 19 },
+	{ .compatible = "LDO20", .data = 20 },
+	{ .compatible = "LDO21", .data = 21 },
+	{ .compatible = "LDO22", .data = 22 },
+	{ .compatible = "LDO23", .data = 23 },
+	{ .compatible = "LDO24", .data = 24 },
+	{ .compatible = "LDO25", .data = 25 },
+	{ .compatible = "LDO26", .data = 26 },
+	{ },
+};
+
+U_BOOT_DRIVER(max77686_ldo) = {
+	.name = "max77686 ldo",
+	.id = UCLASS_REGULATOR,
+	.ops = &max77686_ldo_ops,
+	.of_match = max77686_ldo_ids,
+	.ofdata_to_platdata = max77686_ldo_ofdata_to_platdata,
+};
+
+static const struct dm_regulator_ops max77686_buck_ops = {
+	.get_value  = buck_get_value,
+	.set_value  = buck_set_value,
+	.get_enable = buck_get_enable,
+	.set_enable = buck_set_enable,
+	.get_mode   = buck_get_mode,
+	.set_mode   = buck_set_mode,
+};
+
+static const struct udevice_id max77686_buck_ids[] = {
+	{ .compatible = "BUCK1", .data = 1 },
+	{ .compatible = "BUCK2", .data = 2 },
+	{ .compatible = "BUCK3", .data = 3 },
+	{ .compatible = "BUCK4", .data = 4 },
+	{ .compatible = "BUCK5", .data = 5 },
+	{ .compatible = "BUCK6", .data = 6 },
+	{ .compatible = "BUCK7", .data = 7 },
+	{ .compatible = "BUCK8", .data = 8 },
+	{ .compatible = "BUCK9", .data = 9 },
+	{ },
+};
+
+U_BOOT_DRIVER(max77686_buck) = {
+	.name = "max77686 buck",
+	.id = UCLASS_REGULATOR,
+	.ops = &max77686_buck_ops,
+	.of_match = max77686_buck_ids,
+	.ofdata_to_platdata = max77686_buck_ofdata_to_platdata,
+};
diff --git a/include/power/max77686_pmic.h b/include/power/max77686_pmic.h
index fe26d13..81c27d8 100644
--- a/include/power/max77686_pmic.h
+++ b/include/power/max77686_pmic.h
@@ -126,7 +126,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,23 +146,29 @@ 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_voltage(struct pmic *p, int buck, ulong uV);
 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
@@ -170,6 +179,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 */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 12/17] dm: regulator: add fixed voltage regulator driver
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (10 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 11/17] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:08       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 13/17] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
                       ` (7 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

This driver implements regulator uclass features for fixed value regulators.
For getting the basic regulator device-tree node constraints, this driver calls
function 'regulator_ofdata_to_platdata()'. The typical fixed regulator node
provides few additional properties:
- gpio
- gpio-open-drain
- enable-active-high
- startup-delay-us
All above are checked and keept in structure of type 'fixed_regulator_priv',
which is private for each fixed-regulator device (dev->priv).

The driver implements only three of regulator uclass features:
- get_value
- get_enable
- set_enable

The regulator calls and command line features can be used for fixed-regulator,
and the proper error will be returned for prohibited.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>

Changes v3:
- new file
- Kconfig add fixed-regulator entry
---
 drivers/power/Kconfig            |   8 +++
 drivers/power/regulator/Makefile |   1 +
 drivers/power/regulator/fixed.c  | 124 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)
 create mode 100644 drivers/power/regulator/fixed.c

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 97abbf0..da1e866 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -120,6 +120,14 @@ config DM_REGULATOR_MAX77686
 	features for REGULATOR MAX77686. The driver implements get/set api for:
 	value, enable and mode.
 
+config DM_REGULATOR_FIXED
+	bool "Enable Driver Model for REGULATOR Fixed value"
+	depends on DM_REGULATOR
+	---help---
+	This config enables implementation of driver-model regulator uclass
+	features for fixed value regulators. The driver implements get/set api
+	for enable and get only for voltage value.
+
 config AXP221_DCDC1_VOLT
 	int "axp221 dcdc1 voltage"
 	depends on AXP221_POWER
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index 9d282e3..0a6a6d9 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -5,4 +5,5 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
+obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o
 obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
diff --git a/drivers/power/regulator/fixed.c b/drivers/power/regulator/fixed.c
new file mode 100644
index 0000000..45e9f84
--- /dev/null
+++ b/drivers/power/regulator/fixed.c
@@ -0,0 +1,124 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics
+ *
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <dm.h>
+#include <asm/gpio.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <errno.h>
+#include <dm.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct fixed_regulator_priv {
+	struct gpio_desc gpio;
+	bool gpio_open_drain;
+	bool enable_active_high;
+	unsigned startup_delay_us;
+};
+
+static int fixed_regulator_ofdata_to_platdata(struct udevice *dev)
+{
+	struct dm_regulator_info *info = dev->uclass_priv;
+	struct fixed_regulator_priv *priv = dev->priv;
+	int ret, offset = dev->of_offset;
+
+	/* Get the basic regulator constraints */
+	ret = regulator_ofdata_to_platdata(dev);
+	if (ret) {
+		error("Can't get regulator constraints for %s", dev->name);
+		return ret;
+	}
+
+	/* Get fixed regulator gpio desc */
+	ret = gpio_request_by_name_nodev(gd->fdt_blob, offset, "gpio", 0,
+					 &priv->gpio, GPIOD_IS_OUT);
+	if (ret) {
+		error("Fixed regulator gpio - not found! Error: %d", ret);
+		return ret;
+	}
+
+	/* Get fixed regulator addidional constraints */
+	priv->gpio_open_drain = fdtdec_get_bool(gd->fdt_blob, offset,
+						"gpio-open-drain");
+	priv->enable_active_high = fdtdec_get_bool(gd->fdt_blob, offset,
+						   "enable-active-high");
+	priv->startup_delay_us = fdtdec_get_int(gd->fdt_blob, offset,
+						"startup-delay-us", 0);
+
+	/* Set type to fixed - used by regulator command */
+	info->type = REGULATOR_TYPE_FIXED;
+
+	debug("%s:%d\n", __func__, __LINE__);
+	debug(" name:%s, boot_on:%d, active_hi: %d start_delay:%u\n",
+		info->name, info->boot_on, priv->enable_active_high,
+		priv->startup_delay_us);
+
+	return 0;
+}
+
+static int fixed_regulator_get_value(struct udevice *dev)
+{
+	struct dm_regulator_info *info;
+	int ret;
+
+	ret = regulator_info(dev, &info);
+	if (ret)
+		return ret;
+
+	if (info->min_uV == info->max_uV)
+		return info->min_uV;
+
+	error("Invalid constraints for: %s\n", info->name);
+
+	return -EINVAL;
+}
+
+static bool fixed_regulator_get_enable(struct udevice *dev)
+{
+	struct fixed_regulator_priv *priv = dev->priv;
+
+	return dm_gpio_get_value(&priv->gpio);
+}
+
+static int fixed_regulator_set_enable(struct udevice *dev, bool enable)
+{
+	struct fixed_regulator_priv *priv = dev->priv;
+	int ret;
+
+	ret = dm_gpio_set_value(&priv->gpio, enable);
+	if (ret) {
+		error("Can't set regulator : %s gpio to: %d\n", dev->name,
+		      enable);
+		return ret;
+	}
+	return 0;
+}
+
+static const struct dm_regulator_ops fixed_regulator_ops = {
+	.get_value  = fixed_regulator_get_value,
+	.get_enable = fixed_regulator_get_enable,
+	.set_enable = fixed_regulator_set_enable,
+};
+
+static const struct udevice_id fixed_regulator_ids[] = {
+	{ .compatible = "regulator-fixed" },
+	{ },
+};
+
+U_BOOT_DRIVER(fixed_regulator) = {
+	.name = "fixed regulator",
+	.id = UCLASS_REGULATOR,
+	.ops = &fixed_regulator_ops,
+	.of_match = fixed_regulator_ids,
+	.ofdata_to_platdata = fixed_regulator_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct fixed_regulator_priv),
+};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 13/17] doc: driver-model: pmic and regulator uclass documentation
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (11 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 12/17] dm: regulator: add fixed voltage " Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:08       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 14/17] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
                       ` (6 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes v2, V3:
- update documentation with the framework api changes
- remove doc file name 'dm' prefix
---
 doc/driver-model/pmic-framework.txt | 350 ++++++++++++++++++++++++++++++++++++
 1 file changed, 350 insertions(+)
 create mode 100644 doc/driver-model/pmic-framework.txt

diff --git a/doc/driver-model/pmic-framework.txt b/doc/driver-model/pmic-framework.txt
new file mode 100644
index 0000000..72651dc
--- /dev/null
+++ b/doc/driver-model/pmic-framework.txt
@@ -0,0 +1,350 @@
+#
+# (C) Copyright 2014-2015 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+PMIC framework based on Driver Model
+====================================
+TOC:
+1. Introduction
+2. How does it work
+3. Pmic driver api
+4. Pmic driver
+5. Pmic command
+6. Regulator driver api
+7. Regulator driver
+8. Regulator command
+
+1. Introduction
+===============
+This is an introduction to driver-model multi uclass PMIC devices support.
+At present it is based on two uclass types:
+
+- UCLASS_PMIC      - basic uclass type for PMIC I/O, which provides common
+                     read/write interface.
+- UCLASS_REGULATOR - additional uclass type for specific PMIC features, which
+                     are various voltage regulators.
+
+New files:
+UCLASS_PMIC:
+- drivers/power/pmic-uclass.c
+- include/power/pmic.h
+UCLASS_REGULATOR:
+- drivers/power/regulator-uclass.c
+- include/power/regulator.h
+
+Commands:
+- lib/cmd_pmic.c
+- lib/cmd_regulator.c
+
+2. How doees it work
+====================
+The Power Management Integrated Circuits (PMIC) are used in embedded systems
+to provide stable, precise and specific voltage power source with over-voltage
+and thermal protection circuits.
+
+The single PMIC can provide various functionalities with single or multiple
+interfaces, like in the example below.
+
+-- SoC
+ |
+ |            ______________________________________
+ | BUS 0     |       Multi interface PMIC IC        |--> LDO out 1
+ | e.g.I2C0  |                                      |--> LDO out N
+ |-----------|---- PMIC device 0 (READ/WRITE ops)   |
+ | or SPI0   |    |_ REGULATOR device (ldo/... ops) |--> BUCK out 1
+ |           |    |_ CHARGER device (charger ops)   |--> BUCK out M
+ |           |    |_ MUIC device (microUSB con ops) |
+ | BUS 1     |    |_ ...                            |---> BATTERY
+ | e.g.I2C1  |                                      |
+ |-----------|---- PMIC device 1 (READ/WRITE ops)   |---> USB in 1
+ . or SPI1   |    |_ RTC device (rtc ops)           |---> USB in 2
+ .           |______________________________________|---> USB out
+ .
+
+Since U-Boot provides driver model features for I2C and SPI bus drivers,
+the PMIC devices should also support this. With the new basic uclass types
+for PMIC I/O and regulator features, PMIC drivers can simply provide common
+features, with multiple interface and instance support.
+
+Basic design assumptions:
+
+- Common I/O api - UCLASS_PMIC
+The main assumption is to use UCLASS_PMIC device to provide I/O interface,
+for devices other uclass types. It is no matter what is the type of device
+physical I/O interface. Usually PMIC devices are using SPI or I2C interface,
+but use of any other interface (e.g. when PMIC is not directly connected
+to the SoC) - is now possible. Drivers can use the same read/write api.
+
+- Common regulator api - UCLASS_REGULATOR
+For setting the attributes of verious types of regulators with common api,
+this uclass can be implemented. This allows to drive the each regulator output
+value, on/off state and custom defined operation modes. It also provides the
+user interface for all operations.
+For the very simple implementation, the regulator drivers are not required,
+so the code could base on pmic read/write only.
+
+When board device-tree file includes pmic subnode and the U_Boot compatible
+driver exists, then the pmic device bind should looks like this:
+
+|_ root - will bind the device for I2C/SPI bus node
+  |_ i2c/spi - should bind a device for pmic node
+    |_ pmic (parent) - should bind child devices for its features
+      |_ regulator (child)
+      |_ charger   (child)
+      |_ other     (child)
+
+Usually PMIC design provides:
+ - single I/O interface (single UCLASS_PMIC driver)
+   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 (UCLASS_PMIC driver for each)
+   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.
+
+3. Pmic driver api
+===================
+To use the pmic API, config: CONFIG_DM_PMIC is required.
+The new driver API is very simple and is based on 'struct dm_pmic_ops',
+which define two basic operations: device read and write.
+
+The platform data is introduced as 'struct pmic_platdata', to keep an info
+about the device interface.
+
+The api is described in file: 'include/power/pmic.h'
+Getting the device:
+- by name  -  int pmic_get(char *name, struct udevice **pmic);
+
+Device I/O interface
+Can be used with UCLASS_PMIC devices:
+- Read from the device 'len' bytes at 'reg' into the buffer:
+  int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len);
+
+- Write to the device 'len' bytes at 'reg' from the buffer:
+  int pmic_write(struct udevice *dev, uint reg, uint8_t *buffer, int len);
+
+4. Pmic driver
+============================
+As an example of the pmic driver, please take a look into the MAX77686 driver
+'drivers/power/pmic/max77686.c' and the description in 'include/power/pmic.h'
+
+The pmic driver can be defined by U_BOOT_DRIVER() macro:
+
+U_BOOT_DRIVER(pmic_max77686) = {
+	.name = "max77686 pmic",
+	.id = UCLASS_PMIC,
+	.of_match = max77686_ids,     - Allows to bind by compatible
+	.bind = max77686_bind,        - Function called at device bind
+	.ops = &max77686_ops,         - Pmic api function calls
+	.platdata_auto_alloc_size = sizeof(struct pmic_platdata),
+};
+
+To bind the pmic device, field '.of_match' is required with proper compatible.
+
+Driver ops:
+.reg_count = MAX77686_NUM_OF_REGS - number of pmic registers
+.read = max77686_read() - allows to use pmic_read()
+.write = max77686_write() - allows to use pmic_write()
+
+Driver bind:
+- max77686_bind(): called on bind and calls pmic_child_node_scan() to bind the
+  childs which are int "voltage-regulators" subnode. The bind is done using
+  "voltage-regulators" property name.
+
+5. Pmic command
+===============
+To use the pmic command, config: CONFIG_DM_PMIC_CMD is required.
+The new pmic command allows to:
+- list pmic devices
+- choose the current device (like the mmc command)
+- read or write the pmic register
+- dump all pmic registers
+
+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 0          - set dev seq 0 as current device
+# pmic dump           - dump the registers of the current pmic dev
+# pmic read 0x0       - read the register at address 0x0
+# pmic write 0x0 0x1  - write 0x1 to the register at addres 0x0
+
+6. Regulator driver API
+===================================
+To use the regulator API, config: CONFIG_DM_REGULATOR is required.
+The api is described in file: 'include/power/regulator.h'
+The api is based on structure types:
+- 'dm_regulator_info' - dev->uc_priv (auto-allocated)
+- 'dm_regulator_mode' - included in regualtor info
+
+Regulator info keeps the constraints taken from the device-tree and also device
+modes set by the driver (single or array).
+
+The fixed-regulator common driver keeps provides private structure type, to keep
+its gpio attributes. The structure type 'fixed_regulator_priv' is keept in field
+'dev->priv' and is allocated by the driver.
+
+6.1 Regulator device-tree node:
+The regulator node should looks like in the example:
+ldo1 {
+        regulator-compatible = "LDO1"; (not used here, but required for bind)
+        regulator-name = "VDD_MMC_1.8V"; (mandatory) *
+        regulator-min-microvolt = <1000000>; (mandatory) *
+        regulator-max-microvolt = <1000000>; (mandatory) *
+        regulator-min-microamp = <1000>; (optional) *
+        regulator-max-microamp = <1000>; (optional) *
+        regulator-always-on; (optional) *
+        regulator-boot-on; (optional) *
+};
+
+For the fixed-voltage regulator, voltage min and max must be equal:
+VDD_MMC: regulator at 0 {
+        compatible = "regulator-fixed";
+        regulator-name = "VDD_MMC"; (mandatory) *
+        regulator-min-microvolt = <2800000>; (mandatory) *
+        regulator-max-microvolt = <2800000>; (mandatory) *
+        regulator-always-on; (optional)
+        regulator-boot-on; (optional)
+        gpio = <&gpc 1 GPIO_ACTIVE_HIGH>; (optional)
+        gpio-open-drain; (optional)
+        enable-active-high; (optional)
+        startup-delay-us
+};
+
+The attributes signed with '*' can be taken by the common function which is
+regulator_ofdata_to_platdata(). The rest attributes for fixed-regulator, are
+taken by the driver.
+
+6.2 Getting the device:
+- by name - int regulator_get(char *name, struct udevice **regulator);
+
+6.3 Device I/O interface
+According to the framework assumptions, where uclass pmic device is a parent
+of pmic devices other uclass types, the regulator devices should use uclass
+pmic interface, with the parent as the device, like this:
+- pmic_read(regulator->parent, some_reg, &some_buff, count);
+- pmic_write(regulator->parent, some_reg, &some_buff, count);
+
+6.4 Device regulator operations
+The regulator function calls are based on few data types:
+- enum regulator_type {...} - standard types: LDO, BUCK, DVS, FIXED
+- struct dm_regulator_info {...} - output name and value limits
+- struct dm_regulator_mode {...} - output operation mode value and name
+- struct dm_regulator_ops {...} - regulator driver function calls
+
+The first argument is always device. And the device uclass id must be always:
+- 'UCLASS_REGULATOR'
+
+Function details are described in regulator header. The basic features are:
+- regulator_info()            - get the regulator info structure
+- regulator_mode()            - get the regulator mode info structure
+- regulator_get/set_value()   - get/set the regulator output voltage
+- regulator_get/set_current() - get/set the regulator output current
+- regulator_get/set_enable()  - get/set the regulator output enable state
+- regulator_get/set_mode()    - get/set the regulator output operation mode
+
+7. Regulator driver
+======================================
+As an example of the regulator driver, please take a look into the MAX77686
+regulator driver (drivers/power/regulator/max77686.c). It implements two drivers
+for the buck and ldo regulators. The buck driver structure:
+
+U_BOOT_DRIVER(max77686_buck) = {
+        .name = "max77686 buck",
+        .id = UCLASS_REGULATOR,
+        .ops = &max77686_buck_ops,
+        .of_match = max77686_buck_ids,
+        .ofdata_to_platdata = max77686_buck_ofdata_to_platdata,
+};
+
+The device-tree buck compatibles:
+static const struct udevice_id max77686_buck_ids[] = {
+        { .compatible = "BUCK1", .data = 1 },
+        { .compatible = "BUCK2", .data = 2 },
+        ...
+        { }, (need always put null at the end)
+};
+
+For each compatible, the data is set as a number of buck. This allows to get
+the device number without parsing the compatible.
+
+The buck driver '.ofdata_to_platdata' call implementation:
+static int max77686_buck_ofdata_to_platdata(struct udevice *dev)
+{
+        struct dm_regulator_info *info = dev->uclass_priv;
+        int ret;
+
+       /* Get the regulation constraints by the common function */
+        ret = regulator_ofdata_to_platdata(dev);
+        if (ret)
+                return ret;
+
+        info->type = REGULATOR_TYPE_BUCK;
+        /**
+         * The device mode array is filled by internal driver function with
+         * the proper name for each mode id.
+         */
+        info->mode_count = max77686_buck_modes(dev2num(dev), &info->mode);
+
+        return 0;
+}
+
+And the call to regulator_ofdata_to_platdata() is responsible for filling the
+regulator output constraints, which are keepts in device-tree regulator nodes,
+e.g. 'arch/arm/dts/exynos4412-odroid.dts'
+
+For the I/O, this driver uses pmic_read/write calls, with the parent device
+as the first argument, e.g.: pmic_read(dev->parent, adr, &val, 1);
+
+8. Regulator command
+====================
+To use the pmic command, config: CONFIG_DM_REGULATOR_CMD is required.
+
+This command is based on driver model regulator api.
+User interface features:
+- list                   - list UCLASS regulator devices
+- regulator dev [id]     - show or [set] operating regulator device
+- regulator [info]       - print constraints info
+- regulator [status]     - print operating status
+- regulator [value] [-f] - print/[set] voltage value [uV] (force)
+- regulator [current]    - print/[set] current value [uA]
+- regulator [mode_id]    - print/[set] operating mode id
+- regulator [enable]     - enable the regulator output
+- regulator [disable]    - disable the regulator output
+
+If pmic device driver provides support to this another pmic uclass, then this
+command provides useful user interface. It was designed to allow safe I/O access
+to the pmic device, without the pmic documentation. If driver provide regulator
+output - value and mode info - then user can operate on it.
+
+Example of usage:
+regulator list              - look after regulator 'seq' number
+regulator dev 'seq'         - set current device
+regulator status            - show device status
+regulator info              - list device available regulation constraints
+regulator value             - prints device voltage value
+regulator value 1000000     - set device voltage value to 1000000 uV
+regulator value 1200000 -f  - if value exceeds limits - set force
+regulator mode              - print device operation mode id
+regulator mode 5            - set devices operation mode to '5' (mode id)
+
+The -f option (forcibly) or mode - only if descriptor is available
+
+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.
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 14/17] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (12 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 13/17] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:09       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 15/17] odroid: board: add support to dm pmic api Przemyslaw Marczak
                       ` (5 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 board/samsung/common/board.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/board/samsung/common/board.c b/board/samsung/common/board.c
index 2e17da8..c4cbb63 100644
--- a/board/samsung/common/board.c
+++ b/board/samsung/common/board.c
@@ -21,9 +21,9 @@
 #include <asm/arch/pinmux.h>
 #include <asm/arch/power.h>
 #include <asm/arch/system.h>
-#include <power/pmic.h>
 #include <asm/arch/sromc.h>
 #include <lcd.h>
+#include <i2c.h>
 #include <samsung/misc.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -168,7 +168,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();
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 15/17] odroid: board: add support to dm pmic api
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (13 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 14/17] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:08       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 16/17] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
                       ` (4 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

This commit change the old pmic framework calls with the new ones.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes v2:
- remove board_init_i2c() call
- update regulator calls
- update headers
- samsung/misc.c: include required header

Changes v3:
- adjust regulator calls to new api
---
 board/samsung/common/misc.c   |   1 +
 board/samsung/odroid/odroid.c | 113 +++++++++++++++++++++++++++++++++---------
 2 files changed, 91 insertions(+), 23 deletions(-)

diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
index 1a77c82..f0d69d4 100644
--- a/board/samsung/common/misc.c
+++ b/board/samsung/common/misc.c
@@ -16,6 +16,7 @@
 #include <asm/arch/cpu.h>
 #include <asm/gpio.h>
 #include <linux/input.h>
+#include <dm.h>
 #include <power/pmic.h>
 #include <mmc.h>
 
diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
index ae41c29..aa3b0ff 100644
--- a/board/samsung/odroid/odroid.c
+++ b/board/samsung/odroid/odroid.c
@@ -12,7 +12,9 @@
 #include <asm/arch/gpio.h>
 #include <asm/gpio.h>
 #include <asm/arch/cpu.h>
+#include <dm.h>
 #include <power/pmic.h>
+#include <power/regulator.h>
 #include <power/max77686_pmic.h>
 #include <errno.h>
 #include <mmc.h>
@@ -405,15 +407,62 @@ static void board_gpio_init(void)
 
 static int pmic_init_max77686(void)
 {
-	struct pmic *p = pmic_get("MAX77686_PMIC");
+	struct udevice *dev;
+	int ret;
 
-	if (pmic_probe(p))
-		return -ENODEV;
+	ret = regulator_get("VDDQ_EMMC_1.8V", &dev);
+	if (ret) {
+		error("Regulator get error: %d", ret);
+		return ret;
+	}
+
+	ret = regulator_set_value(dev, 1800000);
+	if (ret) {
+		error("Regulator %s value setting error: %d", dev->name, ret);
+		return ret;
+	}
+
+	ret = regulator_set_enable(dev, true);
+	if (ret) {
+		error("Regulator %s enable error: %d", dev->name, ret);
+		return ret;
+	}
+
+	ret = regulator_get("TFLASH_2.8V", &dev);
+	if (ret) {
+		error("Regulator get error: %d", ret);
+		return ret;
+	}
+
+	ret = regulator_set_value(dev, 2800000);
+	if (ret) {
+		error("Regulator %s value setting error: %d", dev->name, ret);
+		return ret;
+	}
+
+	ret = regulator_set_enable(dev, true);
+	if (ret) {
+		error("Regulator %s enable error: %d", dev->name, ret);
+		return ret;
+	}
+
+	ret = regulator_get("VDDQ_EMMC_2.8V", &dev);
+	if (ret) {
+		error("Regulator get error: %d", ret);
+		return ret;
+	}
 
-	/* 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 */
+	ret = regulator_set_value(dev, 2800000);
+	if (ret) {
+		error("Regulator %s value setting error: %d", dev->name, ret);
+		return ret;
+	}
+
+	ret = regulator_set_enable(dev, true);
+	if (ret) {
+		error("Regulator %s enable error: %d", dev->name, ret);
+		return ret;
+	}
 
 	return 0;
 }
@@ -434,7 +483,6 @@ int exynos_init(void)
 
 int exynos_power_init(void)
 {
-	pmic_init(0);
 	pmic_init_max77686();
 
 	return 0;
@@ -443,19 +491,20 @@ int exynos_power_init(void)
 #ifdef CONFIG_USB_GADGET
 static int s5pc210_phy_control(int on)
 {
-	struct pmic *p_pmic;
-
-	p_pmic = pmic_get("MAX77686_PMIC");
-	if (!p_pmic)
-		return -ENODEV;
+	struct udevice *dev;
+	int ret;
 
-	if (pmic_probe(p_pmic))
-		return -1;
+	ret = regulator_get("VDD_UOTG_3.0V", &dev);
+	if (ret) {
+		error("Regulator get error: %d", ret);
+		return ret;
+	}
 
 	if (on)
-		return max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
+		return regulator_set_mode(dev, OPMODE_ON);
 	else
-		return max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
+		return regulator_set_mode(dev, OPMODE_LPM);
+
 }
 
 struct s3c_plat_otg_data s5pc210_otg_data = {
@@ -472,7 +521,8 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
 int board_usb_init(int index, enum usb_init_type init)
 {
 #ifdef CONFIG_CMD_USB
-	struct pmic *p_pmic;
+	struct udevice *dev;
+	int ret;
 
 	/* Set Ref freq 0 => 24MHz, 1 => 26MHz*/
 	/* Odroid Us have it at 24MHz, Odroid Xs at 26MHz */
@@ -490,14 +540,31 @@ int board_usb_init(int index, enum usb_init_type init)
 	/* Power off and on BUCK8 for LAN9730 */
 	debug("LAN9730 - Turning power buck 8 OFF and ON.\n");
 
-	p_pmic = pmic_get("MAX77686_PMIC");
-	if (p_pmic && !pmic_probe(p_pmic)) {
-		max77686_set_buck_voltage(p_pmic, 8, 750000);
-		max77686_set_buck_voltage(p_pmic, 8, 3300000);
+	ret = regulator_get("VCC_P3V3_2.85V", &dev);
+	if (ret) {
+		error("Regulator get error: %d", ret);
+		return ret;
 	}
 
-#endif
+	ret = regulator_set_enable(dev, true);
+	if (ret) {
+		error("Regulator %s enable setting error: %d", dev->name, ret);
+		return ret;
+	}
+
+	ret = regulator_set_value(dev, 750000);
+	if (ret) {
+		error("Regulator %s value setting error: %d", dev->name, ret);
+		return ret;
+	}
 
+	ret = regulator_set_value(dev, 3300000);
+	if (ret) {
+		error("Regulator %s value setting error: %d", dev->name, ret);
+		return ret;
+	}
+
+#endif
 	debug("USB_udc_probe\n");
 	return s3c_udc_probe(&s5pc210_otg_data);
 }
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 16/17] odroid: dts: add 'voltage-regulators' description to max77686 node
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (14 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 15/17] odroid: board: add support to dm pmic api Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:10       ` Simon Glass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 17/17] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
                       ` (3 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- odroid: dts: remove pmic alias

Changes V3:
- none
---
 arch/arm/dts/exynos4412-odroid.dts | 247 +++++++++++++++++++++++++++++++++++++
 1 file changed, 247 insertions(+)

diff --git a/arch/arm/dts/exynos4412-odroid.dts b/arch/arm/dts/exynos4412-odroid.dts
index 5507d9a..bc516a0 100644
--- a/arch/arm/dts/exynos4412-odroid.dts
+++ b/arch/arm/dts/exynos4412-odroid.dts
@@ -40,6 +40,253 @@
 			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>;
+				};
+			};
 		};
 	};
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 17/17] odroid: config: enable dm pmic, dm regulator and max77686 driver
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (15 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 16/17] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
@ 2015-03-24 20:30     ` Przemyslaw Marczak
  2015-03-29 13:10       ` Simon Glass
  2015-03-25  7:47     ` [U-Boot] [PATCH v3 00/17] Power(full) framework based on Driver Model Przemyslaw Marczak
                       ` (2 subsequent siblings)
  19 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-24 20:30 UTC (permalink / raw)
  To: u-boot

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

This commits enables:
- CONFIG_ERRNO_STR
- CONFIG_DM_PMIC
- CONFIG_DM_PMIC_CMD
- CONFIG_DM_PMIC_MAX77686
- CONFIG_DM_REGULATOR
- CONFIG_DM_REGULATOR_CMD
- CONFIG_DM_REGULATOR_MAX77686

And removes the unused:
- CONFIG_DM_I2C_COMPAT
- CONFIG_POWER
- CONFIG_POWER_I2C
- CONFIG_POWER_MAX77686

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- config: enable dm i2c; cleanup
- remove CONFIG_DM_I2C_COMPAT
- enable regulator command

Changes V3:
- move options to defconfig
---
 configs/odroid_defconfig | 8 +++++++-
 include/configs/odroid.h | 5 -----
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/configs/odroid_defconfig b/configs/odroid_defconfig
index d32b5b5..1e29abe 100644
--- a/configs/odroid_defconfig
+++ b/configs/odroid_defconfig
@@ -4,5 +4,11 @@ CONFIG_TARGET_ODROID=y
 CONFIG_OF_CONTROL=y
 CONFIG_DEFAULT_DEVICE_TREE="exynos4412-odroid"
 CONFIG_DM_I2C=y
-CONFIG_DM_I2C_COMPAT=y
 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_ERRNO_STR=y
+CONFIG_DM_PMIC=y
+CONFIG_DM_PMIC_CMD=y
+CONFIG_DM_PMIC_MAX77686=y
+CONFIG_DM_REGULATOR=y
+CONFIG_DM_REGULATOR_CMD=y
+CONFIG_DM_REGULATOR_MAX77686=y
diff --git a/include/configs/odroid.h b/include/configs/odroid.h
index 5ee0abe..3874baa 100644
--- a/include/configs/odroid.h
+++ b/include/configs/odroid.h
@@ -182,11 +182,6 @@
 #define CONFIG_SYS_I2C_S3C24X0_SPEED	100000
 #define CONFIG_SYS_I2C_S3C24X0_SLAVE	0
 
-/* POWER */
-#define CONFIG_POWER
-#define CONFIG_POWER_I2C
-#define CONFIG_POWER_MAX77686
-
 /* GPT */
 #define CONFIG_RANDOM_UUID
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 00/17] Power(full) framework based on Driver Model
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (16 preceding siblings ...)
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 17/17] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
@ 2015-03-25  7:47     ` Przemyslaw Marczak
  2015-03-29 13:05     ` Simon Glass
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
  19 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-25  7:47 UTC (permalink / raw)
  To: u-boot

Hello,

On 03/24/2015 09:30 PM, Przemyslaw Marczak wrote:
> Hello,
> Here is the third RFC version of the new PMIC framework.Big thanks to
> Simon Glass, your comments were really helpful, and I think, that this
> version is much more better to discuss, than the previous. The changes
> made in this version are described below each commit. Sorry that I didn't
> reply to each patch, I agreed with most and just started the work.
>
> Best regards
>
> Przemyslaw Marczak (17):
>    exynos5: fix build break by adding CONFIG_POWER
>    fdt_ro.c: add new function: fdt_node_check_prop_compatible()
>    dm: core: lists.c: add new function lists_bind_fdt_by_prop()
>    lib: Kconfig: add entry for errno_str() function
>    dm: pmic: add implementation of driver model pmic uclass
>    dm: regulator: add implementation of driver model regulator uclass
>    dm: pmic: add pmic command
>    dm: regulator: add regulator command
>    pmic: max77686 set the same compatible as in the kernel
>    dm: pmic: add max77686 pmic driver
>    dm: regulator: add max77686 regulator driver
>    dm: regulator: add fixed voltage 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 +++++++++-
>   arch/arm/dts/exynos4412-trats2.dts   |   2 +-
>   arch/arm/dts/exynos5250-smdk5250.dts |   2 +-
>   arch/arm/dts/exynos5250-snow.dts     |   2 +-
>   board/samsung/common/board.c         |   4 +-
>   board/samsung/common/misc.c          |   1 +
>   board/samsung/odroid/odroid.c        | 113 ++++-
>   common/Kconfig                       |  36 ++
>   common/Makefile                      |   4 +
>   common/cmd_pmic.c                    | 210 +++++++++
>   common/cmd_regulator.c               | 385 +++++++++++++++
>   configs/odroid_defconfig             |   8 +-
>   doc/driver-model/pmic-framework.txt  | 350 ++++++++++++++
>   drivers/core/lists.c                 |  28 +-
>   drivers/power/Kconfig                | 124 ++++-
>   drivers/power/Makefile               |   3 +-
>   drivers/power/pmic-uclass.c          | 130 ++++++
>   drivers/power/pmic/Makefile          |   1 +
>   drivers/power/pmic/max77686.c        |  76 +++
>   drivers/power/pmic/pmic_max77686.c   |   2 +-
>   drivers/power/regulator-uclass.c     | 219 +++++++++
>   drivers/power/regulator/Makefile     |   9 +
>   drivers/power/regulator/fixed.c      | 124 +++++
>   drivers/power/regulator/max77686.c   | 876 +++++++++++++++++++++++++++++++++++
>   include/configs/exynos5-common.h     |   4 +
>   include/configs/odroid.h             |   5 -
>   include/dm/lists.h                   |  18 +
>   include/dm/uclass-id.h               |   4 +
>   include/libfdt.h                     |  27 ++
>   include/power/max77686_pmic.h        |  26 +-
>   include/power/pmic.h                 | 210 +++++++++
>   include/power/regulator.h            | 259 +++++++++++
>   lib/Kconfig                          |   8 +
>   lib/fdtdec.c                         |   2 +-
>   lib/libfdt/fdt_ro.c                  |  14 +-
>   36 files changed, 3481 insertions(+), 55 deletions(-)
>   create mode 100644 common/cmd_pmic.c
>   create mode 100644 common/cmd_regulator.c
>   create mode 100644 doc/driver-model/pmic-framework.txt
>   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/fixed.c
>   create mode 100644 drivers/power/regulator/max77686.c
>   create mode 100644 include/power/regulator.h
>

This patchset is available at:
https://github.com/bobenstein/u-boot
branch: dm-pmic-v3

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 02/12] dm: device: add function device_get_first_child_by_uclass_id()
  2015-03-06 14:11     ` Simon Glass
@ 2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-25 16:08 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/06/2015 03:11 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>>
>> 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.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes V2:
>> - new commit
>> ---
>>   drivers/core/device.c | 15 +++++++++++++++
>>   include/dm/device.h   | 16 ++++++++++++++++
>>   2 files changed, 31 insertions(+)
>>
>> diff --git a/drivers/core/device.c b/drivers/core/device.c
>> index 73c3e07..76b22cf 100644
>> --- a/drivers/core/device.c
>> +++ b/drivers/core/device.c
>> @@ -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?
>
>>
>> +                                       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)
>>   {
>> diff --git a/include/dm/device.h b/include/dm/device.h
>> index 7a48eb8..9f0d6ce 100644
>> --- a/include/dm/device.h
>> +++ b/include/dm/device.h
>> @@ -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
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

This function is discarded in the V3.

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 03/12] dm: pmic: add implementation of driver model pmic uclass
  2015-03-06 14:11     ` Simon Glass
@ 2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-25 16:08 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/06/2015 03:11 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> 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
>
> This should have a Kconfig file.
>
>>
>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> index 2145652..5c9a189 100644
>> --- a/drivers/power/Makefile
>> +++ b/drivers/power/Makefile
>> @@ -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
>> diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
>> new file mode 100644
>> index 0000000..309463e
>> --- /dev/null
>> +++ b/drivers/power/pmic-uclass.c
>> @@ -0,0 +1,191 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * 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",
>> +};
>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>> index 91bb90d..ff360ac 100644
>> --- a/include/dm/uclass-id.h
>> +++ b/include/dm/uclass-id.h
>> @@ -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
>
>> +       UCLASS_PMIC,
>> +
>>          UCLASS_COUNT,
>>          UCLASS_INVALID = -1,
>>   };
>> diff --git a/include/power/pmic.h b/include/power/pmic.h
>> index afbc5aa..94171ac 100644
>> --- a/include/power/pmic.h
>> +++ b/include/power/pmic.h
>> @@ -1,4 +1,7 @@
>>   /*
>> + *  Copyright (C) 2014-2015 Samsung Electronics
>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>>    *  Copyright (C) 2011-2012 Samsung Electronics
>>    *  Lukasz Majewski <l.majewski@samsung.com>
>>    *
>> @@ -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],
>> + *                                           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.

>> + * ...
>> + * }
>> + *
>> + * 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
>> + * @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.
>

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

>> +
>> +/**
>> + * 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?
>

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

>> +       int (*write)(struct udevice *pmic, unsigned reg, unsigned char val);
>> +};
>> +
>> +/* enum pmic_op_type - used for various pmic devices operation calls,
>
> Comment style
>
>> + * 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,
>> +};
>> +
>> +/**
>> + * 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
>
>> +{
>> +       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:
>> + * @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.
>

After rework, the V3 doesn't use this.

>> +
>> +/**
>> + * 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"/"--"
>> + *
>> + * @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.
>
>> +
>> +/**
>> + * pmic_read/write: read/write to the UCLASS_PMIC device
>> + *
>> + * 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/ ?
>
>> + */
>> +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)
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thank you for comments.

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 04/12] dm: pmic: add implementation of driver model regulator uclass
  2015-03-06 14:12     ` Simon Glass
@ 2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-25 16:08 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/06/2015 03:12 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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?
>
>> - 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.
>
>>
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> 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
>>
>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> index 5c9a189..a6b7012 100644
>> --- a/drivers/power/Makefile
>> +++ b/drivers/power/Makefile
>> @@ -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
>> diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
>> new file mode 100644
>> index 0000000..6b5c678
>> --- /dev/null
>> +++ b/drivers/power/regulator-uclass.c
>> @@ -0,0 +1,227 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * 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;
>
> This is an error so you should be able to just assert(). Failing that
> let's use -ENXIO as you do below.
>
>> +
>> +       if (!ops->get_cnt)
>> +               return -EPERM;
>
> -ENOSYS for these.
>

Ah, I missed this one, will fix in V4.

>> +
>> +       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",
>> +};
>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>> index ff360ac..0a1e664 100644
>> --- a/include/dm/uclass-id.h
>> +++ b/include/dm/uclass-id.h
>> @@ -37,6 +37,7 @@ enum uclass_id {
>>
>>          /* PMIC uclass and PMIC related uclasses */
>>          UCLASS_PMIC,
>> +       UCLASS_PMIC_REGULATOR,
>>
>>          UCLASS_COUNT,
>>          UCLASS_INVALID = -1,
>> diff --git a/include/power/regulator.h b/include/power/regulator.h
>> new file mode 100644
>> index 0000000..ee8a280
>> --- /dev/null
>> +++ b/include/power/regulator.h
>> @@ -0,0 +1,310 @@
>> +/*
>> + *  Copyright (C) 2014-2015 Samsung Electronics
>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * 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,
>> +};
>
> If we will only ever have on/off perhaps we don't need this enum and
> could just use a bool?
>

Fixed in V3.

>> +
>> +/**
>> + * 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.
>

This suggestion is implemented in V3.

>> + * @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()?
>

Also implemented in V3.

>> +};
>> +
>> +/**
>> + * 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)
>
>> + *
>> + * @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
>
>> + * @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);
>
> get_count
>
> Return count in return value instead of a parameter.
>
>> +
>> +       /**
>> +        * 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);
>
> You could use descp, to indicate it returns a pointer.
>
>> +
>> +       /**
>> +        * 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
>
>> +        * 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?
>
>> +                                  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);
>
> Return value in return value instead of a parameter.
>
>> +       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
>
>> +        *
>> +        * 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.
>
>> +       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);
>
> Return mode in return value.
>
>> +       int (*set_mode)(struct udevice *dev, int type, int number, int mode);
>> +};
>> +
>> +/**
>> + * regulator_get_cnt: returns the number of driver registered regulators.
>
> driver-registered
>
>> + * 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.
>
>> +
>> +/**
>> + * 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.
>
> You may as well return the voltage in the return value, unless it can
> be -ve (I assume not?)
>
>> + */
>> +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);
>
> Return state in return value instead of a parameter.
>
>> +
>> +/**
>> + * 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);
>
> Return mode in return value instead of a parameter.
>
>> +
>> +/**
>> + * 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:
>> + * - 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!
>
>> +
>> +#endif /* _INCLUDE_REGULATOR_H_ */
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thank you,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 04/12] dm: pmic: add implementation of driver model regulator uclass
  2015-03-10 11:41     ` Robert Baldyga
@ 2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-25 16:08 UTC (permalink / raw)
  To: u-boot

Hello Robert,

On 03/10/2015 12:41 PM, Robert Baldyga wrote:
> Hi,
>
> On 03/03/2015 05:24 PM, Przemyslaw Marczak wrote:
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> 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
>>
>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> index 5c9a189..a6b7012 100644
>> --- a/drivers/power/Makefile
>> +++ b/drivers/power/Makefile
>> @@ -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
>> diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
>> new file mode 100644
>> index 0000000..6b5c678
>> --- /dev/null
>> +++ b/drivers/power/regulator-uclass.c
>> @@ -0,0 +1,227 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * 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;
>> +
>> +		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.

>
> Br,
> Robert
>

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 05/12] dm: pmic: new commands: pmic and regulator
  2015-03-06 14:13     ` Simon Glass
@ 2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-25 16:08 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/06/2015 03:13 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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.
>
> This help could go in Kconfig.
>
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> 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
>>
>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> index a6b7012..943b38f 100644
>> --- a/drivers/power/Makefile
>> +++ b/drivers/power/Makefile
>> @@ -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.
>
>> diff --git a/drivers/power/cmd_pmic.c b/drivers/power/cmd_pmic.c
>> new file mode 100644
>> index 0000000..996bfe7
>> --- /dev/null
>> +++ b/drivers/power/cmd_pmic.c
>> @@ -0,0 +1,820 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +#include <common.h>
>> +#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"
>
>> +#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;
>> +}
>> +
>> +/**
>> + * 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?
>
>> +{
>> +       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;
>> +       }
>> +
>> +       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 '.'?
>
>> +
>> +               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
>> +                       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?
>
>> +
>> +               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)
>> +                               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?
>
>> +
>> +       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)
>> +{
>> +       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.
>
>> +
>> +       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.
>
>> +               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;
>> +       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?
>
>> +                                       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)
>> +{
>> +       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?
>
>> +
>> +       if (!set_force && check_reg_val(dev, reg_num, set_val, type))
>> +               return -EPERM;
>> +
>> +       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.
>
>> +}
>> +
>> +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 */
>> +       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?
>
>> +
>> +       /* 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);
>> +       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.
>
>> +               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"
>> +       "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?
>
>> +);
>> +#endif /* CONFIG_DM_REGULATOR_CMD */
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thanks for the suggestions. The commands in the V3 are reimplemented 
into a separated files. The rework of API allowed to make this more clear.

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 07/12] dm: regulator: add max77686 regulator driver
  2015-03-06 14:14     ` Simon Glass
@ 2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-25 16:08 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/06/2015 03:14 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes V2:
>> - change debug() to error()
>> - code cleanup
>> - fix data types
>> - ldo/buck state implementation
>> - adjust to new uclass api
>> ---
>>   Makefile                           |   1 +
>>   drivers/power/Makefile             |   1 -
>>   drivers/power/regulator/Makefile   |   8 +
>>   drivers/power/regulator/max77686.c | 926 +++++++++++++++++++++++++++++++++++++
>>   include/power/max77686_pmic.h      |  24 +-
>>   5 files changed, 956 insertions(+), 4 deletions(-)
>>   create mode 100644 drivers/power/regulator/Makefile
>>   create mode 100644 drivers/power/regulator/max77686.c
>>
>> diff --git a/Makefile b/Makefile
>> index 6da4215..fcb37ae 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -627,6 +627,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/
>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> index 943b38f..7ff1baa 100644
>> --- a/drivers/power/Makefile
>> +++ b/drivers/power/Makefile
>> @@ -15,7 +15,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
>> diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
>> new file mode 100644
>> index 0000000..9d282e3
>> --- /dev/null
>> +++ b/drivers/power/regulator/Makefile
>> @@ -0,0 +1,8 @@
>> +#
>> +# Copyright (C) 2014 Samsung Electronics
>> +# Przemyslaw Marczak <p.marczak@samsung.com>
>> +#
>> +# SPDX-License-Identifier:     GPL-2.0+
>> +#
>> +
>> +obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
>> diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c
>> new file mode 100644
>> index 0000000..711c19c
>> --- /dev/null
>> +++ b/drivers/power/regulator/max77686.c
>> @@ -0,0 +1,926 @@
>> +/*
>> + *  Copyright (C) 2012-2015 Samsung Electronics
>> + *
>> + *  Rajeshwari Shinde <rajeshwari.s@samsung.com>
>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#include <common.h>
>> +#include <fdtdec.h>
>> +#include <i2c.h>
>> +#include <dm.h>
>> +#include <power/pmic.h>
>> +#include <power/regulator.h>
>> +#include <power/max77686_pmic.h>
>> +#include <errno.h>
>> +#include <dm.h>
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +struct max77686_regulator_info {
>> +       int ldo_cnt;
>> +       int buck_cnt;
>> +       struct regulator_value_desc ldo_desc[MAX77686_LDO_NUM + 1];
>
> Why +1? Then let's use MAX77686_LDO_COUNT for the number of LDOs.
>
>> +       struct regulator_value_desc buck_desc[MAX77686_BUCK_NUM + 1];
>> +};
>> +
>> +#define MODE(_mode, _val, _name) { \
>> +       .mode = _mode, \
>> +       .register_value = _val, \
>> +       .name = _name, \
>> +}
>> +
>> +/* LDO: 1,3,4,5,9,17,18,19,20,21,22,23,24,26,26,27 */
>> +static struct regulator_mode_desc max77686_ldo_mode_standby1[] = {
>> +       MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
>> +       MODE(OPMODE_LPM, MAX77686_LDO_MODE_LPM, "LPM"),
>> +       MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
>> +       MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
>> +};
>> +
>> +/* LDO: 2,6,7,8,10,11,12,14,15,16 */
>> +static struct regulator_mode_desc max77686_ldo_mode_standby2[] = {
>> +       MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
>> +       MODE(OPMODE_STANDBY, MAX77686_LDO_MODE_STANDBY, "ON/OFF"),
>> +       MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
>> +       MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
>> +};
>> +
>> +/* Buck: 1 */
>> +static struct regulator_mode_desc max77686_buck_mode_standby[] = {
>> +       MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
>> +       MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
>> +       MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
>> +};
>> +
>> +/* Buck: 2,3,4 */
>> +static struct regulator_mode_desc max77686_buck_mode_lpm[] = {
>> +       MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
>> +       MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
>> +       MODE(OPMODE_LPM, MAX77686_BUCK_MODE_LPM, "LPM"),
>> +       MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
>> +};
>> +
>> +/* Buck: 5,6,7,8,9 */
>> +static struct regulator_mode_desc max77686_buck_mode_onoff[] = {
>> +       MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
>> +       MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
>> +};
>> +
>> +static const char max77686_buck_addr[] = {
>> +       0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38
>> +};
>> +
>> +static int max77686_buck_volt2hex(int buck, int uV)
>> +{
>> +       unsigned int hex = 0;
>> +       unsigned int hex_max = 0;
>> +
>> +       switch (buck) {
>> +       case 2:
>> +       case 3:
>> +       case 4:
>> +               /* hex = (uV - 600000) / 12500; */
>> +               hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP;
>> +               hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
>> +               /**
>> +                * This uses voltage scaller - temporary not implemented
>> +                * so return just 0
>> +                */
>> +               return 0;
>> +       default:
>> +               /* hex = (uV - 750000) / 50000; */
>> +               hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP;
>> +               hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
>> +               break;
>> +       }
>> +
>> +       if (hex >= 0 && hex <= hex_max)
>> +               return hex;
>> +
>> +       error("Value: %d uV is wrong for BUCK%d", uV, buck);
>> +       return -EINVAL;
>> +}
>> +
>> +static int max77686_buck_hex2volt(int buck, int hex)
>> +{
>> +       unsigned uV = 0;
>> +       unsigned int hex_max = 0;
>> +
>> +       if (hex < 0)
>> +               goto bad_hex;
>> +
>> +       switch (buck) {
>> +       case 2:
>> +       case 3:
>> +       case 4:
>> +               hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
>> +               if (hex > hex_max)
>> +                       goto bad_hex;
>> +
>> +               /* uV = hex * 12500 + 600000; */
>> +               uV = hex * MAX77686_BUCK_UV_LSTEP + MAX77686_BUCK_UV_LMIN;
>> +               break;
>> +       default:
>> +               hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
>> +               if (hex > hex_max)
>> +                       goto bad_hex;
>> +
>> +               /* uV = hex * 50000 + 750000; */
>> +               uV = hex * MAX77686_BUCK_UV_HSTEP + MAX77686_BUCK_UV_HMIN;
>> +               break;
>> +       }
>> +
>> +       return uV;
>> +
>> +bad_hex:
>> +       error("Value: %#x is wrong for BUCK%d", hex, buck);
>> +       return -EINVAL;
>> +}
>> +
>> +static int max77686_ldo_volt2hex(int ldo, int uV)
>> +{
>> +       unsigned int hex = 0;
>> +
>> +       switch (ldo) {
>> +       case 1:
>> +       case 2:
>> +       case 6:
>> +       case 7:
>> +       case 8:
>> +       case 15:
>> +               hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_LSTEP;
>> +               /* hex = (uV - 800000) / 25000; */
>> +               break;
>> +       default:
>> +               hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_HSTEP;
>> +               /* hex = (uV - 800000) / 50000; */
>> +       }
>> +
>> +       if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX)
>> +               return hex;
>> +
>> +       error("Value: %d uV is wrong for LDO%d", uV, ldo);
>> +       return -EINVAL;
>> +}
>> +
>> +static int max77686_ldo_hex2volt(int ldo, int hex)
>> +{
>> +       unsigned int uV = 0;
>> +
>> +       if (hex > MAX77686_LDO_VOLT_MAX_HEX)
>> +               goto bad_hex;
>> +
>> +       switch (ldo) {
>> +       case 1:
>> +       case 2:
>> +       case 6:
>> +       case 7:
>> +       case 8:
>> +       case 15:
>> +               /* uV = hex * 25000 + 800000; */
>> +               uV = hex * MAX77686_LDO_UV_LSTEP + MAX77686_LDO_UV_MIN;
>> +               break;
>> +       default:
>> +               /* uV = hex * 50000 + 800000; */
>> +               uV = hex * MAX77686_LDO_UV_HSTEP + MAX77686_LDO_UV_MIN;
>> +       }
>> +
>> +       return uV;
>> +
>> +bad_hex:
>> +       error("Value: %#x is wrong for ldo%d", hex, ldo);
>> +       return -EINVAL;
>> +}
>> +
>> +static int max77686_ldo_hex2mode(int ldo, int hex)
>> +{
>> +       if (hex > MAX77686_LDO_MODE_MASK)
>> +               return -EINVAL;
>> +
>> +       switch (hex) {
>> +       case MAX77686_LDO_MODE_OFF:
>> +               return OPMODE_OFF;
>> +       case MAX77686_LDO_MODE_LPM: /* == MAX77686_LDO_MODE_STANDBY: */
>> +               /* The same mode values but different meaning for each ldo */
>> +               switch (ldo) {
>> +               case 2:
>> +               case 6:
>> +               case 7:
>> +               case 8:
>> +               case 10:
>> +               case 11:
>> +               case 12:
>> +               case 14:
>> +               case 15:
>> +               case 16:
>> +                       return OPMODE_STANDBY;
>> +               default:
>> +                       return OPMODE_LPM;
>> +               }
>> +       case MAX77686_LDO_MODE_STANDBY_LPM:
>> +               return OPMODE_STANDBY_LPM;
>> +       case MAX77686_LDO_MODE_ON:
>> +               return OPMODE_ON;
>> +       default:
>> +               return -EINVAL;
>> +       }
>> +}
>> +
>> +static int max77686_buck_hex2mode(int buck, int hex)
>> +{
>> +       if (hex > MAX77686_BUCK_MODE_MASK)
>> +               return -EINVAL;
>> +
>> +       switch (hex) {
>> +       case MAX77686_BUCK_MODE_OFF:
>> +               return OPMODE_OFF;
>> +       case MAX77686_BUCK_MODE_ON:
>> +               return OPMODE_ON;
>> +       case MAX77686_BUCK_MODE_STANDBY:
>> +               switch (buck) {
>> +               case 1:
>> +               case 2:
>> +               case 3:
>> +               case 4:
>> +                       return OPMODE_STANDBY;
>> +               default:
>> +                       return -EINVAL;
>> +               }
>> +       case MAX77686_BUCK_MODE_LPM:
>> +               switch (buck) {
>> +               case 2:
>> +               case 3:
>> +               case 4:
>> +                       return OPMODE_LPM;
>> +               default:
>> +                       return -EINVAL;
>> +               }
>> +       default:
>> +               return -EINVAL;
>> +       }
>> +}
>> +
>> +static int max77686_get_value_desc(struct udevice *dev, int type, int d_num,
>> +                                  struct regulator_value_desc **desc)
>> +{
>> +       struct max77686_regulator_info *dev_info;
>
> Can we just pass struct max77686_regulator_info into this function
> avoid all this checking? Similar elsewhere it applied.
>
>> +
>> +       if (!dev)
>> +               return -ENODEV;
>> +
>> +       if (!dev->priv)
>> +               return -ENXIO;
>> +
>> +       dev_info = dev->priv;
>> +
>> +       switch (type) {
>> +       case REGULATOR_TYPE_LDO:
>> +               if (d_num < 1 || d_num > 26)
>> +                       return -EINVAL;
>> +
>> +               *desc = &dev_info->ldo_desc[d_num];
>> +               return 0;
>> +       case REGULATOR_TYPE_BUCK:
>> +               if (d_num < 1 || d_num > 9)
>> +                       return -EINVAL;
>> +
>> +               *desc = &dev_info->buck_desc[d_num];
>> +               return 0;
>> +       default:
>> +               return -EPERM;
>> +       }
>> +}
>> +
>> +static int max77686_get_mode_desc_array(struct udevice *dev, int type,
>> +                                       int d_num, int *d_mode_cnt,
>> +                                       struct regulator_mode_desc **desc)
>> +{
>> +       struct max77686_regulator_info *dev_info;
>> +
>> +       if (!dev)
>> +               return -ENODEV;
>> +
>> +       if (!dev->priv)
>> +               return -ENXIO;
>> +
>> +       dev_info = dev->priv;
>> +       *d_mode_cnt = 0;
>> +
>> +       if (type == REGULATOR_TYPE_LDO) {
>> +               if (d_num < 1 || d_num > dev_info->ldo_cnt)
>> +                       return -EINVAL;
>> +
>> +               switch (d_num) {
>> +               case 2:
>> +               case 6:
>> +               case 7:
>> +               case 8:
>> +               case 10:
>> +               case 11:
>> +               case 12:
>> +               case 14:
>> +               case 15:
>> +               case 16:
>> +                       *d_mode_cnt = ARRAY_SIZE(max77686_ldo_mode_standby2);
>> +                       *desc = max77686_ldo_mode_standby2;
>> +                       return 0;
>> +               default:
>> +                       *d_mode_cnt = ARRAY_SIZE(max77686_ldo_mode_standby1);
>> +                       *desc = max77686_ldo_mode_standby1;
>> +                       return 0;
>> +               }
>> +       } else if (type == REGULATOR_TYPE_BUCK) {
>> +               if (d_num < 1 || d_num > dev_info->buck_cnt)
>> +                       return -EINVAL;
>> +
>> +               switch (d_num) {
>> +               case 1:
>> +                       *d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_standby);
>> +                       *desc = max77686_buck_mode_standby;
>> +                       return 0;
>> +               case 2:
>> +               case 3:
>> +               case 4:
>> +                       *d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_lpm);
>> +                       *desc = max77686_buck_mode_lpm;
>> +                       return 0;
>> +               default:
>> +                       *d_mode_cnt = ARRAY_SIZE(max77686_buck_mode_onoff);
>> +                       *desc = max77686_buck_mode_onoff;
>> +                       return 0;
>> +               }
>> +       }
>> +
>> +       return -EINVAL;
>> +}
>> +
>> +static int max77686_ldo_val(struct udevice *dev, int op,
>> +                               int ldo, int *uV)
>> +{
>> +       unsigned int ret, hex, adr;
>> +       unsigned char val;
>> +
>> +       if (op == PMIC_OP_GET)
>> +               *uV = 0;
>> +
>> +       if (ldo < 1 || ldo > 26) {
>> +               error("Wrong ldo number: %d", ldo);
>> +               return -EINVAL;
>> +       }
>> +
>> +       adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
>> +
>> +       ret = pmic_read(dev->parent, adr, &val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (op == PMIC_OP_GET) {
>> +               val &= MAX77686_LDO_VOLT_MASK;
>> +               ret = max77686_ldo_hex2volt(ldo, val);
>> +               if (ret < 0)
>> +                       return ret;
>> +               *uV = ret;
>> +               return 0;
>> +       }
>> +
>> +       hex = max77686_ldo_volt2hex(ldo, *uV);
>> +       if (hex < 0)
>> +               return -EINVAL;
>> +
>> +       val &= ~MAX77686_LDO_VOLT_MASK;
>> +       val |= hex;
>> +       ret |= pmic_write(dev->parent, adr, val);
>
> You should not | error values together as you will get nonsense. Try:
>
> ret = pmic_write()
> if (ret)
>    return ret
>
> or
>
> if (ret)
>     goto err:
>
> /* more code *.
>
> err:
>    return ret;
>
>> +
>> +       return ret;
>> +}
>> +
>> +static int max77686_buck_val(struct udevice *dev, int op,
>> +                               int buck, int *uV)
>> +{
>> +       unsigned int hex, ret, mask, adr;
>> +       unsigned char val;
>> +
>> +       if (buck < 1 || buck > 9) {
>> +               error("Wrong buck number: %d", buck);
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (op == PMIC_OP_GET)
>> +               *uV = 0;
>> +
>> +       /* &buck_out = ctrl + 1 */
>> +       adr = max77686_buck_addr[buck] + 1;
>> +
>> +       /* mask */
>> +       switch (buck) {
>> +       case 2:
>> +       case 3:
>> +       case 4:
>> +               /* Those uses voltage scallers - will support in the future */
>> +               mask = MAX77686_BUCK234_VOLT_MASK;
>> +               return -EPERM;
>> +       default:
>> +               mask = MAX77686_BUCK_VOLT_MASK;
>> +       }
>> +
>> +       ret = pmic_read(dev->parent, adr, &val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (op == PMIC_OP_GET) {
>> +               val &= mask;
>> +               ret = max77686_buck_hex2volt(buck, val);
>> +               if (ret < 0)
>> +                       return ret;
>> +               *uV = ret;
>> +               return 0;
>> +       }
>> +
>> +       hex = max77686_buck_volt2hex(buck, *uV);
>> +       if (hex < 0)
>> +               return -EINVAL;
>> +
>> +       val &= ~mask;
>> +       val |= hex;
>> +       ret = pmic_write(dev->parent, adr, val);
>> +
>> +       return ret;
>> +}
>> +
>> +static int max77686_ldo_mode(struct udevice *dev, int op, int ldo, int *opmode)
>> +{
>> +       unsigned int ret, adr, mode;
>> +       unsigned char val;
>> +
>> +       if (op == PMIC_OP_GET)
>> +               *opmode = -EINVAL;
>> +
>> +       if (ldo < 1 || ldo > 26) {
>> +               error("Wrong ldo number: %d", ldo);
>> +               return -EINVAL;
>> +       }
>> +
>> +       adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
>> +
>> +       ret = pmic_read(dev->parent, adr, &val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (op == PMIC_OP_GET) {
>> +               val &= MAX77686_LDO_MODE_MASK;
>> +               ret = max77686_ldo_hex2mode(ldo, val);
>> +               if (ret < 0)
>> +                       return ret;
>> +               *opmode = ret;
>> +               return 0;
>> +       }
>> +
>> +       /* mode */
>> +       switch (*opmode) {
>> +       case OPMODE_OFF:
>> +               mode = MAX77686_LDO_MODE_OFF;
>> +               break;
>> +       case OPMODE_LPM:
>> +               switch (ldo) {
>> +               case 2:
>> +               case 6:
>> +               case 7:
>> +               case 8:
>> +               case 10:
>> +               case 11:
>> +               case 12:
>> +               case 14:
>> +               case 15:
>> +               case 16:
>> +                       return -EINVAL;
>> +               default:
>> +                       mode = MAX77686_LDO_MODE_LPM;
>> +               }
>> +               break;
>> +       case OPMODE_STANDBY:
>> +               switch (ldo) {
>> +               case 2:
>> +               case 6:
>> +               case 7:
>> +               case 8:
>> +               case 10:
>> +               case 11:
>> +               case 12:
>> +               case 14:
>> +               case 15:
>> +               case 16:
>> +                       mode = MAX77686_LDO_MODE_STANDBY;
>> +                       break;
>> +               default:
>> +                       return -EINVAL;
>> +               }
>> +               break;
>> +       case OPMODE_STANDBY_LPM:
>> +               mode = MAX77686_LDO_MODE_STANDBY_LPM;
>> +               break;
>> +       case OPMODE_ON:
>> +               mode = MAX77686_LDO_MODE_ON;
>> +               break;
>> +       default:
>> +               mode = 0xff;
>> +       }
>> +
>> +       if (mode == 0xff) {
>> +               error("Wrong mode: %d for ldo%d", *opmode, ldo);
>> +               return -EINVAL;
>> +       }
>> +
>> +       val &= ~MAX77686_LDO_MODE_MASK;
>> +       val |= mode;
>> +       ret |= pmic_write(dev->parent, adr, val);
>> +
>> +       return ret;
>> +}
>> +
>> +static int max77686_ldo_state(struct udevice *dev, int op, int ldo, int *state)
>> +{
>> +       int ret, on_off;
>> +
>> +       if (op == PMIC_OP_GET) {
>> +               ret = max77686_ldo_mode(dev, op, ldo, &on_off);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               switch (on_off) {
>> +               case OPMODE_OFF:
>> +                       *state = 0;
>> +                       break;
>> +               case OPMODE_ON:
>> +                       *state = 1;
>> +                       break;
>> +               default:
>> +                       return -EINVAL;
>> +               }
>> +       } else if (op == PMIC_OP_SET) {
>> +               switch (*state) {
>> +               case 0:
>> +                       on_off = OPMODE_OFF;
>> +                       break;
>> +               case 1:
>> +                       on_off = OPMODE_ON;
>> +                       break;
>> +               default:
>> +                       return -EINVAL;
>> +               }
>> +
>> +               ret = max77686_ldo_mode(dev, op, ldo, &on_off);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int max77686_buck_mode(struct udevice *dev, int op, int buck,
>> +                             int *opmode)
>> +{
>> +       unsigned int ret, mask, adr, mode, mode_shift;
>> +       unsigned char val;
>> +
>> +       if (buck < 1 || buck > 9) {
>> +               error("Wrong buck number: %d", buck);
>> +               return -EINVAL;
>> +       }
>> +
>> +       adr = max77686_buck_addr[buck];
>> +
>> +       /* mask */
>> +       switch (buck) {
>> +       case 2:
>> +       case 3:
>> +       case 4:
>> +               mode_shift = MAX77686_BUCK_MODE_SHIFT_2;
>> +               break;
>> +       default:
>> +               mode_shift = MAX77686_BUCK_MODE_SHIFT_1;
>> +       }
>> +
>> +       mask = MAX77686_BUCK_MODE_MASK << mode_shift;
>> +
>> +       ret = pmic_read(dev->parent, adr, &val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (op == PMIC_OP_GET) {
>> +               val &= mask;
>> +               val >>= mode_shift;
>> +               ret = max77686_buck_hex2mode(buck, val);
>> +               if (ret < 0)
>> +                       return ret;
>> +               *opmode = ret;
>> +               return 0;
>> +       }
>> +
>> +       /* mode */
>> +       switch (*opmode) {
>> +       case OPMODE_OFF:
>> +               mode = MAX77686_BUCK_MODE_OFF;
>> +               break;
>> +       case OPMODE_STANDBY:
>> +               switch (buck) {
>> +               case 1:
>> +               case 2:
>> +               case 3:
>> +               case 4:
>> +                       mode = MAX77686_BUCK_MODE_STANDBY << mode_shift;
>> +                       break;
>> +               default:
>> +                       mode = 0xff;
>> +               }
>> +               break;
>> +       case OPMODE_LPM:
>> +               switch (buck) {
>> +               case 2:
>> +               case 3:
>> +               case 4:
>> +                       mode = MAX77686_BUCK_MODE_LPM << mode_shift;
>> +                       break;
>> +               default:
>> +                       mode = 0xff;
>> +               }
>> +               break;
>> +       case OPMODE_ON:
>> +               mode = MAX77686_BUCK_MODE_ON << mode_shift;
>> +               break;
>> +       default:
>> +               mode = 0xff;
>> +       }
>> +
>> +       if (mode == 0xff) {
>> +               error("Wrong mode:%d for buck%d\n", *opmode, buck);
>> +               return -EINVAL;
>> +       }
>> +
>> +       val &= ~mask;
>> +       val |= mode;
>> +       ret |= pmic_write(dev->parent, adr, val);
>> +
>> +       return ret;
>> +}
>> +
>> +static int max77686_buck_state(struct udevice *dev, int op,
>> +                              int buck, int *state)
>> +{
>> +       int ret, on_off;
>> +
>> +       if (op == PMIC_OP_GET) {
>> +               ret = max77686_buck_mode(dev, op, buck, &on_off);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               switch (on_off) {
>> +               case OPMODE_OFF:
>> +                       *state = 0;
>> +                       break;
>> +               case OPMODE_ON:
>> +                       *state = 1;
>> +                       break;
>> +               default:
>> +                       return -EINVAL;
>> +               }
>> +       } else if (op == PMIC_OP_SET) {
>> +               switch (*state) {
>> +               case 0:
>> +                       on_off = OPMODE_OFF;
>> +                       break;
>> +               case 1:
>> +                       on_off = OPMODE_ON;
>> +                       break;
>> +               default:
>> +                       return -EINVAL;
>> +               }
>> +
>> +               ret = max77686_buck_mode(dev, op, buck, &on_off);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +int fill_reg_desc(int offset, struct regulator_value_desc *desc, int reg_num,
>> +                 int desc_type)
>> +{
>> +       const void *blob = gd->fdt_blob;
>> +       char *reg_name;
>> +       int reg_min, reg_max, len;
>> +
>> +       desc->type = desc_type;
>> +       desc->number = reg_num;
>> +
>> +       reg_name = (char *)fdt_getprop(blob, offset, "regulator-name", &len);
>
> You can drop the cast.
>
>> +       if (reg_name) {
>> +               strncpy(&desc->name[0], reg_name, DESC_NAME_LEN);
>> +               desc->name[DESC_NAME_LEN - 1] = '\0';
>> +       } else {
>> +               sprintf(&desc->name[0], "-");
>> +       }
>> +
>> +       reg_min = fdtdec_get_int(blob, offset, "regulator-min-microvolt", -1);
>> +       desc->min_uV = reg_min;
>> +
>> +       reg_max = fdtdec_get_int(blob, offset, "regulator-min-microvolt", -1);
>> +       desc->max_uV = reg_max;
>> +
>> +       return 0;
>> +}
>> +
>> +static int get_desc_for_compat(int regulators_node, char *compat, int desc_type,
>> +                              struct regulator_value_desc *desc, int desc_num)
>> +{
>> +       const void *blob = gd->fdt_blob;
>> +       const char *reg_compat;
>> +       int compat_len = strlen(compat);
>> +       int reg_num;
>> +       int offset;
>> +       int cnt = 0;
>> +
>> +       /* First subnode of "voltage_regulators" node */
>> +       offset = fdt_first_subnode(blob, regulators_node);
>> +
>> +       while (offset > 0) {
>> +               /* Get regulator properties */
>> +               reg_compat = (char *)fdt_getprop(blob, offset,
>> +                            "regulator-compatible", NULL);
>> +
>> +               /* Check compat and compare the number only */
>> +               reg_num = 0;
>> +               if (strstr(reg_compat, compat))
>> +                       reg_num = simple_strtoul(reg_compat + compat_len,
>> +                                                NULL, 0);
>> +               else
>> +                       goto next_offset;
>> +
>> +               if (reg_num && reg_num <= desc_num)
>> +                       fill_reg_desc(offset, &desc[reg_num], reg_num,
>> +                                     desc_type);
>> +
>> +               cnt++;
>> +               if (cnt == desc_num)
>> +                       return cnt;
>> +
>> +next_offset:
>> +               offset = fdt_next_subnode(blob, offset);
>> +       }
>> +
>> +       return cnt;
>> +}
>> +
>> +static int reg_max77686_ofdata_to_platdata(struct udevice *dev)
>> +{
>> +       struct max77686_regulator_info *dev_info = dev->priv;
>> +       const void *blob = gd->fdt_blob;
>> +       int node = dev->of_offset;
>> +       int offset;
>> +
>> +       if (node < 0) {
>> +               error("Device: %s - bad of_offset", dev->name);
>> +               return 0;
>> +       }
>> +
>> +       offset = fdt_subnode_offset(blob, node,  "voltage-regulators");
>> +       if (offset < 0) {
>> +               error("Node %s has no 'voltage-regulators' subnode", dev->name);
>> +               return 0;
>> +       }
>> +
>> +       dev_info->ldo_cnt = get_desc_for_compat(offset, "LDO", REGULATOR_TYPE_LDO,
>> +                                               dev_info->ldo_desc,
>> +                                               MAX77686_LDO_NUM);
>> +
>> +       dev_info->buck_cnt = get_desc_for_compat(offset, "BUCK", REGULATOR_TYPE_BUCK,
>> +                                                dev_info->buck_desc,
>> +                                                MAX77686_BUCK_NUM);
>
> Do you think these should be child devices?
>
>> +
>> +       /* Even if there is no voltage regulators node in dts,
>
> Comment style
>
>> +        * always return success and allow the driver to bind.
>> +        * Then we can still do read/write pmic registers.
>> +        */
>> +       return 0;
>> +
>> +}
>> +
>> +static int max77686_get_cnt(struct udevice *dev, int type, int *cnt)
>> +{
>> +       switch (type) {
>> +       case REGULATOR_TYPE_LDO:
>> +               *cnt = MAX77686_LDO_NUM;
>> +               break;
>> +       case REGULATOR_TYPE_BUCK:
>> +               *cnt = MAX77686_BUCK_NUM;
>> +               break;
>> +       default:
>> +               *cnt = 0;
>> +               return -EPERM;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +
>> +static int max77686_get_value(struct udevice *dev, int type, int number,
>> +                             int *value)
>> +{
>> +       switch (type) {
>> +       case REGULATOR_TYPE_LDO:
>> +               return max77686_ldo_val(dev, PMIC_OP_GET, number, value);
>> +       case REGULATOR_TYPE_BUCK:
>> +               return max77686_buck_val(dev, PMIC_OP_GET, number, value);
>> +       default:
>> +               return -EPERM;
>> +       }
>> +}
>> +
>> +static int max77686_set_value(struct udevice *dev, int type, int number,
>> +                             int value)
>> +{
>> +       switch (type) {
>> +       case REGULATOR_TYPE_LDO:
>> +               return max77686_ldo_val(dev, PMIC_OP_SET, number, &value);
>> +       case REGULATOR_TYPE_BUCK:
>> +               return max77686_buck_val(dev, PMIC_OP_SET, number, &value);
>> +       default:
>> +               return -EPERM;
>> +       }
>> +}
>> +
>> +static int max77686_get_state(struct udevice *dev, int type, int number,
>> +                             int *state)
>> +{
>> +       switch (type) {
>> +       case REGULATOR_TYPE_LDO:
>> +               return max77686_ldo_state(dev, PMIC_OP_GET, number, state);
>> +       case REGULATOR_TYPE_BUCK:
>> +               return max77686_buck_state(dev, PMIC_OP_GET, number, state);
>> +       default:
>> +               *state = -1;
>> +               return -EPERM;
>> +       }
>> +}
>> +
>> +static int max77686_set_state(struct udevice *dev, int type, int number,
>> +                             int state)
>> +{
>> +       switch (type) {
>> +       case REGULATOR_TYPE_LDO:
>> +               return max77686_ldo_state(dev, PMIC_OP_SET, number, &state);
>> +       case REGULATOR_TYPE_BUCK:
>> +               return max77686_buck_state(dev, PMIC_OP_SET, number, &state);
>> +       default:
>> +               return -EPERM;
>> +       }
>> +}
>> +
>> +static int max77686_get_mode(struct udevice *dev, int type, int number,
>> +                            int *mode)
>> +{
>> +       switch (type) {
>> +       case REGULATOR_TYPE_LDO:
>> +               return max77686_ldo_mode(dev, PMIC_OP_GET, number, mode);
>> +       case REGULATOR_TYPE_BUCK:
>> +               return max77686_buck_mode(dev, PMIC_OP_GET, number, mode);
>> +       default:
>> +               return -EPERM;
>> +       }
>> +}
>> +
>> +static int max77686_set_mode(struct udevice *dev, int type, int number,
>> +                            int mode)
>> +{
>> +       switch (type) {
>> +       case REGULATOR_TYPE_LDO:
>> +               return max77686_ldo_mode(dev, PMIC_OP_SET, number, &mode);
>> +       case REGULATOR_TYPE_BUCK:
>> +               return max77686_buck_mode(dev, PMIC_OP_SET, number, &mode);
>> +       default:
>> +               return -EPERM;
>> +       }
>> +}
>> +
>> +static const struct dm_regulator_ops reg_max77686_ops = {
>> +       .get_cnt                = max77686_get_cnt,
>> +       .get_value_desc         = max77686_get_value_desc,
>> +       .get_mode_desc_array    = max77686_get_mode_desc_array,
>> +       .get_value              = max77686_get_value,
>> +       .set_value              = max77686_set_value,
>> +       .get_state              = max77686_get_state,
>> +       .set_state              = max77686_set_state,
>> +       .get_mode               = max77686_get_mode,
>> +       .set_mode               = max77686_set_mode,
>> +};
>> +
>> +U_BOOT_DRIVER(max77686_regulator) = {
>> +       .name = "max77686 regulator",
>> +       .id = UCLASS_PMIC_REGULATOR,
>> +       .ops = &reg_max77686_ops,
>> +       .ofdata_to_platdata = reg_max77686_ofdata_to_platdata,
>> +       .priv_auto_alloc_size = sizeof(struct max77686_regulator_info),
>> +};
>> diff --git a/include/power/max77686_pmic.h b/include/power/max77686_pmic.h
>> index fe26d13..81c27d8 100644
>> --- a/include/power/max77686_pmic.h
>> +++ b/include/power/max77686_pmic.h
>> @@ -126,7 +126,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,23 +146,29 @@ 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_voltage(struct pmic *p, int buck, ulong uV);
>>   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
>> @@ -170,6 +179,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 */
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

After the rework in the V3, the binding is done by each regulator 
compatible, so now each ldo and each buck are independent childs. And 
the regulator file implements two drivers(ldo and buck).
Thanks for the suggestions!

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 08/12] doc: driver-model: pmic and regulator uclass documentation
  2015-03-06 14:14     ` Simon Glass
@ 2015-03-25 16:08       ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-25 16:08 UTC (permalink / raw)
  To: u-boot

Hello,

On 03/06/2015 03:14 PM, Simon Glass wrote:

[...]

>
> This is a good and thorough overview. Can we drop the 'dm-' prefix on
> the filename and word-wrap it a bit more consistently towards the end
> (lots of short lines at present). Some of the phrasing is a bit hard
> to parse, but we can figure out that later.
>
> Regards,
> Simon
>

The V3 version of this file, is updated with the new framework changes, 
I will compress the lines in the next patch version.

-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 10/12] odroid: board: add support to dm pmic api
  2015-03-06 14:14     ` Simon Glass
@ 2015-03-25 16:09       ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-25 16:09 UTC (permalink / raw)
  To: u-boot

Hello,

On 03/06/2015 03:14 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This commit change the old pmic framework calls with the new ones.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes v2:
>> - remove board_init_i2c() call
>> - update regulator calls
>> - update headers
>> - samsung/misc.c: include required header
>> ---
>>   board/samsung/common/misc.c   |  1 +
>>   board/samsung/odroid/odroid.c | 52 ++++++++++++++++++++++++++-----------------
>>   2 files changed, 33 insertions(+), 20 deletions(-)
>>
>> diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
>> index 4538ac7..18d71e8 100644
>> --- a/board/samsung/common/misc.c
>> +++ b/board/samsung/common/misc.c
>> @@ -16,6 +16,7 @@
>>   #include <asm/arch/cpu.h>
>>   #include <asm/gpio.h>
>>   #include <linux/input.h>
>> +#include <dm.h>
>>   #include <power/pmic.h>
>>   #include <mmc.h>
>>
>> diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
>> index bff6ac9..2448cde 100644
>> --- a/board/samsung/odroid/odroid.c
>> +++ b/board/samsung/odroid/odroid.c
>> @@ -12,7 +12,9 @@
>>   #include <asm/arch/gpio.h>
>>   #include <asm/gpio.h>
>>   #include <asm/arch/cpu.h>
>> +#include <dm.h>
>>   #include <power/pmic.h>
>> +#include <power/regulator.h>
>>   #include <power/max77686_pmic.h>
>>   #include <errno.h>
>>   #include <usb.h>
>> @@ -402,15 +404,23 @@ static void board_gpio_init(void)
>>
>>   static int pmic_init_max77686(void)
>>   {
>> -       struct pmic *p = pmic_get("MAX77686_PMIC");
>> +       struct udevice *reg;
>> +       int type;
>>
>> -       if (pmic_probe(p))
>> +       if (regulator_get("max77686", &reg)) {
>> +               error("Regulator get error\n");
>>                  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 */
>> +       type = REGULATOR_TYPE_LDO;
>> +       regulator_set_value(reg, type, 20, 1800000);    /* LDO20 eMMC */
>> +       regulator_set_value(reg, type, 21, 2800000);    /* LDO21 SD */
>> +       regulator_set_value(reg, type, 22, 2800000);    /* LDO22 eMMC */
>> +
>> +       regulator_set_mode(reg, type, 20, OPMODE_ON);
>> +       regulator_set_mode(reg, type, 21, OPMODE_ON);
>> +       regulator_set_mode(reg, type, 22, OPMODE_ON);
>
> These 20, 21, 22 values seem bad to me. Do we not have names? Also I
> see in the device tree that these regulators have the same min and max
> voltage, so perhaps you can use that voltage automatically?
>
> I think when you change the LDOs to devices you might end up with:
>
> struct udevice *ldo;
>
> ret = regulator_get("VCCQ_MMC2_2.8V", &ldo);   // use the regulator
> device name which comes from regulator-name property.
> if (ret) {
>     debug(...)
>     goto err;
> }
> ret = regulator_set_voltage(ldo, 1800000);
> ...error check
> ret = regulator_set_mode(ldo, OPMODE_ON);
> ...error check
>

The above suggestion is implemented in V3.

>>
>>          return 0;
>>   }
>> @@ -435,7 +445,6 @@ int exynos_init(void)
>>
>>   int exynos_power_init(void)
>>   {
>> -       pmic_init(0);
>>          pmic_init_max77686();
>>
>>          return 0;
>> @@ -444,19 +453,21 @@ int exynos_power_init(void)
>>   #ifdef CONFIG_USB_GADGET
>>   static int s5pc210_phy_control(int on)
>>   {
>> -       struct pmic *p_pmic;
>> +       struct udevice *reg;
>> +       int type;
>>
>> -       p_pmic = pmic_get("MAX77686_PMIC");
>> -       if (!p_pmic)
>> +       if (regulator_get("max77686", &reg)) {
>> +               error("Regulator get error\n");
>>                  return -ENODEV;
>> +       }
>>
>> -       if (pmic_probe(p_pmic))
>> -               return -1;
>> +       type = REGULATOR_TYPE_LDO;
>>
>>          if (on)
>> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
>> +               return regulator_set_mode(reg, type, 12, OPMODE_ON);
>>          else
>> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
>> +               return regulator_set_mode(reg, type, 12, OPMODE_LPM);
>> +
>>   }
>>
>>   struct s3c_plat_otg_data s5pc210_otg_data = {
>> @@ -473,7 +484,8 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
>>   int board_usb_init(int index, enum usb_init_type init)
>>   {
>>   #ifdef CONFIG_CMD_USB
>> -       struct pmic *p_pmic;
>> +       struct udevice *reg;
>> +       int type, ret;
>>
>>          /* Set Ref freq 0 => 24MHz, 1 => 26MHz*/
>>          /* Odroid Us have it at 24MHz, Odroid Xs at 26MHz */
>> @@ -491,14 +503,14 @@ int board_usb_init(int index, enum usb_init_type init)
>>          /* Power off and on BUCK8 for LAN9730 */
>>          debug("LAN9730 - Turning power buck 8 OFF and ON.\n");
>>
>> -       p_pmic = pmic_get("MAX77686_PMIC");
>> -       if (p_pmic && !pmic_probe(p_pmic)) {
>> -               max77686_set_buck_voltage(p_pmic, 8, 750000);
>> -               max77686_set_buck_voltage(p_pmic, 8, 3300000);
>> +       if (regulator_get("max77686", &reg)) {
>> +               type = REGULATOR_TYPE_BUCK;
>> +               ret = regulator_set_value(reg, type, 8, 750000);
>> +               ret |= regulator_set_value(reg, type, 8, 3300000);
>> +               if (ret)
>> +                       error("Can't set regulator\n");
>
> We should return a proper error number. Also print it here as it might
> provide clues.
>
>>          }
>> -
>>   #endif
>> -
>>          debug("USB_udc_probe\n");
>>          return s3c_udc_probe(&s5pc210_otg_data);
>>   }
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model
  2015-03-10  2:12     ` Simon Glass
@ 2015-03-25 16:09       ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-03-25 16:09 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/10/2015 03:12 AM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 6 March 2015 at 07:10, Simon Glass <sjg@chromium.org> wrote:
>> Hi Przemyslaw,
>>
>> On 3 March 2015 at 09:24, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>>> 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
>>
>> This is an impressive pieces of work! It will be great to get these in.
>>
>> Here are some high-level comments on this series:
>>

So, just as a summary of V3 rework.

>> 1. I think regulator LDOs and bucks should be proper devices (struct
>> udevice), bound by the pmic when it is probed. The advantages are
>>
>> a. You can see them in the device list - the pmic will end up with a
>> lot more children
This is done.

>> b. You can use the same regulator uclass for each, but have different
>> operations for each driver (e.g. max77686 might provide two different
>> drivers, one for LDO, one for buck).
This is done.

>> c. Things like your 'switch (type)' in max7786_get_state() etc. will go away
This is done.

>> d. You can perform operations on them without having to specify their
>> parent and number - e.g. regulator_set_mode(struct udevice *ldo, int
>> mode) which is much more natural for users
This is done.

>> e. You avoid needing your own list of regulators and bucks - struct
>> max7786_regulator_info. After all, keeping track of child devices is
>> something that driver model can do
This is done. Now, each regulator device keeps the common regulator 
constraints as uclass private data (dev->uclass_priv) and the additional 
constraints for the fixed-regulator (gpio constraints) are kept as the 
device private data (dev->priv).

>>
>> 2. I see device tree support, but the Linux device tree bindings are
>> not fully supported, e.g. the regulators sub-node uses
>> regulator-compatible instead of regulator-name. I think it should be
>> exactly the same (and we should copy the device tree files, only
>> leaving out what we don't support)
This is done. Since the compatible is stored in a different property 
than just 'compatible', I added the function to bind devices by custom 
compatible property name. So regulators are bind by 
'regulator-compatible' property compatibles

>>
>> 3. The I2C/SPI difference is a bit clunky. We should try to hide this
>> away. The most obvious problem is in getting the device. Instead of
>> pmic_i2c_get() we should use the "power-supply" property in the device
>> tree, so we need a function which can find the regulator given the
>> device node (a bit like gpio_request_by_name() but for PMICs). The
>> pmic_get() function is OK and will be needed also, as I am sure we
>> will use names in some places. We should remove any mention of the bus
>> type from the API I think. Also regulator number seems and odd concept
>> - better to use the name/device tree link to find the right device.
This is done partially. My time was limited. The bus type and address is 
now removed from the api. I think that the 'pmic_get()' by the name 
should be ok, when we have different device-tree names for each pmic 
intarface.
 From the other side this is not so needed, since we are going to 
provide the device features by driver-model uclass drivers.
This will take some time, so I add some extended function for getting 
the pmic in the next version.
The 'power-supply' seem to be right for all devices, I will work on it 
in the next version.

>> One way to avoid I2C/SPI problems is to create a helper file which
>> allows you to read and write registers given a struct udevice. It
>> could look at whether the device is I2C or SPI and do the right thing.
>> This could be generally useful, not just for PMICs.
Yes, this would be useful, and actually will make something like the 
relationship for the pmic device and it's regulator child.

>>
>> 4. Should use Kconfig now.
This is done.

>
> Sorry I don't know how I forgot to mention this:
>
> 5. A test PMIC device for sandbox so that the tests can run (test/dm/pmic.c).
This one, will be task for the future.

>
> Regards,
> Simon
>

Thank you again for the helpful suggestions!

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 00/17] Power(full) framework based on Driver Model
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (17 preceding siblings ...)
  2015-03-25  7:47     ` [U-Boot] [PATCH v3 00/17] Power(full) framework based on Driver Model Przemyslaw Marczak
@ 2015-03-29 13:05     ` Simon Glass
  2015-04-03 16:11       ` Przemyslaw Marczak
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
  19 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:05 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello,
> Here is the third RFC version of the new PMIC framework.Big thanks to
> Simon Glass, your comments were really helpful, and I think, that this
> version is much more better to discuss, than the previous. The changes
> made in this version are described below each commit. Sorry that I didn't
> reply to each patch, I agreed with most and just started the work.

This is looking really good. Here are a few overall comments.

1. There is one oddity that I'd like to address before merging.

I don't think the fdt_node_check_prop_compatible() is a good idea, nor
necessary. I don't think we should consider the regulator-compatible
property to be a compatible string. It has values like LDO8, LDO9 and
these don't look like compatible strings, which are normally unique
and point to a driver. Here they point to a particular device.

A similar problem is faced in pinctrl and if you look at
gpio_exynos_bind() you will see that it works through the sub-nodes,
creating devices as needed.

I don't think using udevice_id is right here either.

Here is my suggestion:

a. Create a new structure like this:

struct pmic_child_info {
   const char *prefix;   // "LDO" or "BUCK"
   const char *driver_name;   // "max77686_ldo" or "max77686_buck"
};

b. Pass a list of these to pmic_child_node_scan(). In your case there
will be three entries, one for LDO, one for BUCK, plus a NULL
termination entry,

c. It can work through the subnodes looking for the given prefixes. It
then calls device_bind_driver() on each. Then it changes the returned
device's of_data to hold the correct value (obtained with strtol() on
the part of the name that follows the prefix - e.g. 17 for "LDO17").
This will be easier if you rebase on u-boot-dm/usb-working, where the
data is just a long, not a device tree pointer.

d. Now you have the same effect as before, but you can drop the tables
like max77686_ldo_ids[] and avoid misappropriating driver model's
device lookup.

2. Should we put the regulator stuff in drivers/regulator, as with Linux?

3. Can you please bring in the regulator and pmic device tree binding
files, plus max77686?

4. We really do need tests! I suspect that you could create a sandbox
I2C pmic that has a few registers and regulators. See
i2c_eeprom_emul.c for a basic example. Then you can write some tests
that find the pmi,c find the regulator, read and write a few
registers, and read and write a few regulators. That would be enough
test coverage to get us started. I know this is different from
previous U-Boot policy, but tests are a big win during development and
also for years to come (as people can change the framework and have
some confidence that they did not break anything).

It can be a follow-on patch separate from your series but I'm really
not keen on bringing in a major new driver model framework with no
tests. If you are struggling for time, let me know and I can try to
help by writing a sandbox I2C PMIC for example.

Regards,
Simon

>
> Best regards
>
> Przemyslaw Marczak (17):
>   exynos5: fix build break by adding CONFIG_POWER
>   fdt_ro.c: add new function: fdt_node_check_prop_compatible()
>   dm: core: lists.c: add new function lists_bind_fdt_by_prop()
>   lib: Kconfig: add entry for errno_str() function
>   dm: pmic: add implementation of driver model pmic uclass
>   dm: regulator: add implementation of driver model regulator uclass
>   dm: pmic: add pmic command
>   dm: regulator: add regulator command
>   pmic: max77686 set the same compatible as in the kernel
>   dm: pmic: add max77686 pmic driver
>   dm: regulator: add max77686 regulator driver
>   dm: regulator: add fixed voltage 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 +++++++++-
>  arch/arm/dts/exynos4412-trats2.dts   |   2 +-
>  arch/arm/dts/exynos5250-smdk5250.dts |   2 +-
>  arch/arm/dts/exynos5250-snow.dts     |   2 +-
>  board/samsung/common/board.c         |   4 +-
>  board/samsung/common/misc.c          |   1 +
>  board/samsung/odroid/odroid.c        | 113 ++++-
>  common/Kconfig                       |  36 ++
>  common/Makefile                      |   4 +
>  common/cmd_pmic.c                    | 210 +++++++++
>  common/cmd_regulator.c               | 385 +++++++++++++++
>  configs/odroid_defconfig             |   8 +-
>  doc/driver-model/pmic-framework.txt  | 350 ++++++++++++++
>  drivers/core/lists.c                 |  28 +-
>  drivers/power/Kconfig                | 124 ++++-
>  drivers/power/Makefile               |   3 +-
>  drivers/power/pmic-uclass.c          | 130 ++++++
>  drivers/power/pmic/Makefile          |   1 +
>  drivers/power/pmic/max77686.c        |  76 +++
>  drivers/power/pmic/pmic_max77686.c   |   2 +-
>  drivers/power/regulator-uclass.c     | 219 +++++++++
>  drivers/power/regulator/Makefile     |   9 +
>  drivers/power/regulator/fixed.c      | 124 +++++
>  drivers/power/regulator/max77686.c   | 876 +++++++++++++++++++++++++++++++++++
>  include/configs/exynos5-common.h     |   4 +
>  include/configs/odroid.h             |   5 -
>  include/dm/lists.h                   |  18 +
>  include/dm/uclass-id.h               |   4 +
>  include/libfdt.h                     |  27 ++
>  include/power/max77686_pmic.h        |  26 +-
>  include/power/pmic.h                 | 210 +++++++++
>  include/power/regulator.h            | 259 +++++++++++
>  lib/Kconfig                          |   8 +
>  lib/fdtdec.c                         |   2 +-
>  lib/libfdt/fdt_ro.c                  |  14 +-
>  36 files changed, 3481 insertions(+), 55 deletions(-)
>  create mode 100644 common/cmd_pmic.c
>  create mode 100644 common/cmd_regulator.c
>  create mode 100644 doc/driver-model/pmic-framework.txt
>  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/fixed.c
>  create mode 100644 drivers/power/regulator/max77686.c
>  create mode 100644 include/power/regulator.h
>
> --
> 1.9.1
>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 01/17] exynos5: fix build break by adding CONFIG_POWER
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 01/17] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
@ 2015-03-29 13:05       ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:05 UTC (permalink / raw)
  To: u-boot

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  include/configs/exynos5-common.h | 4 ++++
>  1 file changed, 4 insertions(+)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 02/17] fdt_ro.c: add new function: fdt_node_check_prop_compatible()
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 02/17] fdt_ro.c: add new function: fdt_node_check_prop_compatible() Przemyslaw Marczak
@ 2015-03-29 13:07       ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:07 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This commit:
> - moves fdt_node_check_compatible() code to fdt_node_check_prop_compatible()
> - adds call to fdt_node_check_prop_compatible() in fdt_node_check_compatible()
>   with 'compatible' as the name of compatible property.
>
> The function: fdt_node_check_compatible() - works the same as previous.
> The function fdt_node_check_prop_compatible() - allows for checking compatible
> string in given property name.
>
> If some fdt node uses different name for compatible property, than 'compatible',
> then the function fdt_node_check_prop_compatible() can be used with the custom
> compatible property name as an argument.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>

I doubt this would be accepted by libfdt upstream. I don't think we
need it - see my cover letter response.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 03/17] dm: core: lists.c: add new function lists_bind_fdt_by_prop()
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 03/17] dm: core: lists.c: add new function lists_bind_fdt_by_prop() Przemyslaw Marczak
@ 2015-03-29 13:07       ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:07 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This change adds new function: lists_bind_fdt_by_prop(), which can be used
> for bind the devices by custom property name for the compatible string.
>
> The function lists_bind_fdt() works the same as previous.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  drivers/core/lists.c | 28 +++++++++++++++++++---------
>  include/dm/lists.h   | 18 ++++++++++++++++++
>  2 files changed, 37 insertions(+), 9 deletions(-)

I think we should do this another way - see my cover letter response.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 04/17] lib: Kconfig: add entry for errno_str() function
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 04/17] lib: Kconfig: add entry for errno_str() function Przemyslaw Marczak
@ 2015-03-29 13:07       ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:07 UTC (permalink / raw)
  To: u-boot

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  lib/Kconfig | 8 ++++++++
>  1 file changed, 8 insertions(+)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 05/17] dm: pmic: add implementation of driver model pmic uclass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 05/17] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
@ 2015-03-29 13:07       ` Simon Glass
  2015-04-03 16:08         ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:07 UTC (permalink / raw)
  To: u-boot

Hi Prazemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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.

Your Kconfig docs are a model to others! It describes the function
very nicely without a lot of words.

Please excuse the nits, they are intended to help it read better.

>
> 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/driver-model/pmic-framework.txt
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> 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
>
> Changes V3:
> - pmic-uclass.c and pmic.h:
>   -- remove  pmic_if_* functions
>   -- add new function pmic_child_node_scan()
> - add Kconfig entry
> ---
>  drivers/power/Kconfig       |  68 ++++++++++++++
>  drivers/power/Makefile      |   1 +
>  drivers/power/pmic-uclass.c | 130 +++++++++++++++++++++++++++
>  include/dm/uclass-id.h      |   3 +
>  include/power/pmic.h        | 210 ++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 412 insertions(+)
>  create mode 100644 drivers/power/pmic-uclass.c
>
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index f8f0239..3513b46 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -1,3 +1,71 @@
> +config DM_PMIC
> +       bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC)"
> +       depends on DM
> +       ---help---
> +       This config enables the driver-model multi uclass PMIC support.
> +       Its basic uclass type is: UCLASS_PMIC, which is designed to provide
> +       a common I/O interface for pmic child devices of various uclass types.

Should be consistent - and use PMIC instead pmic.

> +
> +       Usually PMIC IC's provides more than one functionality, which means

s/functionality/function/

> +       that we should implement new uclass operations for each one. Usually
> +       PMIC's provide those various functionalities by one or more interfaces.

functions

> +       And this could looks like this:
> +
> +       root device
> +       |_ BUS 0 device (e.g. I2C0)                 - UCLASS_I2C/SPI/...
> +       | |_ PMIC device (READ/WRITE ops)           - UCLASS_PMIC
> +       |   |  (pmic sub-devices)
> +       |   |_ REGULATOR device (ldo/buck/... ops)  - UCLASS_REGULATOR
> +       |   |_ CHARGER device (charger ops)         - UCLASS_CHARGER (future)
> +       |   |_ MUIC device (microUSB connector ops) - UCLASS_MUIC    (future)
> +       |   |_ ...
> +       |
> +       |_ BUS 1 device (e.g. I2C1)                 - UCLASS_I2C/SPI/...
> +          |_ PMIC device (READ/WRITE ops)          - UCLASS_PMIC
> +            |  (pmic sub-devices)
> +            |_ RTC device (rtc ops)                - UCLASS_MUIC (future)

Would this be UCLASS_RTC?

> +
> +       From the I/O interface point of view, there can be found two PMIC types:
> +       - single I/O interface - then UCLASS_PMIC device should be a parent of
> +         all pmic sub-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

Remove the word 'only'?

> +       nodes/subnodes are proper defined, then:

how about a blank line here?

> +       |_ 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:
> +             |  (pmic sub-devices)
> +             |_ regulator (child)
> +             |_ charger   (child)
> +             |_ other     (child)
> +
> +       The same for other I/O bus nodes, if pmic uses more then one interface.
> +
> +       Note:
> +       Each PMIC interface driver should use different compatible string.

use a different

> +
> +       There are few basic functions in the UCLASS_PMIC driver API, declared

There are a few

> +       in the file 'include/power/pmic.h':
> +        - int pmic_get(...);
> +        - int pmic_read(struct udevice *pmic, ...);
> +        - int pmic_write(struct udevice *pmic, ...);
> +       For the simple implementation, in some cases the pmic uclass device,
> +       can be self-sufficient to drive the PMIC functionality. In other case,

s/self-sufficient/enough/

> +       if each pmic sub-device(child) driver need access to the pmic specified

PMIC-specified

> +       registers, it need to know only the register address and then the access

s/to//

> +       is done through the parent pmic driver. Like in the example:

s/is/can be/

For example: (blank line here?)

> +       _ root driver
> +       |_ dev: bus I2C0                                   - UCLASS_I2C
> +       | |_ dev: my_pmic        (read/write)     (parent) - UCLASS_PMIC
> +       |   |_ dev: my_regulator (set value/etc.) (child)  - UCLASS_REGULATOR
> +       So the call will looks like below:
> +       'pmic_write(regulator->parent, addr, value, len);'
> +
>  config AXP221_POWER
>         boolean "axp221 / axp223 pmic support"
>         depends on MACH_SUN6I || MACH_SUN8I
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index 2145652..5c9a189 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -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
> diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
> new file mode 100644
> index 0000000..df49a4b
> --- /dev/null
> +++ b/drivers/power/pmic-uclass.c
> @@ -0,0 +1,130 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * 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;
> +
> +int pmic_get(char *name, struct udevice **devp)
> +{
> +       struct udevice *dev;
> +       int ret;
> +
> +       *devp = NULL;
> +
> +       for (ret = uclass_first_device(UCLASS_PMIC, &dev);
> +            dev;
> +            ret = uclass_next_device(&dev)) {
> +               if (!strcmp(name, dev->name)) {
> +                       *devp = dev;
> +                       return ret;
> +               }
> +       }
> +
> +       return -ENODEV;

This function will probe every PMIC, looking for the correct one.

I think you should change pmic_get() to call a core function in
uclass.c, like uclass_get_device_by_name(). It should use
uclass_foreach_dev() so that it doesn't probe anything except the one
it finds.You can use uclass_get_device_tail() at the end of that
function - see uclass_get_device_by_of_offset() as an example.

> +}
> +
> +int pmic_reg_count(struct udevice *dev)
> +{
> +       const struct dm_pmic_ops *ops;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC);
> +       if (!ops)
> +               return -ENODEV;
> +
> +       return ops->reg_count;
> +}
> +
> +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len)
> +{
> +       const struct dm_pmic_ops *ops;
> +
> +       if (!buffer)
> +               return -EFAULT;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC);
> +       if (!ops)
> +               return -ENODEV;

-ENOSYS, although IMO assert() would be OK.

> +
> +       if (!ops->read)
> +               return -EPERM;

-ENOSYS

> +
> +       if (ops->read(dev, reg, buffer, len))
> +               return -EIO;

We must not swallow errors.

ret = ops->read(...)
if (ret)
   return ret;

Please adjust below also.

> +
> +       return 0;
> +}
> +
> +int pmic_write(struct udevice *dev, uint reg, uint8_t *buffer, int len)
> +{
> +       const struct dm_pmic_ops *ops;
> +
> +       if (!buffer)
> +               return -EFAULT;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC);
> +       if (!ops)
> +               return -ENODEV;
> +
> +       if (!ops->write)
> +               return -EPERM;
> +
> +       if (ops->write(dev, reg, buffer, len))
> +               return -EIO;
> +
> +       return 0;
> +}
> +
> +int pmic_child_node_scan(struct udevice *parent,
> +                        const char *optional_subnode,
> +                        const char *compatible_property_name)
> +{
> +       const void *blob = gd->fdt_blob;
> +       struct udevice *childp;
> +       int offset = parent->of_offset;
> +       int childs = 0;
> +       int ret;
> +
> +       debug("%s: bind child for %s\n", __func__, parent->name);
> +
> +       if (optional_subnode) {
> +               debug("Looking for %s optional subnode: %s \n", parent->name,
> +                     optional_subnode);
> +               offset = fdt_subnode_offset(blob, offset, optional_subnode);
> +               if (offset <= 0) {
> +                       debug("Pmic: %s subnode: %s not found!",
> +                             parent->name, optional_subnode);
> +                       return -ENXIO;
> +               }
> +       }
> +
> +       for (offset = fdt_first_subnode(blob, offset); offset > 0;
> +            offset = fdt_next_subnode(blob, offset)) {
> +               ret = lists_bind_fdt_by_prop(parent, blob, offset,
> +                                            compatible_property_name, &childp);
> +               if (ret)
> +                       continue;
> +
> +               childs++;
> +       }
> +
> +       return childs;
> +}
> +
> +UCLASS_DRIVER(pmic) = {
> +       .id             = UCLASS_PMIC,
> +       .name           = "pmic",
> +};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index 91bb90d..3ecfa23 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -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 uclass types */

This comment seemed confusing - is it for PMICs or other things also?

> +       UCLASS_PMIC,
> +
>         UCLASS_COUNT,
>         UCLASS_INVALID = -1,
>  };
> diff --git a/include/power/pmic.h b/include/power/pmic.h
> index afbc5aa..b55304f 100644
> --- a/include/power/pmic.h
> +++ b/include/power/pmic.h
> @@ -1,4 +1,7 @@
>  /*
> + *  Copyright (C) 2014-2015 Samsung Electronics
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
>   *  Copyright (C) 2011-2012 Samsung Electronics
>   *  Lukasz Majewski <l.majewski@samsung.com>
>   *
> @@ -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,210 @@ 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.

I worry about having the docs in multiple places. Should you adjust
this to point to the Kconfig? Or change the Kconfig to point here? Or
maybe it would be better to drop both and put these in your README? I
am concerned that if we later change something, we end up with
inconsistent docs.

> + *
> + * 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 the example below. It should be by the call
> + * to 'pmic_child_node_scan()' - it allows bind in optional subnode and with
> + * optional compatible property, e.g. "regulator-compatible" or "regulator".
> + *
> + * my_pmic.c - pmic driver:
> + *
> + * int my_pmic_bind(struct udevice *my_pmic)
> + * {
> + * ...
> + *      ret = pmic_child_node_scan(my_pmic, NULL, "compatible");
> + *                 ...
> + * ...
> + * }
> + *
> + * 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.
> + */
> +
> +/**
> + * 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.
> + *
> + * @reg_count: devices register count
> + * @read:      read 'len' bytes at "reg" and store it into the 'buffer'
> + * @write:     write 'len' bytes from the 'buffer' to the register at 'reg' address
> + */
> +struct dm_pmic_ops {
> +       int reg_count;
> +       int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int len);
> +       int (*write)(struct udevice *dev, uint reg, uint8_t *buffer, int len);

nit: const uint8_t *buffer?

> +};
> +
> +/* enum pmic_op_type - used for various pmic devices operation calls,
> + * 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,
> +};
> +
> +/**
> + * 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)
> +{
> +       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_child_node_scan - scan the pmic node or its 'optional_subnode' for its
> + * childs, by the compatible property name given by arg 'compatible_prop_name'.
> + *
> + * Few dts files with a different pmic regulators, uses different property
> + * name for its driver 'compatible' string, like:
> + * - 'compatible'
> + * 'regulator-compatible'
> + * The pmic driver should should bind its devices by proper compatible property
> + * name.
> + *
> + * @parent                   - pmic parent device (usually UCLASS_PMIC)
> + * @optional_subnode         - optional pmic subnode with the childs within it
> + * @compatible_property_name - e.g.: 'compatible'; 'regulator-compatible', ...

@return

> + */
> +int pmic_child_node_scan(struct udevice *parent,
> +                        const char *optional_subnode,
> +                        const char *compatible_property_name);
> +
> +/**
> + * pmic_get: get the pmic device using its name
> + *
> + * @name - device name
> + * @devp - returned pointer to the pmic device
> + * Returns: 0 on success or negative value of errno.

* @return 0 on success or negative value of errno.

> + *
> + * The returned devp device can be used with pmic_read/write calls
> + */
> +int pmic_get(char *name, struct udevice **devp);
> +
> +/**
> + * pmic_reg_count: get the pmic register count
> + *
> + * The required pmic device can be obtained by 'pmic_get()'
> + *
> + * @dev - pointer to the UCLASS_PMIC device
> + *
> + * Returns: register count value on success or negative value of errno.
> + */
> +int pmic_reg_count(struct udevice *dev);
> +
> +/**
> + * pmic_read/write: read/write to the UCLASS_PMIC device
> + *
> + * The required pmic device can be obtained by 'pmic_get()'
> + *
> + * @pmic - pointer to the UCLASS_PMIC device
> + * @reg  - device register offset
> + * @val  - value for write or its pointer to read

This doesn't match the functions.

> + *
> + * Returns: 0 on success or negative value of errno.
> + */
> +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len);
> +int pmic_write(struct udevice *dev, uint reg, uint8_t *buffer, int len);

const uint8_t buffer for pmic_write()?

> +#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 +297,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)
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass Przemyslaw Marczak
@ 2015-03-29 13:07       ` Simon Glass
  2015-04-03 16:09         ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:07 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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.
>
> To get the regulator device:
> - regulator_get()             - get the regulator device
>
> The regulator framework is based on a 'struct dm_regulator_ops'.
> It provides a common function calls, for it's basic features:
> - regulator_info()            - get the regulator info structure
> - regulator_mode()            - get the regulator mode info structure
> - regulator_get/set_value()   - get/set the regulator output voltage
> - regulator_get/set_current() - get/set the regulator output current
> - regulator_get/set_enable()  - get/set the regulator output enable state
> - regulator_get/set_mode()    - get/set the regulator output operation mode
>
> An optional and useful regulator framework features are two descriptors:
> - struct dm_regulator_info- describes the regulator name and output value limits
>
> - struct dm_regulator_mode - (array) describes the regulators operation modes
>
> 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
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> 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
>
> Changes V3:
> - regulator-uclass.c and regulator.h:
>   -- api cleanup
>   -- new function regulator_ofdata_to_platdata()
>   -- update of comments
>   -- add Kconfig
> ---
>  drivers/power/Kconfig            |  33 ++++-
>  drivers/power/Makefile           |   1 +
>  drivers/power/regulator-uclass.c | 219 +++++++++++++++++++++++++++++++++
>  include/dm/uclass-id.h           |   1 +
>  include/power/regulator.h        | 259 +++++++++++++++++++++++++++++++++++++++
>  5 files changed, 512 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/power/regulator-uclass.c
>  create mode 100644 include/power/regulator.h
>
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index 3513b46..1e73c7a 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -66,7 +66,38 @@ config DM_PMIC
>         So the call will looks like below:
>         'pmic_write(regulator->parent, addr, value, len);'
>
> -config AXP221_POWER
> +config DM_REGULATOR

Can you move this to drivers/power/regulator?

> +       bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)"
> +       depends on DM
> +       ---help---
> +       This config enables the driver-model regulator uclass support, which
> +       provides implementation of driver model regulator uclass api.
> +
> +       Regulator uclass API calls:
> +       To get the regulator device:
> +       - regulator_get()             - get the regulator device
> +
> +       The regulator framework is based on a 'struct dm_regulator_ops'.
> +       It provides a common function calls, for it's basic features:
> +       - regulator_info()            - get the regulator info structure
> +       - regulator_mode()            - get the regulator mode info structure
> +       - regulator_get/set_value()   - operate on output voltage value
> +       - regulator_get/set_current() - operate on output current value
> +       - regulator_get/set_enable()  - operate on output enable state
> +       - regulator_get/set_mode()    - operate on output operation mode
> +
> +       An optional and useful regulator framework features are two descriptors:
> +       - struct dm_regulator_info - describes the regulator name and output limits
> +       - struct dm_regulator_mode - describes the regulators operation mode
> +
> +       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 regulato
> +
> +       config AXP221_POWER

I don't think this should be indented.

>         boolean "axp221 / axp223 pmic support"
>         depends on MACH_SUN6I || MACH_SUN8I
>         default y
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index 5c9a189..a6b7012 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -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
> diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
> new file mode 100644
> index 0000000..b6a00c6
> --- /dev/null
> +++ b/drivers/power/regulator-uclass.c
> @@ -0,0 +1,219 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * 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_info(struct udevice *dev, struct dm_regulator_info **infop)
> +{
> +       if (!dev || !dev->uclass_priv)
> +               return -ENODEV;

assert() would be OK here, but if you prefer this, OK.

> +
> +       *infop = dev->uclass_priv;
> +
> +       return 0;
> +}
> +
> +int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep)
> +{
> +       struct dm_regulator_info *info;
> +       int ret;
> +
> +       ret = regulator_info(dev, &info);
> +       if (ret)
> +               return ret;
> +
> +       *modep = info->mode;
> +       return info->mode_count;
> +}
> +
> +int regulator_get_value(struct udevice *dev)
> +{
> +       const struct dm_regulator_ops *ops;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);

Can you instead define regulator_get_uclass_ops()?

> +       if (!ops)
> +               return -ENODEV;

-ENOSYS

We normally assume that operations existing, so assert() is OK. But if
you are worried about this, then this is OK.

> +
> +       if (!ops->get_value)
> +               return -EPERM;

-ENOSYS

i.e.

if (!ops || !ops->get_value)
   return -ENOSYS;

Please fix below also.

> +
> +       return ops->get_value(dev);
> +}
> +
> +int regulator_set_value(struct udevice *dev, int uV)
> +{
> +       const struct dm_regulator_ops *ops;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
> +       if (!ops)
> +               return -ENODEV;
> +
> +       if (!ops->set_value)
> +               return -EPERM;
> +
> +       return ops->set_value(dev, uV);
> +}
> +
> +int regulator_get_current(struct udevice *dev)
> +{
> +       const struct dm_regulator_ops *ops;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
> +       if (!ops)
> +               return -ENODEV;
> +
> +       if (!ops->get_current)
> +               return -EPERM;
> +
> +       return ops->get_current(dev);
> +}
> +
> +int regulator_set_current(struct udevice *dev, int uA)
> +{
> +       const struct dm_regulator_ops *ops;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
> +       if (!ops)
> +               return -ENODEV;
> +
> +       if (!ops->set_current)
> +               return -EPERM;
> +
> +       return ops->set_current(dev, uA);
> +}
> +
> +bool regulator_get_enable(struct udevice *dev)
> +{
> +       const struct dm_regulator_ops *ops;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
> +       if (!ops)
> +               return -ENODEV;
> +
> +       if (!ops->get_enable)
> +               return -EPERM;
> +
> +       return ops->get_enable(dev);
> +}
> +
> +int regulator_set_enable(struct udevice *dev, bool enable)
> +{
> +       const struct dm_regulator_ops *ops;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
> +       if (!ops)
> +               return -ENODEV;
> +
> +       if (!ops->set_enable)
> +               return -EPERM;
> +
> +       return ops->set_enable(dev, enable);
> +}
> +
> +int regulator_get_mode(struct udevice *dev)
> +{
> +       const struct dm_regulator_ops *ops;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
> +       if (!ops)
> +               return -ENODEV;
> +
> +       if (!ops->get_mode)
> +               return -EPERM;
> +
> +       return ops->get_mode(dev);
> +}
> +
> +int regulator_set_mode(struct udevice *dev, int mode)
> +{
> +       const struct dm_regulator_ops *ops;
> +
> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
> +       if (!ops)
> +               return -ENODEV;
> +
> +       if (!ops->set_mode)
> +               return -EPERM;
> +
> +       return ops->set_mode(dev, mode);
> +}
> +
> +int regulator_ofdata_to_platdata(struct udevice *dev)
> +{
> +       struct dm_regulator_info *info = dev->uclass_priv;
> +       int offset = dev->of_offset;
> +       int len;
> +
> +       /* Mandatory constraints */
> +       info->name = strdup(fdt_getprop(gd->fdt_blob, offset,
> +                                       "regulator-name", &len));
> +       if (!info->name)
> +               return -ENXIO;

-ENOMEM

But there is no need to strdup() this - the device tree does not move.

> +
> +       info->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
> +                                     "regulator-min-microvolt", -1);
> +       if (info->min_uV < 0)
> +               return -ENXIO;
> +
> +       info->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
> +                                     "regulator-max-microvolt", -1);
> +       if (info->max_uV < 0)
> +               return -ENXIO;
> +
> +       /* Optional constraints */
> +       info->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
> +                                     "regulator-min-microamp", -1);
> +       info->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
> +                                     "regulator-max-microamp", -1);
> +       info->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
> +                                        "regulator-always-on");
> +       info->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
> +                                        "regulator-boot-on");
> +
> +       return 0;
> +}
> +
> +int regulator_get(char *name, struct udevice **devp)
> +{
> +       struct dm_regulator_info *info;
> +       struct udevice *dev;
> +       int ret;
> +
> +       *devp = NULL;
> +
> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev);
> +            dev;
> +            ret = uclass_next_device(&dev)) {

This will probe all devices. See my suggestion about creating
uclass_find_first_device()/uclass_find_next_device() in the next
patch.

As before, I think this could use a function like uclass_get_device_by_name().

> +               info = dev->uclass_priv;
> +               if (!info)
> +                       continue;
> +
> +               if (!strcmp(name, info->name)) {
> +                       *devp = dev;
> +                       return ret;
> +               }
> +       }
> +
> +       return -ENODEV;
> +}
> +
> +UCLASS_DRIVER(regulator) = {
> +       .id             = UCLASS_REGULATOR,
> +       .name           = "regulator",
> +       .per_device_auto_alloc_size = sizeof(struct dm_regulator_info),
> +};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index 3ecfa23..23356f3 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -37,6 +37,7 @@ enum uclass_id {
>
>         /* PMIC uclass and PMIC-related uclass types */
>         UCLASS_PMIC,
> +       UCLASS_REGULATOR,

OK I see what the comment means now. I think it would be nice to have
a comment here 'Voltage regulator'

>
>         UCLASS_COUNT,
>         UCLASS_INVALID = -1,
> diff --git a/include/power/regulator.h b/include/power/regulator.h
> new file mode 100644
> index 0000000..cf083c5
> --- /dev/null
> +++ b/include/power/regulator.h
> @@ -0,0 +1,259 @@
> +/*
> + *  Copyright (C) 2014-2015 Samsung Electronics
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#ifndef _INCLUDE_REGULATOR_H_
> +#define _INCLUDE_REGULATOR_H_
> +
> +/* enum regulator_type - used for regulator_*() variant calls */
> +enum regulator_type {
> +       REGULATOR_TYPE_LDO = 0,
> +       REGULATOR_TYPE_BUCK,
> +       REGULATOR_TYPE_DVS,
> +       REGULATOR_TYPE_FIXED,
> +       REGULATOR_TYPE_OTHER,
> +};
> +
> +/**
> + * struct dm_regulator_mode - this structure holds an information about
> + * each regulator operation mode. Probably in most cases - an array.
> + * This will be probably a driver-static data, since it is device-specific.
> + *
> + * @id             - a driver-specific mode id
> + * @register_value - a driver-specific value for its mode id
> + * @name           - the name of mode - used for regulator command
> + * Note:
> + * The field 'id', should be always a positive number, since the negative values
> + * are reserved for the errno numbers when returns the mode id.
> + */
> +struct dm_regulator_mode {
> +       int id; /* Set only as >= 0 (negative value is reserved for errno) */
> +       int register_value;
> +       const char *name;
> +};
> +
> +/**
> + * struct dm_regulator_info - this structure holds an information about
> + * each regulator constraints and supported operation modes. There is no "step"
> + * voltage value - so driver should take care of this.
> + *
> + * @type       - one of 'enum regulator_type'
> + * @mode       - pointer to the regulator mode (array if more than one)
> + * @mode_count - number of '.mode' entries
> + * @min_uV*    - minimum voltage (micro Volts)
> + * @max_uV*    - maximum voltage (micro Volts)
> + * @min_uA*    - minimum amperage (micro Amps)
> + * @max_uA*    - maximum amperage (micro Amps)
> + * @always_on* - bool type, true or false
> + * @boot_on*   - bool type, true or false
> + * @name*      - fdt regulator name - should be taken from the device tree
> + *
> + * Note: for attributes signed with '*'
> + * These platform-specific constraints can be taken by regulator api function,
> + * which is 'regulator_ofdata_to_platdata()'. Please read the description, which
> + * can be found near the declaration of the mentioned function.
> +*/
> +struct dm_regulator_info {
> +       enum regulator_type type;
> +       struct dm_regulator_mode *mode;
> +       int mode_count;
> +       int min_uV;
> +       int max_uV;
> +       int min_uA;
> +       int max_uA;
> +       bool always_on;
> +       bool boot_on;
> +       const char *name;
> +};
> +
> +/* PMIC regulator device operations */
> +struct dm_regulator_ops {
> +       /**
> +        * The regulator output value function calls operates on a micro Volts.
> +        *
> +        * get/set_value - get/set output value of the given output number
> +        * @dev          - regulator device
> +        * Sets:
> +        * @uV           - set the output value [micro Volts]
> +        * Returns: output value [uV] on success or negative errno if fail.
> +        */
> +       int (*get_value)(struct udevice *dev);
> +       int (*set_value)(struct udevice *dev, int uV);
> +
> +       /**
> +        * The regulator output current function calls operates on a micro Amps.
> +        *
> +        * get/set_current - get/set output current of the given output number
> +        * @dev            - regulator device
> +        * Sets:
> +        * @uA           - set the output current [micro Amps]
> +        * Returns: output value [uA] on success or negative errno if fail.
> +        */
> +       int (*get_current)(struct udevice *dev);
> +       int (*set_current)(struct udevice *dev, int uA);
> +
> +       /**
> +        * The most basic feature of the regulator output is its enable state.
> +        *
> +        * get/set_enable - get/set enable state of the given output number
> +        * @dev           - regulator device
> +        * Sets:
> +        * @enable         - set true - enable or false - disable
> +        * Returns: true/false for get; or 0 / -errno for set.
> +        */
> +       bool (*get_enable)(struct udevice *dev);
> +       int (*set_enable)(struct udevice *dev, bool enable);
> +
> +       /**
> +        * The 'get/set_mode()' function calls should operate on a driver

driver-

> +        * specific mode definitions, which should be found in:
> +        * field 'mode' of struct mode_desc.
> +        *
> +        * get/set_mode - get/set operation mode of the given output number
> +        * @dev         - regulator device
> +        * Sets
> +        * @mode_id     - set output mode id (struct dm_regulator_mode->id)
> +        * Returns: id/0 for get/set on success or negative errno if fail.
> +        * Note:
> +        * The field 'id' of struct type 'dm_regulator_mode', should be always
> +        * positive number, since the negative is reserved for the error.
> +        */
> +       int (*get_mode)(struct udevice *dev);
> +       int (*set_mode)(struct udevice *dev, int mode_id);
> +};
> +
> +/**
> + * regulator_info: returns a pointer to the devices regulator info structure
> + *
> + * @dev    - pointer to the regulator device
> + * @infop  - pointer to the returned regulator info
> + * Returns - 0 on success or negative value of errno.

@return 0 on success...

> + */
> +int regulator_info(struct udevice *dev, struct dm_regulator_info **infop);
> +
> +/**
> + * regulator_mode: returns a pointer to the array of regulator mode info
> + *
> + * @dev        - pointer to the regulator device
> + * @modep      - pointer to the returned mode info array
> + * Returns     - count of modep entries on success or negative errno if fail.
> + */
> +int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep);
> +
> +/**
> + * regulator_get_value: get microvoltage voltage value of a given regulator
> + *
> + * @dev    - pointer to the regulator device
> + * Returns - positive output value [uV] on success or negative errno if fail.
> + */
> +int regulator_get_value(struct udevice *dev);
> +
> +/**
> + * regulator_set_value: set the microvoltage value of a given regulator.
> + *
> + * @dev    - pointer to the regulator device
> + * @uV     - the output value to set [micro Volts]
> + * Returns - 0 on success or -errno val if fails
> + */
> +int regulator_set_value(struct udevice *dev, int uV);
> +
> +/**
> + * regulator_get_current: get microampere value of a given regulator
> + *
> + * @dev    - pointer to the regulator device
> + * Returns - positive output current [uA] on success or negative errno if fail.
> + */
> +int regulator_get_current(struct udevice *dev);
> +
> +/**
> + * regulator_set_current: set the microampere value of a given regulator.
> + *
> + * @dev    - pointer to the regulator device
> + * @uA     - set the output current [micro Amps]
> + * Returns - 0 on success or -errno val if fails
> + */
> +int regulator_set_current(struct udevice *dev, int uA);
> +
> +/**
> + * regulator_get_enable: get regulator device enable state.
> + *
> + * @dev    - pointer to the regulator device
> + * Returns - true/false of enable state
> + */
> +bool regulator_get_enable(struct udevice *dev);
> +
> +/**
> + * regulator_set_enable: set regulator enable state
> + *
> + * @dev    - pointer to the regulator device
> + * @enable - set true or false
> + * Returns - 0 on success or -errno val if fails
> + */
> +int regulator_set_enable(struct udevice *dev, bool enable);
> +
> +/**
> + * regulator_get_mode: get mode of a given device regulator
> + *
> + * @dev    - pointer to the regulator device
> + * Returns - positive  mode number 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 'mode_desc' provides a field 'mode'
> + * for this purpose and register_value for a raw register value.
> + */
> +int regulator_get_mode(struct udevice *dev);
> +
> +/**
> + * regulator_set_mode: set given regulator mode
> + *
> + * @dev    - pointer to the regulator device
> + * @mode   - mode type (field 'mode' of struct mode_desc)
> + * 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 mode);
> +
> +/**
> + * regulator_ofdata_to_platdata: get the regulator constraints from its fdt node
> + * and put it into the regulator 'dm_regulator_info' (dev->uclass_priv).
> + *
> + * An example of required regulator fdt node constraints:
> + * ldo1 {
> + *      regulator-compatible = "LDO1"; (not used here, but required for bind)
> + *      regulator-name = "VDD_MMC_1.8V"; (mandatory)
> + *      regulator-min-microvolt = <1000000>; (mandatory)
> + *      regulator-max-microvolt = <1000000>; (mandatory)
> + *      regulator-min-microamp = <1000>; (optional)
> + *      regulator-max-microamp = <1000>; (optional)
> + *      regulator-always-on; (optional)
> + *      regulator-boot-on; (optional)
> + * };
> + *
> + * @dev    - pointer to the regulator device
> + * Returns - 0 on success or -errno value if fails
> + * Note2:
> + * This function can be called at stage in which 'dev->uclass_priv' is non-NULL.
> + * It is possible at two driver call stages: '.ofdata_to_platdata' or '.probe'.
> + */
> +int regulator_ofdata_to_platdata(struct udevice *dev);
> +
> +/**
> + * regulator_get: returns the pointer to the pmic regulator device based on
> + * regulator fdt node data
> + *
> + * @fdt_name - property 'regulator-name' value of regulator fdt node
> + * @devp     - returned pointer to the regulator device
> + * Returns: 0 on success or negative value of errno.
> + *
> + * The returned 'regulator' device can be used with:
> + * - regulator_get/set_*
> + */
> +int regulator_get(char *fdt_name, struct udevice **devp);

Can we s/fdt_name/name/ since by now it is a device name, not a device
tree name.
> +
> +#endif /* _INCLUDE_REGULATOR_H_ */
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 07/17] dm: pmic: add pmic command
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 07/17] dm: pmic: add pmic command Przemyslaw Marczak
@ 2015-03-29 13:07       ` Simon Glass
  2015-04-03 16:08         ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:07 UTC (permalink / raw)
  To: u-boot

Hi Prazemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This is new command for the pmic devices based on driver model pmic api.
> Command features are unchanged:
> - list          - list UCLASS pmic devices
> - pmic dev [id]      - show or [set] operating pmic device (NEW)
> - pmic dump          - dump registers
> - pmic read address  - read byte of register at address
> - pmic write address - write byte to register at address
>
> The only one change for this command is 'dev' subcommand.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>
> Changes v3:
> - new file
> - add Kconfig
> ---
>  common/Kconfig    |  14 ++++
>  common/Makefile   |   3 +
>  common/cmd_pmic.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 227 insertions(+)
>  create mode 100644 common/cmd_pmic.c
>
> diff --git a/common/Kconfig b/common/Kconfig
> index e662774..1125e6d 100644
> --- a/common/Kconfig
> +++ b/common/Kconfig
> @@ -335,4 +335,18 @@ config CMD_SETGETDCR
>
>  endmenu
>
> +menu "Power commands"
> +config DM_PMIC_CMD

CMD_DM_PMIC

since this fits better with the other ones

> +       bool "Enable Driver Model PMIC command"
> +       depends on DM_PMIC
> +       help
> +         This is new command for the pmic devices based on driver model pmic api.
> +         Command features are unchanged:
> +         - list               - list UCLASS pmic devices
> +         - pmic dev [id]      - show or [set] operating pmic device (NEW)
> +         - pmic dump          - dump registers
> +         - pmic read address  - read byte of register at address
> +         - pmic write address - write byte to register at address
> +         The only one change for this command is 'dev' subcommand.
> +endmenu
>  endmenu
> diff --git a/common/Makefile b/common/Makefile
> index 7216a13..d908851 100644
> --- a/common/Makefile
> +++ b/common/Makefile
> @@ -208,6 +208,9 @@ obj-$(CONFIG_UPDATE_TFTP) += update.o
>  obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
>  obj-$(CONFIG_CMD_DFU) += cmd_dfu.o
>  obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
> +
> +# Power
> +obj-$(CONFIG_DM_PMIC_CMD) += cmd_pmic.o
>  endif
>
>  ifdef CONFIG_SPL_BUILD
> diff --git a/common/cmd_pmic.c b/common/cmd_pmic.c
> new file mode 100644
> index 0000000..978a94a
> --- /dev/null
> +++ b/common/cmd_pmic.c
> @@ -0,0 +1,210 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +#include <common.h>
> +#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>
> +#include <i2c.h>
> +#include <compiler.h>
> +#include <errno.h>
> +
> +#define LIMIT_SEQ      3
> +#define LIMIT_DEVNAME  20
> +
> +static struct udevice *pmic_curr;
> +
> +static int failed(const char *getset, const char *thing,
> +                 const char *for_dev, int ret)
> +{
> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing, for_dev,
> +                                                   ret, errno_str(ret));
> +       return CMD_RET_FAILURE;
> +}
> +
> +static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       int seq, ret = -ENODEV;
> +
> +       switch (argc) {
> +       case 2:
> +               seq = simple_strtoul(argv[1], NULL, 0);
> +               ret = uclass_get_device_by_seq(UCLASS_PMIC, seq, &pmic_curr);
> +       case 1:
> +               if (!pmic_curr)
> +                       return failed("get", "the", "device", ret);
> +
> +               printf("dev: %d @ %s\n", pmic_curr->seq, pmic_curr->name);
> +       }
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       const char *parent_uc;
> +       int ret;
> +
> +       printf("|%*s | %-*.*s| %-*.*s| %s @ %s\n",
> +              LIMIT_SEQ, "Seq",
> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Parent name",
> +              "Parent uclass", "seq");
> +
> +       for (ret = uclass_first_device(UCLASS_PMIC, &dev); dev;
> +            ret = uclass_next_device(&dev)) {

Note this will probe everything.

Perhaps we need uclass_find_first_device() and
uclass_find_next_device() which don't probe before returning each
device?


> +               if (!dev)
> +                       continue;
> +
> +               /* Parent uclass name*/
> +               parent_uc = dev->parent->uclass->uc_drv->name;

What do you think about a new function at some point, so you can call
dev_uclass_name(dev_get_parent(dev))? We want to avoid digging around
in the driver model data structures outside drivers/core.

> +
> +               printf("|%*d | %-*.*s| %-*.*s| %s @ %d\n",
> +                      LIMIT_SEQ, dev->seq,
> +                      LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
> +                      LIMIT_DEVNAME, LIMIT_DEVNAME, dev->parent->name,
> +                      parent_uc, dev->parent->seq);
> +       }
> +
> +       if (ret)
> +               return CMD_RET_FAILURE;
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_dump(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       int regs, ret;
> +       uint8_t value;
> +       uint reg;
> +
> +       if (!pmic_curr)
> +               return failed("get", "current", "device", -ENODEV);
> +
> +       dev = pmic_curr;
> +
> +       printf("Dump pmic: %s registers\n", dev->name);
> +
> +       regs = pmic_reg_count(dev);
> +       reg = 0;
> +       while (reg < regs) {

for (ret = 0; ret < regs; reg++) {

> +               ret = pmic_read(dev, reg, &value, 1);
> +               if (ret)
> +                       return failed("read", dev->name, "register", ret);
> +
> +               if (!(reg % 16))
> +                       printf("\n0x%02x: ", reg);
> +
> +               printf("%2.2x ", value);
> +               reg++;
> +       }
> +       printf("\n");
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_read(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       int regs, ret;
> +       uint8_t value;
> +       uint reg;
> +
> +       if (!pmic_curr)
> +               return failed("get", "current", "device", -ENODEV);
> +
> +       dev = pmic_curr;
> +
> +       if (argc != 2)
> +               return CMD_RET_USAGE;
> +
> +       reg = simple_strtoul(argv[1], NULL, 0);
> +       regs = pmic_reg_count(dev);
> +       if (reg > regs) {
> +               printf("Pmic max reg: %d\n", regs);
> +               return failed("read", "given", "address", -EFAULT);
> +       }
> +
> +       ret = pmic_read(dev, reg, &value, 1);
> +       if (ret)
> +               return failed("read", dev->name, "register", ret);
> +
> +       printf("0x%02x: 0x%2.2x\n", reg, value);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       int regs, ret;
> +       uint8_t value;
> +       uint reg;
> +
> +       if (!pmic_curr)
> +               return failed("get", "current", "device", -ENODEV);
> +
> +       dev = pmic_curr;
> +
> +       if (argc != 3)
> +               return CMD_RET_USAGE;
> +
> +       reg = simple_strtoul(argv[1], NULL, 0);
> +       regs = pmic_reg_count(dev);
> +       if (reg > regs) {
> +               printf("Pmic max reg: %d\n", regs);
> +               return failed("write", "given", "address", -EFAULT);
> +       }
> +
> +       value = simple_strtoul(argv[2], NULL, 0);
> +
> +       ret = pmic_write(dev, reg, &value, 1);
> +       if (ret)
> +               return failed("write", dev->name, "register", ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static cmd_tbl_t subcmd[] = {
> +       U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
> +       U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
> +       U_BOOT_CMD_MKENT(dump, 1, 1, do_dump, "", ""),
> +       U_BOOT_CMD_MKENT(read, 2, 1, do_read, "", ""),
> +       U_BOOT_CMD_MKENT(write, 3, 1, do_write, "", ""),
> +};
> +
> +static int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc,
> +                       char * const argv[])
> +{
> +       cmd_tbl_t *cmd;
> +
> +       argc--;
> +       argv++;
> +
> +       cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
> +       if (cmd == NULL || argc > cmd->maxargs)
> +               return CMD_RET_USAGE;
> +
> +       return cmd->cmd(cmdtp, flag, argc, argv);
> +}
> +
> +U_BOOT_CMD(pmic, CONFIG_SYS_MAXARGS, 1, do_pmic,
> +       "uclass operations",
> +       "list          - list UCLASS pmic devices\n"

Can we drop the 'UCLASS' ?

> +       "pmic dev [id]      - show or [set] operating pmic device\n"
> +       "pmic dump          - dump registers\n"
> +       "pmic read address  - read byte of register at address\n"
> +       "pmic write address - write byte to register at address\n"
> +);
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 08/17] dm: regulator: add regulator command
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 08/17] dm: regulator: add regulator command Przemyslaw Marczak
@ 2015-03-29 13:07       ` Simon Glass
  2015-04-03 16:08         ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:07 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This command is based on driver model regulator api.
> User interface features:
> - list                   - list UCLASS regulator devices
> - regulator dev [id]     - show or [set] operating regulator device
> - regulator [info]       - print constraints info
> - regulator [status]     - print operating status
> - regulator [value] [-f] - print/[set] voltage value [uV] (force)
> - regulator [current]    - print/[set] current value [uA]
> - regulator [mode_id]    - print/[set] operating mode id
> - regulator [enable]     - enable the regulator output
> - regulator [disable]    - disable the regulator output
>
> The 'force' option can be used for setting the value which exceeds the limits,
> which are found in device-tree and are keept in regulators info structure.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>
> ---
> Changes v3:
> - new file
> - Kconfig entry
> ---
>  common/Kconfig         |  22 +++
>  common/Makefile        |   1 +
>  common/cmd_regulator.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 408 insertions(+)
>  create mode 100644 common/cmd_regulator.c
>
> diff --git a/common/Kconfig b/common/Kconfig
> index 1125e6d..48f360f 100644
> --- a/common/Kconfig
> +++ b/common/Kconfig
> @@ -348,5 +348,27 @@ config DM_PMIC_CMD
>           - pmic read address  - read byte of register at address
>           - pmic write address - write byte to register at address
>           The only one change for this command is 'dev' subcommand.
> +
> +config DM_REGULATOR_CMD

CMD_DM_REGULATOR

> +       bool "Enable Driver Model REGULATOR command"
> +       depends on DM_REGULATOR
> +       help
> +         This command is based on driver model regulator api.
> +         User interface features:
> +         - list                   - list UCLASS regulator devices

Do you need 'UCLASS" in there? What does it mean?

> +         - regulator dev [id]     - show or [set] operating regulator device
> +         - regulator [info]       - print constraints info
> +         - regulator [status]     - print operating status
> +         - regulator [value] [-f] - print/[set] voltage value [uV] (force)
> +         - regulator [current]    - print/[set] current value [uA]
> +         - regulator [mode_id]    - print/[set] operating mode id
> +         - regulator [enable]     - enable the regulator output
> +         - regulator [disable]    - disable the regulator output

I don't think the sub-commands should be in [].

> +
> +         The 'force' option can be used for setting the value which exceeds
> +         the limit which are found in device-tree and are keept in regulators
> +         info structure.
> +
>  endmenu
> +
>  endmenu
> diff --git a/common/Makefile b/common/Makefile
> index d908851..d63fe12 100644
> --- a/common/Makefile
> +++ b/common/Makefile
> @@ -211,6 +211,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
>
>  # Power
>  obj-$(CONFIG_DM_PMIC_CMD) += cmd_pmic.o
> +obj-$(CONFIG_DM_REGULATOR_CMD) += cmd_regulator.o
>  endif
>
>  ifdef CONFIG_SPL_BUILD
> diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
> new file mode 100644
> index 0000000..d388b14
> --- /dev/null
> +++ b/common/cmd_regulator.c
> @@ -0,0 +1,385 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +#include <common.h>
> +#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>
> +#include <i2c.h>
> +#include <compiler.h>
> +#include <errno.h>
> +
> +#define LIMIT_SEQ      3
> +#define LIMIT_DEVNAME  20
> +#define LIMIT_OFNAME   20
> +#define LIMIT_INFO     12
> +
> +static struct udevice *reg_curr;
> +
> +static int failed(const char *getset, const char *thing,
> +                 const char *for_dev, int ret)
> +{
> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing, for_dev,
> +                                                   ret, errno_str(ret));
> +       return CMD_RET_FAILURE;
> +}
> +
> +static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct dm_regulator_info *info;
> +       int seq, ret = CMD_RET_FAILURE;
> +
> +       switch (argc) {
> +       case 2:
> +               seq = simple_strtoul(argv[1], NULL, 0);
> +               uclass_get_device_by_seq(UCLASS_REGULATOR, seq, &reg_curr);

This can return an error.

> +       case 1:
> +               ret = regulator_info(reg_curr, &info);
> +               if (ret)
> +                       return failed("get", "the", "device", ret);
> +
> +               printf("dev: %d @ %s\n", reg_curr->seq, info->name);
> +       }
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int get_curr_dev_and_info(struct udevice **devp,
> +                                struct dm_regulator_info **infop)
> +{
> +       int ret = -ENODEV;
> +
> +       *devp = reg_curr;
> +       if (!*devp)
> +               return failed("get", "current", "device", ret);
> +
> +       ret = regulator_info(*devp, infop);
> +       if (ret)
> +               return failed("get", reg_curr->name, "info", ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct dm_regulator_info *info;
> +       const char *parent_uc;
> +       struct udevice *dev;
> +       int ret;
> +
> +       printf("|%*s | %*.*s @ %-*.*s| %s @ %s\n",
> +              LIMIT_SEQ, "Seq",
> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
> +              LIMIT_OFNAME, LIMIT_OFNAME, "fdtname",
> +              "Parent", "uclass");
> +
> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
> +            ret = uclass_next_device(&dev)) {
> +               if (regulator_info(dev, &info)) {
> +                       printf("(null) info for: %s\n", dev->name);
> +                       continue;
> +               }
> +
> +               /* Parent uclass name*/
> +               parent_uc = dev->parent->uclass->uc_drv->name;
> +
> +               printf("|%*d | %*.*s @ %-*.*s| %s @ %s\n",
> +                      LIMIT_SEQ, dev->seq,
> +                      LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
> +                      LIMIT_OFNAME, LIMIT_OFNAME, info->name,
> +                      dev->parent->name, parent_uc);
> +       }
> +
> +       if (ret)
> +               return CMD_RET_FAILURE;
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_info(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_info *info;
> +       struct dm_regulator_mode *modes;
> +       const char *parent_uc;
> +       int mode_count;
> +       int ret;
> +       int i;
> +
> +       ret = get_curr_dev_and_info(&dev, &info);
> +       if (ret)
> +               return CMD_RET_FAILURE;
> +
> +       parent_uc = dev->parent->uclass->uc_drv->name;
> +
> +       printf("Uclass regulator dev %d info:\n", dev->seq);
> +       printf("%-*s %s @ %s\n%-*s %s\n%-*s %s\n",
> +              LIMIT_INFO, "* parent:", dev->parent->name, parent_uc,
> +              LIMIT_INFO, "* dev name:", dev->name,
> +              LIMIT_INFO, "* fdt name:", info->name);
> +
> +       printf("%-*s %d\n%-*s %d\n%-*s %d\n%-*s %d\n%-*s %s\n%-*s %s\n",
> +              LIMIT_INFO, "* min uV:", info->min_uV,
> +              LIMIT_INFO, "* max uV:", info->max_uV,
> +              LIMIT_INFO, "* min uA:", info->min_uA,
> +              LIMIT_INFO, "* max uA:", info->max_uA,
> +              LIMIT_INFO, "* always on:", info->always_on ? "true" : "false",
> +              LIMIT_INFO, "* boot on:", info->boot_on ? "true" : "false");
> +
> +       mode_count = regulator_mode(dev, &modes);
> +       if (!mode_count)
> +               return failed("get mode", "for mode", "null count", -EPERM);
> +
> +       if (mode_count < 0)
> +               return failed("get", info->name, "mode count", mode_count);
> +
> +       printf("* operation modes:\n");
> +       for (i = 0; i < mode_count; i++, modes++)
> +               printf("  - mode %d (%s)\n", modes->id, modes->name);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static const char *get_mode_name(struct dm_regulator_mode *mode,
> +                                int mode_count,
> +                                int mode_id)
> +{
> +       while (mode_count--) {
> +               if (mode->id == mode_id)
> +                       return mode->name;
> +               mode++;
> +       }
> +
> +       return NULL;
> +}
> +
> +static int do_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_info *info;
> +       const char *mode_name;
> +       int value, mode, ret;
> +       bool enabled;
> +
> +       ret = get_curr_dev_and_info(&dev, &info);
> +       if (ret)
> +               return CMD_RET_FAILURE;
> +
> +       /* Value is mandatory */
> +       value = regulator_get_value(dev);
> +       if (value < 0)
> +               return failed("get", info->name, "voltage value", value);
> +
> +       mode = regulator_get_mode(dev);
> +       mode_name = get_mode_name(info->mode, info->mode_count, mode);
> +       enabled = regulator_get_enable(dev);
> +
> +       printf("Regulator seq %d status:\n", dev->seq);
> +       printf("%-*s %d\n%-*s %d (%s)\n%-*s %s\n",
> +              LIMIT_INFO, " * value:", value,
> +              LIMIT_INFO, " * mode:", mode, mode_name,
> +              LIMIT_INFO, " * enabled:", enabled ? "true" : "false");
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_value(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_info *info;
> +       int value;
> +       int force;
> +       int ret;
> +
> +       ret = get_curr_dev_and_info(&dev, &info);
> +       if (ret)
> +               return CMD_RET_FAILURE;
> +
> +       if (argc == 1) {
> +               value = regulator_get_value(dev);
> +               if (value < 0)
> +                       return failed("get", info->name, "voltage", value);
> +
> +               printf("%d uV\n", value);
> +               return CMD_RET_SUCCESS;
> +       }
> +
> +       if (info->type == REGULATOR_TYPE_FIXED) {
> +               printf("Fixed regulator value change not allowed.\n");
> +               return CMD_RET_SUCCESS;
> +       }
> +
> +       if (argc == 3)
> +               force = !strcmp("-f", argv[2]);
> +       else
> +               force = 0;
> +
> +       value = simple_strtoul(argv[1], NULL, 0);
> +       if ((value < info->min_uV || value > info->max_uV) && !force) {
> +               printf("Value exceeds regulator constraint limits\n");
> +               return CMD_RET_FAILURE;
> +       }
> +
> +       ret = regulator_set_value(dev, value);
> +       if (ret)
> +               return failed("set", info->name, "voltage value", ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_current(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_info *info;
> +       int current;
> +       int ret;
> +
> +       ret = get_curr_dev_and_info(&dev, &info);
> +       if (ret)
> +               return CMD_RET_FAILURE;
> +
> +       if (argc == 1) {
> +               current = regulator_get_current(dev);
> +               if (current < 0)
> +                       return failed("get", info->name, "current", current);
> +
> +               printf("%d uA\n", current);
> +               return CMD_RET_SUCCESS;
> +       }
> +
> +       if (info->type == REGULATOR_TYPE_FIXED) {
> +               printf("Fixed regulator current change not allowed.\n");
> +               return CMD_RET_SUCCESS;
> +       }
> +
> +       current = simple_strtoul(argv[1], NULL, 0);
> +       if (current < info->min_uA || current > info->max_uA) {
> +               printf("Current exceeds regulator constraint limits\n");
> +               return CMD_RET_FAILURE;
> +       }
> +
> +       ret = regulator_set_current(dev, current);
> +       if (ret)
> +               return failed("set", info->name, "current value", ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_info *info;
> +       int new_mode;
> +       int mode;
> +       int ret;
> +
> +       ret = get_curr_dev_and_info(&dev, &info);
> +       if (ret)
> +               return CMD_RET_FAILURE;
> +
> +       if (info->type == REGULATOR_TYPE_FIXED) {
> +               printf("Fixed regulator mode option not allowed.\n");
> +               return CMD_RET_SUCCESS;
> +       }
> +
> +       if (argc == 1) {
> +               mode = regulator_get_mode(dev);
> +               if (mode < 0)
> +                       return failed("get", info->name, "mode", ret);
> +
> +               printf("mode id: %d\n", mode);
> +               return CMD_RET_SUCCESS;
> +       }

The code above is the same for the above 2 functions also. Can you
move it to a common function? Something list
get_non_fixed_regulator()?

> +
> +       new_mode = simple_strtoul(argv[1], NULL, 0);
> +
> +       ret = regulator_set_mode(dev, new_mode);
> +       if (ret)
> +               return failed("set", info->name, "mode", ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_enable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_info *info;
> +       int ret;
> +
> +       ret = get_curr_dev_and_info(&dev, &info);
> +       if (ret)
> +               return CMD_RET_FAILURE;
> +
> +       ret = regulator_set_enable(dev, true);
> +       if (ret)
> +               return failed("enable", "regulator", info->name, ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_disable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_info *info;
> +       int ret;
> +
> +       ret = get_curr_dev_and_info(&dev, &info);
> +       if (ret)
> +               return CMD_RET_FAILURE;
> +
> +       ret = regulator_set_enable(dev, false);
> +       if (ret)
> +               return failed("disable", "regulator", info->name, ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static cmd_tbl_t subcmd[] = {
> +       U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
> +       U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
> +       U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""),
> +       U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
> +       U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""),
> +       U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""),
> +       U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""),
> +       U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""),
> +       U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""),
> +};
> +
> +static int do_regulator(cmd_tbl_t *cmdtp, int flag, int argc,
> +                       char * const argv[])
> +{
> +       cmd_tbl_t *cmd;
> +
> +       argc--;
> +       argv++;
> +
> +       cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
> +       if (cmd == NULL || argc > cmd->maxargs)
> +               return CMD_RET_USAGE;
> +
> +       return cmd->cmd(cmdtp, flag, argc, argv);
> +}
> +
> +U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator,
> +       "uclass operations",
> +       "list         - list UCLASS regulator devices\n"
> +       "regulator dev [id]     - show or [set] operating regulator device\n"
> +       "regulator [info]       - print constraints info\n"
> +       "regulator [status]     - print operating status\n"
> +       "regulator [value] [-f] - print/[set] voltage value [uV] (force)\n"
> +       "regulator [current]    - print/[set] current value [uA]\n"
> +       "regulator [mode_id]    - print/[set] operating mode id\n"
> +       "regulator [enable]     - enable the regulator output\n"
> +       "regulator [disable]    - disable the regulator output\n"

I think [info] should not be in brackets as it is not optional.
Similar with other ones.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 09/17] pmic: max77686 set the same compatible as in the kernel
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 09/17] pmic: max77686 set the same compatible as in the kernel Przemyslaw Marczak
@ 2015-03-29 13:08       ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:08 UTC (permalink / raw)
  To: u-boot

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This commit also updates the proper dts files.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  arch/arm/dts/exynos4412-odroid.dts   | 2 +-
>  arch/arm/dts/exynos4412-trats2.dts   | 2 +-
>  arch/arm/dts/exynos5250-smdk5250.dts | 2 +-
>  arch/arm/dts/exynos5250-snow.dts     | 2 +-
>  lib/fdtdec.c                         | 2 +-
>  5 files changed, 5 insertions(+), 5 deletions(-)
>

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 10/17] dm: pmic: add max77686 pmic driver
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 10/17] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
@ 2015-03-29 13:08       ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:08 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This is the implementation of driver model uclass pmic driver.
> The max77686 pmic driver implements read/write operations and driver
> bind method - to bind other pmic uclass devices as a parent pmic device.
> This driver provides pmic_platdata for also for child regulator.
>
> This driver will try to bind the regulator device with regulator driver.
> This should succeed if regulator driver is compiled.
>
> If no regulator driver found, then the pmic can still provide read/write
> operations, and can be used with pmic framework function calls.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>

Acked-by: Simon Glass <sjg@chromium.org>

One nit below.

> ---
> Changes V2:
> - add implementation of pmic read/write
> - max77686: add new operations
> - max77686: header: change PMIC_NUM_OF_REGS to MAX77686_NUM_OF_REGS
>
> Changes V3:
> - pmic/max77686.c: call pmic_child_node_scan() to bind regulator device
> - remove use of pmic platdata
> - remove unused endian conversions
> - Kconfig: add max77686 pmic entry
> ---
>  drivers/power/Kconfig              |  7 ++++
>  drivers/power/pmic/Makefile        |  1 +
>  drivers/power/pmic/max77686.c      | 76 ++++++++++++++++++++++++++++++++++++++
>  drivers/power/pmic/pmic_max77686.c |  2 +-
>  include/power/max77686_pmic.h      |  2 +-
>  5 files changed, 86 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/power/pmic/max77686.c
>
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index 1e73c7a..c4d4c72 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -66,6 +66,13 @@ config DM_PMIC
>         So the call will looks like below:
>         'pmic_write(regulator->parent, addr, value, len);'
>
> +config DM_PMIC_MAX77686
> +       bool "Enable Driver Model for PMIC MAX77686"
> +       depends on DM_PMIC
> +       ---help---
> +       This config enables implementation of driver-model pmic uclass features
> +       for PMIC MAX77686. The driver implements read/write operations/

nit: '.' at end?

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 11/17] dm: regulator: add max77686 regulator driver
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 11/17] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
@ 2015-03-29 13:08       ` Simon Glass
  2015-04-03 16:08         ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:08 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> 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
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>

Acked-by: Simon Glass <sjg@chromium.org>

See nit below.

> ---
> Changes V2:
> - change debug() to error()
> - code cleanup
> - fix data types
> - ldo/buck state implementation
> - adjust to new uclass api
>
> Changes V3:
> - regulator/max77686.c:
>   -- adjust to api changes
>   -- add separeted drivers for buck and ldo
>   -- bind regulators by its compatibles
> - Kconfig: add regulator max77686 entry
> ---
>  Makefile                           |   1 +
>  drivers/power/Kconfig              |   8 +
>  drivers/power/Makefile             |   1 -
>  drivers/power/regulator/Makefile   |   8 +
>  drivers/power/regulator/max77686.c | 876 +++++++++++++++++++++++++++++++++++++
>  include/power/max77686_pmic.h      |  24 +-
>  6 files changed, 914 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/power/regulator/Makefile
>  create mode 100644 drivers/power/regulator/max77686.c
>
> diff --git a/Makefile b/Makefile
> index 1b3ebe7..9ecf3bb 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -632,6 +632,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/
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index c4d4c72..97abbf0 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -112,6 +112,14 @@ config DM_REGULATOR
>         Say y here to enable support for the axp221 / axp223 pmic found on most
>         sun6i (A31) / sun8i (A23) boards.
>
> +config DM_REGULATOR_MAX77686
> +       bool "Enable Driver Model for REGULATOR MAX77686"
> +       depends on DM_REGULATOR && DM_PMIC_MAX77686
> +       ---help---
> +       This config enables implementation of driver-model regulator uclass
> +       features for REGULATOR MAX77686. The driver implements get/set api for:
> +       value, enable and mode.

This should probably go in drivers/power/regulator/Kconfig.

> +
>  config AXP221_DCDC1_VOLT
>         int "axp221 dcdc1 voltage"
>         depends on AXP221_POWER
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index a6b7012..f206bdd 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -15,7 +15,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
> diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
> new file mode 100644
> index 0000000..9d282e3
> --- /dev/null
> +++ b/drivers/power/regulator/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# Copyright (C) 2014 Samsung Electronics
> +# Przemyslaw Marczak <p.marczak@samsung.com>
> +#
> +# SPDX-License-Identifier:     GPL-2.0+
> +#
> +
> +obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
> diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c
> new file mode 100644
> index 0000000..496c70a
> --- /dev/null
> +++ b/drivers/power/regulator/max77686.c
> @@ -0,0 +1,876 @@
> +/*
> + *  Copyright (C) 2012-2015 Samsung Electronics
> + *
> + *  Rajeshwari Shinde <rajeshwari.s@samsung.com>
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <fdtdec.h>
> +#include <i2c.h>
> +#include <dm.h>
> +#include <power/pmic.h>
> +#include <power/regulator.h>
> +#include <power/max77686_pmic.h>
> +#include <errno.h>
> +#include <dm.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define MODE(_id, _val, _name) { \
> +       .id = _id, \
> +       .register_value = _val, \
> +       .name = _name, \
> +}
> +
> +/* LDO: 1,3,4,5,9,17,18,19,20,21,22,23,24,26,26,27 */
> +static struct dm_regulator_mode max77686_ldo_mode_standby1[] = {
> +       MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
> +       MODE(OPMODE_LPM, MAX77686_LDO_MODE_LPM, "LPM"),
> +       MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
> +       MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
> +};
> +
> +/* LDO: 2,6,7,8,10,11,12,14,15,16 */
> +static struct dm_regulator_mode max77686_ldo_mode_standby2[] = {
> +       MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
> +       MODE(OPMODE_STANDBY, MAX77686_LDO_MODE_STANDBY, "ON/OFF"),
> +       MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
> +       MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
> +};
> +
> +/* Buck: 1 */
> +static struct dm_regulator_mode max77686_buck_mode_standby[] = {
> +       MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
> +       MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
> +       MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
> +};
> +
> +/* Buck: 2,3,4 */
> +static struct dm_regulator_mode max77686_buck_mode_lpm[] = {
> +       MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
> +       MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
> +       MODE(OPMODE_LPM, MAX77686_BUCK_MODE_LPM, "LPM"),
> +       MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
> +};
> +
> +/* Buck: 5,6,7,8,9 */
> +static struct dm_regulator_mode max77686_buck_mode_onoff[] = {
> +       MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
> +       MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
> +};
> +
> +static const char max77686_buck_addr[] = {
> +       0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38
> +};
> +
> +static int max77686_buck_volt2hex(int buck, int uV)
> +{
> +       unsigned int hex = 0;
> +       unsigned int hex_max = 0;
> +
> +       switch (buck) {
> +       case 2:
> +       case 3:
> +       case 4:
> +               /* hex = (uV - 600000) / 12500; */
> +               hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP;
> +               hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
> +               /**
> +                * This uses voltage scaller - temporary not implemented
> +                * so return just 0
> +                */
> +               return 0;
> +       default:
> +               /* hex = (uV - 750000) / 50000; */
> +               hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP;
> +               hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
> +               break;
> +       }
> +
> +       if (hex >= 0 && hex <= hex_max)
> +               return hex;
> +
> +       error("Value: %d uV is wrong for BUCK%d", uV, buck);
> +       return -EINVAL;
> +}
> +
> +static int max77686_buck_hex2volt(int buck, int hex)
> +{
> +       unsigned uV = 0;
> +       unsigned int hex_max = 0;
> +
> +       if (hex < 0)
> +               goto bad_hex;
> +
> +       switch (buck) {
> +       case 2:
> +       case 3:
> +       case 4:
> +               hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
> +               if (hex > hex_max)
> +                       goto bad_hex;
> +
> +               /* uV = hex * 12500 + 600000; */
> +               uV = hex * MAX77686_BUCK_UV_LSTEP + MAX77686_BUCK_UV_LMIN;
> +               break;
> +       default:
> +               hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
> +               if (hex > hex_max)
> +                       goto bad_hex;
> +
> +               /* uV = hex * 50000 + 750000; */
> +               uV = hex * MAX77686_BUCK_UV_HSTEP + MAX77686_BUCK_UV_HMIN;
> +               break;
> +       }
> +
> +       return uV;
> +
> +bad_hex:
> +       error("Value: %#x is wrong for BUCK%d", hex, buck);
> +       return -EINVAL;
> +}
> +
> +static int max77686_ldo_volt2hex(int ldo, int uV)
> +{
> +       unsigned int hex = 0;
> +
> +       switch (ldo) {
> +       case 1:
> +       case 2:
> +       case 6:
> +       case 7:
> +       case 8:
> +       case 15:
> +               hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_LSTEP;
> +               /* hex = (uV - 800000) / 25000; */
> +               break;
> +       default:
> +               hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_HSTEP;
> +               /* hex = (uV - 800000) / 50000; */
> +       }
> +
> +       if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX)
> +               return hex;
> +
> +       error("Value: %d uV is wrong for LDO%d", uV, ldo);
> +       return -EINVAL;
> +}
> +
> +static int max77686_ldo_hex2volt(int ldo, int hex)
> +{
> +       unsigned int uV = 0;
> +
> +       if (hex > MAX77686_LDO_VOLT_MAX_HEX)
> +               goto bad_hex;
> +
> +       switch (ldo) {
> +       case 1:
> +       case 2:
> +       case 6:
> +       case 7:
> +       case 8:
> +       case 15:
> +               /* uV = hex * 25000 + 800000; */
> +               uV = hex * MAX77686_LDO_UV_LSTEP + MAX77686_LDO_UV_MIN;
> +               break;
> +       default:
> +               /* uV = hex * 50000 + 800000; */
> +               uV = hex * MAX77686_LDO_UV_HSTEP + MAX77686_LDO_UV_MIN;
> +       }
> +
> +       return uV;
> +
> +bad_hex:
> +       error("Value: %#x is wrong for ldo%d", hex, ldo);
> +       return -EINVAL;
> +}
> +
> +static int max77686_ldo_hex2mode(int ldo, int hex)
> +{
> +       if (hex > MAX77686_LDO_MODE_MASK)
> +               return -EINVAL;
> +
> +       switch (hex) {
> +       case MAX77686_LDO_MODE_OFF:
> +               return OPMODE_OFF;
> +       case MAX77686_LDO_MODE_LPM: /* == MAX77686_LDO_MODE_STANDBY: */
> +               /* The same mode values but different meaning for each ldo */
> +               switch (ldo) {
> +               case 2:
> +               case 6:
> +               case 7:
> +               case 8:
> +               case 10:
> +               case 11:
> +               case 12:
> +               case 14:
> +               case 15:
> +               case 16:
> +                       return OPMODE_STANDBY;
> +               default:
> +                       return OPMODE_LPM;
> +               }
> +       case MAX77686_LDO_MODE_STANDBY_LPM:
> +               return OPMODE_STANDBY_LPM;
> +       case MAX77686_LDO_MODE_ON:
> +               return OPMODE_ON;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int max77686_buck_hex2mode(int buck, int hex)
> +{
> +       if (hex > MAX77686_BUCK_MODE_MASK)
> +               return -EINVAL;
> +
> +       switch (hex) {
> +       case MAX77686_BUCK_MODE_OFF:
> +               return OPMODE_OFF;
> +       case MAX77686_BUCK_MODE_ON:
> +               return OPMODE_ON;
> +       case MAX77686_BUCK_MODE_STANDBY:
> +               switch (buck) {
> +               case 1:
> +               case 2:
> +               case 3:
> +               case 4:
> +                       return OPMODE_STANDBY;
> +               default:
> +                       return -EINVAL;
> +               }
> +       case MAX77686_BUCK_MODE_LPM:
> +               switch (buck) {
> +               case 2:
> +               case 3:
> +               case 4:
> +                       return OPMODE_LPM;
> +               default:
> +                       return -EINVAL;
> +               }
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int max77686_buck_modes(int buck, struct dm_regulator_mode **modesp)
> +{
> +       int ret = -EINVAL;
> +
> +       if (buck < 1 || buck > MAX77686_BUCK_NUM)
> +               return ret;
> +
> +       switch (buck) {
> +       case 1:
> +               *modesp = max77686_buck_mode_standby;
> +               ret = ARRAY_SIZE(max77686_buck_mode_standby);
> +       case 2:
> +       case 3:
> +       case 4:
> +               *modesp = max77686_buck_mode_lpm;
> +               ret = ARRAY_SIZE(max77686_buck_mode_lpm);
> +       default:
> +               *modesp = max77686_buck_mode_onoff;
> +               ret = ARRAY_SIZE(max77686_buck_mode_onoff);
> +       }
> +
> +       return ret;
> +}
> +
> +static int max77686_ldo_modes(int ldo, struct dm_regulator_mode **modesp)
> +{
> +       int ret = -EINVAL;
> +
> +       if (ldo < 1 || ldo > MAX77686_LDO_NUM)
> +               return ret;
> +
> +       switch (ldo) {
> +       case 2:
> +       case 6:
> +       case 7:
> +       case 8:
> +       case 10:
> +       case 11:
> +       case 12:
> +       case 14:
> +       case 15:
> +       case 16:
> +               *modesp = max77686_ldo_mode_standby2;
> +               ret = ARRAY_SIZE(max77686_ldo_mode_standby2);
> +       default:
> +               *modesp = max77686_ldo_mode_standby1;
> +               ret = ARRAY_SIZE(max77686_ldo_mode_standby1);
> +       }
> +
> +       return ret;
> +}
> +
> +static int dev2num(struct udevice *dev)
> +{
> +       return dev->of_id->data;
> +}
> +
> +static int max77686_ldo_val(struct udevice *dev, int op, int *uV)
> +{
> +       unsigned int ret, hex, adr;
> +       unsigned char val;
> +       int ldo;
> +
> +       if (op == PMIC_OP_GET)
> +               *uV = 0;
> +
> +       ldo = dev2num(dev);
> +       if (ldo < 1 || ldo > MAX77686_LDO_NUM) {
> +               error("Wrong ldo number: %d", ldo);
> +               return -EINVAL;
> +       }
> +
> +       adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
> +
> +       ret = pmic_read(dev->parent, adr, &val, 1);
> +       if (ret)
> +               return ret;
> +
> +       if (op == PMIC_OP_GET) {
> +               val &= MAX77686_LDO_VOLT_MASK;
> +               ret = max77686_ldo_hex2volt(ldo, val);
> +               if (ret < 0)
> +                       return ret;
> +               *uV = ret;
> +               return 0;
> +       }
> +
> +       hex = max77686_ldo_volt2hex(ldo, *uV);
> +       if (hex < 0)
> +               return -EINVAL;
> +
> +       val &= ~MAX77686_LDO_VOLT_MASK;
> +       val |= hex;
> +       ret = pmic_write(dev->parent, adr, &val, 1);
> +
> +       return ret;
> +}
> +
> +static int max77686_buck_val(struct udevice *dev, int op, int *uV)
> +{
> +       unsigned int hex, ret, mask, adr;
> +       unsigned char val;
> +       int buck;
> +
> +       buck = dev2num(dev);
> +       if (buck < 1 || buck > MAX77686_BUCK_NUM) {
> +               error("Wrong buck number: %d", buck);
> +               return -EINVAL;
> +       }
> +
> +       if (op == PMIC_OP_GET)
> +               *uV = 0;
> +
> +       /* &buck_out = ctrl + 1 */
> +       adr = max77686_buck_addr[buck] + 1;
> +
> +       /* mask */
> +       switch (buck) {
> +       case 2:
> +       case 3:
> +       case 4:
> +               /* Those uses voltage scallers - will support in the future */
> +               mask = MAX77686_BUCK234_VOLT_MASK;
> +               return -EPERM;
> +       default:
> +               mask = MAX77686_BUCK_VOLT_MASK;
> +       }
> +
> +       ret = pmic_read(dev->parent, adr, &val, 1);
> +       if (ret)
> +               return ret;
> +
> +       if (op == PMIC_OP_GET) {
> +               val &= mask;
> +               ret = max77686_buck_hex2volt(buck, val);
> +               if (ret < 0)
> +                       return ret;
> +               *uV = ret;
> +               return 0;
> +       }
> +
> +       hex = max77686_buck_volt2hex(buck, *uV);
> +       if (hex < 0)
> +               return -EINVAL;
> +
> +       val &= ~mask;
> +       val |= hex;
> +       ret = pmic_write(dev->parent, adr, &val, 1);
> +
> +       return ret;
> +}
> +
> +static int max77686_ldo_mode(struct udevice *dev, int op, int *opmode)
> +{
> +       unsigned int ret, adr, mode;
> +       unsigned char val;
> +       int ldo;
> +
> +       if (op == PMIC_OP_GET)
> +               *opmode = -EINVAL;
> +
> +       ldo = dev2num(dev);
> +       if (ldo < 1 || ldo > MAX77686_LDO_NUM) {
> +               error("Wrong ldo number: %d", ldo);
> +               return -EINVAL;
> +       }
> +
> +       adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
> +
> +       ret = pmic_read(dev->parent, adr, &val, 1);
> +       if (ret)
> +               return ret;
> +
> +       if (op == PMIC_OP_GET) {
> +               val &= MAX77686_LDO_MODE_MASK;
> +               ret = max77686_ldo_hex2mode(ldo, val);
> +               if (ret < 0)
> +                       return ret;
> +               *opmode = ret;
> +               return 0;
> +       }
> +
> +       /* mode */
> +       switch (*opmode) {
> +       case OPMODE_OFF:
> +               mode = MAX77686_LDO_MODE_OFF;
> +               break;
> +       case OPMODE_LPM:
> +               switch (ldo) {
> +               case 2:
> +               case 6:
> +               case 7:
> +               case 8:
> +               case 10:
> +               case 11:
> +               case 12:
> +               case 14:
> +               case 15:
> +               case 16:
> +                       return -EINVAL;
> +               default:
> +                       mode = MAX77686_LDO_MODE_LPM;
> +               }
> +               break;
> +       case OPMODE_STANDBY:
> +               switch (ldo) {
> +               case 2:
> +               case 6:
> +               case 7:
> +               case 8:
> +               case 10:
> +               case 11:
> +               case 12:
> +               case 14:
> +               case 15:
> +               case 16:
> +                       mode = MAX77686_LDO_MODE_STANDBY;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +               break;
> +       case OPMODE_STANDBY_LPM:
> +               mode = MAX77686_LDO_MODE_STANDBY_LPM;
> +               break;
> +       case OPMODE_ON:
> +               mode = MAX77686_LDO_MODE_ON;
> +               break;
> +       default:
> +               mode = 0xff;
> +       }
> +
> +       if (mode == 0xff) {
> +               error("Wrong mode: %d for ldo%d", *opmode, ldo);
> +               return -EINVAL;
> +       }
> +
> +       val &= ~MAX77686_LDO_MODE_MASK;
> +       val |= mode;
> +       ret = pmic_write(dev->parent, adr, &val, 1);
> +
> +       return ret;
> +}
> +
> +static int max77686_ldo_enable(struct udevice *dev, int op, bool *enable)
> +{
> +       int ret, on_off;
> +
> +       if (op == PMIC_OP_GET) {
> +               ret = max77686_ldo_mode(dev, op, &on_off);
> +               if (ret)
> +                       return ret;
> +
> +               switch (on_off) {
> +               case OPMODE_OFF:
> +                       *enable = 0;
> +                       break;
> +               case OPMODE_ON:
> +                       *enable = 1;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +       } else if (op == PMIC_OP_SET) {
> +               switch (*enable) {
> +               case 0:
> +                       on_off = OPMODE_OFF;
> +                       break;
> +               case 1:
> +                       on_off = OPMODE_ON;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +
> +               ret = max77686_ldo_mode(dev, op, &on_off);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int max77686_buck_mode(struct udevice *dev, int op, int *opmode)
> +{
> +       unsigned int ret, mask, adr, mode, mode_shift;
> +       unsigned char val;
> +       int buck;
> +
> +       buck = dev2num(dev);
> +       if (buck < 1 || buck > MAX77686_BUCK_NUM) {
> +               error("Wrong buck number: %d", buck);
> +               return -EINVAL;
> +       }
> +
> +       adr = max77686_buck_addr[buck];
> +
> +       /* mask */
> +       switch (buck) {
> +       case 2:
> +       case 3:
> +       case 4:
> +               mode_shift = MAX77686_BUCK_MODE_SHIFT_2;
> +               break;
> +       default:
> +               mode_shift = MAX77686_BUCK_MODE_SHIFT_1;
> +       }
> +
> +       mask = MAX77686_BUCK_MODE_MASK << mode_shift;
> +
> +       ret = pmic_read(dev->parent, adr, &val, 1);
> +       if (ret)
> +               return ret;
> +
> +       if (op == PMIC_OP_GET) {
> +               val &= mask;
> +               val >>= mode_shift;
> +               ret = max77686_buck_hex2mode(buck, val);
> +               if (ret < 0)
> +                       return ret;
> +               *opmode = ret;
> +               return 0;
> +       }
> +
> +       /* mode */
> +       switch (*opmode) {
> +       case OPMODE_OFF:
> +               mode = MAX77686_BUCK_MODE_OFF;
> +               break;
> +       case OPMODE_STANDBY:
> +               switch (buck) {
> +               case 1:
> +               case 2:
> +               case 3:
> +               case 4:
> +                       mode = MAX77686_BUCK_MODE_STANDBY << mode_shift;
> +                       break;
> +               default:
> +                       mode = 0xff;
> +               }
> +               break;
> +       case OPMODE_LPM:
> +               switch (buck) {
> +               case 2:
> +               case 3:
> +               case 4:
> +                       mode = MAX77686_BUCK_MODE_LPM << mode_shift;
> +                       break;
> +               default:
> +                       mode = 0xff;
> +               }
> +               break;
> +       case OPMODE_ON:
> +               mode = MAX77686_BUCK_MODE_ON << mode_shift;
> +               break;
> +       default:
> +               mode = 0xff;
> +       }
> +
> +       if (mode == 0xff) {
> +               error("Wrong mode: %d for buck: %d\n", *opmode, buck);
> +               return -EINVAL;
> +       }
> +
> +       val &= ~mask;
> +       val |= mode;
> +       ret = pmic_write(dev->parent, adr, &val, 1);
> +
> +       return ret;
> +}
> +
> +static int max77686_buck_enable(struct udevice *dev, int op, bool *enable)
> +{
> +       int ret, on_off;
> +
> +       if (op == PMIC_OP_GET) {
> +               ret = max77686_buck_mode(dev, op, &on_off);
> +               if (ret)
> +                       return ret;
> +
> +               switch (on_off) {
> +               case OPMODE_OFF:
> +                       *enable = false;
> +                       break;
> +               case OPMODE_ON:
> +                       *enable = true;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +       } else if (op == PMIC_OP_SET) {
> +               switch (*enable) {
> +               case 0:
> +                       on_off = OPMODE_OFF;
> +                       break;
> +               case 1:
> +                       on_off = OPMODE_ON;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +
> +               ret = max77686_buck_mode(dev, op, &on_off);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int max77686_ldo_ofdata_to_platdata(struct udevice *dev)
> +{
> +       struct dm_regulator_info *info = dev->uclass_priv;
> +       int ret;
> +
> +       ret = regulator_ofdata_to_platdata(dev);
> +       if (ret)
> +               return ret;
> +
> +       info->type = REGULATOR_TYPE_LDO;
> +       info->mode_count = max77686_ldo_modes(dev2num(dev), &info->mode);
> +
> +       return 0;
> +}
> +
> +static int ldo_get_value(struct udevice *dev)
> +{
> +       int uV;
> +       int ret;
> +
> +       ret = max77686_ldo_val(dev, PMIC_OP_GET, &uV);
> +       if (ret)
> +               return ret;
> +
> +       return uV;
> +}
> +
> +static int ldo_set_value(struct udevice *dev, int uV)
> +{
> +       return max77686_ldo_val(dev, PMIC_OP_SET, &uV);
> +}
> +
> +static bool ldo_get_enable(struct udevice *dev)
> +{
> +       bool enable = false;
> +       int ret;
> +
> +       ret = max77686_ldo_enable(dev, PMIC_OP_GET, &enable);
> +       if (ret)
> +               return ret;
> +
> +       return enable;
> +}
> +
> +static int ldo_set_enable(struct udevice *dev, bool enable)
> +{
> +       return max77686_ldo_enable(dev, PMIC_OP_SET, &enable);
> +}
> +
> +static int ldo_get_mode(struct udevice *dev)
> +{
> +       int mode;
> +       int ret;
> +
> +       ret = max77686_ldo_mode(dev, PMIC_OP_GET, &mode);
> +       if (ret)
> +               return ret;
> +
> +       return mode;
> +}
> +
> +static int ldo_set_mode(struct udevice *dev, int mode)
> +{
> +       return max77686_ldo_mode(dev, PMIC_OP_SET, &mode);
> +}
> +
> +static int max77686_buck_ofdata_to_platdata(struct udevice *dev)
> +{
> +       struct dm_regulator_info *info = dev->uclass_priv;
> +       int ret;
> +
> +       ret = regulator_ofdata_to_platdata(dev);
> +       if (ret)
> +               return ret;
> +
> +       info->type = REGULATOR_TYPE_BUCK;
> +       info->mode_count = max77686_buck_modes(dev2num(dev), &info->mode);
> +
> +       return 0;
> +}
> +
> +static int buck_get_value(struct udevice *dev)
> +{
> +       int uV;
> +       int ret;
> +
> +       ret = max77686_buck_val(dev, PMIC_OP_GET, &uV);
> +       if (ret)
> +               return ret;
> +
> +       return uV;
> +}
> +
> +static int buck_set_value(struct udevice *dev, int uV)
> +{
> +       return max77686_buck_val(dev, PMIC_OP_SET, &uV);
> +}
> +
> +static bool buck_get_enable(struct udevice *dev)
> +{
> +       bool enable = false;
> +       int ret;
> +
> +       ret = max77686_buck_enable(dev, PMIC_OP_GET, &enable);
> +       if (ret)
> +               return ret;
> +
> +       return enable;
> +}
> +
> +static int buck_set_enable(struct udevice *dev, bool enable)
> +{
> +       return max77686_buck_enable(dev, PMIC_OP_SET, &enable);
> +}
> +
> +static int buck_get_mode(struct udevice *dev)
> +{
> +       int mode;
> +       int ret;
> +
> +       ret = max77686_buck_mode(dev, PMIC_OP_GET, &mode);
> +       if (ret)
> +               return ret;
> +
> +       return mode;
> +}
> +
> +static int buck_set_mode(struct udevice *dev, int mode)
> +{
> +       return max77686_buck_mode(dev, PMIC_OP_SET, &mode);
> +}
> +
> +static const struct dm_regulator_ops max77686_ldo_ops = {
> +       .get_value  = ldo_get_value,
> +       .set_value  = ldo_set_value,
> +       .get_enable = ldo_get_enable,
> +       .set_enable = ldo_set_enable,
> +       .get_mode   = ldo_get_mode,
> +       .set_mode   = ldo_set_mode,
> +};
> +
> +static const struct udevice_id max77686_ldo_ids[] = {
> +       { .compatible = "LDO1", .data = 1 },
> +       { .compatible = "LDO2", .data = 2 },
> +       { .compatible = "LDO3", .data = 3 },
> +       { .compatible = "LDO4", .data = 4 },
> +       { .compatible = "LDO5", .data = 5 },
> +       { .compatible = "LDO6", .data = 6 },
> +       { .compatible = "LDO7", .data = 7 },
> +       { .compatible = "LDO8", .data = 8 },
> +       { .compatible = "LDO9", .data = 9 },
> +       { .compatible = "LDO10", .data = 10 },
> +       { .compatible = "LDO11", .data = 11 },
> +       { .compatible = "LDO12", .data = 12 },
> +       { .compatible = "LDO13", .data = 13 },
> +       { .compatible = "LDO14", .data = 14 },
> +       { .compatible = "LDO15", .data = 15 },
> +       { .compatible = "LDO16", .data = 16 },
> +       { .compatible = "LDO17", .data = 17 },
> +       { .compatible = "LDO18", .data = 18 },
> +       { .compatible = "LDO19", .data = 19 },
> +       { .compatible = "LDO20", .data = 20 },
> +       { .compatible = "LDO21", .data = 21 },
> +       { .compatible = "LDO22", .data = 22 },
> +       { .compatible = "LDO23", .data = 23 },
> +       { .compatible = "LDO24", .data = 24 },
> +       { .compatible = "LDO25", .data = 25 },
> +       { .compatible = "LDO26", .data = 26 },
> +       { },
> +};
> +
> +U_BOOT_DRIVER(max77686_ldo) = {
> +       .name = "max77686 ldo",
> +       .id = UCLASS_REGULATOR,
> +       .ops = &max77686_ldo_ops,
> +       .of_match = max77686_ldo_ids,
> +       .ofdata_to_platdata = max77686_ldo_ofdata_to_platdata,
> +};
> +
> +static const struct dm_regulator_ops max77686_buck_ops = {
> +       .get_value  = buck_get_value,
> +       .set_value  = buck_set_value,
> +       .get_enable = buck_get_enable,
> +       .set_enable = buck_set_enable,
> +       .get_mode   = buck_get_mode,
> +       .set_mode   = buck_set_mode,
> +};
> +
> +static const struct udevice_id max77686_buck_ids[] = {
> +       { .compatible = "BUCK1", .data = 1 },
> +       { .compatible = "BUCK2", .data = 2 },
> +       { .compatible = "BUCK3", .data = 3 },
> +       { .compatible = "BUCK4", .data = 4 },
> +       { .compatible = "BUCK5", .data = 5 },
> +       { .compatible = "BUCK6", .data = 6 },
> +       { .compatible = "BUCK7", .data = 7 },
> +       { .compatible = "BUCK8", .data = 8 },
> +       { .compatible = "BUCK9", .data = 9 },
> +       { },
> +};
> +
> +U_BOOT_DRIVER(max77686_buck) = {
> +       .name = "max77686 buck",
> +       .id = UCLASS_REGULATOR,
> +       .ops = &max77686_buck_ops,
> +       .of_match = max77686_buck_ids,
> +       .ofdata_to_platdata = max77686_buck_ofdata_to_platdata,
> +};
> diff --git a/include/power/max77686_pmic.h b/include/power/max77686_pmic.h
> index fe26d13..81c27d8 100644
> --- a/include/power/max77686_pmic.h
> +++ b/include/power/max77686_pmic.h
> @@ -126,7 +126,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,23 +146,29 @@ 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_voltage(struct pmic *p, int buck, ulong uV);
>  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
> @@ -170,6 +179,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 */
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 12/17] dm: regulator: add fixed voltage regulator driver
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 12/17] dm: regulator: add fixed voltage " Przemyslaw Marczak
@ 2015-03-29 13:08       ` Simon Glass
  2015-04-03 16:09         ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:08 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This driver implements regulator uclass features for fixed value regulators.
> For getting the basic regulator device-tree node constraints, this driver calls
> function 'regulator_ofdata_to_platdata()'. The typical fixed regulator node
> provides few additional properties:
> - gpio
> - gpio-open-drain
> - enable-active-high
> - startup-delay-us
> All above are checked and keept in structure of type 'fixed_regulator_priv',
> which is private for each fixed-regulator device (dev->priv).
>
> The driver implements only three of regulator uclass features:
> - get_value
> - get_enable
> - set_enable
>
> The regulator calls and command line features can be used for fixed-regulator,
> and the proper error will be returned for prohibited.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>
> Changes v3:
> - new file
> - Kconfig add fixed-regulator entry
> ---
>  drivers/power/Kconfig            |   8 +++
>  drivers/power/regulator/Makefile |   1 +
>  drivers/power/regulator/fixed.c  | 124 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 133 insertions(+)
>  create mode 100644 drivers/power/regulator/fixed.c
>
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index 97abbf0..da1e866 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -120,6 +120,14 @@ config DM_REGULATOR_MAX77686
>         features for REGULATOR MAX77686. The driver implements get/set api for:
>         value, enable and mode.
>
> +config DM_REGULATOR_FIXED
> +       bool "Enable Driver Model for REGULATOR Fixed value"
> +       depends on DM_REGULATOR
> +       ---help---
> +       This config enables implementation of driver-model regulator uclass
> +       features for fixed value regulators. The driver implements get/set api
> +       for enable and get only for voltage value.
> +

Should be in drivers/regulator/Kconfig I think

>  config AXP221_DCDC1_VOLT
>         int "axp221 dcdc1 voltage"
>         depends on AXP221_POWER
> diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
> index 9d282e3..0a6a6d9 100644
> --- a/drivers/power/regulator/Makefile
> +++ b/drivers/power/regulator/Makefile
> @@ -5,4 +5,5 @@
>  # SPDX-License-Identifier:     GPL-2.0+
>  #
>
> +obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o
>  obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
> diff --git a/drivers/power/regulator/fixed.c b/drivers/power/regulator/fixed.c
> new file mode 100644
> index 0000000..45e9f84
> --- /dev/null
> +++ b/drivers/power/regulator/fixed.c
> @@ -0,0 +1,124 @@
> +/*
> + *  Copyright (C) 2015 Samsung Electronics
> + *
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <fdtdec.h>
> +#include <i2c.h>
> +#include <dm.h>
> +#include <asm/gpio.h>
> +#include <power/pmic.h>
> +#include <power/regulator.h>
> +#include <errno.h>
> +#include <dm.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +struct fixed_regulator_priv {
> +       struct gpio_desc gpio;
> +       bool gpio_open_drain;
> +       bool enable_active_high;
> +       unsigned startup_delay_us;

Docs for these?

> +};
> +
> +static int fixed_regulator_ofdata_to_platdata(struct udevice *dev)
> +{
> +       struct dm_regulator_info *info = dev->uclass_priv;
> +       struct fixed_regulator_priv *priv = dev->priv;
> +       int ret, offset = dev->of_offset;
> +
> +       /* Get the basic regulator constraints */
> +       ret = regulator_ofdata_to_platdata(dev);
> +       if (ret) {
> +               error("Can't get regulator constraints for %s", dev->name);
> +               return ret;
> +       }
> +
> +       /* Get fixed regulator gpio desc */
> +       ret = gpio_request_by_name_nodev(gd->fdt_blob, offset, "gpio", 0,
> +                                        &priv->gpio, GPIOD_IS_OUT);

Should not use the nodev version - you have a device.

> +       if (ret) {
> +               error("Fixed regulator gpio - not found! Error: %d", ret);
> +               return ret;
> +       }
> +
> +       /* Get fixed regulator addidional constraints */
> +       priv->gpio_open_drain = fdtdec_get_bool(gd->fdt_blob, offset,
> +                                               "gpio-open-drain");
> +       priv->enable_active_high = fdtdec_get_bool(gd->fdt_blob, offset,
> +                                                  "enable-active-high");
> +       priv->startup_delay_us = fdtdec_get_int(gd->fdt_blob, offset,
> +                                               "startup-delay-us", 0);
> +
> +       /* Set type to fixed - used by regulator command */
> +       info->type = REGULATOR_TYPE_FIXED;
> +
> +       debug("%s:%d\n", __func__, __LINE__);
> +       debug(" name:%s, boot_on:%d, active_hi: %d start_delay:%u\n",
> +               info->name, info->boot_on, priv->enable_active_high,
> +               priv->startup_delay_us);
> +
> +       return 0;
> +}
> +
> +static int fixed_regulator_get_value(struct udevice *dev)
> +{
> +       struct dm_regulator_info *info;
> +       int ret;
> +
> +       ret = regulator_info(dev, &info);
> +       if (ret)
> +               return ret;
> +
> +       if (info->min_uV == info->max_uV)
> +               return info->min_uV;
> +
> +       error("Invalid constraints for: %s\n", info->name);
> +
> +       return -EINVAL;
> +}
> +
> +static bool fixed_regulator_get_enable(struct udevice *dev)
> +{
> +       struct fixed_regulator_priv *priv = dev->priv;

get_get_priv(dev)

Please use that everywhere.

> +
> +       return dm_gpio_get_value(&priv->gpio);
> +}
> +
> +static int fixed_regulator_set_enable(struct udevice *dev, bool enable)
> +{
> +       struct fixed_regulator_priv *priv = dev->priv;
> +       int ret;
> +
> +       ret = dm_gpio_set_value(&priv->gpio, enable);
> +       if (ret) {
> +               error("Can't set regulator : %s gpio to: %d\n", dev->name,
> +                     enable);
> +               return ret;
> +       }
> +       return 0;
> +}
> +
> +static const struct dm_regulator_ops fixed_regulator_ops = {
> +       .get_value  = fixed_regulator_get_value,
> +       .get_enable = fixed_regulator_get_enable,
> +       .set_enable = fixed_regulator_set_enable,
> +};
> +
> +static const struct udevice_id fixed_regulator_ids[] = {
> +       { .compatible = "regulator-fixed" },
> +       { },
> +};
> +
> +U_BOOT_DRIVER(fixed_regulator) = {
> +       .name = "fixed regulator",
> +       .id = UCLASS_REGULATOR,
> +       .ops = &fixed_regulator_ops,
> +       .of_match = fixed_regulator_ids,
> +       .ofdata_to_platdata = fixed_regulator_ofdata_to_platdata,
> +       .priv_auto_alloc_size = sizeof(struct fixed_regulator_priv),
> +};
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 13/17] doc: driver-model: pmic and regulator uclass documentation
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 13/17] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
@ 2015-03-29 13:08       ` Simon Glass
  2015-04-03 16:09         ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:08 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes v2, V3:
> - update documentation with the framework api changes
> - remove doc file name 'dm' prefix
> ---
>  doc/driver-model/pmic-framework.txt | 350 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 350 insertions(+)
>  create mode 100644 doc/driver-model/pmic-framework.txt
>
> diff --git a/doc/driver-model/pmic-framework.txt b/doc/driver-model/pmic-framework.txt
> new file mode 100644
> index 0000000..72651dc
> --- /dev/null
> +++ b/doc/driver-model/pmic-framework.txt
> @@ -0,0 +1,350 @@
> +#
> +# (C) Copyright 2014-2015 Samsung Electronics
> +# Przemyslaw Marczak <p.marczak@samsung.com>
> +#
> +# SPDX-License-Identifier:      GPL-2.0+
> +#
> +
> +PMIC framework based on Driver Model
> +====================================
> +TOC:
> +1. Introduction
> +2. How does it work
> +3. Pmic driver api
> +4. Pmic driver
> +5. Pmic command
> +6. Regulator driver api
> +7. Regulator driver
> +8. Regulator command
> +
> +1. Introduction
> +===============
> +This is an introduction to driver-model multi uclass PMIC devices support.
> +At present it is based on two uclass types:
> +
> +- UCLASS_PMIC      - basic uclass type for PMIC I/O, which provides common
> +                     read/write interface.
> +- UCLASS_REGULATOR - additional uclass type for specific PMIC features, which
> +                     are various voltage regulators.
> +
> +New files:
> +UCLASS_PMIC:
> +- drivers/power/pmic-uclass.c
> +- include/power/pmic.h
> +UCLASS_REGULATOR:
> +- drivers/power/regulator-uclass.c
> +- include/power/regulator.h
> +
> +Commands:
> +- lib/cmd_pmic.c
> +- lib/cmd_regulator.c
> +
> +2. How doees it work
> +====================
> +The Power Management Integrated Circuits (PMIC) are used in embedded systems
> +to provide stable, precise and specific voltage power source with over-voltage
> +and thermal protection circuits.
> +
> +The single PMIC can provide various functionalities with single or multiple
> +interfaces, like in the example below.
> +
> +-- SoC
> + |
> + |            ______________________________________
> + | BUS 0     |       Multi interface PMIC IC        |--> LDO out 1
> + | e.g.I2C0  |                                      |--> LDO out N
> + |-----------|---- PMIC device 0 (READ/WRITE ops)   |
> + | or SPI0   |    |_ REGULATOR device (ldo/... ops) |--> BUCK out 1
> + |           |    |_ CHARGER device (charger ops)   |--> BUCK out M
> + |           |    |_ MUIC device (microUSB con ops) |
> + | BUS 1     |    |_ ...                            |---> BATTERY
> + | e.g.I2C1  |                                      |
> + |-----------|---- PMIC device 1 (READ/WRITE ops)   |---> USB in 1
> + . or SPI1   |    |_ RTC device (rtc ops)           |---> USB in 2
> + .           |______________________________________|---> USB out
> + .
> +
> +Since U-Boot provides driver model features for I2C and SPI bus drivers,
> +the PMIC devices should also support this. With the new basic uclass types
> +for PMIC I/O and regulator features, PMIC drivers can simply provide common
> +features, with multiple interface and instance support.
> +
> +Basic design assumptions:
> +
> +- Common I/O api - UCLASS_PMIC
> +The main assumption is to use UCLASS_PMIC device to provide I/O interface,

an I/O interface

> +for devices other uclass types. It is no matter what is the type of device
> +physical I/O interface.

devices of other uclass types. It doesn't matter what type of physical
I/O interface is used.


Usually PMIC devices are using SPI or I2C interface,

s/are using/use/

> +but use of any other interface (e.g. when PMIC is not directly connected
> +to the SoC) - is now possible. Drivers can use the same read/write api.
> +
> +- Common regulator api - UCLASS_REGULATOR
> +For setting the attributes of verious types of regulators with common api,

various

with a common

> +this uclass can be implemented. This allows to drive the each regulator output

allows driving each regulator's output

> +value, on/off state and custom defined operation modes. It also provides the

custom-defined

or perhaps just 'particular'

> +user interface for all operations.
> +For the very simple implementation, the regulator drivers are not required,

For simple implementations, regulator drivers are not required, so the
code can use pmic read/write directly.

> +so the code could base on pmic read/write only.
> +
> +When board device-tree file includes pmic subnode and the U_Boot compatible
> +driver exists, then the pmic device bind should looks like this:
> +
> +|_ root - will bind the device for I2C/SPI bus node
> +  |_ i2c/spi - should bind a device for pmic node
> +    |_ pmic (parent) - should bind child devices for its features
> +      |_ regulator (child)
> +      |_ charger   (child)
> +      |_ other     (child)
> +
> +Usually PMIC design provides:
> + - single I/O interface (single UCLASS_PMIC driver)
> +   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 (UCLASS_PMIC driver for each)
> +   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.
> +
> +3. Pmic driver api
> +===================
> +To use the pmic API, config: CONFIG_DM_PMIC is required.
> +The new driver API is very simple and is based on 'struct dm_pmic_ops',
> +which define two basic operations: device read and write.
> +
> +The platform data is introduced as 'struct pmic_platdata', to keep an info
> +about the device interface.
> +
> +The api is described in file: 'include/power/pmic.h'
> +Getting the device:
> +- by name  -  int pmic_get(char *name, struct udevice **pmic);
> +
> +Device I/O interface
> +Can be used with UCLASS_PMIC devices:
> +- Read from the device 'len' bytes at 'reg' into the buffer:
> +  int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len);
> +
> +- Write to the device 'len' bytes at 'reg' from the buffer:
> +  int pmic_write(struct udevice *dev, uint reg, uint8_t *buffer, int len);
> +
> +4. Pmic driver
> +============================
> +As an example of the pmic driver, please take a look into the MAX77686 driver
> +'drivers/power/pmic/max77686.c' and the description in 'include/power/pmic.h'
> +
> +The pmic driver can be defined by U_BOOT_DRIVER() macro:
> +
> +U_BOOT_DRIVER(pmic_max77686) = {
> +       .name = "max77686 pmic",
> +       .id = UCLASS_PMIC,
> +       .of_match = max77686_ids,     - Allows to bind by compatible
> +       .bind = max77686_bind,        - Function called at device bind
> +       .ops = &max77686_ops,         - Pmic api function calls
> +       .platdata_auto_alloc_size = sizeof(struct pmic_platdata),
> +};
> +
> +To bind the pmic device, field '.of_match' is required with proper compatible.
> +
> +Driver ops:
> +.reg_count = MAX77686_NUM_OF_REGS - number of pmic registers
> +.read = max77686_read() - allows to use pmic_read()
> +.write = max77686_write() - allows to use pmic_write()
> +
> +Driver bind:
> +- max77686_bind(): called on bind and calls pmic_child_node_scan() to bind the
> +  childs which are int "voltage-regulators" subnode. The bind is done using
> +  "voltage-regulators" property name.
> +
> +5. Pmic command
> +===============
> +To use the pmic command, config: CONFIG_DM_PMIC_CMD is required.
> +The new pmic command allows to:
> +- list pmic devices
> +- choose the current device (like the mmc command)
> +- read or write the pmic register
> +- dump all pmic registers
> +
> +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 0          - set dev seq 0 as current device
> +# pmic dump           - dump the registers of the current pmic dev
> +# pmic read 0x0       - read the register at address 0x0
> +# pmic write 0x0 0x1  - write 0x1 to the register at addres 0x0
> +
> +6. Regulator driver API
> +===================================
> +To use the regulator API, config: CONFIG_DM_REGULATOR is required.
> +The api is described in file: 'include/power/regulator.h'
> +The api is based on structure types:
> +- 'dm_regulator_info' - dev->uc_priv (auto-allocated)
> +- 'dm_regulator_mode' - included in regualtor info
> +
> +Regulator info keeps the constraints taken from the device-tree and also device
> +modes set by the driver (single or array).
> +
> +The fixed-regulator common driver keeps provides private structure type, to keep
> +its gpio attributes. The structure type 'fixed_regulator_priv' is keept in field

kept

> +'dev->priv' and is allocated by the driver.
> +
> +6.1 Regulator device-tree node:
> +The regulator node should looks like in the example:
> +ldo1 {
> +        regulator-compatible = "LDO1"; (not used here, but required for bind)
> +        regulator-name = "VDD_MMC_1.8V"; (mandatory) *
> +        regulator-min-microvolt = <1000000>; (mandatory) *
> +        regulator-max-microvolt = <1000000>; (mandatory) *
> +        regulator-min-microamp = <1000>; (optional) *
> +        regulator-max-microamp = <1000>; (optional) *
> +        regulator-always-on; (optional) *
> +        regulator-boot-on; (optional) *
> +};
> +
> +For the fixed-voltage regulator, voltage min and max must be equal:
> +VDD_MMC: regulator at 0 {
> +        compatible = "regulator-fixed";
> +        regulator-name = "VDD_MMC"; (mandatory) *
> +        regulator-min-microvolt = <2800000>; (mandatory) *
> +        regulator-max-microvolt = <2800000>; (mandatory) *
> +        regulator-always-on; (optional)
> +        regulator-boot-on; (optional)
> +        gpio = <&gpc 1 GPIO_ACTIVE_HIGH>; (optional)
> +        gpio-open-drain; (optional)
> +        enable-active-high; (optional)
> +        startup-delay-us
> +};
> +
> +The attributes signed with '*' can be taken by the common function which is
> +regulator_ofdata_to_platdata(). The rest attributes for fixed-regulator, are
> +taken by the driver.
> +
> +6.2 Getting the device:
> +- by name - int regulator_get(char *name, struct udevice **regulator);
> +
> +6.3 Device I/O interface
> +According to the framework assumptions, where uclass pmic device is a parent
> +of pmic devices other uclass types, the regulator devices should use uclass
> +pmic interface, with the parent as the device, like this:
> +- pmic_read(regulator->parent, some_reg, &some_buff, count);
> +- pmic_write(regulator->parent, some_reg, &some_buff, count);
> +
> +6.4 Device regulator operations
> +The regulator function calls are based on few data types:
> +- enum regulator_type {...} - standard types: LDO, BUCK, DVS, FIXED
> +- struct dm_regulator_info {...} - output name and value limits
> +- struct dm_regulator_mode {...} - output operation mode value and name
> +- struct dm_regulator_ops {...} - regulator driver function calls
> +
> +The first argument is always device. And the device uclass id must be always:
> +- 'UCLASS_REGULATOR'
> +
> +Function details are described in regulator header. The basic features are:
> +- regulator_info()            - get the regulator info structure
> +- regulator_mode()            - get the regulator mode info structure
> +- regulator_get/set_value()   - get/set the regulator output voltage
> +- regulator_get/set_current() - get/set the regulator output current
> +- regulator_get/set_enable()  - get/set the regulator output enable state
> +- regulator_get/set_mode()    - get/set the regulator output operation mode
> +
> +7. Regulator driver
> +======================================
> +As an example of the regulator driver, please take a look into the MAX77686
> +regulator driver (drivers/power/regulator/max77686.c). It implements two drivers
> +for the buck and ldo regulators. The buck driver structure:
> +
> +U_BOOT_DRIVER(max77686_buck) = {
> +        .name = "max77686 buck",
> +        .id = UCLASS_REGULATOR,
> +        .ops = &max77686_buck_ops,
> +        .of_match = max77686_buck_ids,
> +        .ofdata_to_platdata = max77686_buck_ofdata_to_platdata,
> +};
> +
> +The device-tree buck compatibles:
> +static const struct udevice_id max77686_buck_ids[] = {
> +        { .compatible = "BUCK1", .data = 1 },
> +        { .compatible = "BUCK2", .data = 2 },
> +        ...
> +        { }, (need always put null at the end)
> +};
> +
> +For each compatible, the data is set as a number of buck. This allows to get
> +the device number without parsing the compatible.
> +
> +The buck driver '.ofdata_to_platdata' call implementation:
> +static int max77686_buck_ofdata_to_platdata(struct udevice *dev)
> +{
> +        struct dm_regulator_info *info = dev->uclass_priv;
> +        int ret;
> +
> +       /* Get the regulation constraints by the common function */
> +        ret = regulator_ofdata_to_platdata(dev);
> +        if (ret)
> +                return ret;
> +
> +        info->type = REGULATOR_TYPE_BUCK;
> +        /**
> +         * The device mode array is filled by internal driver function with
> +         * the proper name for each mode id.
> +         */
> +        info->mode_count = max77686_buck_modes(dev2num(dev), &info->mode);
> +
> +        return 0;
> +}
> +
> +And the call to regulator_ofdata_to_platdata() is responsible for filling the
> +regulator output constraints, which are keepts in device-tree regulator nodes,
> +e.g. 'arch/arm/dts/exynos4412-odroid.dts'
> +
> +For the I/O, this driver uses pmic_read/write calls, with the parent device
> +as the first argument, e.g.: pmic_read(dev->parent, adr, &val, 1);
> +
> +8. Regulator command
> +====================
> +To use the pmic command, config: CONFIG_DM_REGULATOR_CMD is required.
> +
> +This command is based on driver model regulator api.
> +User interface features:
> +- list                   - list UCLASS regulator devices
> +- regulator dev [id]     - show or [set] operating regulator device
> +- regulator [info]       - print constraints info
> +- regulator [status]     - print operating status
> +- regulator [value] [-f] - print/[set] voltage value [uV] (force)
> +- regulator [current]    - print/[set] current value [uA]
> +- regulator [mode_id]    - print/[set] operating mode id
> +- regulator [enable]     - enable the regulator output
> +- regulator [disable]    - disable the regulator output
> +
> +If pmic device driver provides support to this another pmic uclass, then this
> +command provides useful user interface. It was designed to allow safe I/O access
> +to the pmic device, without the pmic documentation. If driver provide regulator
> +output - value and mode info - then user can operate on it.
> +
> +Example of usage:
> +regulator list              - look after regulator 'seq' number
> +regulator dev 'seq'         - set current device
> +regulator status            - show device status
> +regulator info              - list device available regulation constraints
> +regulator value             - prints device voltage value
> +regulator value 1000000     - set device voltage value to 1000000 uV
> +regulator value 1200000 -f  - if value exceeds limits - set force
> +regulator mode              - print device operation mode id
> +regulator mode 5            - set devices operation mode to '5' (mode id)
> +
> +The -f option (forcibly) or mode - only if descriptor is available

I think something like this would be clearer:

The -f option (forcibly) and 'mode' sub-command are only available if
the regulator descriptor is available


> +
> +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.

changed.

What does "e..g. if some hardware uses pins header" mean? Can you
reword that to be clearer?

> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 15/17] odroid: board: add support to dm pmic api
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 15/17] odroid: board: add support to dm pmic api Przemyslaw Marczak
@ 2015-03-29 13:08       ` Simon Glass
  2015-04-03 16:09         ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:08 UTC (permalink / raw)
  To: u-boot

 Hi Przemyslaw,

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This commit change the old pmic framework calls with the new ones.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes v2:
> - remove board_init_i2c() call
> - update regulator calls
> - update headers
> - samsung/misc.c: include required header
>
> Changes v3:
> - adjust regulator calls to new api
> ---
>  board/samsung/common/misc.c   |   1 +
>  board/samsung/odroid/odroid.c | 113 +++++++++++++++++++++++++++++++++---------
>  2 files changed, 91 insertions(+), 23 deletions(-)
>
> diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
> index 1a77c82..f0d69d4 100644
> --- a/board/samsung/common/misc.c
> +++ b/board/samsung/common/misc.c
> @@ -16,6 +16,7 @@
>  #include <asm/arch/cpu.h>
>  #include <asm/gpio.h>
>  #include <linux/input.h>
> +#include <dm.h>
>  #include <power/pmic.h>
>  #include <mmc.h>
>
> diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
> index ae41c29..aa3b0ff 100644
> --- a/board/samsung/odroid/odroid.c
> +++ b/board/samsung/odroid/odroid.c
> @@ -12,7 +12,9 @@
>  #include <asm/arch/gpio.h>
>  #include <asm/gpio.h>
>  #include <asm/arch/cpu.h>
> +#include <dm.h>
>  #include <power/pmic.h>
> +#include <power/regulator.h>
>  #include <power/max77686_pmic.h>
>  #include <errno.h>
>  #include <mmc.h>
> @@ -405,15 +407,62 @@ static void board_gpio_init(void)
>
>  static int pmic_init_max77686(void)
>  {
> -       struct pmic *p = pmic_get("MAX77686_PMIC");
> +       struct udevice *dev;
> +       int ret;
>
> -       if (pmic_probe(p))
> -               return -ENODEV;
> +       ret = regulator_get("VDDQ_EMMC_1.8V", &dev);
> +       if (ret) {
> +               error("Regulator get error: %d", ret);
> +               return ret;
> +       }
> +
> +       ret = regulator_set_value(dev, 1800000);
> +       if (ret) {
> +               error("Regulator %s value setting error: %d", dev->name, ret);
> +               return ret;
> +       }
> +
> +       ret = regulator_set_enable(dev, true);
> +       if (ret) {
> +               error("Regulator %s enable error: %d", dev->name, ret);
> +               return ret;
> +       }

How about adding a function that finds a regulator, sets its voltage
and enables it? Then you can avoid duplicating the same code 3 times.

> +
> +       ret = regulator_get("TFLASH_2.8V", &dev);
> +       if (ret) {
> +               error("Regulator get error: %d", ret);
> +               return ret;
> +       }
> +
> +       ret = regulator_set_value(dev, 2800000);
> +       if (ret) {
> +               error("Regulator %s value setting error: %d", dev->name, ret);
> +               return ret;
> +       }
> +
> +       ret = regulator_set_enable(dev, true);
> +       if (ret) {
> +               error("Regulator %s enable error: %d", dev->name, ret);
> +               return ret;
> +       }
> +
> +       ret = regulator_get("VDDQ_EMMC_2.8V", &dev);
> +       if (ret) {
> +               error("Regulator get error: %d", ret);
> +               return ret;
> +       }
>
> -       /* 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 */
> +       ret = regulator_set_value(dev, 2800000);
> +       if (ret) {
> +               error("Regulator %s value setting error: %d", dev->name, ret);
> +               return ret;
> +       }
> +
> +       ret = regulator_set_enable(dev, true);
> +       if (ret) {
> +               error("Regulator %s enable error: %d", dev->name, ret);
> +               return ret;
> +       }
>
>         return 0;
>  }
> @@ -434,7 +483,6 @@ int exynos_init(void)
>
>  int exynos_power_init(void)
>  {
> -       pmic_init(0);
>         pmic_init_max77686();
>
>         return 0;
> @@ -443,19 +491,20 @@ int exynos_power_init(void)
>  #ifdef CONFIG_USB_GADGET
>  static int s5pc210_phy_control(int on)
>  {
> -       struct pmic *p_pmic;
> -
> -       p_pmic = pmic_get("MAX77686_PMIC");
> -       if (!p_pmic)
> -               return -ENODEV;
> +       struct udevice *dev;
> +       int ret;
>
> -       if (pmic_probe(p_pmic))
> -               return -1;
> +       ret = regulator_get("VDD_UOTG_3.0V", &dev);
> +       if (ret) {
> +               error("Regulator get error: %d", ret);
> +               return ret;
> +       }
>
>         if (on)
> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
> +               return regulator_set_mode(dev, OPMODE_ON);
>         else
> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
> +               return regulator_set_mode(dev, OPMODE_LPM);
> +
>  }
>
>  struct s3c_plat_otg_data s5pc210_otg_data = {
> @@ -472,7 +521,8 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
>  int board_usb_init(int index, enum usb_init_type init)
>  {
>  #ifdef CONFIG_CMD_USB
> -       struct pmic *p_pmic;
> +       struct udevice *dev;
> +       int ret;
>
>         /* Set Ref freq 0 => 24MHz, 1 => 26MHz*/
>         /* Odroid Us have it at 24MHz, Odroid Xs at 26MHz */
> @@ -490,14 +540,31 @@ int board_usb_init(int index, enum usb_init_type init)
>         /* Power off and on BUCK8 for LAN9730 */
>         debug("LAN9730 - Turning power buck 8 OFF and ON.\n");
>
> -       p_pmic = pmic_get("MAX77686_PMIC");
> -       if (p_pmic && !pmic_probe(p_pmic)) {
> -               max77686_set_buck_voltage(p_pmic, 8, 750000);
> -               max77686_set_buck_voltage(p_pmic, 8, 3300000);
> +       ret = regulator_get("VCC_P3V3_2.85V", &dev);
> +       if (ret) {
> +               error("Regulator get error: %d", ret);
> +               return ret;
>         }
>
> -#endif
> +       ret = regulator_set_enable(dev, true);
> +       if (ret) {
> +               error("Regulator %s enable setting error: %d", dev->name, ret);
> +               return ret;
> +       }
> +
> +       ret = regulator_set_value(dev, 750000);
> +       if (ret) {
> +               error("Regulator %s value setting error: %d", dev->name, ret);
> +               return ret;
> +       }
>
> +       ret = regulator_set_value(dev, 3300000);
> +       if (ret) {
> +               error("Regulator %s value setting error: %d", dev->name, ret);
> +               return ret;
> +       }
> +
> +#endif
>         debug("USB_udc_probe\n");
>         return s3c_udc_probe(&s5pc210_otg_data);
>  }
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 14/17] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 14/17] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
@ 2015-03-29 13:09       ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:09 UTC (permalink / raw)
  To: u-boot

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> In the power_init_board function call, regulator driver init is called,
> so before compile, make sure that any power framework is defined.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  board/samsung/common/board.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)

Acked-by: Simon Glass <sjg@chromium.org>


>
> diff --git a/board/samsung/common/board.c b/board/samsung/common/board.c
> index 2e17da8..c4cbb63 100644
> --- a/board/samsung/common/board.c
> +++ b/board/samsung/common/board.c
> @@ -21,9 +21,9 @@
>  #include <asm/arch/pinmux.h>
>  #include <asm/arch/power.h>
>  #include <asm/arch/system.h>
> -#include <power/pmic.h>
>  #include <asm/arch/sromc.h>
>  #include <lcd.h>
> +#include <i2c.h>
>  #include <samsung/misc.h>
>
>  DECLARE_GLOBAL_DATA_PTR;
> @@ -168,7 +168,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();
> --
> 1.9.1
>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 16/17] odroid: dts: add 'voltage-regulators' description to max77686 node
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 16/17] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
@ 2015-03-29 13:10       ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:10 UTC (permalink / raw)
  To: u-boot

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Adding regulators subnode to fdt max77686 node, allows properly init
> regulators by the max77686 regulator driver. This enables the complete
> functionality of the regulator command.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes V2:
> - odroid: dts: remove pmic alias
>
> Changes V3:
> - none
> ---
>  arch/arm/dts/exynos4412-odroid.dts | 247 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 247 insertions(+)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 17/17] odroid: config: enable dm pmic, dm regulator and max77686 driver
  2015-03-24 20:30     ` [U-Boot] [PATCH v3 17/17] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
@ 2015-03-29 13:10       ` Simon Glass
  2015-04-03 16:10         ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-03-29 13:10 UTC (permalink / raw)
  To: u-boot

On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This change enables the configs required to init and setup max77686
> regulator driver, using the new driver model pmic and regulator API.
>
> This commits enables:
> - CONFIG_ERRNO_STR
> - CONFIG_DM_PMIC
> - CONFIG_DM_PMIC_CMD
> - CONFIG_DM_PMIC_MAX77686
> - CONFIG_DM_REGULATOR
> - CONFIG_DM_REGULATOR_CMD
> - CONFIG_DM_REGULATOR_MAX77686
>
> And removes the unused:
> - CONFIG_DM_I2C_COMPAT
> - CONFIG_POWER
> - CONFIG_POWER_I2C
> - CONFIG_POWER_MAX77686
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>

Acked-by: Simon Glass <sjg@chromium.org>

(but pelase rename the commands so that they are CONFIG_CMD_DM_PCI and
CONFIG_CMD_DM_REGULATOR)

> ---
> Changes V2:
> - config: enable dm i2c; cleanup
> - remove CONFIG_DM_I2C_COMPAT
> - enable regulator command
>
> Changes V3:
> - move options to defconfig
> ---
>  configs/odroid_defconfig | 8 +++++++-
>  include/configs/odroid.h | 5 -----
>  2 files changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/configs/odroid_defconfig b/configs/odroid_defconfig
> index d32b5b5..1e29abe 100644
> --- a/configs/odroid_defconfig
> +++ b/configs/odroid_defconfig
> @@ -4,5 +4,11 @@ CONFIG_TARGET_ODROID=y
>  CONFIG_OF_CONTROL=y
>  CONFIG_DEFAULT_DEVICE_TREE="exynos4412-odroid"
>  CONFIG_DM_I2C=y
> -CONFIG_DM_I2C_COMPAT=y
>  # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
> +CONFIG_ERRNO_STR=y
> +CONFIG_DM_PMIC=y
> +CONFIG_DM_PMIC_CMD=y
> +CONFIG_DM_PMIC_MAX77686=y
> +CONFIG_DM_REGULATOR=y
> +CONFIG_DM_REGULATOR_CMD=y
> +CONFIG_DM_REGULATOR_MAX77686=y
> diff --git a/include/configs/odroid.h b/include/configs/odroid.h
> index 5ee0abe..3874baa 100644
> --- a/include/configs/odroid.h
> +++ b/include/configs/odroid.h
> @@ -182,11 +182,6 @@
>  #define CONFIG_SYS_I2C_S3C24X0_SPEED   100000
>  #define CONFIG_SYS_I2C_S3C24X0_SLAVE   0
>
> -/* POWER */
> -#define CONFIG_POWER
> -#define CONFIG_POWER_I2C
> -#define CONFIG_POWER_MAX77686
> -
>  /* GPT */
>  #define CONFIG_RANDOM_UUID
>
> --
> 1.9.1
>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 05/17] dm: pmic: add implementation of driver model pmic uclass
  2015-03-29 13:07       ` Simon Glass
@ 2015-04-03 16:08         ` Przemyslaw Marczak
  2015-04-05 18:30           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-03 16:08 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/29/2015 03:07 PM, Simon Glass wrote:
> Hi Prazemyslaw,
>
> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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.
>
> Your Kconfig docs are a model to others! It describes the function
> very nicely without a lot of words.
>
> Please excuse the nits, they are intended to help it read better.
>

That's ok, it's better to fix if needs.

>>
>> 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/driver-model/pmic-framework.txt
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> 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
>>
>> Changes V3:
>> - pmic-uclass.c and pmic.h:
>>    -- remove  pmic_if_* functions
>>    -- add new function pmic_child_node_scan()
>> - add Kconfig entry
>> ---
>>   drivers/power/Kconfig       |  68 ++++++++++++++
>>   drivers/power/Makefile      |   1 +
>>   drivers/power/pmic-uclass.c | 130 +++++++++++++++++++++++++++
>>   include/dm/uclass-id.h      |   3 +
>>   include/power/pmic.h        | 210 ++++++++++++++++++++++++++++++++++++++++++++
>>   5 files changed, 412 insertions(+)
>>   create mode 100644 drivers/power/pmic-uclass.c
>>
>> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
>> index f8f0239..3513b46 100644
>> --- a/drivers/power/Kconfig
>> +++ b/drivers/power/Kconfig
>> @@ -1,3 +1,71 @@
>> +config DM_PMIC
>> +       bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC)"
>> +       depends on DM
>> +       ---help---
>> +       This config enables the driver-model multi uclass PMIC support.
>> +       Its basic uclass type is: UCLASS_PMIC, which is designed to provide
>> +       a common I/O interface for pmic child devices of various uclass types.
>
> Should be consistent - and use PMIC instead pmic.
>
>> +
>> +       Usually PMIC IC's provides more than one functionality, which means
>
> s/functionality/function/
>
>> +       that we should implement new uclass operations for each one. Usually
>> +       PMIC's provide those various functionalities by one or more interfaces.
>
> functions
>
>> +       And this could looks like this:
>> +
>> +       root device
>> +       |_ BUS 0 device (e.g. I2C0)                 - UCLASS_I2C/SPI/...
>> +       | |_ PMIC device (READ/WRITE ops)           - UCLASS_PMIC
>> +       |   |  (pmic sub-devices)
>> +       |   |_ REGULATOR device (ldo/buck/... ops)  - UCLASS_REGULATOR
>> +       |   |_ CHARGER device (charger ops)         - UCLASS_CHARGER (future)
>> +       |   |_ MUIC device (microUSB connector ops) - UCLASS_MUIC    (future)
>> +       |   |_ ...
>> +       |
>> +       |_ BUS 1 device (e.g. I2C1)                 - UCLASS_I2C/SPI/...
>> +          |_ PMIC device (READ/WRITE ops)          - UCLASS_PMIC
>> +            |  (pmic sub-devices)
>> +            |_ RTC device (rtc ops)                - UCLASS_MUIC (future)
>
> Would this be UCLASS_RTC?
>
>> +
>> +       From the I/O interface point of view, there can be found two PMIC types:
>> +       - single I/O interface - then UCLASS_PMIC device should be a parent of
>> +         all pmic sub-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
>
> Remove the word 'only'?
>
>> +       nodes/subnodes are proper defined, then:
>
> how about a blank line here?
>
>> +       |_ 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:
>> +             |  (pmic sub-devices)
>> +             |_ regulator (child)
>> +             |_ charger   (child)
>> +             |_ other     (child)
>> +
>> +       The same for other I/O bus nodes, if pmic uses more then one interface.
>> +
>> +       Note:
>> +       Each PMIC interface driver should use different compatible string.
>
> use a different
>
>> +
>> +       There are few basic functions in the UCLASS_PMIC driver API, declared
>
> There are a few
>
>> +       in the file 'include/power/pmic.h':
>> +        - int pmic_get(...);
>> +        - int pmic_read(struct udevice *pmic, ...);
>> +        - int pmic_write(struct udevice *pmic, ...);
>> +       For the simple implementation, in some cases the pmic uclass device,
>> +       can be self-sufficient to drive the PMIC functionality. In other case,
>
> s/self-sufficient/enough/
>
>> +       if each pmic sub-device(child) driver need access to the pmic specified
>
> PMIC-specified
>
>> +       registers, it need to know only the register address and then the access
>
> s/to//
>
>> +       is done through the parent pmic driver. Like in the example:
>
> s/is/can be/
>
> For example: (blank line here?)
>

Thanks, will fix the all above.

>> +       _ root driver
>> +       |_ dev: bus I2C0                                   - UCLASS_I2C
>> +       | |_ dev: my_pmic        (read/write)     (parent) - UCLASS_PMIC
>> +       |   |_ dev: my_regulator (set value/etc.) (child)  - UCLASS_REGULATOR
>> +       So the call will looks like below:
>> +       'pmic_write(regulator->parent, addr, value, len);'
>> +
>>   config AXP221_POWER
>>          boolean "axp221 / axp223 pmic support"
>>          depends on MACH_SUN6I || MACH_SUN8I
>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> index 2145652..5c9a189 100644
>> --- a/drivers/power/Makefile
>> +++ b/drivers/power/Makefile
>> @@ -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
>> diff --git a/drivers/power/pmic-uclass.c b/drivers/power/pmic-uclass.c
>> new file mode 100644
>> index 0000000..df49a4b
>> --- /dev/null
>> +++ b/drivers/power/pmic-uclass.c
>> @@ -0,0 +1,130 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * 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;
>> +
>> +int pmic_get(char *name, struct udevice **devp)
>> +{
>> +       struct udevice *dev;
>> +       int ret;
>> +
>> +       *devp = NULL;
>> +
>> +       for (ret = uclass_first_device(UCLASS_PMIC, &dev);
>> +            dev;
>> +            ret = uclass_next_device(&dev)) {
>> +               if (!strcmp(name, dev->name)) {
>> +                       *devp = dev;
>> +                       return ret;
>> +               }
>> +       }
>> +
>> +       return -ENODEV;
>
> This function will probe every PMIC, looking for the correct one.
>
> I think you should change pmic_get() to call a core function in
> uclass.c, like uclass_get_device_by_name(). It should use
> uclass_foreach_dev() so that it doesn't probe anything except the one
> it finds.You can use uclass_get_device_tail() at the end of that
> function - see uclass_get_device_by_of_offset() as an example.
>

Right, this is not good. The sequence, which you described, was in V2 of 
this code. I will adjust it and move to uclass.c.

>> +}
>> +
>> +int pmic_reg_count(struct udevice *dev)
>> +{
>> +       const struct dm_pmic_ops *ops;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC);
>> +       if (!ops)
>> +               return -ENODEV;
>> +
>> +       return ops->reg_count;
>> +}
>> +
>> +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len)
>> +{
>> +       const struct dm_pmic_ops *ops;
>> +
>> +       if (!buffer)
>> +               return -EFAULT;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC);
>> +       if (!ops)
>> +               return -ENODEV;
>
> -ENOSYS, although IMO assert() would be OK.
>
>> +
>> +       if (!ops->read)
>> +               return -EPERM;
>
> -ENOSYS
>
>> +
>> +       if (ops->read(dev, reg, buffer, len))
>> +               return -EIO;
>
> We must not swallow errors.
>
> ret = ops->read(...)
> if (ret)
>     return ret;
>
> Please adjust below also.
>

Right, will fix.

>> +
>> +       return 0;
>> +}
>> +
>> +int pmic_write(struct udevice *dev, uint reg, uint8_t *buffer, int len)
>> +{
>> +       const struct dm_pmic_ops *ops;
>> +
>> +       if (!buffer)
>> +               return -EFAULT;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_PMIC);
>> +       if (!ops)
>> +               return -ENODEV;
>> +
>> +       if (!ops->write)
>> +               return -EPERM;
>> +
>> +       if (ops->write(dev, reg, buffer, len))
>> +               return -EIO;
>> +
>> +       return 0;
>> +}
>> +
>> +int pmic_child_node_scan(struct udevice *parent,
>> +                        const char *optional_subnode,
>> +                        const char *compatible_property_name)
>> +{
>> +       const void *blob = gd->fdt_blob;
>> +       struct udevice *childp;
>> +       int offset = parent->of_offset;
>> +       int childs = 0;
>> +       int ret;
>> +
>> +       debug("%s: bind child for %s\n", __func__, parent->name);
>> +
>> +       if (optional_subnode) {
>> +               debug("Looking for %s optional subnode: %s \n", parent->name,
>> +                     optional_subnode);
>> +               offset = fdt_subnode_offset(blob, offset, optional_subnode);
>> +               if (offset <= 0) {
>> +                       debug("Pmic: %s subnode: %s not found!",
>> +                             parent->name, optional_subnode);
>> +                       return -ENXIO;
>> +               }
>> +       }
>> +
>> +       for (offset = fdt_first_subnode(blob, offset); offset > 0;
>> +            offset = fdt_next_subnode(blob, offset)) {
>> +               ret = lists_bind_fdt_by_prop(parent, blob, offset,
>> +                                            compatible_property_name, &childp);
>> +               if (ret)
>> +                       continue;
>> +
>> +               childs++;
>> +       }
>> +
>> +       return childs;
>> +}
>> +
>> +UCLASS_DRIVER(pmic) = {
>> +       .id             = UCLASS_PMIC,
>> +       .name           = "pmic",
>> +};
>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>> index 91bb90d..3ecfa23 100644
>> --- a/include/dm/uclass-id.h
>> +++ b/include/dm/uclass-id.h
>> @@ -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 uclass types */
>
> This comment seemed confusing - is it for PMICs or other things also?
>

Ah, my mistake when rebasing the code. This should be added with 
regulator uclass.

>> +       UCLASS_PMIC,
>> +
>>          UCLASS_COUNT,
>>          UCLASS_INVALID = -1,
>>   };
>> diff --git a/include/power/pmic.h b/include/power/pmic.h
>> index afbc5aa..b55304f 100644
>> --- a/include/power/pmic.h
>> +++ b/include/power/pmic.h
>> @@ -1,4 +1,7 @@
>>   /*
>> + *  Copyright (C) 2014-2015 Samsung Electronics
>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>>    *  Copyright (C) 2011-2012 Samsung Electronics
>>    *  Lukasz Majewski <l.majewski@samsung.com>
>>    *
>> @@ -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,210 @@ 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.
>
> I worry about having the docs in multiple places. Should you adjust
> this to point to the Kconfig? Or change the Kconfig to point here? Or
> maybe it would be better to drop both and put these in your README? I
> am concerned that if we later change something, we end up with
> inconsistent docs.
>

Right, this could be an issue in the future. I suppose that first thing 
is to look at this header file, before anyone starts work with this 
framework.
So maybe it's better keep the description only in this file, and add 
some basic info to Kconfig and doc, with the links to here?

>> + *
>> + * 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 the example below. It should be by the call
>> + * to 'pmic_child_node_scan()' - it allows bind in optional subnode and with
>> + * optional compatible property, e.g. "regulator-compatible" or "regulator".
>> + *
>> + * my_pmic.c - pmic driver:
>> + *
>> + * int my_pmic_bind(struct udevice *my_pmic)
>> + * {
>> + * ...
>> + *      ret = pmic_child_node_scan(my_pmic, NULL, "compatible");
>> + *                 ...
>> + * ...
>> + * }
>> + *
>> + * 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.
>> + */
>> +
>> +/**
>> + * 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.
>> + *
>> + * @reg_count: devices register count
>> + * @read:      read 'len' bytes at "reg" and store it into the 'buffer'
>> + * @write:     write 'len' bytes from the 'buffer' to the register at 'reg' address
>> + */
>> +struct dm_pmic_ops {
>> +       int reg_count;
>> +       int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int len);
>> +       int (*write)(struct udevice *dev, uint reg, uint8_t *buffer, int len);
>
> nit: const uint8_t *buffer?
>

Right, will fix this.

>> +};
>> +
>> +/* enum pmic_op_type - used for various pmic devices operation calls,
>> + * 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,
>> +};
>> +
>> +/**
>> + * 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)
>> +{
>> +       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_child_node_scan - scan the pmic node or its 'optional_subnode' for its
>> + * childs, by the compatible property name given by arg 'compatible_prop_name'.
>> + *
>> + * Few dts files with a different pmic regulators, uses different property
>> + * name for its driver 'compatible' string, like:
>> + * - 'compatible'
>> + * 'regulator-compatible'
>> + * The pmic driver should should bind its devices by proper compatible property
>> + * name.
>> + *
>> + * @parent                   - pmic parent device (usually UCLASS_PMIC)
>> + * @optional_subnode         - optional pmic subnode with the childs within it
>> + * @compatible_property_name - e.g.: 'compatible'; 'regulator-compatible', ...
>
> @return
>

Ok

>> + */
>> +int pmic_child_node_scan(struct udevice *parent,
>> +                        const char *optional_subnode,
>> +                        const char *compatible_property_name);
>> +
>> +/**
>> + * pmic_get: get the pmic device using its name
>> + *
>> + * @name - device name
>> + * @devp - returned pointer to the pmic device
>> + * Returns: 0 on success or negative value of errno.
>
> * @return 0 on success or negative value of errno.
>
>> + *
>> + * The returned devp device can be used with pmic_read/write calls
>> + */
>> +int pmic_get(char *name, struct udevice **devp);
>> +
>> +/**
>> + * pmic_reg_count: get the pmic register count
>> + *
>> + * The required pmic device can be obtained by 'pmic_get()'
>> + *
>> + * @dev - pointer to the UCLASS_PMIC device
>> + *
>> + * Returns: register count value on success or negative value of errno.
>> + */
>> +int pmic_reg_count(struct udevice *dev);
>> +
>> +/**
>> + * pmic_read/write: read/write to the UCLASS_PMIC device
>> + *
>> + * The required pmic device can be obtained by 'pmic_get()'
>> + *
>> + * @pmic - pointer to the UCLASS_PMIC device
>> + * @reg  - device register offset
>> + * @val  - value for write or its pointer to read
>
> This doesn't match the functions.
>

Ah, missed this - will fix

>> + *
>> + * Returns: 0 on success or negative value of errno.
>> + */
>> +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len);
>> +int pmic_write(struct udevice *dev, uint reg, uint8_t *buffer, int len);
>
> const uint8_t buffer for pmic_write()?
>

Yes, will adjust to ops prototypes.

>> +#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 +297,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)
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 07/17] dm: pmic: add pmic command
  2015-03-29 13:07       ` Simon Glass
@ 2015-04-03 16:08         ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-03 16:08 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/29/2015 03:07 PM, Simon Glass wrote:
> Hi Prazemyslaw,
>
> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This is new command for the pmic devices based on driver model pmic api.
>> Command features are unchanged:
>> - list          - list UCLASS pmic devices
>> - pmic dev [id]      - show or [set] operating pmic device (NEW)
>> - pmic dump          - dump registers
>> - pmic read address  - read byte of register at address
>> - pmic write address - write byte to register at address
>>
>> The only one change for this command is 'dev' subcommand.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>
>> Changes v3:
>> - new file
>> - add Kconfig
>> ---
>>   common/Kconfig    |  14 ++++
>>   common/Makefile   |   3 +
>>   common/cmd_pmic.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 227 insertions(+)
>>   create mode 100644 common/cmd_pmic.c
>>
>> diff --git a/common/Kconfig b/common/Kconfig
>> index e662774..1125e6d 100644
>> --- a/common/Kconfig
>> +++ b/common/Kconfig
>> @@ -335,4 +335,18 @@ config CMD_SETGETDCR
>>
>>   endmenu
>>
>> +menu "Power commands"
>> +config DM_PMIC_CMD
>
> CMD_DM_PMIC
>
> since this fits better with the other ones
>

Ok

>> +       bool "Enable Driver Model PMIC command"
>> +       depends on DM_PMIC
>> +       help
>> +         This is new command for the pmic devices based on driver model pmic api.
>> +         Command features are unchanged:
>> +         - list               - list UCLASS pmic devices
>> +         - pmic dev [id]      - show or [set] operating pmic device (NEW)
>> +         - pmic dump          - dump registers
>> +         - pmic read address  - read byte of register at address
>> +         - pmic write address - write byte to register at address
>> +         The only one change for this command is 'dev' subcommand.
>> +endmenu
>>   endmenu
>> diff --git a/common/Makefile b/common/Makefile
>> index 7216a13..d908851 100644
>> --- a/common/Makefile
>> +++ b/common/Makefile
>> @@ -208,6 +208,9 @@ obj-$(CONFIG_UPDATE_TFTP) += update.o
>>   obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
>>   obj-$(CONFIG_CMD_DFU) += cmd_dfu.o
>>   obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
>> +
>> +# Power
>> +obj-$(CONFIG_DM_PMIC_CMD) += cmd_pmic.o
>>   endif
>>
>>   ifdef CONFIG_SPL_BUILD
>> diff --git a/common/cmd_pmic.c b/common/cmd_pmic.c
>> new file mode 100644
>> index 0000000..978a94a
>> --- /dev/null
>> +++ b/common/cmd_pmic.c
>> @@ -0,0 +1,210 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +#include <common.h>
>> +#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>
>> +#include <i2c.h>
>> +#include <compiler.h>
>> +#include <errno.h>
>> +
>> +#define LIMIT_SEQ      3
>> +#define LIMIT_DEVNAME  20
>> +
>> +static struct udevice *pmic_curr;
>> +
>> +static int failed(const char *getset, const char *thing,
>> +                 const char *for_dev, int ret)
>> +{
>> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing, for_dev,
>> +                                                   ret, errno_str(ret));
>> +       return CMD_RET_FAILURE;
>> +}
>> +
>> +static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       int seq, ret = -ENODEV;
>> +
>> +       switch (argc) {
>> +       case 2:
>> +               seq = simple_strtoul(argv[1], NULL, 0);
>> +               ret = uclass_get_device_by_seq(UCLASS_PMIC, seq, &pmic_curr);
>> +       case 1:
>> +               if (!pmic_curr)
>> +                       return failed("get", "the", "device", ret);
>> +
>> +               printf("dev: %d @ %s\n", pmic_curr->seq, pmic_curr->name);
>> +       }
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       const char *parent_uc;
>> +       int ret;
>> +
>> +       printf("|%*s | %-*.*s| %-*.*s| %s @ %s\n",
>> +              LIMIT_SEQ, "Seq",
>> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
>> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Parent name",
>> +              "Parent uclass", "seq");
>> +
>> +       for (ret = uclass_first_device(UCLASS_PMIC, &dev); dev;
>> +            ret = uclass_next_device(&dev)) {
>
> Note this will probe everything.
>
> Perhaps we need uclass_find_first_device() and
> uclass_find_next_device() which don't probe before returning each
> device?
>
>

Right, I will extend the uclass.c API.

>> +               if (!dev)
>> +                       continue;
>> +
>> +               /* Parent uclass name*/
>> +               parent_uc = dev->parent->uclass->uc_drv->name;
>
> What do you think about a new function at some point, so you can call
> dev_uclass_name(dev_get_parent(dev))? We want to avoid digging around
> in the driver model data structures outside drivers/core.
>

Good idea, will move to uclass API.

>> +
>> +               printf("|%*d | %-*.*s| %-*.*s| %s @ %d\n",
>> +                      LIMIT_SEQ, dev->seq,
>> +                      LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
>> +                      LIMIT_DEVNAME, LIMIT_DEVNAME, dev->parent->name,
>> +                      parent_uc, dev->parent->seq);
>> +       }
>> +
>> +       if (ret)
>> +               return CMD_RET_FAILURE;
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_dump(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       int regs, ret;
>> +       uint8_t value;
>> +       uint reg;
>> +
>> +       if (!pmic_curr)
>> +               return failed("get", "current", "device", -ENODEV);
>> +
>> +       dev = pmic_curr;
>> +
>> +       printf("Dump pmic: %s registers\n", dev->name);
>> +
>> +       regs = pmic_reg_count(dev);
>> +       reg = 0;
>> +       while (reg < regs) {
>
> for (ret = 0; ret < regs; reg++) {
>

Ok.

>> +               ret = pmic_read(dev, reg, &value, 1);
>> +               if (ret)
>> +                       return failed("read", dev->name, "register", ret);
>> +
>> +               if (!(reg % 16))
>> +                       printf("\n0x%02x: ", reg);
>> +
>> +               printf("%2.2x ", value);
>> +               reg++;
>> +       }
>> +       printf("\n");
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_read(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       int regs, ret;
>> +       uint8_t value;
>> +       uint reg;
>> +
>> +       if (!pmic_curr)
>> +               return failed("get", "current", "device", -ENODEV);
>> +
>> +       dev = pmic_curr;
>> +
>> +       if (argc != 2)
>> +               return CMD_RET_USAGE;
>> +
>> +       reg = simple_strtoul(argv[1], NULL, 0);
>> +       regs = pmic_reg_count(dev);
>> +       if (reg > regs) {
>> +               printf("Pmic max reg: %d\n", regs);
>> +               return failed("read", "given", "address", -EFAULT);
>> +       }
>> +
>> +       ret = pmic_read(dev, reg, &value, 1);
>> +       if (ret)
>> +               return failed("read", dev->name, "register", ret);
>> +
>> +       printf("0x%02x: 0x%2.2x\n", reg, value);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       int regs, ret;
>> +       uint8_t value;
>> +       uint reg;
>> +
>> +       if (!pmic_curr)
>> +               return failed("get", "current", "device", -ENODEV);
>> +
>> +       dev = pmic_curr;
>> +
>> +       if (argc != 3)
>> +               return CMD_RET_USAGE;
>> +
>> +       reg = simple_strtoul(argv[1], NULL, 0);
>> +       regs = pmic_reg_count(dev);
>> +       if (reg > regs) {
>> +               printf("Pmic max reg: %d\n", regs);
>> +               return failed("write", "given", "address", -EFAULT);
>> +       }
>> +
>> +       value = simple_strtoul(argv[2], NULL, 0);
>> +
>> +       ret = pmic_write(dev, reg, &value, 1);
>> +       if (ret)
>> +               return failed("write", dev->name, "register", ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static cmd_tbl_t subcmd[] = {
>> +       U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
>> +       U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
>> +       U_BOOT_CMD_MKENT(dump, 1, 1, do_dump, "", ""),
>> +       U_BOOT_CMD_MKENT(read, 2, 1, do_read, "", ""),
>> +       U_BOOT_CMD_MKENT(write, 3, 1, do_write, "", ""),
>> +};
>> +
>> +static int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc,
>> +                       char * const argv[])
>> +{
>> +       cmd_tbl_t *cmd;
>> +
>> +       argc--;
>> +       argv++;
>> +
>> +       cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
>> +       if (cmd == NULL || argc > cmd->maxargs)
>> +               return CMD_RET_USAGE;
>> +
>> +       return cmd->cmd(cmdtp, flag, argc, argv);
>> +}
>> +
>> +U_BOOT_CMD(pmic, CONFIG_SYS_MAXARGS, 1, do_pmic,
>> +       "uclass operations",
>> +       "list          - list UCLASS pmic devices\n"
>
> Can we drop the 'UCLASS' ?
>

Ok

>> +       "pmic dev [id]      - show or [set] operating pmic device\n"
>> +       "pmic dump          - dump registers\n"
>> +       "pmic read address  - read byte of register at address\n"
>> +       "pmic write address - write byte to register at address\n"
>> +);
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 08/17] dm: regulator: add regulator command
  2015-03-29 13:07       ` Simon Glass
@ 2015-04-03 16:08         ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-03 16:08 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/29/2015 03:07 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This command is based on driver model regulator api.
>> User interface features:
>> - list                   - list UCLASS regulator devices
>> - regulator dev [id]     - show or [set] operating regulator device
>> - regulator [info]       - print constraints info
>> - regulator [status]     - print operating status
>> - regulator [value] [-f] - print/[set] voltage value [uV] (force)
>> - regulator [current]    - print/[set] current value [uA]
>> - regulator [mode_id]    - print/[set] operating mode id
>> - regulator [enable]     - enable the regulator output
>> - regulator [disable]    - disable the regulator output
>>
>> The 'force' option can be used for setting the value which exceeds the limits,
>> which are found in device-tree and are keept in regulators info structure.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>
>> ---
>> Changes v3:
>> - new file
>> - Kconfig entry
>> ---
>>   common/Kconfig         |  22 +++
>>   common/Makefile        |   1 +
>>   common/cmd_regulator.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 408 insertions(+)
>>   create mode 100644 common/cmd_regulator.c
>>
>> diff --git a/common/Kconfig b/common/Kconfig
>> index 1125e6d..48f360f 100644
>> --- a/common/Kconfig
>> +++ b/common/Kconfig
>> @@ -348,5 +348,27 @@ config DM_PMIC_CMD
>>            - pmic read address  - read byte of register at address
>>            - pmic write address - write byte to register at address
>>            The only one change for this command is 'dev' subcommand.
>> +
>> +config DM_REGULATOR_CMD
>
> CMD_DM_REGULATOR
>

Ok

>> +       bool "Enable Driver Model REGULATOR command"
>> +       depends on DM_REGULATOR
>> +       help
>> +         This command is based on driver model regulator api.
>> +         User interface features:
>> +         - list                   - list UCLASS regulator devices
>
> Do you need 'UCLASS" in there? What does it mean?
>

Right, it has no sense...

>> +         - regulator dev [id]     - show or [set] operating regulator device
>> +         - regulator [info]       - print constraints info
>> +         - regulator [status]     - print operating status
>> +         - regulator [value] [-f] - print/[set] voltage value [uV] (force)
>> +         - regulator [current]    - print/[set] current value [uA]
>> +         - regulator [mode_id]    - print/[set] operating mode id
>> +         - regulator [enable]     - enable the regulator output
>> +         - regulator [disable]    - disable the regulator output
>
> I don't think the sub-commands should be in [].
>

Right, will fix it.

>> +
>> +         The 'force' option can be used for setting the value which exceeds
>> +         the limit which are found in device-tree and are keept in regulators
>> +         info structure.
>> +
>>   endmenu
>> +
>>   endmenu
>> diff --git a/common/Makefile b/common/Makefile
>> index d908851..d63fe12 100644
>> --- a/common/Makefile
>> +++ b/common/Makefile
>> @@ -211,6 +211,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
>>
>>   # Power
>>   obj-$(CONFIG_DM_PMIC_CMD) += cmd_pmic.o
>> +obj-$(CONFIG_DM_REGULATOR_CMD) += cmd_regulator.o
>>   endif
>>
>>   ifdef CONFIG_SPL_BUILD
>> diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
>> new file mode 100644
>> index 0000000..d388b14
>> --- /dev/null
>> +++ b/common/cmd_regulator.c
>> @@ -0,0 +1,385 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +#include <common.h>
>> +#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>
>> +#include <i2c.h>
>> +#include <compiler.h>
>> +#include <errno.h>
>> +
>> +#define LIMIT_SEQ      3
>> +#define LIMIT_DEVNAME  20
>> +#define LIMIT_OFNAME   20
>> +#define LIMIT_INFO     12
>> +
>> +static struct udevice *reg_curr;
>> +
>> +static int failed(const char *getset, const char *thing,
>> +                 const char *for_dev, int ret)
>> +{
>> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing, for_dev,
>> +                                                   ret, errno_str(ret));
>> +       return CMD_RET_FAILURE;
>> +}
>> +
>> +static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct dm_regulator_info *info;
>> +       int seq, ret = CMD_RET_FAILURE;
>> +
>> +       switch (argc) {
>> +       case 2:
>> +               seq = simple_strtoul(argv[1], NULL, 0);
>> +               uclass_get_device_by_seq(UCLASS_REGULATOR, seq, &reg_curr);
>
> This can return an error.
>

Yes, but there is no case break, and the below regulator_info(), checks 
if reg_curr isn't NULL, and here the ret is returned.

>> +       case 1:
>> +               ret = regulator_info(reg_curr, &info);
>> +               if (ret)
>> +                       return failed("get", "the", "device", ret);
>> +
>> +               printf("dev: %d @ %s\n", reg_curr->seq, info->name);
>> +       }
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int get_curr_dev_and_info(struct udevice **devp,
>> +                                struct dm_regulator_info **infop)
>> +{
>> +       int ret = -ENODEV;
>> +
>> +       *devp = reg_curr;
>> +       if (!*devp)
>> +               return failed("get", "current", "device", ret);
>> +
>> +       ret = regulator_info(*devp, infop);
>> +       if (ret)
>> +               return failed("get", reg_curr->name, "info", ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct dm_regulator_info *info;
>> +       const char *parent_uc;
>> +       struct udevice *dev;
>> +       int ret;
>> +
>> +       printf("|%*s | %*.*s @ %-*.*s| %s @ %s\n",
>> +              LIMIT_SEQ, "Seq",
>> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
>> +              LIMIT_OFNAME, LIMIT_OFNAME, "fdtname",
>> +              "Parent", "uclass");
>> +
>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
>> +            ret = uclass_next_device(&dev)) {
>> +               if (regulator_info(dev, &info)) {
>> +                       printf("(null) info for: %s\n", dev->name);
>> +                       continue;
>> +               }
>> +
>> +               /* Parent uclass name*/
>> +               parent_uc = dev->parent->uclass->uc_drv->name;
>> +
>> +               printf("|%*d | %*.*s @ %-*.*s| %s @ %s\n",
>> +                      LIMIT_SEQ, dev->seq,
>> +                      LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
>> +                      LIMIT_OFNAME, LIMIT_OFNAME, info->name,
>> +                      dev->parent->name, parent_uc);
>> +       }
>> +
>> +       if (ret)
>> +               return CMD_RET_FAILURE;
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_info(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_info *info;
>> +       struct dm_regulator_mode *modes;
>> +       const char *parent_uc;
>> +       int mode_count;
>> +       int ret;
>> +       int i;
>> +
>> +       ret = get_curr_dev_and_info(&dev, &info);
>> +       if (ret)
>> +               return CMD_RET_FAILURE;
>> +
>> +       parent_uc = dev->parent->uclass->uc_drv->name;
>> +
>> +       printf("Uclass regulator dev %d info:\n", dev->seq);
>> +       printf("%-*s %s @ %s\n%-*s %s\n%-*s %s\n",
>> +              LIMIT_INFO, "* parent:", dev->parent->name, parent_uc,
>> +              LIMIT_INFO, "* dev name:", dev->name,
>> +              LIMIT_INFO, "* fdt name:", info->name);
>> +
>> +       printf("%-*s %d\n%-*s %d\n%-*s %d\n%-*s %d\n%-*s %s\n%-*s %s\n",
>> +              LIMIT_INFO, "* min uV:", info->min_uV,
>> +              LIMIT_INFO, "* max uV:", info->max_uV,
>> +              LIMIT_INFO, "* min uA:", info->min_uA,
>> +              LIMIT_INFO, "* max uA:", info->max_uA,
>> +              LIMIT_INFO, "* always on:", info->always_on ? "true" : "false",
>> +              LIMIT_INFO, "* boot on:", info->boot_on ? "true" : "false");
>> +
>> +       mode_count = regulator_mode(dev, &modes);
>> +       if (!mode_count)
>> +               return failed("get mode", "for mode", "null count", -EPERM);
>> +
>> +       if (mode_count < 0)
>> +               return failed("get", info->name, "mode count", mode_count);
>> +
>> +       printf("* operation modes:\n");
>> +       for (i = 0; i < mode_count; i++, modes++)
>> +               printf("  - mode %d (%s)\n", modes->id, modes->name);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static const char *get_mode_name(struct dm_regulator_mode *mode,
>> +                                int mode_count,
>> +                                int mode_id)
>> +{
>> +       while (mode_count--) {
>> +               if (mode->id == mode_id)
>> +                       return mode->name;
>> +               mode++;
>> +       }
>> +
>> +       return NULL;
>> +}
>> +
>> +static int do_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_info *info;
>> +       const char *mode_name;
>> +       int value, mode, ret;
>> +       bool enabled;
>> +
>> +       ret = get_curr_dev_and_info(&dev, &info);
>> +       if (ret)
>> +               return CMD_RET_FAILURE;
>> +
>> +       /* Value is mandatory */
>> +       value = regulator_get_value(dev);
>> +       if (value < 0)
>> +               return failed("get", info->name, "voltage value", value);
>> +
>> +       mode = regulator_get_mode(dev);
>> +       mode_name = get_mode_name(info->mode, info->mode_count, mode);
>> +       enabled = regulator_get_enable(dev);
>> +
>> +       printf("Regulator seq %d status:\n", dev->seq);
>> +       printf("%-*s %d\n%-*s %d (%s)\n%-*s %s\n",
>> +              LIMIT_INFO, " * value:", value,
>> +              LIMIT_INFO, " * mode:", mode, mode_name,
>> +              LIMIT_INFO, " * enabled:", enabled ? "true" : "false");
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_value(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_info *info;
>> +       int value;
>> +       int force;
>> +       int ret;
>> +
>> +       ret = get_curr_dev_and_info(&dev, &info);
>> +       if (ret)
>> +               return CMD_RET_FAILURE;
>> +
>> +       if (argc == 1) {
>> +               value = regulator_get_value(dev);
>> +               if (value < 0)
>> +                       return failed("get", info->name, "voltage", value);
>> +
>> +               printf("%d uV\n", value);
>> +               return CMD_RET_SUCCESS;
>> +       }
>> +
>> +       if (info->type == REGULATOR_TYPE_FIXED) {
>> +               printf("Fixed regulator value change not allowed.\n");
>> +               return CMD_RET_SUCCESS;
>> +       }
>> +
>> +       if (argc == 3)
>> +               force = !strcmp("-f", argv[2]);
>> +       else
>> +               force = 0;
>> +
>> +       value = simple_strtoul(argv[1], NULL, 0);
>> +       if ((value < info->min_uV || value > info->max_uV) && !force) {
>> +               printf("Value exceeds regulator constraint limits\n");
>> +               return CMD_RET_FAILURE;
>> +       }
>> +
>> +       ret = regulator_set_value(dev, value);
>> +       if (ret)
>> +               return failed("set", info->name, "voltage value", ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_current(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_info *info;
>> +       int current;
>> +       int ret;
>> +
>> +       ret = get_curr_dev_and_info(&dev, &info);
>> +       if (ret)
>> +               return CMD_RET_FAILURE;
>> +
>> +       if (argc == 1) {
>> +               current = regulator_get_current(dev);
>> +               if (current < 0)
>> +                       return failed("get", info->name, "current", current);
>> +
>> +               printf("%d uA\n", current);
>> +               return CMD_RET_SUCCESS;
>> +       }
>> +
>> +       if (info->type == REGULATOR_TYPE_FIXED) {
>> +               printf("Fixed regulator current change not allowed.\n");
>> +               return CMD_RET_SUCCESS;
>> +       }
>> +
>> +       current = simple_strtoul(argv[1], NULL, 0);
>> +       if (current < info->min_uA || current > info->max_uA) {
>> +               printf("Current exceeds regulator constraint limits\n");
>> +               return CMD_RET_FAILURE;
>> +       }
>> +
>> +       ret = regulator_set_current(dev, current);
>> +       if (ret)
>> +               return failed("set", info->name, "current value", ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_info *info;
>> +       int new_mode;
>> +       int mode;
>> +       int ret;
>> +
>> +       ret = get_curr_dev_and_info(&dev, &info);
>> +       if (ret)
>> +               return CMD_RET_FAILURE;
>> +
>> +       if (info->type == REGULATOR_TYPE_FIXED) {
>> +               printf("Fixed regulator mode option not allowed.\n");
>> +               return CMD_RET_SUCCESS;
>> +       }
>> +
>> +       if (argc == 1) {
>> +               mode = regulator_get_mode(dev);
>> +               if (mode < 0)
>> +                       return failed("get", info->name, "mode", ret);
>> +
>> +               printf("mode id: %d\n", mode);
>> +               return CMD_RET_SUCCESS;
>> +       }
>
> The code above is the same for the above 2 functions also. Can you
> move it to a common function? Something list
> get_non_fixed_regulator()?
>

Ok, will add something like this.

>> +
>> +       new_mode = simple_strtoul(argv[1], NULL, 0);
>> +
>> +       ret = regulator_set_mode(dev, new_mode);
>> +       if (ret)
>> +               return failed("set", info->name, "mode", ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_enable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_info *info;
>> +       int ret;
>> +
>> +       ret = get_curr_dev_and_info(&dev, &info);
>> +       if (ret)
>> +               return CMD_RET_FAILURE;
>> +
>> +       ret = regulator_set_enable(dev, true);
>> +       if (ret)
>> +               return failed("enable", "regulator", info->name, ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_disable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_info *info;
>> +       int ret;
>> +
>> +       ret = get_curr_dev_and_info(&dev, &info);
>> +       if (ret)
>> +               return CMD_RET_FAILURE;
>> +
>> +       ret = regulator_set_enable(dev, false);
>> +       if (ret)
>> +               return failed("disable", "regulator", info->name, ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static cmd_tbl_t subcmd[] = {
>> +       U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
>> +       U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
>> +       U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""),
>> +       U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
>> +       U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""),
>> +       U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""),
>> +       U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""),
>> +       U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""),
>> +       U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""),
>> +};
>> +
>> +static int do_regulator(cmd_tbl_t *cmdtp, int flag, int argc,
>> +                       char * const argv[])
>> +{
>> +       cmd_tbl_t *cmd;
>> +
>> +       argc--;
>> +       argv++;
>> +
>> +       cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
>> +       if (cmd == NULL || argc > cmd->maxargs)
>> +               return CMD_RET_USAGE;
>> +
>> +       return cmd->cmd(cmdtp, flag, argc, argv);
>> +}
>> +
>> +U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator,
>> +       "uclass operations",
>> +       "list         - list UCLASS regulator devices\n"
>> +       "regulator dev [id]     - show or [set] operating regulator device\n"
>> +       "regulator [info]       - print constraints info\n"
>> +       "regulator [status]     - print operating status\n"
>> +       "regulator [value] [-f] - print/[set] voltage value [uV] (force)\n"
>> +       "regulator [current]    - print/[set] current value [uA]\n"
>> +       "regulator [mode_id]    - print/[set] operating mode id\n"
>> +       "regulator [enable]     - enable the regulator output\n"
>> +       "regulator [disable]    - disable the regulator output\n"
>
> I think [info] should not be in brackets as it is not optional.
> Similar with other ones.

Right, will fix.

>
> Regards,
> Simon
>

Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 11/17] dm: regulator: add max77686 regulator driver
  2015-03-29 13:08       ` Simon Glass
@ 2015-04-03 16:08         ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-03 16:08 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/29/2015 03:08 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>
> Acked-by: Simon Glass <sjg@chromium.org>
>
> See nit below.
>
>> ---
>> Changes V2:
>> - change debug() to error()
>> - code cleanup
>> - fix data types
>> - ldo/buck state implementation
>> - adjust to new uclass api
>>
>> Changes V3:
>> - regulator/max77686.c:
>>    -- adjust to api changes
>>    -- add separeted drivers for buck and ldo
>>    -- bind regulators by its compatibles
>> - Kconfig: add regulator max77686 entry
>> ---
>>   Makefile                           |   1 +
>>   drivers/power/Kconfig              |   8 +
>>   drivers/power/Makefile             |   1 -
>>   drivers/power/regulator/Makefile   |   8 +
>>   drivers/power/regulator/max77686.c | 876 +++++++++++++++++++++++++++++++++++++
>>   include/power/max77686_pmic.h      |  24 +-
>>   6 files changed, 914 insertions(+), 4 deletions(-)
>>   create mode 100644 drivers/power/regulator/Makefile
>>   create mode 100644 drivers/power/regulator/max77686.c
>>
>> diff --git a/Makefile b/Makefile
>> index 1b3ebe7..9ecf3bb 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -632,6 +632,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/
>> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
>> index c4d4c72..97abbf0 100644
>> --- a/drivers/power/Kconfig
>> +++ b/drivers/power/Kconfig
>> @@ -112,6 +112,14 @@ config DM_REGULATOR
>>          Say y here to enable support for the axp221 / axp223 pmic found on most
>>          sun6i (A31) / sun8i (A23) boards.
>>
>> +config DM_REGULATOR_MAX77686
>> +       bool "Enable Driver Model for REGULATOR MAX77686"
>> +       depends on DM_REGULATOR && DM_PMIC_MAX77686
>> +       ---help---
>> +       This config enables implementation of driver-model regulator uclass
>> +       features for REGULATOR MAX77686. The driver implements get/set api for:
>> +       value, enable and mode.
>
> This should probably go in drivers/power/regulator/Kconfig.
>

Ok, will fix this.

[snip]

>
> Regards,
> Simon
>

Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass
  2015-03-29 13:07       ` Simon Glass
@ 2015-04-03 16:09         ` Przemyslaw Marczak
  2015-04-05 18:30           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-03 16:09 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/29/2015 03:07 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> 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.
>>
>> To get the regulator device:
>> - regulator_get()             - get the regulator device
>>
>> The regulator framework is based on a 'struct dm_regulator_ops'.
>> It provides a common function calls, for it's basic features:
>> - regulator_info()            - get the regulator info structure
>> - regulator_mode()            - get the regulator mode info structure
>> - regulator_get/set_value()   - get/set the regulator output voltage
>> - regulator_get/set_current() - get/set the regulator output current
>> - regulator_get/set_enable()  - get/set the regulator output enable state
>> - regulator_get/set_mode()    - get/set the regulator output operation mode
>>
>> An optional and useful regulator framework features are two descriptors:
>> - struct dm_regulator_info- describes the regulator name and output value limits
>>
>> - struct dm_regulator_mode - (array) describes the regulators operation modes
>>
>> 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
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> 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
>>
>> Changes V3:
>> - regulator-uclass.c and regulator.h:
>>    -- api cleanup
>>    -- new function regulator_ofdata_to_platdata()
>>    -- update of comments
>>    -- add Kconfig
>> ---
>>   drivers/power/Kconfig            |  33 ++++-
>>   drivers/power/Makefile           |   1 +
>>   drivers/power/regulator-uclass.c | 219 +++++++++++++++++++++++++++++++++
>>   include/dm/uclass-id.h           |   1 +
>>   include/power/regulator.h        | 259 +++++++++++++++++++++++++++++++++++++++
>>   5 files changed, 512 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/power/regulator-uclass.c
>>   create mode 100644 include/power/regulator.h
>>
>> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
>> index 3513b46..1e73c7a 100644
>> --- a/drivers/power/Kconfig
>> +++ b/drivers/power/Kconfig
>> @@ -66,7 +66,38 @@ config DM_PMIC
>>          So the call will looks like below:
>>          'pmic_write(regulator->parent, addr, value, len);'
>>
>> -config AXP221_POWER
>> +config DM_REGULATOR
>
> Can you move this to drivers/power/regulator?
>

Ok, will do this.

>> +       bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)"
>> +       depends on DM
>> +       ---help---
>> +       This config enables the driver-model regulator uclass support, which
>> +       provides implementation of driver model regulator uclass api.
>> +
>> +       Regulator uclass API calls:
>> +       To get the regulator device:
>> +       - regulator_get()             - get the regulator device
>> +
>> +       The regulator framework is based on a 'struct dm_regulator_ops'.
>> +       It provides a common function calls, for it's basic features:
>> +       - regulator_info()            - get the regulator info structure
>> +       - regulator_mode()            - get the regulator mode info structure
>> +       - regulator_get/set_value()   - operate on output voltage value
>> +       - regulator_get/set_current() - operate on output current value
>> +       - regulator_get/set_enable()  - operate on output enable state
>> +       - regulator_get/set_mode()    - operate on output operation mode
>> +
>> +       An optional and useful regulator framework features are two descriptors:
>> +       - struct dm_regulator_info - describes the regulator name and output limits
>> +       - struct dm_regulator_mode - describes the regulators operation mode
>> +
>> +       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 regulato
>> +
>> +       config AXP221_POWER
>
> I don't think this should be indented.
>

Right, will fix.

>>          boolean "axp221 / axp223 pmic support"
>>          depends on MACH_SUN6I || MACH_SUN8I
>>          default y
>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> index 5c9a189..a6b7012 100644
>> --- a/drivers/power/Makefile
>> +++ b/drivers/power/Makefile
>> @@ -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
>> diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
>> new file mode 100644
>> index 0000000..b6a00c6
>> --- /dev/null
>> +++ b/drivers/power/regulator-uclass.c
>> @@ -0,0 +1,219 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * 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_info(struct udevice *dev, struct dm_regulator_info **infop)
>> +{
>> +       if (!dev || !dev->uclass_priv)
>> +               return -ENODEV;
>
> assert() would be OK here, but if you prefer this, OK.
>

Sorry, I should add this in this version - will fix.

>> +
>> +       *infop = dev->uclass_priv;
>> +
>> +       return 0;
>> +}
>> +
>> +int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep)
>> +{
>> +       struct dm_regulator_info *info;
>> +       int ret;
>> +
>> +       ret = regulator_info(dev, &info);
>> +       if (ret)
>> +               return ret;
>> +
>> +       *modep = info->mode;
>> +       return info->mode_count;
>> +}
>> +
>> +int regulator_get_value(struct udevice *dev)
>> +{
>> +       const struct dm_regulator_ops *ops;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
>
> Can you instead define regulator_get_uclass_ops()?
>

So maybe I will add this to uclass.c ?

>> +       if (!ops)
>> +               return -ENODEV;
>
> -ENOSYS
>
> We normally assume that operations existing, so assert() is OK. But if
> you are worried about this, then this is OK.
>

Yes, but the pmic_get_uclass_ops, can return NULL, so I would prefer to 
leave this null-checking here and also add the assert.

>> +
>> +       if (!ops->get_value)
>> +               return -EPERM;
>
> -ENOSYS
>
> i.e.
>
> if (!ops || !ops->get_value)
>     return -ENOSYS;
>
> Please fix below also.
>

Ok.

>> +
>> +       return ops->get_value(dev);
>> +}
>> +
>> +int regulator_set_value(struct udevice *dev, int uV)
>> +{
>> +       const struct dm_regulator_ops *ops;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
>> +       if (!ops)
>> +               return -ENODEV;
>> +
>> +       if (!ops->set_value)
>> +               return -EPERM;
>> +
>> +       return ops->set_value(dev, uV);
>> +}
>> +
>> +int regulator_get_current(struct udevice *dev)
>> +{
>> +       const struct dm_regulator_ops *ops;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
>> +       if (!ops)
>> +               return -ENODEV;
>> +
>> +       if (!ops->get_current)
>> +               return -EPERM;
>> +
>> +       return ops->get_current(dev);
>> +}
>> +
>> +int regulator_set_current(struct udevice *dev, int uA)
>> +{
>> +       const struct dm_regulator_ops *ops;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
>> +       if (!ops)
>> +               return -ENODEV;
>> +
>> +       if (!ops->set_current)
>> +               return -EPERM;
>> +
>> +       return ops->set_current(dev, uA);
>> +}
>> +
>> +bool regulator_get_enable(struct udevice *dev)
>> +{
>> +       const struct dm_regulator_ops *ops;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
>> +       if (!ops)
>> +               return -ENODEV;
>> +
>> +       if (!ops->get_enable)
>> +               return -EPERM;
>> +
>> +       return ops->get_enable(dev);
>> +}
>> +
>> +int regulator_set_enable(struct udevice *dev, bool enable)
>> +{
>> +       const struct dm_regulator_ops *ops;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
>> +       if (!ops)
>> +               return -ENODEV;
>> +
>> +       if (!ops->set_enable)
>> +               return -EPERM;
>> +
>> +       return ops->set_enable(dev, enable);
>> +}
>> +
>> +int regulator_get_mode(struct udevice *dev)
>> +{
>> +       const struct dm_regulator_ops *ops;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
>> +       if (!ops)
>> +               return -ENODEV;
>> +
>> +       if (!ops->get_mode)
>> +               return -EPERM;
>> +
>> +       return ops->get_mode(dev);
>> +}
>> +
>> +int regulator_set_mode(struct udevice *dev, int mode)
>> +{
>> +       const struct dm_regulator_ops *ops;
>> +
>> +       ops = pmic_get_uclass_ops(dev, UCLASS_REGULATOR);
>> +       if (!ops)
>> +               return -ENODEV;
>> +
>> +       if (!ops->set_mode)
>> +               return -EPERM;
>> +
>> +       return ops->set_mode(dev, mode);
>> +}
>> +
>> +int regulator_ofdata_to_platdata(struct udevice *dev)
>> +{
>> +       struct dm_regulator_info *info = dev->uclass_priv;
>> +       int offset = dev->of_offset;
>> +       int len;
>> +
>> +       /* Mandatory constraints */
>> +       info->name = strdup(fdt_getprop(gd->fdt_blob, offset,
>> +                                       "regulator-name", &len));
>> +       if (!info->name)
>> +               return -ENXIO;
>
> -ENOMEM
>
> But there is no need to strdup() this - the device tree does not move.
>

Ok, will fix.

>> +
>> +       info->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
>> +                                     "regulator-min-microvolt", -1);
>> +       if (info->min_uV < 0)
>> +               return -ENXIO;
>> +
>> +       info->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
>> +                                     "regulator-max-microvolt", -1);
>> +       if (info->max_uV < 0)
>> +               return -ENXIO;
>> +
>> +       /* Optional constraints */
>> +       info->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
>> +                                     "regulator-min-microamp", -1);
>> +       info->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
>> +                                     "regulator-max-microamp", -1);
>> +       info->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
>> +                                        "regulator-always-on");
>> +       info->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
>> +                                        "regulator-boot-on");
>> +
>> +       return 0;
>> +}
>> +
>> +int regulator_get(char *name, struct udevice **devp)
>> +{
>> +       struct dm_regulator_info *info;
>> +       struct udevice *dev;
>> +       int ret;
>> +
>> +       *devp = NULL;
>> +
>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev);
>> +            dev;
>> +            ret = uclass_next_device(&dev)) {
>
> This will probe all devices. See my suggestion about creating
> uclass_find_first_device()/uclass_find_next_device() in the next
> patch.
>
> As before, I think this could use a function like uclass_get_device_by_name().
>

Yes, this is the same. But in this case, there is one more  issue, which 
is the regulator device name.
Usually after bind -> the dev->name is the same as node name. This is 
good, since it's natural that regulator IC, provides e.g "ldo1", or some 
other device-output name.
But we would like check the "regulator-name" property. For this 
patch-set, the name is fixed at device probe stage, when 
dev->ofdata_to_platdata() is called, and the regulator constraints, the 
same as it's name goes to struct dm_regulator_info.

I put the dm_regulator_info into uclass priv, because it it 
uclass-specific, the same as struct dm_i2c_bus is specific for i2c buses.

But, the ucalss priv is allocated at device probe stage.

I can't use the .per_child_platdata_auto_alloc_size, since the parent is 
a pmic, and its uclass type is different.

Actually I could, move the dm_regulator_info only to device platdata, 
but then, the drivers should take care of this uclass-specific structure 
allocation.
Is it acceptable?

But then, also ambiguous seem to be filling platdata (struct 
dm_regulator_info) in uclass post_bind() method.

So then, maybe reasonable is:
- move dm_regulator_info from dev->uclass_priv to dev->platdata - is 
allocated after device bind

- add .post_bind() method to regulator uclass, and get the 
"regulator-name" in it - only

- fill all platdata constraints on call to dev->ofdata_to_platdata()

Then, I could avoid probing every device, when checking the regulator 
name. But, still I can't use the uclass.c functions, since I'm checking 
the regulator info at dev->platdata.

So I update the function as below:

+ uclass_foreach_dev(dev, uc) {
+	if (!dev->platdata)
+		continue;
+
+	info = dev->platdata;
+	if (!strcmp(name, info->name)) {
+		ret = device_probe(dev);
+		if (ret)
+                              ....
+			*regulator = dev;
+			return ret;
+		}
+	}


>> +               info = dev->uclass_priv;
>> +               if (!info)
>> +                       continue;
>> +
>> +               if (!strcmp(name, info->name)) {
>> +                       *devp = dev;
>> +                       return ret;
>> +               }
>> +       }
>> +
>> +       return -ENODEV;
>> +}
>> +
>> +UCLASS_DRIVER(regulator) = {
>> +       .id             = UCLASS_REGULATOR,
>> +       .name           = "regulator",
>> +       .per_device_auto_alloc_size = sizeof(struct dm_regulator_info),
>> +};
>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>> index 3ecfa23..23356f3 100644
>> --- a/include/dm/uclass-id.h
>> +++ b/include/dm/uclass-id.h
>> @@ -37,6 +37,7 @@ enum uclass_id {
>>
>>          /* PMIC uclass and PMIC-related uclass types */
>>          UCLASS_PMIC,
>> +       UCLASS_REGULATOR,
>
> OK I see what the comment means now. I think it would be nice to have
> a comment here 'Voltage regulator'
>

Yes, will fix it.

>>
>>          UCLASS_COUNT,
>>          UCLASS_INVALID = -1,
>> diff --git a/include/power/regulator.h b/include/power/regulator.h
>> new file mode 100644
>> index 0000000..cf083c5
>> --- /dev/null
>> +++ b/include/power/regulator.h
>> @@ -0,0 +1,259 @@
>> +/*
>> + *  Copyright (C) 2014-2015 Samsung Electronics
>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#ifndef _INCLUDE_REGULATOR_H_
>> +#define _INCLUDE_REGULATOR_H_
>> +
>> +/* enum regulator_type - used for regulator_*() variant calls */
>> +enum regulator_type {
>> +       REGULATOR_TYPE_LDO = 0,
>> +       REGULATOR_TYPE_BUCK,
>> +       REGULATOR_TYPE_DVS,
>> +       REGULATOR_TYPE_FIXED,
>> +       REGULATOR_TYPE_OTHER,
>> +};
>> +
>> +/**
>> + * struct dm_regulator_mode - this structure holds an information about
>> + * each regulator operation mode. Probably in most cases - an array.
>> + * This will be probably a driver-static data, since it is device-specific.
>> + *
>> + * @id             - a driver-specific mode id
>> + * @register_value - a driver-specific value for its mode id
>> + * @name           - the name of mode - used for regulator command
>> + * Note:
>> + * The field 'id', should be always a positive number, since the negative values
>> + * are reserved for the errno numbers when returns the mode id.
>> + */
>> +struct dm_regulator_mode {
>> +       int id; /* Set only as >= 0 (negative value is reserved for errno) */
>> +       int register_value;
>> +       const char *name;
>> +};
>> +
>> +/**
>> + * struct dm_regulator_info - this structure holds an information about
>> + * each regulator constraints and supported operation modes. There is no "step"
>> + * voltage value - so driver should take care of this.
>> + *
>> + * @type       - one of 'enum regulator_type'
>> + * @mode       - pointer to the regulator mode (array if more than one)
>> + * @mode_count - number of '.mode' entries
>> + * @min_uV*    - minimum voltage (micro Volts)
>> + * @max_uV*    - maximum voltage (micro Volts)
>> + * @min_uA*    - minimum amperage (micro Amps)
>> + * @max_uA*    - maximum amperage (micro Amps)
>> + * @always_on* - bool type, true or false
>> + * @boot_on*   - bool type, true or false
>> + * @name*      - fdt regulator name - should be taken from the device tree
>> + *
>> + * Note: for attributes signed with '*'
>> + * These platform-specific constraints can be taken by regulator api function,
>> + * which is 'regulator_ofdata_to_platdata()'. Please read the description, which
>> + * can be found near the declaration of the mentioned function.
>> +*/
>> +struct dm_regulator_info {
>> +       enum regulator_type type;
>> +       struct dm_regulator_mode *mode;
>> +       int mode_count;
>> +       int min_uV;
>> +       int max_uV;
>> +       int min_uA;
>> +       int max_uA;
>> +       bool always_on;
>> +       bool boot_on;
>> +       const char *name;
>> +};
>> +
>> +/* PMIC regulator device operations */
>> +struct dm_regulator_ops {
>> +       /**
>> +        * The regulator output value function calls operates on a micro Volts.
>> +        *
>> +        * get/set_value - get/set output value of the given output number
>> +        * @dev          - regulator device
>> +        * Sets:
>> +        * @uV           - set the output value [micro Volts]
>> +        * Returns: output value [uV] on success or negative errno if fail.
>> +        */
>> +       int (*get_value)(struct udevice *dev);
>> +       int (*set_value)(struct udevice *dev, int uV);
>> +
>> +       /**
>> +        * The regulator output current function calls operates on a micro Amps.
>> +        *
>> +        * get/set_current - get/set output current of the given output number
>> +        * @dev            - regulator device
>> +        * Sets:
>> +        * @uA           - set the output current [micro Amps]
>> +        * Returns: output value [uA] on success or negative errno if fail.
>> +        */
>> +       int (*get_current)(struct udevice *dev);
>> +       int (*set_current)(struct udevice *dev, int uA);
>> +
>> +       /**
>> +        * The most basic feature of the regulator output is its enable state.
>> +        *
>> +        * get/set_enable - get/set enable state of the given output number
>> +        * @dev           - regulator device
>> +        * Sets:
>> +        * @enable         - set true - enable or false - disable
>> +        * Returns: true/false for get; or 0 / -errno for set.
>> +        */
>> +       bool (*get_enable)(struct udevice *dev);
>> +       int (*set_enable)(struct udevice *dev, bool enable);
>> +
>> +       /**
>> +        * The 'get/set_mode()' function calls should operate on a driver
>
> driver-
>
>> +        * specific mode definitions, which should be found in:
>> +        * field 'mode' of struct mode_desc.
>> +        *
>> +        * get/set_mode - get/set operation mode of the given output number
>> +        * @dev         - regulator device
>> +        * Sets
>> +        * @mode_id     - set output mode id (struct dm_regulator_mode->id)
>> +        * Returns: id/0 for get/set on success or negative errno if fail.
>> +        * Note:
>> +        * The field 'id' of struct type 'dm_regulator_mode', should be always
>> +        * positive number, since the negative is reserved for the error.
>> +        */
>> +       int (*get_mode)(struct udevice *dev);
>> +       int (*set_mode)(struct udevice *dev, int mode_id);
>> +};
>> +
>> +/**
>> + * regulator_info: returns a pointer to the devices regulator info structure
>> + *
>> + * @dev    - pointer to the regulator device
>> + * @infop  - pointer to the returned regulator info
>> + * Returns - 0 on success or negative value of errno.
>
> @return 0 on success...
>
>> + */
>> +int regulator_info(struct udevice *dev, struct dm_regulator_info **infop);
>> +
>> +/**
>> + * regulator_mode: returns a pointer to the array of regulator mode info
>> + *
>> + * @dev        - pointer to the regulator device
>> + * @modep      - pointer to the returned mode info array
>> + * Returns     - count of modep entries on success or negative errno if fail.
>> + */
>> +int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep);
>> +
>> +/**
>> + * regulator_get_value: get microvoltage voltage value of a given regulator
>> + *
>> + * @dev    - pointer to the regulator device
>> + * Returns - positive output value [uV] on success or negative errno if fail.
>> + */
>> +int regulator_get_value(struct udevice *dev);
>> +
>> +/**
>> + * regulator_set_value: set the microvoltage value of a given regulator.
>> + *
>> + * @dev    - pointer to the regulator device
>> + * @uV     - the output value to set [micro Volts]
>> + * Returns - 0 on success or -errno val if fails
>> + */
>> +int regulator_set_value(struct udevice *dev, int uV);
>> +
>> +/**
>> + * regulator_get_current: get microampere value of a given regulator
>> + *
>> + * @dev    - pointer to the regulator device
>> + * Returns - positive output current [uA] on success or negative errno if fail.
>> + */
>> +int regulator_get_current(struct udevice *dev);
>> +
>> +/**
>> + * regulator_set_current: set the microampere value of a given regulator.
>> + *
>> + * @dev    - pointer to the regulator device
>> + * @uA     - set the output current [micro Amps]
>> + * Returns - 0 on success or -errno val if fails
>> + */
>> +int regulator_set_current(struct udevice *dev, int uA);
>> +
>> +/**
>> + * regulator_get_enable: get regulator device enable state.
>> + *
>> + * @dev    - pointer to the regulator device
>> + * Returns - true/false of enable state
>> + */
>> +bool regulator_get_enable(struct udevice *dev);
>> +
>> +/**
>> + * regulator_set_enable: set regulator enable state
>> + *
>> + * @dev    - pointer to the regulator device
>> + * @enable - set true or false
>> + * Returns - 0 on success or -errno val if fails
>> + */
>> +int regulator_set_enable(struct udevice *dev, bool enable);
>> +
>> +/**
>> + * regulator_get_mode: get mode of a given device regulator
>> + *
>> + * @dev    - pointer to the regulator device
>> + * Returns - positive  mode number 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 'mode_desc' provides a field 'mode'
>> + * for this purpose and register_value for a raw register value.
>> + */
>> +int regulator_get_mode(struct udevice *dev);
>> +
>> +/**
>> + * regulator_set_mode: set given regulator mode
>> + *
>> + * @dev    - pointer to the regulator device
>> + * @mode   - mode type (field 'mode' of struct mode_desc)
>> + * 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 mode);
>> +
>> +/**
>> + * regulator_ofdata_to_platdata: get the regulator constraints from its fdt node
>> + * and put it into the regulator 'dm_regulator_info' (dev->uclass_priv).
>> + *
>> + * An example of required regulator fdt node constraints:
>> + * ldo1 {
>> + *      regulator-compatible = "LDO1"; (not used here, but required for bind)
>> + *      regulator-name = "VDD_MMC_1.8V"; (mandatory)
>> + *      regulator-min-microvolt = <1000000>; (mandatory)
>> + *      regulator-max-microvolt = <1000000>; (mandatory)
>> + *      regulator-min-microamp = <1000>; (optional)
>> + *      regulator-max-microamp = <1000>; (optional)
>> + *      regulator-always-on; (optional)
>> + *      regulator-boot-on; (optional)
>> + * };
>> + *
>> + * @dev    - pointer to the regulator device
>> + * Returns - 0 on success or -errno value if fails
>> + * Note2:
>> + * This function can be called at stage in which 'dev->uclass_priv' is non-NULL.
>> + * It is possible at two driver call stages: '.ofdata_to_platdata' or '.probe'.
>> + */
>> +int regulator_ofdata_to_platdata(struct udevice *dev);
>> +
>> +/**
>> + * regulator_get: returns the pointer to the pmic regulator device based on
>> + * regulator fdt node data
>> + *
>> + * @fdt_name - property 'regulator-name' value of regulator fdt node
>> + * @devp     - returned pointer to the regulator device
>> + * Returns: 0 on success or negative value of errno.
>> + *
>> + * The returned 'regulator' device can be used with:
>> + * - regulator_get/set_*
>> + */
>> +int regulator_get(char *fdt_name, struct udevice **devp);
>
> Can we s/fdt_name/name/ since by now it is a device name, not a device
> tree name.

Right, thanks for catching this all.

>> +
>> +#endif /* _INCLUDE_REGULATOR_H_ */
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 12/17] dm: regulator: add fixed voltage regulator driver
  2015-03-29 13:08       ` Simon Glass
@ 2015-04-03 16:09         ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-03 16:09 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/29/2015 03:08 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This driver implements regulator uclass features for fixed value regulators.
>> For getting the basic regulator device-tree node constraints, this driver calls
>> function 'regulator_ofdata_to_platdata()'. The typical fixed regulator node
>> provides few additional properties:
>> - gpio
>> - gpio-open-drain
>> - enable-active-high
>> - startup-delay-us
>> All above are checked and keept in structure of type 'fixed_regulator_priv',
>> which is private for each fixed-regulator device (dev->priv).
>>
>> The driver implements only three of regulator uclass features:
>> - get_value
>> - get_enable
>> - set_enable
>>
>> The regulator calls and command line features can be used for fixed-regulator,
>> and the proper error will be returned for prohibited.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>
>> Changes v3:
>> - new file
>> - Kconfig add fixed-regulator entry
>> ---
>>   drivers/power/Kconfig            |   8 +++
>>   drivers/power/regulator/Makefile |   1 +
>>   drivers/power/regulator/fixed.c  | 124 +++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 133 insertions(+)
>>   create mode 100644 drivers/power/regulator/fixed.c
>>
>> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
>> index 97abbf0..da1e866 100644
>> --- a/drivers/power/Kconfig
>> +++ b/drivers/power/Kconfig
>> @@ -120,6 +120,14 @@ config DM_REGULATOR_MAX77686
>>          features for REGULATOR MAX77686. The driver implements get/set api for:
>>          value, enable and mode.
>>
>> +config DM_REGULATOR_FIXED
>> +       bool "Enable Driver Model for REGULATOR Fixed value"
>> +       depends on DM_REGULATOR
>> +       ---help---
>> +       This config enables implementation of driver-model regulator uclass
>> +       features for fixed value regulators. The driver implements get/set api
>> +       for enable and get only for voltage value.
>> +
>
> Should be in drivers/regulator/Kconfig I think
>

Right, will fix.

>>   config AXP221_DCDC1_VOLT
>>          int "axp221 dcdc1 voltage"
>>          depends on AXP221_POWER
>> diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
>> index 9d282e3..0a6a6d9 100644
>> --- a/drivers/power/regulator/Makefile
>> +++ b/drivers/power/regulator/Makefile
>> @@ -5,4 +5,5 @@
>>   # SPDX-License-Identifier:     GPL-2.0+
>>   #
>>
>> +obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o
>>   obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
>> diff --git a/drivers/power/regulator/fixed.c b/drivers/power/regulator/fixed.c
>> new file mode 100644
>> index 0000000..45e9f84
>> --- /dev/null
>> +++ b/drivers/power/regulator/fixed.c
>> @@ -0,0 +1,124 @@
>> +/*
>> + *  Copyright (C) 2015 Samsung Electronics
>> + *
>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#include <common.h>
>> +#include <fdtdec.h>
>> +#include <i2c.h>
>> +#include <dm.h>
>> +#include <asm/gpio.h>
>> +#include <power/pmic.h>
>> +#include <power/regulator.h>
>> +#include <errno.h>
>> +#include <dm.h>
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +struct fixed_regulator_priv {
>> +       struct gpio_desc gpio;
>> +       bool gpio_open_drain;
>> +       bool enable_active_high;
>> +       unsigned startup_delay_us;
>
> Docs for these?
>

Right, will add.

>> +};
>> +
>> +static int fixed_regulator_ofdata_to_platdata(struct udevice *dev)
>> +{
>> +       struct dm_regulator_info *info = dev->uclass_priv;
>> +       struct fixed_regulator_priv *priv = dev->priv;
>> +       int ret, offset = dev->of_offset;
>> +
>> +       /* Get the basic regulator constraints */
>> +       ret = regulator_ofdata_to_platdata(dev);
>> +       if (ret) {
>> +               error("Can't get regulator constraints for %s", dev->name);
>> +               return ret;
>> +       }
>> +
>> +       /* Get fixed regulator gpio desc */
>> +       ret = gpio_request_by_name_nodev(gd->fdt_blob, offset, "gpio", 0,
>> +                                        &priv->gpio, GPIOD_IS_OUT);
>
> Should not use the nodev version - you have a device.
>

Yes, will update this.

>> +       if (ret) {
>> +               error("Fixed regulator gpio - not found! Error: %d", ret);
>> +               return ret;
>> +       }
>> +
>> +       /* Get fixed regulator addidional constraints */
>> +       priv->gpio_open_drain = fdtdec_get_bool(gd->fdt_blob, offset,
>> +                                               "gpio-open-drain");
>> +       priv->enable_active_high = fdtdec_get_bool(gd->fdt_blob, offset,
>> +                                                  "enable-active-high");
>> +       priv->startup_delay_us = fdtdec_get_int(gd->fdt_blob, offset,
>> +                                               "startup-delay-us", 0);
>> +
>> +       /* Set type to fixed - used by regulator command */
>> +       info->type = REGULATOR_TYPE_FIXED;
>> +
>> +       debug("%s:%d\n", __func__, __LINE__);
>> +       debug(" name:%s, boot_on:%d, active_hi: %d start_delay:%u\n",
>> +               info->name, info->boot_on, priv->enable_active_high,
>> +               priv->startup_delay_us);
>> +
>> +       return 0;
>> +}
>> +
>> +static int fixed_regulator_get_value(struct udevice *dev)
>> +{
>> +       struct dm_regulator_info *info;
>> +       int ret;
>> +
>> +       ret = regulator_info(dev, &info);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (info->min_uV == info->max_uV)
>> +               return info->min_uV;
>> +
>> +       error("Invalid constraints for: %s\n", info->name);
>> +
>> +       return -EINVAL;
>> +}
>> +
>> +static bool fixed_regulator_get_enable(struct udevice *dev)
>> +{
>> +       struct fixed_regulator_priv *priv = dev->priv;
>
> get_get_priv(dev)
>
> Please use that everywhere.
>

Ok, will fix this.

>> +
>> +       return dm_gpio_get_value(&priv->gpio);
>> +}
>> +
>> +static int fixed_regulator_set_enable(struct udevice *dev, bool enable)
>> +{
>> +       struct fixed_regulator_priv *priv = dev->priv;
>> +       int ret;
>> +
>> +       ret = dm_gpio_set_value(&priv->gpio, enable);
>> +       if (ret) {
>> +               error("Can't set regulator : %s gpio to: %d\n", dev->name,
>> +                     enable);
>> +               return ret;
>> +       }
>> +       return 0;
>> +}
>> +
>> +static const struct dm_regulator_ops fixed_regulator_ops = {
>> +       .get_value  = fixed_regulator_get_value,
>> +       .get_enable = fixed_regulator_get_enable,
>> +       .set_enable = fixed_regulator_set_enable,
>> +};
>> +
>> +static const struct udevice_id fixed_regulator_ids[] = {
>> +       { .compatible = "regulator-fixed" },
>> +       { },
>> +};
>> +
>> +U_BOOT_DRIVER(fixed_regulator) = {
>> +       .name = "fixed regulator",
>> +       .id = UCLASS_REGULATOR,
>> +       .ops = &fixed_regulator_ops,
>> +       .of_match = fixed_regulator_ids,
>> +       .ofdata_to_platdata = fixed_regulator_ofdata_to_platdata,
>> +       .priv_auto_alloc_size = sizeof(struct fixed_regulator_priv),
>> +};
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 13/17] doc: driver-model: pmic and regulator uclass documentation
  2015-03-29 13:08       ` Simon Glass
@ 2015-04-03 16:09         ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-03 16:09 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/29/2015 03:08 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes v2, V3:
>> - update documentation with the framework api changes
>> - remove doc file name 'dm' prefix
>> ---
>>   doc/driver-model/pmic-framework.txt | 350 ++++++++++++++++++++++++++++++++++++
>>   1 file changed, 350 insertions(+)
>>   create mode 100644 doc/driver-model/pmic-framework.txt
>>
>> diff --git a/doc/driver-model/pmic-framework.txt b/doc/driver-model/pmic-framework.txt
>> new file mode 100644
>> index 0000000..72651dc
>> --- /dev/null
>> +++ b/doc/driver-model/pmic-framework.txt
>> @@ -0,0 +1,350 @@
>> +#
>> +# (C) Copyright 2014-2015 Samsung Electronics
>> +# Przemyslaw Marczak <p.marczak@samsung.com>
>> +#
>> +# SPDX-License-Identifier:      GPL-2.0+
>> +#
>> +
>> +PMIC framework based on Driver Model
>> +====================================
>> +TOC:
>> +1. Introduction
>> +2. How does it work
>> +3. Pmic driver api
>> +4. Pmic driver
>> +5. Pmic command
>> +6. Regulator driver api
>> +7. Regulator driver
>> +8. Regulator command
>> +
>> +1. Introduction
>> +===============
>> +This is an introduction to driver-model multi uclass PMIC devices support.
>> +At present it is based on two uclass types:
>> +
>> +- UCLASS_PMIC      - basic uclass type for PMIC I/O, which provides common
>> +                     read/write interface.
>> +- UCLASS_REGULATOR - additional uclass type for specific PMIC features, which
>> +                     are various voltage regulators.
>> +
>> +New files:
>> +UCLASS_PMIC:
>> +- drivers/power/pmic-uclass.c
>> +- include/power/pmic.h
>> +UCLASS_REGULATOR:
>> +- drivers/power/regulator-uclass.c
>> +- include/power/regulator.h
>> +
>> +Commands:
>> +- lib/cmd_pmic.c
>> +- lib/cmd_regulator.c
>> +
>> +2. How doees it work
>> +====================
>> +The Power Management Integrated Circuits (PMIC) are used in embedded systems
>> +to provide stable, precise and specific voltage power source with over-voltage
>> +and thermal protection circuits.
>> +
>> +The single PMIC can provide various functionalities with single or multiple
>> +interfaces, like in the example below.
>> +
>> +-- SoC
>> + |
>> + |            ______________________________________
>> + | BUS 0     |       Multi interface PMIC IC        |--> LDO out 1
>> + | e.g.I2C0  |                                      |--> LDO out N
>> + |-----------|---- PMIC device 0 (READ/WRITE ops)   |
>> + | or SPI0   |    |_ REGULATOR device (ldo/... ops) |--> BUCK out 1
>> + |           |    |_ CHARGER device (charger ops)   |--> BUCK out M
>> + |           |    |_ MUIC device (microUSB con ops) |
>> + | BUS 1     |    |_ ...                            |---> BATTERY
>> + | e.g.I2C1  |                                      |
>> + |-----------|---- PMIC device 1 (READ/WRITE ops)   |---> USB in 1
>> + . or SPI1   |    |_ RTC device (rtc ops)           |---> USB in 2
>> + .           |______________________________________|---> USB out
>> + .
>> +
>> +Since U-Boot provides driver model features for I2C and SPI bus drivers,
>> +the PMIC devices should also support this. With the new basic uclass types
>> +for PMIC I/O and regulator features, PMIC drivers can simply provide common
>> +features, with multiple interface and instance support.
>> +
>> +Basic design assumptions:
>> +
>> +- Common I/O api - UCLASS_PMIC
>> +The main assumption is to use UCLASS_PMIC device to provide I/O interface,
>
> an I/O interface
>
>> +for devices other uclass types. It is no matter what is the type of device
>> +physical I/O interface.
>
> devices of other uclass types. It doesn't matter what type of physical
> I/O interface is used.
>
>
> Usually PMIC devices are using SPI or I2C interface,
>
> s/are using/use/
>
>> +but use of any other interface (e.g. when PMIC is not directly connected
>> +to the SoC) - is now possible. Drivers can use the same read/write api.
>> +
>> +- Common regulator api - UCLASS_REGULATOR
>> +For setting the attributes of verious types of regulators with common api,
>
> various
>
> with a common
>
>> +this uclass can be implemented. This allows to drive the each regulator output
>
> allows driving each regulator's output
>
>> +value, on/off state and custom defined operation modes. It also provides the
>
> custom-defined
>
> or perhaps just 'particular'
>
>> +user interface for all operations.
>> +For the very simple implementation, the regulator drivers are not required,
>
> For simple implementations, regulator drivers are not required, so the
> code can use pmic read/write directly.
>
>> +so the code could base on pmic read/write only.
>> +
>> +When board device-tree file includes pmic subnode and the U_Boot compatible
>> +driver exists, then the pmic device bind should looks like this:
>> +
>> +|_ root - will bind the device for I2C/SPI bus node
>> +  |_ i2c/spi - should bind a device for pmic node
>> +    |_ pmic (parent) - should bind child devices for its features
>> +      |_ regulator (child)
>> +      |_ charger   (child)
>> +      |_ other     (child)
>> +
>> +Usually PMIC design provides:
>> + - single I/O interface (single UCLASS_PMIC driver)
>> +   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 (UCLASS_PMIC driver for each)
>> +   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.
>> +
>> +3. Pmic driver api
>> +===================
>> +To use the pmic API, config: CONFIG_DM_PMIC is required.
>> +The new driver API is very simple and is based on 'struct dm_pmic_ops',
>> +which define two basic operations: device read and write.
>> +
>> +The platform data is introduced as 'struct pmic_platdata', to keep an info
>> +about the device interface.
>> +
>> +The api is described in file: 'include/power/pmic.h'
>> +Getting the device:
>> +- by name  -  int pmic_get(char *name, struct udevice **pmic);
>> +
>> +Device I/O interface
>> +Can be used with UCLASS_PMIC devices:
>> +- Read from the device 'len' bytes at 'reg' into the buffer:
>> +  int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len);
>> +
>> +- Write to the device 'len' bytes at 'reg' from the buffer:
>> +  int pmic_write(struct udevice *dev, uint reg, uint8_t *buffer, int len);
>> +
>> +4. Pmic driver
>> +============================
>> +As an example of the pmic driver, please take a look into the MAX77686 driver
>> +'drivers/power/pmic/max77686.c' and the description in 'include/power/pmic.h'
>> +
>> +The pmic driver can be defined by U_BOOT_DRIVER() macro:
>> +
>> +U_BOOT_DRIVER(pmic_max77686) = {
>> +       .name = "max77686 pmic",
>> +       .id = UCLASS_PMIC,
>> +       .of_match = max77686_ids,     - Allows to bind by compatible
>> +       .bind = max77686_bind,        - Function called at device bind
>> +       .ops = &max77686_ops,         - Pmic api function calls
>> +       .platdata_auto_alloc_size = sizeof(struct pmic_platdata),
>> +};
>> +
>> +To bind the pmic device, field '.of_match' is required with proper compatible.
>> +
>> +Driver ops:
>> +.reg_count = MAX77686_NUM_OF_REGS - number of pmic registers
>> +.read = max77686_read() - allows to use pmic_read()
>> +.write = max77686_write() - allows to use pmic_write()
>> +
>> +Driver bind:
>> +- max77686_bind(): called on bind and calls pmic_child_node_scan() to bind the
>> +  childs which are int "voltage-regulators" subnode. The bind is done using
>> +  "voltage-regulators" property name.
>> +
>> +5. Pmic command
>> +===============
>> +To use the pmic command, config: CONFIG_DM_PMIC_CMD is required.
>> +The new pmic command allows to:
>> +- list pmic devices
>> +- choose the current device (like the mmc command)
>> +- read or write the pmic register
>> +- dump all pmic registers
>> +
>> +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 0          - set dev seq 0 as current device
>> +# pmic dump           - dump the registers of the current pmic dev
>> +# pmic read 0x0       - read the register at address 0x0
>> +# pmic write 0x0 0x1  - write 0x1 to the register at addres 0x0
>> +
>> +6. Regulator driver API
>> +===================================
>> +To use the regulator API, config: CONFIG_DM_REGULATOR is required.
>> +The api is described in file: 'include/power/regulator.h'
>> +The api is based on structure types:
>> +- 'dm_regulator_info' - dev->uc_priv (auto-allocated)
>> +- 'dm_regulator_mode' - included in regualtor info
>> +
>> +Regulator info keeps the constraints taken from the device-tree and also device
>> +modes set by the driver (single or array).
>> +
>> +The fixed-regulator common driver keeps provides private structure type, to keep
>> +its gpio attributes. The structure type 'fixed_regulator_priv' is keept in field
>
> kept
>
>> +'dev->priv' and is allocated by the driver.
>> +
>> +6.1 Regulator device-tree node:
>> +The regulator node should looks like in the example:
>> +ldo1 {
>> +        regulator-compatible = "LDO1"; (not used here, but required for bind)
>> +        regulator-name = "VDD_MMC_1.8V"; (mandatory) *
>> +        regulator-min-microvolt = <1000000>; (mandatory) *
>> +        regulator-max-microvolt = <1000000>; (mandatory) *
>> +        regulator-min-microamp = <1000>; (optional) *
>> +        regulator-max-microamp = <1000>; (optional) *
>> +        regulator-always-on; (optional) *
>> +        regulator-boot-on; (optional) *
>> +};
>> +
>> +For the fixed-voltage regulator, voltage min and max must be equal:
>> +VDD_MMC: regulator at 0 {
>> +        compatible = "regulator-fixed";
>> +        regulator-name = "VDD_MMC"; (mandatory) *
>> +        regulator-min-microvolt = <2800000>; (mandatory) *
>> +        regulator-max-microvolt = <2800000>; (mandatory) *
>> +        regulator-always-on; (optional)
>> +        regulator-boot-on; (optional)
>> +        gpio = <&gpc 1 GPIO_ACTIVE_HIGH>; (optional)
>> +        gpio-open-drain; (optional)
>> +        enable-active-high; (optional)
>> +        startup-delay-us
>> +};
>> +
>> +The attributes signed with '*' can be taken by the common function which is
>> +regulator_ofdata_to_platdata(). The rest attributes for fixed-regulator, are
>> +taken by the driver.
>> +
>> +6.2 Getting the device:
>> +- by name - int regulator_get(char *name, struct udevice **regulator);
>> +
>> +6.3 Device I/O interface
>> +According to the framework assumptions, where uclass pmic device is a parent
>> +of pmic devices other uclass types, the regulator devices should use uclass
>> +pmic interface, with the parent as the device, like this:
>> +- pmic_read(regulator->parent, some_reg, &some_buff, count);
>> +- pmic_write(regulator->parent, some_reg, &some_buff, count);
>> +
>> +6.4 Device regulator operations
>> +The regulator function calls are based on few data types:
>> +- enum regulator_type {...} - standard types: LDO, BUCK, DVS, FIXED
>> +- struct dm_regulator_info {...} - output name and value limits
>> +- struct dm_regulator_mode {...} - output operation mode value and name
>> +- struct dm_regulator_ops {...} - regulator driver function calls
>> +
>> +The first argument is always device. And the device uclass id must be always:
>> +- 'UCLASS_REGULATOR'
>> +
>> +Function details are described in regulator header. The basic features are:
>> +- regulator_info()            - get the regulator info structure
>> +- regulator_mode()            - get the regulator mode info structure
>> +- regulator_get/set_value()   - get/set the regulator output voltage
>> +- regulator_get/set_current() - get/set the regulator output current
>> +- regulator_get/set_enable()  - get/set the regulator output enable state
>> +- regulator_get/set_mode()    - get/set the regulator output operation mode
>> +
>> +7. Regulator driver
>> +======================================
>> +As an example of the regulator driver, please take a look into the MAX77686
>> +regulator driver (drivers/power/regulator/max77686.c). It implements two drivers
>> +for the buck and ldo regulators. The buck driver structure:
>> +
>> +U_BOOT_DRIVER(max77686_buck) = {
>> +        .name = "max77686 buck",
>> +        .id = UCLASS_REGULATOR,
>> +        .ops = &max77686_buck_ops,
>> +        .of_match = max77686_buck_ids,
>> +        .ofdata_to_platdata = max77686_buck_ofdata_to_platdata,
>> +};
>> +
>> +The device-tree buck compatibles:
>> +static const struct udevice_id max77686_buck_ids[] = {
>> +        { .compatible = "BUCK1", .data = 1 },
>> +        { .compatible = "BUCK2", .data = 2 },
>> +        ...
>> +        { }, (need always put null at the end)
>> +};
>> +
>> +For each compatible, the data is set as a number of buck. This allows to get
>> +the device number without parsing the compatible.
>> +
>> +The buck driver '.ofdata_to_platdata' call implementation:
>> +static int max77686_buck_ofdata_to_platdata(struct udevice *dev)
>> +{
>> +        struct dm_regulator_info *info = dev->uclass_priv;
>> +        int ret;
>> +
>> +       /* Get the regulation constraints by the common function */
>> +        ret = regulator_ofdata_to_platdata(dev);
>> +        if (ret)
>> +                return ret;
>> +
>> +        info->type = REGULATOR_TYPE_BUCK;
>> +        /**
>> +         * The device mode array is filled by internal driver function with
>> +         * the proper name for each mode id.
>> +         */
>> +        info->mode_count = max77686_buck_modes(dev2num(dev), &info->mode);
>> +
>> +        return 0;
>> +}
>> +
>> +And the call to regulator_ofdata_to_platdata() is responsible for filling the
>> +regulator output constraints, which are keepts in device-tree regulator nodes,
>> +e.g. 'arch/arm/dts/exynos4412-odroid.dts'
>> +
>> +For the I/O, this driver uses pmic_read/write calls, with the parent device
>> +as the first argument, e.g.: pmic_read(dev->parent, adr, &val, 1);
>> +
>> +8. Regulator command
>> +====================
>> +To use the pmic command, config: CONFIG_DM_REGULATOR_CMD is required.
>> +
>> +This command is based on driver model regulator api.
>> +User interface features:
>> +- list                   - list UCLASS regulator devices
>> +- regulator dev [id]     - show or [set] operating regulator device
>> +- regulator [info]       - print constraints info
>> +- regulator [status]     - print operating status
>> +- regulator [value] [-f] - print/[set] voltage value [uV] (force)
>> +- regulator [current]    - print/[set] current value [uA]
>> +- regulator [mode_id]    - print/[set] operating mode id
>> +- regulator [enable]     - enable the regulator output
>> +- regulator [disable]    - disable the regulator output
>> +
>> +If pmic device driver provides support to this another pmic uclass, then this
>> +command provides useful user interface. It was designed to allow safe I/O access
>> +to the pmic device, without the pmic documentation. If driver provide regulator
>> +output - value and mode info - then user can operate on it.
>> +
>> +Example of usage:
>> +regulator list              - look after regulator 'seq' number
>> +regulator dev 'seq'         - set current device
>> +regulator status            - show device status
>> +regulator info              - list device available regulation constraints
>> +regulator value             - prints device voltage value
>> +regulator value 1000000     - set device voltage value to 1000000 uV
>> +regulator value 1200000 -f  - if value exceeds limits - set force
>> +regulator mode              - print device operation mode id
>> +regulator mode 5            - set devices operation mode to '5' (mode id)
>> +
>> +The -f option (forcibly) or mode - only if descriptor is available
>
> I think something like this would be clearer:
>
> The -f option (forcibly) and 'mode' sub-command are only available if
> the regulator descriptor is available
>
>
>> +
>> +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.
>
> changed.
>
> What does "e..g. if some hardware uses pins header" mean? Can you
> reword that to be clearer?
>

Right, this was unclear.
I meant the hardware connected by I/O expansion header, which can be 
replaced by other. And each could be supplied by a different voltage value.

>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

I didn't clean this file too much, last time. Will fix in next version.
Thanks for help!
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 15/17] odroid: board: add support to dm pmic api
  2015-03-29 13:08       ` Simon Glass
@ 2015-04-03 16:09         ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-03 16:09 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/29/2015 03:08 PM, Simon Glass wrote:
>   Hi Przemyslaw,
>
> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This commit change the old pmic framework calls with the new ones.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes v2:
>> - remove board_init_i2c() call
>> - update regulator calls
>> - update headers
>> - samsung/misc.c: include required header
>>
>> Changes v3:
>> - adjust regulator calls to new api
>> ---
>>   board/samsung/common/misc.c   |   1 +
>>   board/samsung/odroid/odroid.c | 113 +++++++++++++++++++++++++++++++++---------
>>   2 files changed, 91 insertions(+), 23 deletions(-)
>>
>> diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
>> index 1a77c82..f0d69d4 100644
>> --- a/board/samsung/common/misc.c
>> +++ b/board/samsung/common/misc.c
>> @@ -16,6 +16,7 @@
>>   #include <asm/arch/cpu.h>
>>   #include <asm/gpio.h>
>>   #include <linux/input.h>
>> +#include <dm.h>
>>   #include <power/pmic.h>
>>   #include <mmc.h>
>>
>> diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
>> index ae41c29..aa3b0ff 100644
>> --- a/board/samsung/odroid/odroid.c
>> +++ b/board/samsung/odroid/odroid.c
>> @@ -12,7 +12,9 @@
>>   #include <asm/arch/gpio.h>
>>   #include <asm/gpio.h>
>>   #include <asm/arch/cpu.h>
>> +#include <dm.h>
>>   #include <power/pmic.h>
>> +#include <power/regulator.h>
>>   #include <power/max77686_pmic.h>
>>   #include <errno.h>
>>   #include <mmc.h>
>> @@ -405,15 +407,62 @@ static void board_gpio_init(void)
>>
>>   static int pmic_init_max77686(void)
>>   {
>> -       struct pmic *p = pmic_get("MAX77686_PMIC");
>> +       struct udevice *dev;
>> +       int ret;
>>
>> -       if (pmic_probe(p))
>> -               return -ENODEV;
>> +       ret = regulator_get("VDDQ_EMMC_1.8V", &dev);
>> +       if (ret) {
>> +               error("Regulator get error: %d", ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = regulator_set_value(dev, 1800000);
>> +       if (ret) {
>> +               error("Regulator %s value setting error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = regulator_set_enable(dev, true);
>> +       if (ret) {
>> +               error("Regulator %s enable error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>
> How about adding a function that finds a regulator, sets its voltage
> and enables it? Then you can avoid duplicating the same code 3 times.
>

Yes, will add something like this.

>> +
>> +       ret = regulator_get("TFLASH_2.8V", &dev);
>> +       if (ret) {
>> +               error("Regulator get error: %d", ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = regulator_set_value(dev, 2800000);
>> +       if (ret) {
>> +               error("Regulator %s value setting error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = regulator_set_enable(dev, true);
>> +       if (ret) {
>> +               error("Regulator %s enable error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = regulator_get("VDDQ_EMMC_2.8V", &dev);
>> +       if (ret) {
>> +               error("Regulator get error: %d", ret);
>> +               return ret;
>> +       }
>>
>> -       /* 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 */
>> +       ret = regulator_set_value(dev, 2800000);
>> +       if (ret) {
>> +               error("Regulator %s value setting error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = regulator_set_enable(dev, true);
>> +       if (ret) {
>> +               error("Regulator %s enable error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>>
>>          return 0;
>>   }
>> @@ -434,7 +483,6 @@ int exynos_init(void)
>>
>>   int exynos_power_init(void)
>>   {
>> -       pmic_init(0);
>>          pmic_init_max77686();
>>
>>          return 0;
>> @@ -443,19 +491,20 @@ int exynos_power_init(void)
>>   #ifdef CONFIG_USB_GADGET
>>   static int s5pc210_phy_control(int on)
>>   {
>> -       struct pmic *p_pmic;
>> -
>> -       p_pmic = pmic_get("MAX77686_PMIC");
>> -       if (!p_pmic)
>> -               return -ENODEV;
>> +       struct udevice *dev;
>> +       int ret;
>>
>> -       if (pmic_probe(p_pmic))
>> -               return -1;
>> +       ret = regulator_get("VDD_UOTG_3.0V", &dev);
>> +       if (ret) {
>> +               error("Regulator get error: %d", ret);
>> +               return ret;
>> +       }
>>
>>          if (on)
>> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
>> +               return regulator_set_mode(dev, OPMODE_ON);
>>          else
>> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
>> +               return regulator_set_mode(dev, OPMODE_LPM);
>> +
>>   }
>>
>>   struct s3c_plat_otg_data s5pc210_otg_data = {
>> @@ -472,7 +521,8 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
>>   int board_usb_init(int index, enum usb_init_type init)
>>   {
>>   #ifdef CONFIG_CMD_USB
>> -       struct pmic *p_pmic;
>> +       struct udevice *dev;
>> +       int ret;
>>
>>          /* Set Ref freq 0 => 24MHz, 1 => 26MHz*/
>>          /* Odroid Us have it at 24MHz, Odroid Xs at 26MHz */
>> @@ -490,14 +540,31 @@ int board_usb_init(int index, enum usb_init_type init)
>>          /* Power off and on BUCK8 for LAN9730 */
>>          debug("LAN9730 - Turning power buck 8 OFF and ON.\n");
>>
>> -       p_pmic = pmic_get("MAX77686_PMIC");
>> -       if (p_pmic && !pmic_probe(p_pmic)) {
>> -               max77686_set_buck_voltage(p_pmic, 8, 750000);
>> -               max77686_set_buck_voltage(p_pmic, 8, 3300000);
>> +       ret = regulator_get("VCC_P3V3_2.85V", &dev);
>> +       if (ret) {
>> +               error("Regulator get error: %d", ret);
>> +               return ret;
>>          }
>>
>> -#endif
>> +       ret = regulator_set_enable(dev, true);
>> +       if (ret) {
>> +               error("Regulator %s enable setting error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = regulator_set_value(dev, 750000);
>> +       if (ret) {
>> +               error("Regulator %s value setting error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>>
>> +       ret = regulator_set_value(dev, 3300000);
>> +       if (ret) {
>> +               error("Regulator %s value setting error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>> +
>> +#endif
>>          debug("USB_udc_probe\n");
>>          return s3c_udc_probe(&s5pc210_otg_data);
>>   }
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 17/17] odroid: config: enable dm pmic, dm regulator and max77686 driver
  2015-03-29 13:10       ` Simon Glass
@ 2015-04-03 16:10         ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-03 16:10 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/29/2015 03:10 PM, Simon Glass wrote:
> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This change enables the configs required to init and setup max77686
>> regulator driver, using the new driver model pmic and regulator API.
>>
>> This commits enables:
>> - CONFIG_ERRNO_STR
>> - CONFIG_DM_PMIC
>> - CONFIG_DM_PMIC_CMD
>> - CONFIG_DM_PMIC_MAX77686
>> - CONFIG_DM_REGULATOR
>> - CONFIG_DM_REGULATOR_CMD
>> - CONFIG_DM_REGULATOR_MAX77686
>>
>> And removes the unused:
>> - CONFIG_DM_I2C_COMPAT
>> - CONFIG_POWER
>> - CONFIG_POWER_I2C
>> - CONFIG_POWER_MAX77686
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>
> Acked-by: Simon Glass <sjg@chromium.org>
>
> (but pelase rename the commands so that they are CONFIG_CMD_DM_PCI and
> CONFIG_CMD_DM_REGULATOR)
>

Yes, will do this.

>> ---
>> Changes V2:
>> - config: enable dm i2c; cleanup
>> - remove CONFIG_DM_I2C_COMPAT
>> - enable regulator command
>>
>> Changes V3:
>> - move options to defconfig
>> ---
>>   configs/odroid_defconfig | 8 +++++++-
>>   include/configs/odroid.h | 5 -----
>>   2 files changed, 7 insertions(+), 6 deletions(-)
>>
>> diff --git a/configs/odroid_defconfig b/configs/odroid_defconfig
>> index d32b5b5..1e29abe 100644
>> --- a/configs/odroid_defconfig
>> +++ b/configs/odroid_defconfig
>> @@ -4,5 +4,11 @@ CONFIG_TARGET_ODROID=y
>>   CONFIG_OF_CONTROL=y
>>   CONFIG_DEFAULT_DEVICE_TREE="exynos4412-odroid"
>>   CONFIG_DM_I2C=y
>> -CONFIG_DM_I2C_COMPAT=y
>>   # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
>> +CONFIG_ERRNO_STR=y
>> +CONFIG_DM_PMIC=y
>> +CONFIG_DM_PMIC_CMD=y
>> +CONFIG_DM_PMIC_MAX77686=y
>> +CONFIG_DM_REGULATOR=y
>> +CONFIG_DM_REGULATOR_CMD=y
>> +CONFIG_DM_REGULATOR_MAX77686=y
>> diff --git a/include/configs/odroid.h b/include/configs/odroid.h
>> index 5ee0abe..3874baa 100644
>> --- a/include/configs/odroid.h
>> +++ b/include/configs/odroid.h
>> @@ -182,11 +182,6 @@
>>   #define CONFIG_SYS_I2C_S3C24X0_SPEED   100000
>>   #define CONFIG_SYS_I2C_S3C24X0_SLAVE   0
>>
>> -/* POWER */
>> -#define CONFIG_POWER
>> -#define CONFIG_POWER_I2C
>> -#define CONFIG_POWER_MAX77686
>> -
>>   /* GPT */
>>   #define CONFIG_RANDOM_UUID
>>
>> --
>> 1.9.1
>>
>

Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 00/17] Power(full) framework based on Driver Model
  2015-03-29 13:05     ` Simon Glass
@ 2015-04-03 16:11       ` Przemyslaw Marczak
  2015-04-05 18:30         ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-03 16:11 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 03/29/2015 03:05 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Hello,
>> Here is the third RFC version of the new PMIC framework.Big thanks to
>> Simon Glass, your comments were really helpful, and I think, that this
>> version is much more better to discuss, than the previous. The changes
>> made in this version are described below each commit. Sorry that I didn't
>> reply to each patch, I agreed with most and just started the work.
>
> This is looking really good. Here are a few overall comments.
>
> 1. There is one oddity that I'd like to address before merging.
>
> I don't think the fdt_node_check_prop_compatible() is a good idea, nor
> necessary. I don't think we should consider the regulator-compatible
> property to be a compatible string. It has values like LDO8, LDO9 and
> these don't look like compatible strings, which are normally unique
> and point to a driver. Here they point to a particular device.
>

Right, those compatibles don't point to a driver. This is a specific 
case of use from the kernel.

> A similar problem is faced in pinctrl and if you look at
> gpio_exynos_bind() you will see that it works through the sub-nodes,
> creating devices as needed.
>
> I don't think using udevice_id is right here either.
>

Yes, I know how it's done. But we haven't the compatibles for each GPIO 
as for the regulators, and each GPIO driver, bind the childs by its own 
implementation.

I tried to reuse the existing code. It was nice, but I missed one 
thing...if there are more, than one driver with the same e.g. "LDO1" 
compatible, then the first one will bind - and it could be the wrong one...

But, it could be tune-up, to get the right drivers list by arg.

> Here is my suggestion:
>
> a. Create a new structure like this:
>
> struct pmic_child_info {
>     const char *prefix;   // "LDO" or "BUCK"
>     const char *driver_name;   // "max77686_ldo" or "max77686_buck"
> };
>
> b. Pass a list of these to pmic_child_node_scan(). In your case there
> will be three entries, one for LDO, one for BUCK, plus a NULL
> termination entry,
>

Ok, this could be good.

> c. It can work through the subnodes looking for the given prefixes. It
> then calls device_bind_driver() on each. Then it changes the returned
> device's of_data to hold the correct value (obtained with strtol() on
> the part of the name that follows the prefix - e.g. 17 for "LDO17").
> This will be easier if you rebase on u-boot-dm/usb-working, where the
> data is just a long, not a device tree pointer.

Yes, it's easy. I made something like this in the first version of this 
patchset, to parse the nodes and fill the max77686 regulator descriptors 
in function get_desc_for_compat(), from this patch:
http://lists.denx.de/pipermail/u-boot/2014-October/191024.html

>
> d. Now you have the same effect as before, but you can drop the tables
> like max77686_ldo_ids[] and avoid misappropriating driver model's
> device lookup.

Yes, this is an advantage.

I wonder about the usage of "struct udevice_id", which should contain 
the data, defined only by the driver, right?

In the method you mentioned, we bind the pmic childs and then
we modify the "dev->of_id->data" by putting the regulator number from 
matched prefix in it.

This is ok, but I think, that the second part of this idea should be 
done by the driver. I think, that the external function shouldn't modify 
this driver data on bind.

This could be solved by leave this job for the device driver. For 
example on max77686, we could leave the dev->of_id as null and use 
dev->priv for the node prefix id.

So, it's not a big problem.

Here, I would like mention the problem with the regulator_get() by name, 
for which we want use the "regulator-name" constraint.

For this version, it requires probing of all regulator devices,
since it uses the dev->uclass_priv, which I think, is a good place to 
provide the framework-specific structure type for regulator constraints 
and mode descriptors.

What about moving it back to dev->platdata?
Then:
- the structure type: "dm_regulator_info", will move to 
"dm_regulator_platdata"

- only its .name field could be assigned at regulator bind stage from 
the "regulator-name" constraint

- the bind could fail, when the constraint name not found

- the rest of constraints will assign in .ofdata_to_platdata() function 
call as it is at the present version

Then, we also don't need probe each regulator device, when using 
regulator_get() function.

>
> 2. Should we put the regulator stuff in drivers/regulator, as with Linux?

I will do this in the next version.

>
> 3. Can you please bring in the regulator and pmic device tree binding
> files, plus max77686?

Right, I forgot about this.

>
> 4. We really do need tests! I suspect that you could create a sandbox
> I2C pmic that has a few registers and regulators. See
> i2c_eeprom_emul.c for a basic example. Then you can write some tests
> that find the pmi,c find the regulator, read and write a few
> registers, and read and write a few regulators. That would be enough
> test coverage to get us started. I know this is different from
> previous U-Boot policy, but tests are a big win during development and
> also for years to come (as people can change the framework and have
> some confidence that they did not break anything).
>
> It can be a follow-on patch separate from your series but I'm really
> not keen on bringing in a major new driver model framework with no
> tests. If you are struggling for time, let me know and I can try to
> help by writing a sandbox I2C PMIC for example.
>
> Regards,
> Simon
>

Ok, I will add sandbox drivers for this all, but first let's prepare the 
final patchset version of this framework, and then I will send a 
separate patchset for sandbox before merge of this part.

>>
>> Best regards
>>
>> Przemyslaw Marczak (17):
>>    exynos5: fix build break by adding CONFIG_POWER
>>    fdt_ro.c: add new function: fdt_node_check_prop_compatible()
>>    dm: core: lists.c: add new function lists_bind_fdt_by_prop()
>>    lib: Kconfig: add entry for errno_str() function
>>    dm: pmic: add implementation of driver model pmic uclass
>>    dm: regulator: add implementation of driver model regulator uclass
>>    dm: pmic: add pmic command
>>    dm: regulator: add regulator command
>>    pmic: max77686 set the same compatible as in the kernel
>>    dm: pmic: add max77686 pmic driver
>>    dm: regulator: add max77686 regulator driver
>>    dm: regulator: add fixed voltage 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 +++++++++-
>>   arch/arm/dts/exynos4412-trats2.dts   |   2 +-
>>   arch/arm/dts/exynos5250-smdk5250.dts |   2 +-
>>   arch/arm/dts/exynos5250-snow.dts     |   2 +-
>>   board/samsung/common/board.c         |   4 +-
>>   board/samsung/common/misc.c          |   1 +
>>   board/samsung/odroid/odroid.c        | 113 ++++-
>>   common/Kconfig                       |  36 ++
>>   common/Makefile                      |   4 +
>>   common/cmd_pmic.c                    | 210 +++++++++
>>   common/cmd_regulator.c               | 385 +++++++++++++++
>>   configs/odroid_defconfig             |   8 +-
>>   doc/driver-model/pmic-framework.txt  | 350 ++++++++++++++
>>   drivers/core/lists.c                 |  28 +-
>>   drivers/power/Kconfig                | 124 ++++-
>>   drivers/power/Makefile               |   3 +-
>>   drivers/power/pmic-uclass.c          | 130 ++++++
>>   drivers/power/pmic/Makefile          |   1 +
>>   drivers/power/pmic/max77686.c        |  76 +++
>>   drivers/power/pmic/pmic_max77686.c   |   2 +-
>>   drivers/power/regulator-uclass.c     | 219 +++++++++
>>   drivers/power/regulator/Makefile     |   9 +
>>   drivers/power/regulator/fixed.c      | 124 +++++
>>   drivers/power/regulator/max77686.c   | 876 +++++++++++++++++++++++++++++++++++
>>   include/configs/exynos5-common.h     |   4 +
>>   include/configs/odroid.h             |   5 -
>>   include/dm/lists.h                   |  18 +
>>   include/dm/uclass-id.h               |   4 +
>>   include/libfdt.h                     |  27 ++
>>   include/power/max77686_pmic.h        |  26 +-
>>   include/power/pmic.h                 | 210 +++++++++
>>   include/power/regulator.h            | 259 +++++++++++
>>   lib/Kconfig                          |   8 +
>>   lib/fdtdec.c                         |   2 +-
>>   lib/libfdt/fdt_ro.c                  |  14 +-
>>   36 files changed, 3481 insertions(+), 55 deletions(-)
>>   create mode 100644 common/cmd_pmic.c
>>   create mode 100644 common/cmd_regulator.c
>>   create mode 100644 doc/driver-model/pmic-framework.txt
>>   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/fixed.c
>>   create mode 100644 drivers/power/regulator/max77686.c
>>   create mode 100644 include/power/regulator.h
>>
>> --
>> 1.9.1
>>
>

Thank you again for the review!
I will send the next version probably on 7-8-th April.

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 00/17] Power(full) framework based on Driver Model
  2015-04-03 16:11       ` Przemyslaw Marczak
@ 2015-04-05 18:30         ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-05 18:30 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 April 2015 at 10:11, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
> On 03/29/2015 03:05 PM, Simon Glass wrote:
>>
>> Hi Przemyslaw,
>>
>> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>>> Hello,
>>> Here is the third RFC version of the new PMIC framework.Big thanks to
>>> Simon Glass, your comments were really helpful, and I think, that this
>>> version is much more better to discuss, than the previous. The changes
>>> made in this version are described below each commit. Sorry that I didn't
>>> reply to each patch, I agreed with most and just started the work.
>>
>>
>> This is looking really good. Here are a few overall comments.
>>
>> 1. There is one oddity that I'd like to address before merging.
>>
>> I don't think the fdt_node_check_prop_compatible() is a good idea, nor
>> necessary. I don't think we should consider the regulator-compatible
>> property to be a compatible string. It has values like LDO8, LDO9 and
>> these don't look like compatible strings, which are normally unique
>> and point to a driver. Here they point to a particular device.
>>
>
> Right, those compatibles don't point to a driver. This is a specific case of
> use from the kernel.
>
>> A similar problem is faced in pinctrl and if you look at
>> gpio_exynos_bind() you will see that it works through the sub-nodes,
>> creating devices as needed.
>>
>> I don't think using udevice_id is right here either.
>>
>
> Yes, I know how it's done. But we haven't the compatibles for each GPIO as
> for the regulators, and each GPIO driver, bind the childs by its own
> implementation.
>
> I tried to reuse the existing code. It was nice, but I missed one thing...if
> there are more, than one driver with the same e.g. "LDO1" compatible, then
> the first one will bind - and it could be the wrong one...
>
> But, it could be tune-up, to get the right drivers list by arg.
>
>> Here is my suggestion:
>>
>> a. Create a new structure like this:
>>
>> struct pmic_child_info {
>>     const char *prefix;   // "LDO" or "BUCK"
>>     const char *driver_name;   // "max77686_ldo" or "max77686_buck"
>> };
>>
>> b. Pass a list of these to pmic_child_node_scan(). In your case there
>> will be three entries, one for LDO, one for BUCK, plus a NULL
>> termination entry,
>>
>
> Ok, this could be good.
>
>> c. It can work through the subnodes looking for the given prefixes. It
>> then calls device_bind_driver() on each. Then it changes the returned
>> device's of_data to hold the correct value (obtained with strtol() on
>> the part of the name that follows the prefix - e.g. 17 for "LDO17").
>> This will be easier if you rebase on u-boot-dm/usb-working, where the
>> data is just a long, not a device tree pointer.
>
>
> Yes, it's easy. I made something like this in the first version of this
> patchset, to parse the nodes and fill the max77686 regulator descriptors in
> function get_desc_for_compat(), from this patch:
> http://lists.denx.de/pipermail/u-boot/2014-October/191024.html
>

Yes I see.

>>
>> d. Now you have the same effect as before, but you can drop the tables
>> like max77686_ldo_ids[] and avoid misappropriating driver model's
>> device lookup.
>
>
> Yes, this is an advantage.
>
> I wonder about the usage of "struct udevice_id", which should contain the
> data, defined only by the driver, right?
>
> In the method you mentioned, we bind the pmic childs and then
> we modify the "dev->of_id->data" by putting the regulator number from
> matched prefix in it.
>
> This is ok, but I think, that the second part of this idea should be done by
> the driver. I think, that the external function shouldn't modify this driver
> data on bind.
>
> This could be solved by leave this job for the device driver. For example on
> max77686, we could leave the dev->of_id as null and use dev->priv for the
> node prefix id.
>
> So, it's not a big problem.

As I see it the regulator number (e.g. 1 for LDO1, 3 for BUCK3) is
picked up from the name string, so it feels like this is common code.
You would be calling a helper function from the driver, so yes you can
always have it return the regulator number, and then store it in
driver_data in the driver.

But you should rebase on dm/usb-working to get the new driver_data
member of struct udevice.

My only concern with using driver_data is that this is normally used
to specify the device type where the driver supports multiple hardware
variants. The regulator number is more correctly considered to be
platform data. This leads on to your point below.

>
> Here, I would like mention the problem with the regulator_get() by name, for
> which we want use the "regulator-name" constraint.
>
> For this version, it requires probing of all regulator devices,
> since it uses the dev->uclass_priv, which I think, is a good place to
> provide the framework-specific structure type for regulator constraints and
> mode descriptors.
>
> What about moving it back to dev->platdata?
> Then:
> - the structure type: "dm_regulator_info", will move to
> "dm_regulator_platdata"
>
> - only its .name field could be assigned at regulator bind stage from the
> "regulator-name" constraint
>
> - the bind could fail, when the constraint name not found
>
> - the rest of constraints will assign in .ofdata_to_platdata() function call
> as it is at the present version
>
> Then, we also don't need probe each regulator device, when using
> regulator_get() function.

Here is my understanding on this one:

The regulator name is the device name, so looking up by name should be
easy. It just involves scanning through the regulator uclass comparing
device names.

Based on what you said above, the regulator number should go in
platform data for the regulator as you say. It can be worked out when
the PMIC is bound (perhaps in the PMIC uclass' post_bind method).

The idea is that the PMIC is responsible for binding its children so
that at start-up we know about all the regulators and can find them,
even through none is probed yet.

>
>>
>> 2. Should we put the regulator stuff in drivers/regulator, as with Linux?
>
>
> I will do this in the next version.
>
>>
>> 3. Can you please bring in the regulator and pmic device tree binding
>> files, plus max77686?
>
>
> Right, I forgot about this.
>
>>
>> 4. We really do need tests! I suspect that you could create a sandbox
>> I2C pmic that has a few registers and regulators. See
>> i2c_eeprom_emul.c for a basic example. Then you can write some tests
>> that find the pmi,c find the regulator, read and write a few
>> registers, and read and write a few regulators. That would be enough
>> test coverage to get us started. I know this is different from
>> previous U-Boot policy, but tests are a big win during development and
>> also for years to come (as people can change the framework and have
>> some confidence that they did not break anything).
>>
>> It can be a follow-on patch separate from your series but I'm really
>> not keen on bringing in a major new driver model framework with no
>> tests. If you are struggling for time, let me know and I can try to
>> help by writing a sandbox I2C PMIC for example.
>>
>> Regards,
>> Simon
>>
>
> Ok, I will add sandbox drivers for this all, but first let's prepare the
> final patchset version of this framework, and then I will send a separate
> patchset for sandbox before merge of this part.

OK sounds good.



>
>
>>>
>>> Best regards
>>>
>>> Przemyslaw Marczak (17):
>>>    exynos5: fix build break by adding CONFIG_POWER
>>>    fdt_ro.c: add new function: fdt_node_check_prop_compatible()
>>>    dm: core: lists.c: add new function lists_bind_fdt_by_prop()
>>>    lib: Kconfig: add entry for errno_str() function
>>>    dm: pmic: add implementation of driver model pmic uclass
>>>    dm: regulator: add implementation of driver model regulator uclass
>>>    dm: pmic: add pmic command
>>>    dm: regulator: add regulator command
>>>    pmic: max77686 set the same compatible as in the kernel
>>>    dm: pmic: add max77686 pmic driver
>>>    dm: regulator: add max77686 regulator driver
>>>    dm: regulator: add fixed voltage 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 +++++++++-
>>>   arch/arm/dts/exynos4412-trats2.dts   |   2 +-
>>>   arch/arm/dts/exynos5250-smdk5250.dts |   2 +-
>>>   arch/arm/dts/exynos5250-snow.dts     |   2 +-
>>>   board/samsung/common/board.c         |   4 +-
>>>   board/samsung/common/misc.c          |   1 +
>>>   board/samsung/odroid/odroid.c        | 113 ++++-
>>>   common/Kconfig                       |  36 ++
>>>   common/Makefile                      |   4 +
>>>   common/cmd_pmic.c                    | 210 +++++++++
>>>   common/cmd_regulator.c               | 385 +++++++++++++++
>>>   configs/odroid_defconfig             |   8 +-
>>>   doc/driver-model/pmic-framework.txt  | 350 ++++++++++++++
>>>   drivers/core/lists.c                 |  28 +-
>>>   drivers/power/Kconfig                | 124 ++++-
>>>   drivers/power/Makefile               |   3 +-
>>>   drivers/power/pmic-uclass.c          | 130 ++++++
>>>   drivers/power/pmic/Makefile          |   1 +
>>>   drivers/power/pmic/max77686.c        |  76 +++
>>>   drivers/power/pmic/pmic_max77686.c   |   2 +-
>>>   drivers/power/regulator-uclass.c     | 219 +++++++++
>>>   drivers/power/regulator/Makefile     |   9 +
>>>   drivers/power/regulator/fixed.c      | 124 +++++
>>>   drivers/power/regulator/max77686.c   | 876
>>> +++++++++++++++++++++++++++++++++++
>>>   include/configs/exynos5-common.h     |   4 +
>>>   include/configs/odroid.h             |   5 -
>>>   include/dm/lists.h                   |  18 +
>>>   include/dm/uclass-id.h               |   4 +
>>>   include/libfdt.h                     |  27 ++
>>>   include/power/max77686_pmic.h        |  26 +-
>>>   include/power/pmic.h                 | 210 +++++++++
>>>   include/power/regulator.h            | 259 +++++++++++
>>>   lib/Kconfig                          |   8 +
>>>   lib/fdtdec.c                         |   2 +-
>>>   lib/libfdt/fdt_ro.c                  |  14 +-
>>>   36 files changed, 3481 insertions(+), 55 deletions(-)
>>>   create mode 100644 common/cmd_pmic.c
>>>   create mode 100644 common/cmd_regulator.c
>>>   create mode 100644 doc/driver-model/pmic-framework.txt
>>>   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/fixed.c
>>>   create mode 100644 drivers/power/regulator/max77686.c
>>>   create mode 100644 include/power/regulator.h
>>>
>>> --
>>> 1.9.1
>>>
>>
>
> Thank you again for the review!
> I will send the next version probably on 7-8-th April.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 05/17] dm: pmic: add implementation of driver model pmic uclass
  2015-04-03 16:08         ` Przemyslaw Marczak
@ 2015-04-05 18:30           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-05 18:30 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 April 2015 at 10:08, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
> On 03/29/2015 03:07 PM, Simon Glass wrote:
>>
>> Hi Prazemyslaw,
>>
>> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>
>>> +       UCLASS_PMIC,
>>> +
>>>          UCLASS_COUNT,
>>>          UCLASS_INVALID = -1,
>>>   };
>>> diff --git a/include/power/pmic.h b/include/power/pmic.h
>>> index afbc5aa..b55304f 100644
>>> --- a/include/power/pmic.h
>>> +++ b/include/power/pmic.h
>>> @@ -1,4 +1,7 @@
>>>   /*
>>> + *  Copyright (C) 2014-2015 Samsung Electronics
>>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>>> + *
>>>    *  Copyright (C) 2011-2012 Samsung Electronics
>>>    *  Lukasz Majewski <l.majewski@samsung.com>
>>>    *
>>> @@ -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,210 @@ 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.
>>
>>
>> I worry about having the docs in multiple places. Should you adjust
>> this to point to the Kconfig? Or change the Kconfig to point here? Or
>> maybe it would be better to drop both and put these in your README? I
>> am concerned that if we later change something, we end up with
>> inconsistent docs.
>>
>
> Right, this could be an issue in the future. I suppose that first thing is
> to look at this header file, before anyone starts work with this framework.
> So maybe it's better keep the description only in this file, and add some
> basic info to Kconfig and doc, with the links to here?

Yes that sounds good to me. It's great that you have documented this so well.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass
  2015-04-03 16:09         ` Przemyslaw Marczak
@ 2015-04-05 18:30           ` Simon Glass
  2015-04-07 15:31             ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-05 18:30 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 3 April 2015 at 10:09, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 03/29/2015 03:07 PM, Simon Glass wrote:
>>
>> Hi Przemyslaw,
>>
>> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:

[snip]

>
>>> +
>>> +       info->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
>>> +                                     "regulator-min-microvolt", -1);
>>> +       if (info->min_uV < 0)
>>> +               return -ENXIO;
>>> +
>>> +       info->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
>>> +                                     "regulator-max-microvolt", -1);
>>> +       if (info->max_uV < 0)
>>> +               return -ENXIO;
>>> +
>>> +       /* Optional constraints */
>>> +       info->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
>>> +                                     "regulator-min-microamp", -1);
>>> +       info->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
>>> +                                     "regulator-max-microamp", -1);
>>> +       info->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
>>> +                                        "regulator-always-on");
>>> +       info->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
>>> +                                        "regulator-boot-on");
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +int regulator_get(char *name, struct udevice **devp)
>>> +{
>>> +       struct dm_regulator_info *info;
>>> +       struct udevice *dev;
>>> +       int ret;
>>> +
>>> +       *devp = NULL;
>>> +
>>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev);
>>> +            dev;
>>> +            ret = uclass_next_device(&dev)) {
>>
>>
>> This will probe all devices. See my suggestion about creating
>> uclass_find_first_device()/uclass_find_next_device() in the next
>> patch.
>>
>> As before, I think this could use a function like
>> uclass_get_device_by_name().
>>
>
> Yes, this is the same. But in this case, there is one more  issue, which is
> the regulator device name.
> Usually after bind -> the dev->name is the same as node name. This is good,
> since it's natural that regulator IC, provides e.g "ldo1", or some other
> device-output name.
> But we would like check the "regulator-name" property. For this patch-set,
> the name is fixed at device probe stage, when dev->ofdata_to_platdata() is
> called, and the regulator constraints, the same as it's name goes to struct
> dm_regulator_info.
>
> I put the dm_regulator_info into uclass priv, because it it uclass-specific,
> the same as struct dm_i2c_bus is specific for i2c buses.
>
> But, the ucalss priv is allocated at device probe stage.
>
> I can't use the .per_child_platdata_auto_alloc_size, since the parent is a
> pmic, and its uclass type is different.
>
> Actually I could, move the dm_regulator_info only to device platdata, but
> then, the drivers should take care of this uclass-specific structure
> allocation.
> Is it acceptable?
>
> But then, also ambiguous seem to be filling platdata (struct
> dm_regulator_info) in uclass post_bind() method.
>
> So then, maybe reasonable is:
> - move dm_regulator_info from dev->uclass_priv to dev->platdata - is
> allocated after device bind
>
> - add .post_bind() method to regulator uclass, and get the "regulator-name"
> in it - only
>
> - fill all platdata constraints on call to dev->ofdata_to_platdata()
>
> Then, I could avoid probing every device, when checking the regulator name.
> But, still I can't use the uclass.c functions, since I'm checking the
> regulator info at dev->platdata.
>
> So I update the function as below:
>
> + uclass_foreach_dev(dev, uc) {
> +       if (!dev->platdata)
> +               continue;
> +
> +       info = dev->platdata;
> +       if (!strcmp(name, info->name)) {
> +               ret = device_probe(dev);
> +               if (ret)
> +                              ....
> +                       *regulator = dev;
> +                       return ret;
> +               }
> +       }
>
>

The problem here is similar to I2C which uses per-child platdata
(specified by the uclass) for the bus address. This is different from
device platdata. I think you are suggesting that we should support
uclass platdata. In this case we would have for each device:

- device platform data
- parent platform data
- uclass platform data

The last one is not supported. I have through several times about
adding it. The alternative is to require each device to provide a
platform data struct at the start of its own platform data, which the
uclass can find and use. This is not really in the spirit of driver
model. But for now this is what I have done with USB (see
dm/usb-working). struct usb_platdata appears at the start of struct
exynos_ehci_platdata but is owned by the uclass.

If PMIC has use for uclass platform data, then perhaps that would be
enough users to warrant it. You could add a patch to do this (don't
forget to update tests) or you could do what I have done with USB and
we can tidy it up later.

Re your naming problem, apart from case the device name and
regulator-compatible name are the same. So perhaps just use
case-insensitive search for regulators? But if not then I take back my
comment about using a common function for searching for regulator
names. You are really searching the platform data for the
regulator-compatible string, not the device name, and so the common
function cannot be used.

[snip]

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass
  2015-04-05 18:30           ` Simon Glass
@ 2015-04-07 15:31             ` Przemyslaw Marczak
  2015-04-08  1:47               ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-07 15:31 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/05/2015 08:30 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 3 April 2015 at 10:09, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Hello Simon,
>>
>>
>> On 03/29/2015 03:07 PM, Simon Glass wrote:
>>>
>>> Hi Przemyslaw,
>>>
>>> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com>
>>> wrote:
>
> [snip]
>
>>
>>>> +
>>>> +       info->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
>>>> +                                     "regulator-min-microvolt", -1);
>>>> +       if (info->min_uV < 0)
>>>> +               return -ENXIO;
>>>> +
>>>> +       info->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
>>>> +                                     "regulator-max-microvolt", -1);
>>>> +       if (info->max_uV < 0)
>>>> +               return -ENXIO;
>>>> +
>>>> +       /* Optional constraints */
>>>> +       info->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
>>>> +                                     "regulator-min-microamp", -1);
>>>> +       info->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
>>>> +                                     "regulator-max-microamp", -1);
>>>> +       info->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
>>>> +                                        "regulator-always-on");
>>>> +       info->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
>>>> +                                        "regulator-boot-on");
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +int regulator_get(char *name, struct udevice **devp)
>>>> +{
>>>> +       struct dm_regulator_info *info;
>>>> +       struct udevice *dev;
>>>> +       int ret;
>>>> +
>>>> +       *devp = NULL;
>>>> +
>>>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev);
>>>> +            dev;
>>>> +            ret = uclass_next_device(&dev)) {
>>>
>>>
>>> This will probe all devices. See my suggestion about creating
>>> uclass_find_first_device()/uclass_find_next_device() in the next
>>> patch.
>>>
>>> As before, I think this could use a function like
>>> uclass_get_device_by_name().
>>>
>>
>> Yes, this is the same. But in this case, there is one more  issue, which is
>> the regulator device name.
>> Usually after bind -> the dev->name is the same as node name. This is good,
>> since it's natural that regulator IC, provides e.g "ldo1", or some other
>> device-output name.
>> But we would like check the "regulator-name" property. For this patch-set,
>> the name is fixed at device probe stage, when dev->ofdata_to_platdata() is
>> called, and the regulator constraints, the same as it's name goes to struct
>> dm_regulator_info.
>>
>> I put the dm_regulator_info into uclass priv, because it it uclass-specific,
>> the same as struct dm_i2c_bus is specific for i2c buses.
>>
>> But, the ucalss priv is allocated at device probe stage.
>>
>> I can't use the .per_child_platdata_auto_alloc_size, since the parent is a
>> pmic, and its uclass type is different.
>>
>> Actually I could, move the dm_regulator_info only to device platdata, but
>> then, the drivers should take care of this uclass-specific structure
>> allocation.
>> Is it acceptable?
>>
>> But then, also ambiguous seem to be filling platdata (struct
>> dm_regulator_info) in uclass post_bind() method.
>>
>> So then, maybe reasonable is:
>> - move dm_regulator_info from dev->uclass_priv to dev->platdata - is
>> allocated after device bind
>>
>> - add .post_bind() method to regulator uclass, and get the "regulator-name"
>> in it - only
>>
>> - fill all platdata constraints on call to dev->ofdata_to_platdata()
>>
>> Then, I could avoid probing every device, when checking the regulator name.
>> But, still I can't use the uclass.c functions, since I'm checking the
>> regulator info at dev->platdata.
>>
>> So I update the function as below:
>>
>> + uclass_foreach_dev(dev, uc) {
>> +       if (!dev->platdata)
>> +               continue;
>> +
>> +       info = dev->platdata;
>> +       if (!strcmp(name, info->name)) {
>> +               ret = device_probe(dev);
>> +               if (ret)
>> +                              ....
>> +                       *regulator = dev;
>> +                       return ret;
>> +               }
>> +       }
>>
>>
>
> The problem here is similar to I2C which uses per-child platdata
> (specified by the uclass) for the bus address. This is different from
> device platdata. I think you are suggesting that we should support
> uclass platdata. In this case we would have for each device:
>
> - device platform data
> - parent platform data
> - uclass platform data

Yes, but note, that the uclass type is the same for I2C bus and i2c 
chip. This is a different than for the PMIC, for which childs uclass 
type are usually different than for the parent.
In this case I can't use the field per-child-platdata.

>
> The last one is not supported. I have through several times about
> adding it. The alternative is to require each device to provide a
> platform data struct at the start of its own platform data, which the
> uclass can find and use. This is not really in the spirit of driver
> model. But for now this is what I have done with USB (see
> dm/usb-working). struct usb_platdata appears at the start of struct
> exynos_ehci_platdata but is owned by the uclass.
>
> If PMIC has use for uclass platform data, then perhaps that would be
> enough users to warrant it. You could add a patch to do this (don't
> forget to update tests) or you could do what I have done with USB and
> we can tidy it up later.

The example of usb is good enough. I could move the "dm_regulator_info" 
into the dm_regulator_platdata, and add "void *dev_platdata" at the end 
of it.
 From the other side, the regulator constraints and modes, are all what 
we need for this uclass.
We have also the fixed-regulators which some platform data are the same 
as for the generic regulators - then the dev->uclass_platdata is 
reasonable for this purpose.

I will add the uclass platdata to struct udevice and also some test to 
test/dm drivers. I will send it as a separate patch.

>
> Re your naming problem, apart from case the device name and
> regulator-compatible name are the same. So perhaps just use
> case-insensitive search for regulators? But if not then I take back my
> comment about using a common function for searching for regulator
> names. You are really searching the platform data for the
> regulator-compatible string, not the device name, and so the common
> function cannot be used.

Then we could provide two functions:
- regulator_by_devname() - search for device -> name
- regulator_by_platname() - search for platdata -> name

>
> [snip]
>
> Regards,
> Simon
>

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass
  2015-04-07 15:31             ` Przemyslaw Marczak
@ 2015-04-08  1:47               ` Simon Glass
  2015-04-08  7:37                 ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-08  1:47 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 7 April 2015 at 09:31, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 04/05/2015 08:30 PM, Simon Glass wrote:
>>
>> Hi Przemyslaw,
>>
>> On 3 April 2015 at 10:09, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>>> Hello Simon,
>>>
>>>
>>> On 03/29/2015 03:07 PM, Simon Glass wrote:
>>>>
>>>>
>>>> Hi Przemyslaw,
>>>>
>>>> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com>
>>>> wrote:
>>
>>
>> [snip]
>>
>>>
>>>>> +
>>>>> +       info->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
>>>>> +                                     "regulator-min-microvolt", -1);
>>>>> +       if (info->min_uV < 0)
>>>>> +               return -ENXIO;
>>>>> +
>>>>> +       info->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
>>>>> +                                     "regulator-max-microvolt", -1);
>>>>> +       if (info->max_uV < 0)
>>>>> +               return -ENXIO;
>>>>> +
>>>>> +       /* Optional constraints */
>>>>> +       info->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
>>>>> +                                     "regulator-min-microamp", -1);
>>>>> +       info->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
>>>>> +                                     "regulator-max-microamp", -1);
>>>>> +       info->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
>>>>> +                                        "regulator-always-on");
>>>>> +       info->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
>>>>> +                                        "regulator-boot-on");
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +int regulator_get(char *name, struct udevice **devp)
>>>>> +{
>>>>> +       struct dm_regulator_info *info;
>>>>> +       struct udevice *dev;
>>>>> +       int ret;
>>>>> +
>>>>> +       *devp = NULL;
>>>>> +
>>>>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev);
>>>>> +            dev;
>>>>> +            ret = uclass_next_device(&dev)) {
>>>>
>>>>
>>>>
>>>> This will probe all devices. See my suggestion about creating
>>>> uclass_find_first_device()/uclass_find_next_device() in the next
>>>> patch.
>>>>
>>>> As before, I think this could use a function like
>>>> uclass_get_device_by_name().
>>>>
>>>
>>> Yes, this is the same. But in this case, there is one more  issue, which
>>> is
>>> the regulator device name.
>>> Usually after bind -> the dev->name is the same as node name. This is
>>> good,
>>> since it's natural that regulator IC, provides e.g "ldo1", or some other
>>> device-output name.
>>> But we would like check the "regulator-name" property. For this
>>> patch-set,
>>> the name is fixed at device probe stage, when dev->ofdata_to_platdata()
>>> is
>>> called, and the regulator constraints, the same as it's name goes to
>>> struct
>>> dm_regulator_info.
>>>
>>> I put the dm_regulator_info into uclass priv, because it it
>>> uclass-specific,
>>> the same as struct dm_i2c_bus is specific for i2c buses.
>>>
>>> But, the ucalss priv is allocated at device probe stage.
>>>
>>> I can't use the .per_child_platdata_auto_alloc_size, since the parent is
>>> a
>>> pmic, and its uclass type is different.
>>>
>>> Actually I could, move the dm_regulator_info only to device platdata, but
>>> then, the drivers should take care of this uclass-specific structure
>>> allocation.
>>> Is it acceptable?
>>>
>>> But then, also ambiguous seem to be filling platdata (struct
>>> dm_regulator_info) in uclass post_bind() method.
>>>
>>> So then, maybe reasonable is:
>>> - move dm_regulator_info from dev->uclass_priv to dev->platdata - is
>>> allocated after device bind
>>>
>>> - add .post_bind() method to regulator uclass, and get the
>>> "regulator-name"
>>> in it - only
>>>
>>> - fill all platdata constraints on call to dev->ofdata_to_platdata()
>>>
>>> Then, I could avoid probing every device, when checking the regulator
>>> name.
>>> But, still I can't use the uclass.c functions, since I'm checking the
>>> regulator info at dev->platdata.
>>>
>>> So I update the function as below:
>>>
>>> + uclass_foreach_dev(dev, uc) {
>>> +       if (!dev->platdata)
>>> +               continue;
>>> +
>>> +       info = dev->platdata;
>>> +       if (!strcmp(name, info->name)) {
>>> +               ret = device_probe(dev);
>>> +               if (ret)
>>> +                              ....
>>> +                       *regulator = dev;
>>> +                       return ret;
>>> +               }
>>> +       }
>>>
>>>
>>
>> The problem here is similar to I2C which uses per-child platdata
>> (specified by the uclass) for the bus address. This is different from
>> device platdata. I think you are suggesting that we should support
>> uclass platdata. In this case we would have for each device:
>>
>> - device platform data
>> - parent platform data
>> - uclass platform data
>
>
> Yes, but note, that the uclass type is the same for I2C bus and i2c chip.
> This is a different than for the PMIC, for which childs uclass type are
> usually different than for the parent.
> In this case I can't use the field per-child-platdata.

The I2C bus uses UCLASS_I2C. The chips use a different UCLASS. If
there is no specific driver for the chip then we will use
UCLASS_I2C_GENERIC, but in general it could be anything. So perhaps
there is no difference here?

Per-child platdata works for I2C buses because its children are all
I2C chips, whatever their uclass.

>
>>
>> The last one is not supported. I have through several times about
>> adding it. The alternative is to require each device to provide a
>> platform data struct at the start of its own platform data, which the
>> uclass can find and use. This is not really in the spirit of driver
>> model. But for now this is what I have done with USB (see
>> dm/usb-working). struct usb_platdata appears at the start of struct
>> exynos_ehci_platdata but is owned by the uclass.
>>
>> If PMIC has use for uclass platform data, then perhaps that would be
>> enough users to warrant it. You could add a patch to do this (don't
>> forget to update tests) or you could do what I have done with USB and
>> we can tidy it up later.
>
>
> The example of usb is good enough. I could move the "dm_regulator_info" into
> the dm_regulator_platdata, and add "void *dev_platdata" at the end of it.
> From the other side, the regulator constraints and modes, are all what we
> need for this uclass.
> We have also the fixed-regulators which some platform data are the same as
> for the generic regulators - then the dev->uclass_platdata is reasonable for
> this purpose.
>
> I will add the uclass platdata to struct udevice and also some test to
> test/dm drivers. I will send it as a separate patch.

OK thanks. As a reminded, please continue to rebase on dm/next.

>
>>
>> Re your naming problem, apart from case the device name and
>> regulator-compatible name are the same. So perhaps just use
>> case-insensitive search for regulators? But if not then I take back my
>> comment about using a common function for searching for regulator
>> names. You are really searching the platform data for the
>> regulator-compatible string, not the device name, and so the common
>> function cannot be used.
>
>
> Then we could provide two functions:
> - regulator_by_devname() - search for device -> name
> - regulator_by_platname() - search for platdata -> name

OK.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass
  2015-04-08  1:47               ` Simon Glass
@ 2015-04-08  7:37                 ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-08  7:37 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/08/2015 03:47 AM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 7 April 2015 at 09:31, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Hello Simon,
>>
>>
>> On 04/05/2015 08:30 PM, Simon Glass wrote:
>>>
>>> Hi Przemyslaw,
>>>
>>> On 3 April 2015 at 10:09, Przemyslaw Marczak <p.marczak@samsung.com>
>>> wrote:
>>>>
>>>> Hello Simon,
>>>>
>>>>
>>>> On 03/29/2015 03:07 PM, Simon Glass wrote:
>>>>>
>>>>>
>>>>> Hi Przemyslaw,
>>>>>
>>>>> On 24 March 2015 at 14:30, Przemyslaw Marczak <p.marczak@samsung.com>
>>>>> wrote:
>>>
>>>
>>> [snip]
>>>
>>>>
>>>>>> +
>>>>>> +       info->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
>>>>>> +                                     "regulator-min-microvolt", -1);
>>>>>> +       if (info->min_uV < 0)
>>>>>> +               return -ENXIO;
>>>>>> +
>>>>>> +       info->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
>>>>>> +                                     "regulator-max-microvolt", -1);
>>>>>> +       if (info->max_uV < 0)
>>>>>> +               return -ENXIO;
>>>>>> +
>>>>>> +       /* Optional constraints */
>>>>>> +       info->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
>>>>>> +                                     "regulator-min-microamp", -1);
>>>>>> +       info->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
>>>>>> +                                     "regulator-max-microamp", -1);
>>>>>> +       info->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
>>>>>> +                                        "regulator-always-on");
>>>>>> +       info->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
>>>>>> +                                        "regulator-boot-on");
>>>>>> +
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +
>>>>>> +int regulator_get(char *name, struct udevice **devp)
>>>>>> +{
>>>>>> +       struct dm_regulator_info *info;
>>>>>> +       struct udevice *dev;
>>>>>> +       int ret;
>>>>>> +
>>>>>> +       *devp = NULL;
>>>>>> +
>>>>>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev);
>>>>>> +            dev;
>>>>>> +            ret = uclass_next_device(&dev)) {
>>>>>
>>>>>
>>>>>
>>>>> This will probe all devices. See my suggestion about creating
>>>>> uclass_find_first_device()/uclass_find_next_device() in the next
>>>>> patch.
>>>>>
>>>>> As before, I think this could use a function like
>>>>> uclass_get_device_by_name().
>>>>>
>>>>
>>>> Yes, this is the same. But in this case, there is one more  issue, which
>>>> is
>>>> the regulator device name.
>>>> Usually after bind -> the dev->name is the same as node name. This is
>>>> good,
>>>> since it's natural that regulator IC, provides e.g "ldo1", or some other
>>>> device-output name.
>>>> But we would like check the "regulator-name" property. For this
>>>> patch-set,
>>>> the name is fixed at device probe stage, when dev->ofdata_to_platdata()
>>>> is
>>>> called, and the regulator constraints, the same as it's name goes to
>>>> struct
>>>> dm_regulator_info.
>>>>
>>>> I put the dm_regulator_info into uclass priv, because it it
>>>> uclass-specific,
>>>> the same as struct dm_i2c_bus is specific for i2c buses.
>>>>
>>>> But, the ucalss priv is allocated at device probe stage.
>>>>
>>>> I can't use the .per_child_platdata_auto_alloc_size, since the parent is
>>>> a
>>>> pmic, and its uclass type is different.
>>>>
>>>> Actually I could, move the dm_regulator_info only to device platdata, but
>>>> then, the drivers should take care of this uclass-specific structure
>>>> allocation.
>>>> Is it acceptable?
>>>>
>>>> But then, also ambiguous seem to be filling platdata (struct
>>>> dm_regulator_info) in uclass post_bind() method.
>>>>
>>>> So then, maybe reasonable is:
>>>> - move dm_regulator_info from dev->uclass_priv to dev->platdata - is
>>>> allocated after device bind
>>>>
>>>> - add .post_bind() method to regulator uclass, and get the
>>>> "regulator-name"
>>>> in it - only
>>>>
>>>> - fill all platdata constraints on call to dev->ofdata_to_platdata()
>>>>
>>>> Then, I could avoid probing every device, when checking the regulator
>>>> name.
>>>> But, still I can't use the uclass.c functions, since I'm checking the
>>>> regulator info at dev->platdata.
>>>>
>>>> So I update the function as below:
>>>>
>>>> + uclass_foreach_dev(dev, uc) {
>>>> +       if (!dev->platdata)
>>>> +               continue;
>>>> +
>>>> +       info = dev->platdata;
>>>> +       if (!strcmp(name, info->name)) {
>>>> +               ret = device_probe(dev);
>>>> +               if (ret)
>>>> +                              ....
>>>> +                       *regulator = dev;
>>>> +                       return ret;
>>>> +               }
>>>> +       }
>>>>
>>>>
>>>
>>> The problem here is similar to I2C which uses per-child platdata
>>> (specified by the uclass) for the bus address. This is different from
>>> device platdata. I think you are suggesting that we should support
>>> uclass platdata. In this case we would have for each device:
>>>
>>> - device platform data
>>> - parent platform data
>>> - uclass platform data
>>
>>
>> Yes, but note, that the uclass type is the same for I2C bus and i2c chip.
>> This is a different than for the PMIC, for which childs uclass type are
>> usually different than for the parent.
>> In this case I can't use the field per-child-platdata.
>
> The I2C bus uses UCLASS_I2C. The chips use a different UCLASS. If
> there is no specific driver for the chip then we will use
> UCLASS_I2C_GENERIC, but in general it could be anything. So perhaps
> there is no difference here?
>
> Per-child platdata works for I2C buses because its children are all
> I2C chips, whatever their uclass.
>

Sorry, I wrote nonsense. I meant something different.
We could use per-child-platdata field for pmic uclass driver with the 
assumption, that each pmic's child's uclass is the same type (e.g. 
regulator). But this assumption will be broken by fixed-regulator. And 
also, pmic's childs are not only regulators - e.g. one can be RTC, other 
charger, etc...

>>
>>>
>>> The last one is not supported. I have through several times about
>>> adding it. The alternative is to require each device to provide a
>>> platform data struct at the start of its own platform data, which the
>>> uclass can find and use. This is not really in the spirit of driver
>>> model. But for now this is what I have done with USB (see
>>> dm/usb-working). struct usb_platdata appears at the start of struct
>>> exynos_ehci_platdata but is owned by the uclass.
>>>
>>> If PMIC has use for uclass platform data, then perhaps that would be
>>> enough users to warrant it. You could add a patch to do this (don't
>>> forget to update tests) or you could do what I have done with USB and
>>> we can tidy it up later.
>>
>>
>> The example of usb is good enough. I could move the "dm_regulator_info" into
>> the dm_regulator_platdata, and add "void *dev_platdata" at the end of it.
>>  From the other side, the regulator constraints and modes, are all what we
>> need for this uclass.
>> We have also the fixed-regulators which some platform data are the same as
>> for the generic regulators - then the dev->uclass_platdata is reasonable for
>> this purpose.
>>
>> I will add the uclass platdata to struct udevice and also some test to
>> test/dm drivers. I will send it as a separate patch.
>
> OK thanks. As a reminded, please continue to rebase on dm/next.
>

Ok, I will rebase it.

>>
>>>
>>> Re your naming problem, apart from case the device name and
>>> regulator-compatible name are the same. So perhaps just use
>>> case-insensitive search for regulators? But if not then I take back my
>>> comment about using a common function for searching for regulator
>>> names. You are really searching the platform data for the
>>> regulator-compatible string, not the device name, and so the common
>>> function cannot be used.
>>
>>
>> Then we could provide two functions:
>> - regulator_by_devname() - search for device -> name
>> - regulator_by_platname() - search for platdata -> name
>
> OK.
>
> Regards,
> Simon
>

Thanks,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 00/16] Power(full) framework based on Driver Model
  2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
                       ` (18 preceding siblings ...)
  2015-03-29 13:05     ` Simon Glass
@ 2015-04-20 18:07     ` Przemyslaw Marczak
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 01/16] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
                         ` (16 more replies)
  19 siblings, 17 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

Hello,
Again the next version. The changes are described below each commit message.
This is rebased on last u-boot-dm/master after apply this patchset:
https://patchwork.ozlabs.org/patch/462775/
https://patchwork.ozlabs.org/patch/462777/
https://patchwork.ozlabs.org/patch/462776/

This all can be found in here:
https://github.com/bobenstein/u-boot/tree/dm-pmic-v4

Best regards,

Przemyslaw Marczak (16):
  exynos5: fix build break by adding CONFIG_POWER
  exynos4-common: remove the unsued CONFIG_CMD_PMIC
  lib: Kconfig: add entry for errno_str() function
  dm: pmic: add implementation of driver model pmic uclass
  dm: regulator: add implementation of driver model regulator uclass
  dm: pmic: add pmic command
  dm: regulator: add regulator command
  pmic: max77686 set the same compatible as in the kernel
  dm: pmic: add max77686 pmic driver
  dm: regulator: add max77686 regulator driver
  dm: regulator: add fixed voltage 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                                         |   3 +-
 arch/arm/dts/exynos4412-odroid.dts               | 255 ++++++-
 arch/arm/dts/exynos4412-trats2.dts               |   2 +-
 arch/arm/dts/exynos5250-smdk5250.dts             |   2 +-
 arch/arm/dts/exynos5250-snow.dts                 |   2 +-
 board/samsung/common/board.c                     |   4 +-
 board/samsung/common/misc.c                      |   1 +
 board/samsung/odroid/odroid.c                    |  77 ++-
 common/Kconfig                                   |  36 +
 common/Makefile                                  |   4 +
 common/cmd_pmic.c                                | 231 +++++++
 common/cmd_regulator.c                           | 403 +++++++++++
 configs/odroid_defconfig                         |   8 +-
 doc/device-tree-bindings/pmic/max77686.txt       |  36 +
 doc/device-tree-bindings/regulator/fixed.txt     |  38 ++
 doc/device-tree-bindings/regulator/max77686.txt  |  70 ++
 doc/device-tree-bindings/regulator/regulator.txt |  55 ++
 doc/driver-model/pmic-framework.txt              | 142 ++++
 drivers/power/Kconfig                            |   8 +
 drivers/power/Makefile                           |   1 -
 drivers/power/pmic/Kconfig                       |  18 +
 drivers/power/pmic/Makefile                      |   2 +
 drivers/power/pmic/max77686.c                    |  87 +++
 drivers/power/pmic/pmic-uclass.c                 | 158 +++++
 drivers/power/pmic/pmic_max77686.c               |   2 +-
 drivers/power/regulator/Kconfig                  |  33 +
 drivers/power/regulator/Makefile                 |  10 +
 drivers/power/regulator/fixed.c                  | 126 ++++
 drivers/power/regulator/max77686.c               | 825 +++++++++++++++++++++++
 drivers/power/regulator/regulator-uclass.c       | 300 +++++++++
 include/configs/exynos4-common.h                 |   1 -
 include/configs/exynos5-common.h                 |   4 +
 include/configs/odroid.h                         |   5 -
 include/dm/uclass-id.h                           |   4 +
 include/power/max77686_pmic.h                    |  29 +-
 include/power/pmic.h                             | 189 ++++++
 include/power/regulator.h                        | 384 +++++++++++
 lib/Kconfig                                      |   8 +
 lib/fdtdec.c                                     |   2 +-
 39 files changed, 3512 insertions(+), 53 deletions(-)
 create mode 100644 common/cmd_pmic.c
 create mode 100644 common/cmd_regulator.c
 create mode 100644 doc/device-tree-bindings/pmic/max77686.txt
 create mode 100644 doc/device-tree-bindings/regulator/fixed.txt
 create mode 100644 doc/device-tree-bindings/regulator/max77686.txt
 create mode 100644 doc/device-tree-bindings/regulator/regulator.txt
 create mode 100644 doc/driver-model/pmic-framework.txt
 create mode 100644 drivers/power/pmic/Kconfig
 create mode 100644 drivers/power/pmic/max77686.c
 create mode 100644 drivers/power/pmic/pmic-uclass.c
 create mode 100644 drivers/power/regulator/Kconfig
 create mode 100644 drivers/power/regulator/Makefile
 create mode 100644 drivers/power/regulator/fixed.c
 create mode 100644 drivers/power/regulator/max77686.c
 create mode 100644 drivers/power/regulator/regulator-uclass.c
 create mode 100644 include/power/regulator.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 01/16] exynos5: fix build break by adding CONFIG_POWER
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:29         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 02/16] exynos4-common: remove the unsued CONFIG_CMD_PMIC Przemyslaw Marczak
                         ` (15 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

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.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 include/configs/exynos5-common.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/configs/exynos5-common.h b/include/configs/exynos5-common.h
index 2eddb07..5476248 100644
--- a/include/configs/exynos5-common.h
+++ b/include/configs/exynos5-common.h
@@ -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
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 02/16] exynos4-common: remove the unsued CONFIG_CMD_PMIC
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 01/16] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:29         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 03/16] lib: Kconfig: add entry for errno_str() function Przemyslaw Marczak
                         ` (14 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

This config name was never used, because the present pmic command
was precompiled for the CONFIG_POWER.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 include/configs/exynos4-common.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/include/configs/exynos4-common.h b/include/configs/exynos4-common.h
index 577afe7..dbe05e4 100644
--- a/include/configs/exynos4-common.h
+++ b/include/configs/exynos4-common.h
@@ -31,7 +31,6 @@
 #undef CONFIG_CMD_MTDPARTS
 #define CONFIG_CMD_DFU
 #define CONFIG_CMD_GPT
-#define CONFIG_CMD_PMIC
 #define CONFIG_CMD_SETEXPR
 
 /* USB Composite download gadget - g_dnl */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 03/16] lib: Kconfig: add entry for errno_str() function
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 01/16] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 02/16] exynos4-common: remove the unsued CONFIG_CMD_PMIC Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:29         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 04/16] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
                         ` (13 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 lib/Kconfig | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/lib/Kconfig b/lib/Kconfig
index d7fd219..b64cd92 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -74,4 +74,12 @@ config SHA_PROG_HW_ACCEL
 	  is performed in hardware.
 endmenu
 
+config ERRNO_STR
+	bool "Enable function for getting errno-related string message"
+	help
+	  The function errno_str(int errno), 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
+
 endmenu
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 04/16] dm: pmic: add implementation of driver model pmic uclass
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (2 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 03/16] lib: Kconfig: add entry for errno_str() function Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:30         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 05/16] dm: regulator: add implementation of driver model regulator uclass Przemyslaw Marczak
                         ` (12 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

This commit introduces the PMIC uclass implementation.
It allows providing the basic I/O interface for PMIC devices.
For the multi-function PMIC devices, this can be used as I/O
parent device, for each IC's interface. Then, each PMIC particular
function can be provided by the child device's operations, and the
child devices will use its parent for read/write by the common API.

Core files:
- 'include/power/pmic.h'
- 'drivers/power/pmic/pmic-uclass.c'

The old pmic framework is still kept and is independent.

For more detailed informations, please look into the header file.

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
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

Changes V3:
- pmic-uclass.c and pmic.h:
  -- remove  pmic_if_* functions
  -- add new function pmic_child_node_scan()
- add Kconfig entry

Changes V4:
- move drivers/power/pmic-uclass.c to drivers/power/pmic/pmic-uclass.c
- move DM_PMIC Kconfig entry: drivers/power/Kconfig to drivers/power/pmic/Kconfig
- drivers/power/Kconfig: Add menu "Power" and include pmic Kconfig
- Kconfig: provide only the general information about the PMIC
- pmic-uclass.c: add pmic_bind_childs()
- pmic-uclass.c: add debug
- pmic-uclass.c: cleanup includes
- pmic-uclass.c: remove pmic_get_uclass_ops() and use of dev_get_driver_ops()
- pmic-uclass.c: use of uclass_get_device_by_name()
- pmic-uclass.c: add str_get_num() - for get number from string
- include/power/pmic.h - start comments rewording
- power/pmic.h: comments update
---
 drivers/power/Kconfig            |   6 ++
 drivers/power/pmic/Kconfig       |  11 +++
 drivers/power/pmic/Makefile      |   1 +
 drivers/power/pmic/pmic-uclass.c | 158 ++++++++++++++++++++++++++++++++
 include/dm/uclass-id.h           |   3 +
 include/power/pmic.h             | 189 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 368 insertions(+)
 create mode 100644 drivers/power/pmic/Kconfig
 create mode 100644 drivers/power/pmic/pmic-uclass.c

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index f8f0239..d03626e 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -1,3 +1,7 @@
+menu "Power"
+
+source "drivers/power/pmic/Kconfig"
+
 config AXP221_POWER
 	boolean "axp221 / axp223 pmic support"
 	depends on MACH_SUN6I || MACH_SUN8I
@@ -73,3 +77,5 @@ config AXP221_ELDO3_VOLT
 	disable eldo3. On some A31(s) tablets it might be used to supply
 	1.2V for the SSD2828 chip (converter of parallel LCD interface
 	into MIPI DSI).
+
+endmenu
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
new file mode 100644
index 0000000..d06d632
--- /dev/null
+++ b/drivers/power/pmic/Kconfig
@@ -0,0 +1,11 @@
+config DM_PMIC
+	bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC)"
+	depends on DM
+	---help---
+	This config enables the driver-model PMIC support.
+	UCLASS_PMIC - designed to provide an I/O interface for PMIC devices.
+	For the multi-function PMIC devices, this can be used as parent I/O
+	device for each IC's interface. Then, each children uses its parent
+	for read/write. For detailed description, please refer to the files:
+	- 'drivers/power/pmic/pmic-uclass.c'
+	- 'include/power/pmic.h'
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 985cfdb..594f620 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -5,6 +5,7 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
 obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o
 obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o
 obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c
new file mode 100644
index 0000000..d82d3da
--- /dev/null
+++ b/drivers/power/pmic/pmic-uclass.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <power/pmic.h>
+#include <linux/ctype.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static ulong str_get_num(const char *ptr, const char *maxptr)
+{
+	if (!ptr || !maxptr)
+		return 0;
+
+	while (!isdigit(*ptr) && ptr++ < maxptr);
+
+	return simple_strtoul(ptr, NULL, 0);
+}
+
+int pmic_bind_childs(struct udevice *pmic, int offset,
+		     const struct pmic_child_info *child_info)
+{
+	const struct pmic_child_info *info;
+	const void *blob = gd->fdt_blob;
+	struct driver *drv;
+	struct udevice *child;
+	const char *node_name;
+	int node_name_len;
+	int bind_count = 0;
+	int node;
+	int prefix_len;
+	int ret;
+
+	debug("%s for '%s' at node offset: %d\n", __func__, pmic->name,
+	      pmic->of_offset);
+
+	for (node = fdt_first_subnode(blob, offset);
+	     node > 0;
+	     node = fdt_next_subnode(blob, node)) {
+		node_name = fdt_get_name(blob, node, &node_name_len);
+
+		debug("* Found child node: '%s' at offset:%d\n", node_name,
+								 node);
+
+		child = NULL;
+		info = child_info;
+		while (info->prefix) {
+			prefix_len = strlen(info->prefix);
+			if (strncasecmp(info->prefix, node_name, prefix_len) ||
+			    !info->driver) {
+				info++;
+				continue;
+			}
+
+			debug("  - compatible prefix: '%s'\n", info->prefix);
+
+			drv = lists_driver_lookup_name(info->driver);
+			if (!drv) {
+				debug("  - driver: '%s' not found!\n",
+				      info->driver);
+				continue;
+			}
+
+			debug("  - found child driver: '%s'\n", drv->name);
+
+			ret = device_bind(pmic, drv, node_name, NULL,
+					  node, &child);
+			if (ret) {
+				debug("  - child binding error: %d\n", ret);
+				continue;
+			}
+
+			debug("  - bound child device: '%s'\n", child->name);
+
+			child->driver_data = str_get_num(node_name +
+							 prefix_len,
+							 node_name +
+							 node_name_len);
+
+			debug("  - set 'child->driver_data': %lu\n",
+			      child->driver_data);
+			break;
+		}
+
+		if (child)
+			bind_count++;
+		else
+			debug("  - compatible prefix not found\n");
+	}
+
+	debug("Bound: %d childs for PMIC: '%s'\n", bind_count, pmic->name);
+	return bind_count;
+}
+
+int pmic_get(const char *name, struct udevice **devp)
+{
+	return uclass_get_device_by_name(UCLASS_PMIC, name, devp);
+}
+
+int pmic_reg_count(struct udevice *dev)
+{
+	const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
+	if (!ops)
+		return -ENOSYS;
+
+	return ops->reg_count;
+}
+
+int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len)
+{
+	const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
+	int ret;
+
+	if (!buffer)
+		return -EFAULT;
+
+	if (!ops || !ops->read)
+		return -ENOSYS;
+
+	ret = ops->read(dev, reg, buffer, len);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len)
+{
+	const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
+	int ret;
+
+	if (!buffer)
+		return -EFAULT;
+
+	if (!ops || !ops->write)
+		return -ENOSYS;
+
+	ret = ops->write(dev, reg, buffer, len);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+UCLASS_DRIVER(pmic) = {
+	.id		= UCLASS_PMIC,
+	.name		= "pmic",
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index fddfd35..23b3eb9 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -46,6 +46,9 @@ enum uclass_id {
 	UCLASS_USB_DEV_GENERIC,	/* USB generic device */
 	UCLASS_MASS_STORAGE,	/* Mass storage device */
 
+	/* Power Management */
+	UCLASS_PMIC,		/* PMIC I/O device */
+
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
 };
diff --git a/include/power/pmic.h b/include/power/pmic.h
index afbc5aa..f7ae781 100644
--- a/include/power/pmic.h
+++ b/include/power/pmic.h
@@ -1,4 +1,7 @@
 /*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
  *  Copyright (C) 2011-2012 Samsung Electronics
  *  Lukasz Majewski <l.majewski@samsung.com>
  *
@@ -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,189 @@ struct pmic {
 	struct pmic *parent;
 	struct list_head list;
 };
+#endif /* CONFIG_POWER */
+
+#ifdef CONFIG_DM_PMIC
+/**
+ * U-Boot PMIC Framework
+ * =====================
+ *
+ * UCLASS_PMIC - The is designed to provide an I/O interface for PMIC devices.
+ *
+ * For the multi-function PMIC devices, this can be used as parent I/O device
+ * for each IC's interface. Then, each children uses its parent for read/write.
+ *
+ * The driver model tree could look 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_RTC     (in the future)
+ *
+ * We can find two PMIC cases in boards design:
+ * - single I/O interface
+ * - multiple I/O interfaces
+ * We bind single PMIC device for each interface, to provide an I/O as a parent,
+ * of proper child devices. Each child usually implements a different function,
+ * controlled by the same interface.
+ *
+ * The binding should be done automatically. If 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 device nodes, for multi-interface PMIC.
+ *
+ * Note:
+ * Each PMIC interface driver should use a different compatible string.
+ *
+ * If each pmic child device driver need access the PMIC-specific registers,
+ * it need know only the register address and the access can be 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 the example below. It should be done by call
+ * the 'pmic_bind_childs()' - please refer to the description of this function
+ * in this header file. This function, should be called in the driver's '.bind'
+ * method.
+ *
+ * For the example driver, please refer the MAX77686 driver:
+ * - 'drivers/power/pmic/max77686.c'
+ */
+
+/**
+ * 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.
+ *
+ * @reg_count: devices register count
+ * @read:      read 'len' bytes at "reg" and store it into the 'buffer'
+ * @write:     write 'len' bytes from the 'buffer' to the register at 'reg' address
+ */
+struct dm_pmic_ops {
+	int reg_count;
+	int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int len);
+	int (*write)(struct udevice *dev, uint reg, const uint8_t *buffer,
+		     int len);
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,
+ * 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,
+};
+
+/**
+ * struct pmic_child_info - basic device's child info for bind child nodes with
+ * the driver by the node name prefix and driver name. This is a helper struct
+ * for function: pmic_bind_childs().
+ *
+ * @prefix - child node name prefix (or its name if is unique or single)
+ * @driver - driver name for the sub-node with prefix
+ */
+struct pmic_child_info {
+	const char *prefix;
+	const char *driver;
+};
+
+/* drivers/power/pmic-uclass.c */
+
+/**
+ * pmic_bind_childs() - bind drivers for given parent pmic, using child info
+ * found in 'child_info' array.
+ *
+ * @pmic       - pmic device - the parent of found child's
+ * @child_info - N-childs info array
+ * @return a positive number of childs, or 0 if no child found (error)
+ *
+ * Note: For N-childs the child_info array should have N+1 entries and the last
+ * entry prefix should be NULL - the same as for drivers compatible.
+ *
+ * For example, a single prefix info (N=1):
+ * static const struct pmic_child_info bind_info[] = {
+ *     { .prefix = "ldo", .driver = "ldo_driver" },
+ *     { },
+ * };
+ *
+ * This function is useful for regulator sub-nodes:
+ * my_regulator at 0xa {
+ *     reg = <0xa>;
+ *     (pmic - bind automatically by compatible)
+ *     compatible = "my_pmic";
+ *     ...
+ *     (pmic's childs - bind by pmic_bind_childs())
+ *     (nodes prefix: "ldo", driver: "my_regulator_ldo")
+ *     ldo1 { ... };
+ *     ldo2 { ... };
+ *
+ *     (nodes prefix: "buck", driver: "my_regulator_buck")
+ *     buck1 { ... };
+ *     buck2 { ... };
+ * };
+ */
+int pmic_bind_childs(struct udevice *pmic, int offset,
+		     const struct pmic_child_info *child_info);
+
+/**
+ * pmic_get: get the pmic device using its name
+ *
+ * @name - device name
+ * @devp - returned pointer to the pmic device
+ * @return 0 on success or negative value of errno.
+ *
+ * The returned devp device can be used with pmic_read/write calls
+ */
+int pmic_get(const char *name, struct udevice **devp);
+
+/**
+ * pmic_reg_count: get the pmic register count
+ *
+ * The required pmic device can be obtained by 'pmic_get()'
+ *
+ * @dev - pointer to the UCLASS_PMIC device
+ * @return register count value on success or negative value of errno.
+ */
+int pmic_reg_count(struct udevice *dev);
+
+/**
+ * pmic_read/write: read/write to the UCLASS_PMIC device
+ *
+ * The required pmic device can be obtained by 'pmic_get()'
+ *
+ * @pmic   - pointer to the UCLASS_PMIC device
+ * @reg    - device register offset
+ * @buffer - pointer to read/write buffer
+ * @len    - byte count for read/write
+ * @return 0 on success or negative value of errno.
+ */
+int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len);
+int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len);
+#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 +276,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)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 05/16] dm: regulator: add implementation of driver model regulator uclass
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (3 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 04/16] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:30         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 06/16] dm: pmic: add pmic command Przemyslaw Marczak
                         ` (11 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

This commit introduces the implementation of dm regulator API.
Device tree support allows for auto binding. And by the basic
uclass operations, it allows to driving the devices in a common
way. For detailed informations, please look into the header file.

Core 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_REGULATOR
- new config: CONFIG_DM_REGULATOR

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
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

Changes V3:
- regulator-uclass.c and regulator.h:
  -- api cleanup
  -- new function regulator_ofdata_to_platdata()
  -- update of comments
  -- add Kconfig

Changes V4:
- move file drivers/power/regulator-uclass.c to
  drivers/power/regulator/regulator-uclass.c
- move DM_REGULATOR Kconfig entry from: drivers/power/Kconfig to
  drivers/power/regulator/Kconfig
- drivers/power/Kconfig: include regulator Kconfig path
- Kconfig: provide only general informations
- regulator-uclass.c: cleanup
- regulator-uclass.c: allow init regulator with name only
- regulator-uclass.c: remove pmic_get_uclass_ops and use dev_get_driver_ops
- regulator-uclass.c: add use of uclass_get_device_by_name()
- regulator.h: add 'struct dm_regulator_uclass_platdata'
- regulator.h: API documentation cleanup
- regulator - add binding info

---
 Makefile                                         |   3 +-
 doc/device-tree-bindings/regulator/regulator.txt |  55 ++++
 drivers/power/Kconfig                            |   2 +
 drivers/power/regulator/Kconfig                  |  17 +
 drivers/power/regulator/Makefile                 |   8 +
 drivers/power/regulator/regulator-uclass.c       | 300 ++++++++++++++++++
 include/dm/uclass-id.h                           |   1 +
 include/power/regulator.h                        | 384 +++++++++++++++++++++++
 8 files changed, 769 insertions(+), 1 deletion(-)
 create mode 100644 doc/device-tree-bindings/regulator/regulator.txt
 create mode 100644 drivers/power/regulator/Kconfig
 create mode 100644 drivers/power/regulator/Makefile
 create mode 100644 drivers/power/regulator/regulator-uclass.c
 create mode 100644 include/power/regulator.h

diff --git a/Makefile b/Makefile
index dc25f70..dfb0d56 100644
--- a/Makefile
+++ b/Makefile
@@ -645,7 +645,8 @@ libs-y += drivers/power/ \
 	drivers/power/fuel_gauge/ \
 	drivers/power/mfd/ \
 	drivers/power/pmic/ \
-	drivers/power/battery/
+	drivers/power/battery/ \
+	drivers/power/regulator/
 libs-y += drivers/spi/
 libs-$(CONFIG_FMAN_ENET) += drivers/net/fm/
 libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
diff --git a/doc/device-tree-bindings/regulator/regulator.txt b/doc/device-tree-bindings/regulator/regulator.txt
new file mode 100644
index 0000000..249e0f5
--- /dev/null
+++ b/doc/device-tree-bindings/regulator/regulator.txt
@@ -0,0 +1,55 @@
+Voltage/Current regulator
+
+Binding:
+The regulator devices don't use the "compatible" property. The binding is done
+by the prefix of regulator node's name. Usually the pmic I/O driver will provide
+the array of 'struct pmic_child_info' with the prefixes and compatible drivers.
+The bind is done by calling function: pmic_bind_childs().
+Example drivers:
+pmic: drivers/power/pmic/max77686.c
+regulator: drivers/power/regulator/max77686.c
+
+For the node name e.g.: "prefix[:alpha:]num { ... }":
+- the driver prefix should be: "prefix" or "PREFIX" - case insensitive
+- the node name's "num" is set as "dev->driver_data" on bind
+
+Example the prefix "ldo" will pass for: "ldo1", "ldo at 1", "LDO1", "LDOREG at 1"...
+
+Required properties:
+- regulator-name: a string, required by the regulator uclass
+
+Note
+The "regulator-name" constraint is used for setting the device's uclass
+platform data '.name' field. And the regulator device name is set from
+it's node name.
+
+Optional properties:
+- regulator-min-microvolt: a minimum allowed Voltage value
+- regulator-max-microvolt: a maximum allowed Voltage value
+- regulator-min-microamp: a minimum allowed Current value
+- regulator-max-microamp: a maximum allowed Current value
+- regulator-always-on: regulator should never be disabled
+- regulator-boot-on: enabled by bootloader/firmware
+
+Other kernel-style properties, are currently not used.
+
+Note:
+For the regulator autoset from constraints, the framework expects that:
+- regulator-min-microvolt is equal to regulator-max-microvolt
+- regulator-min-microamp is equal to regulator-max-microamp
+- regulator-always-on or regulator-boot-on is set
+
+Example:
+ldo0 {
+	/* Mandatory */
+	regulator-name = "VDDQ_EMMC_1.8V";
+
+	/* Optional */
+	regulator-min-microvolt = <1800000>;
+	regulator-max-microvolt = <1800000>;
+	regulator-min-microamp = <100000>;
+	regulator-max-microamp = <100000>;
+	regulator-always-on;
+	regulator-boot-on;
+};
+
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index d03626e..23cdd71 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -2,6 +2,8 @@ menu "Power"
 
 source "drivers/power/pmic/Kconfig"
 
+source "drivers/power/regulator/Kconfig"
+
 config AXP221_POWER
 	boolean "axp221 / axp223 pmic support"
 	depends on MACH_SUN6I || MACH_SUN8I
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
new file mode 100644
index 0000000..cb15162
--- /dev/null
+++ b/drivers/power/regulator/Kconfig
@@ -0,0 +1,17 @@
+config DM_REGULATOR
+	bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)"
+	depends on DM
+	---help---
+	This config enables the driver model regulator support.
+	UCLASS_REGULATOR - designed to provide a common API for basic regulator's
+	functions, like get/set Voltage or Current value, enable state, etc...
+	Note:
+	When enabling this, please read the description, found in the files:
+	- 'include/power/pmic.h'
+	- 'include/power/regulator.h'
+	- 'drivers/power/pmic/pmic-uclass.c'
+	- 'drivers/power/pmic/regulator-uclass.c'
+	It's important to call the device_bind() with the proper node offset,
+	when binding the regulator devices. The pmic_bind_childs() can be used
+	for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_node()
+	otherwise. Detailed informations can be found in the header file.
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
new file mode 100644
index 0000000..27c9006
--- /dev/null
+++ b/drivers/power/regulator/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (C) 2015 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
new file mode 100644
index 0000000..07ce286
--- /dev/null
+++ b/drivers/power/regulator/regulator-uclass.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/uclass-internal.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+
+	*modep = NULL;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata)
+		return -ENXIO;
+
+	*modep = uc_pdata->mode;
+	return uc_pdata->mode_count;
+}
+
+int regulator_get_value(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->get_value)
+		return -ENOSYS;
+
+	return ops->get_value(dev);
+}
+
+int regulator_set_value(struct udevice *dev, int uV)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->set_value)
+		return -ENOSYS;
+
+	return ops->set_value(dev, uV);
+}
+
+int regulator_get_current(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->get_current)
+		return -ENOSYS;
+
+	return ops->get_current(dev);
+}
+
+int regulator_set_current(struct udevice *dev, int uA)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->set_current)
+		return -ENOSYS;
+
+	return ops->set_current(dev, uA);
+}
+
+bool regulator_get_enable(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->get_enable)
+		return -ENOSYS;
+
+	return ops->get_enable(dev);
+}
+
+int regulator_set_enable(struct udevice *dev, bool enable)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->set_enable)
+		return -ENOSYS;
+
+	return ops->set_enable(dev, enable);
+}
+
+int regulator_get_mode(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->get_mode)
+		return -ENOSYS;
+
+	return ops->get_mode(dev);
+}
+
+int regulator_set_mode(struct udevice *dev, int mode)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->set_mode)
+		return -ENOSYS;
+
+	return ops->set_mode(dev, mode);
+}
+
+int regulator_by_platname(const char *plat_name, struct udevice **devp)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	struct udevice *dev;
+
+	*devp = NULL;
+
+	for (uclass_find_first_device(UCLASS_REGULATOR, &dev);
+	     dev;
+	     uclass_find_next_device(&dev)) {
+		uc_pdata = dev_get_uclass_platdata(dev);
+		if (!uc_pdata || strcmp(plat_name, uc_pdata->name))
+			continue;
+
+		return uclass_get_device_tail(dev, 0, devp);
+	}
+
+	debug("%s: can't find: %s\n", __func__, plat_name);
+
+	return -ENODEV;
+}
+
+int regulator_by_devname(const char *devname, struct udevice **devp)
+{
+	return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp);
+}
+
+static int setting_failed(int ret, bool verbose, const char *fmt, ...)
+{
+	va_list args;
+	char buf[64];
+
+	if (verbose == false)
+		return ret;
+
+	va_start(args, fmt);
+	vscnprintf(buf, sizeof(buf), fmt, args);
+	va_end(args);
+
+	printf(buf);
+
+	if (!ret)
+		return 0;
+
+	printf(" (ret: %d)", ret);
+
+	return ret;
+}
+
+int regulator_by_platname_autoset_and_enable(const char *platname,
+					     struct udevice **devp,
+					     bool verbose)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	struct udevice *dev;
+	bool v = verbose;
+	int ret;
+
+	if (devp)
+		*devp = NULL;
+
+	ret = regulator_by_platname(platname, &dev);
+	if (ret) {
+		error("Can get the regulator: %s!", platname);
+		return ret;
+	}
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata) {
+		error("Can get the regulator %s uclass platdata!", platname);
+		return -ENXIO;
+	}
+
+	if (v)
+		printf("%s@%s: ", dev->name, uc_pdata->name);
+
+	/* Those values are optional (-ENODATA if unset) */
+	if ((uc_pdata->min_uV != -ENODATA) &&
+	    (uc_pdata->max_uV != -ENODATA) &&
+	    (uc_pdata->min_uV == uc_pdata->max_uV)) {
+		ret = regulator_set_value(dev, uc_pdata->min_uV);
+		if (setting_failed(ret, v, "set %d uV", uc_pdata->min_uV))
+			goto exit;
+	}
+
+	/* Those values are optional (-ENODATA if unset) */
+	if ((uc_pdata->min_uA != -ENODATA) &&
+	    (uc_pdata->max_uA != -ENODATA) &&
+	    (uc_pdata->min_uA == uc_pdata->max_uA)) {
+		ret = regulator_set_current(dev, uc_pdata->min_uA);
+		if (setting_failed(ret, v, "; set %d uA", uc_pdata->min_uA))
+			goto exit;
+	}
+
+	if (!uc_pdata->always_on && !uc_pdata->boot_on)
+		goto retdev;
+
+	ret = regulator_set_enable(dev, true);
+	if (setting_failed(ret, v, "; enabling", uc_pdata->min_uA))
+		goto exit;
+
+retdev:
+	if (devp)
+		*devp = dev;
+exit:
+	if (v)
+		printf("\n");
+	return ret;
+}
+
+int regulator_by_platname_list_autoset_and_enable(const char *list_platname[],
+						  int list_entries,
+						  struct udevice *list_devp[],
+						  bool verbose)
+{
+	struct udevice *dev;
+	int i, ret, success = 0;
+
+	for (i = 0; i < list_entries; i++) {
+		ret = regulator_autoset(list_platname[i], &dev, verbose);
+		if (!ret)
+			success++;
+
+		if (!list_devp)
+			continue;
+
+		if (ret)
+			list_devp[i] = NULL;
+		else
+			list_devp[i] = dev;
+	}
+
+	return (success != list_entries);
+}
+
+static int regulator_post_bind(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int offset = dev->of_offset;
+	const void *blob = gd->fdt_blob;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata)
+		return -ENXIO;
+
+	/* Regulator's mandatory constraint */
+	uc_pdata->name = fdt_getprop(blob, offset, "regulator-name", NULL);
+	if (!uc_pdata->name) {
+		debug("%s: dev: %s has no property 'regulator-name'\n",
+		      __func__, dev->name);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int regulator_pre_probe(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int offset = dev->of_offset;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata)
+		return -ENXIO;
+
+	/* Regulator's optional constraints */
+	uc_pdata->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
+					  "regulator-min-microvolt", -ENODATA);
+	uc_pdata->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
+					  "regulator-max-microvolt", -ENODATA);
+	uc_pdata->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
+					  "regulator-min-microamp", -ENODATA);
+	uc_pdata->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
+					  "regulator-max-microamp", -ENODATA);
+	uc_pdata->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
+					      "regulator-always-on");
+	uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
+					    "regulator-boot-on");
+
+	return 0;
+}
+
+UCLASS_DRIVER(regulator) = {
+	.id		= UCLASS_REGULATOR,
+	.name		= "regulator",
+	.post_bind	= regulator_post_bind,
+	.pre_probe	= regulator_pre_probe,
+	.per_device_platdata_auto_alloc_size =
+				sizeof(struct dm_regulator_uclass_platdata),
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 23b3eb9..3c572d7 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -48,6 +48,7 @@ enum uclass_id {
 
 	/* Power Management */
 	UCLASS_PMIC,		/* PMIC I/O device */
+	UCLASS_REGULATOR,	/* REGULATOR device */
 
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
diff --git a/include/power/regulator.h b/include/power/regulator.h
new file mode 100644
index 0000000..0302c1d
--- /dev/null
+++ b/include/power/regulator.h
@@ -0,0 +1,384 @@
+/*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _INCLUDE_REGULATOR_H_
+#define _INCLUDE_REGULATOR_H_
+
+/**
+ * U-Boot Voltage/Current Regulator
+ * ================================
+ *
+ * The regulator API is based on a driver model, with the device tree support.
+ * And this header describes the functions and data types for the uclass id:
+ * 'UCLASS_REGULATOR' and the regulator driver API.
+ *
+ * The regulator uclass - is based on uclass platform data which is allocated,
+ * automatically for each regulator device on bind and 'dev->uclass_platdata'
+ * points to it. The data type is: 'struct dm_regulator_uclass_platdata'.
+ * The uclass file: 'drivers/power/regulator/regulator-uclass.c'
+ *
+ * The regulator device - is based on driver's model 'struct udevice'.
+ * The API can use regulator name in two meanings:
+ * - devname  - the regulator device's name: 'dev->name'
+ * - platname - the device's platdata's name. So in the code it looks like:
+ *              'uc_pdata = dev->uclass_platdata'; 'name = uc_pdata->name'.
+ *
+ * The regulator device driver - provide an implementation of uclass operations
+ * pointed by 'dev->driver->ops' as a struct of type 'struct dm_regulator_ops'.
+ *
+ * To proper bind the regulator device, the device tree node should provide
+ * regulator constraints, like in the example below:
+ *
+ * ldo1 {
+ *      regulator-name = "VDD_MMC_1.8V";     (mandatory for bind)
+ *      regulator-min-microvolt = <1000000>; (optional)
+ *      regulator-max-microvolt = <1000000>; (optional)
+ *      regulator-min-microamp = <1000>;     (optional)
+ *      regulator-max-microamp = <1000>;     (optional)
+ *      regulator-always-on;                 (optional)
+ *      regulator-boot-on;                   (optional)
+ * };
+ *
+ * Please take a notice, that for the proper operation at least name constraint
+ * is needed, e.g. for call the device_by_platname(...).
+ *
+ * Regulator bind:
+ * For each regulator device, the device_bind() should be called with passed
+ * device tree offset. This is required for this uclass's '.post_bind' method,
+ * which do the scan on the device node, for the 'regulator-name' constraint.
+ * If the parent is not a PMIC device, and the child is not bind by function:
+ * 'pmic_bind_childs()', then it's recommended to bind the device by call to
+ * dm_scan_fdt_node() - this is usually done automatically for bus devices,
+ * as a post bind method.
+ * Having the device's name constraint, we can call regulator_by_platname(),
+ * to find interesting regulator. Before return, the regulator is probed,
+ * and the rest of its constraints are put into the device's uclass platform
+ * data, by the uclass regulator '.pre_probe' method.
+ *
+ * For more info about PMIC bind, please refer to file: 'include/power/pmic.h'
+ *
+ * Note:
+ * Please do not use the device_bind_by_name() function, since it pass '-1' as
+ * device node offset - and the bind will fail on uclass .post_bind method,
+ * because of missing 'regulator-name' constraint.
+ *
+ *
+ * Fixed Voltage/Current Regulator
+ * ===============================
+ *
+ * When fixed voltage regulator is needed, then enable the config:
+ * - CONFIG_DM_REGULATOR_FIXED
+ *
+ * The driver file: 'drivers/power/regulator/fixed.c', provides basic support
+ * for control the GPIO, and return the device tree constraint values.
+ *
+ * To bind the fixed voltage regulator device, we usually use a 'simple-bus'
+ * node as a parent. And 'regulator-fixed' for the driver compatible. This is
+ * the same as in the kernel. The example node of fixed regulator:
+ *
+ * simple-bus {
+ *     compatible = "simple-bus";
+ *     #address-cells = <1>;
+ *     #size-cells = <0>;
+ *
+ *     blue_led {
+ *         compatible = "regulator-fixed";
+ *         regulator-name = "VDD_LED_3.3V";
+ *         regulator-min-microvolt = <3300000>;
+ *         regulator-max-microvolt = <3300000>;
+ *         gpio = <&gpc1 0 GPIO_ACTIVE_LOW>;
+ *     };
+ * };
+ *
+ * The fixed regulator devices also provide regulator uclass platform data. And
+ * devices bound from such node, can use the regulator drivers API.
+*/
+
+/* enum regulator_type - used for regulator_*() variant calls */
+enum regulator_type {
+	REGULATOR_TYPE_LDO = 0,
+	REGULATOR_TYPE_BUCK,
+	REGULATOR_TYPE_DVS,
+	REGULATOR_TYPE_FIXED,
+	REGULATOR_TYPE_OTHER,
+};
+
+/**
+ * struct dm_regulator_mode - this structure holds an information about
+ * each regulator operation mode. Probably in most cases - an array.
+ * This will be probably a driver-static data, since it is device-specific.
+ *
+ * @id             - a driver-specific mode id
+ * @register_value - a driver-specific value for its mode id
+ * @name           - the name of mode - used for regulator command
+ * Note:
+ * The field 'id', should be always a positive number, since the negative values
+ * are reserved for the errno numbers when returns the mode id.
+ */
+struct dm_regulator_mode {
+	int id; /* Set only as >= 0 (negative value is reserved for errno) */
+	int register_value;
+	const char *name;
+};
+
+/**
+ * struct dm_regulator_uclass_platdata - pointed by dev->uclass_platdata, and
+ * allocated on each regulator bind. This structure holds an information
+ * about each regulator's constraints and supported operation modes.
+ * There is no "step" voltage value - so driver should take care of this.
+ *
+ * @type       - one of 'enum regulator_type'
+ * @mode       - pointer to the regulator mode (array if more than one)
+ * @mode_count - number of '.mode' entries
+ * @min_uV*    - minimum voltage (micro Volts)
+ * @max_uV*    - maximum voltage (micro Volts)
+ * @min_uA*    - minimum amperage (micro Amps)
+ * @max_uA*    - maximum amperage (micro Amps)
+ * @always_on* - bool type, true or false
+ * @boot_on*   - bool type, true or false
+ * @name**     - fdt regulator name - should be taken from the device tree
+ *
+ * Note:
+ * *  - set automatically on device probe by the uclass's '.pre_probe' method.
+ * ** - set automatically on device bind by the uclass's '.post_bind' method.
+ * The constraints: type, mode, mode_count, can be set by device driver, e.g.
+ * by the driver '.probe' method.
+ */
+struct dm_regulator_uclass_platdata {
+	enum regulator_type type;
+	struct dm_regulator_mode *mode;
+	int mode_count;
+	int min_uV;
+	int max_uV;
+	int min_uA;
+	int max_uA;
+	bool always_on;
+	bool boot_on;
+	const char *name;
+};
+
+/* Regulator device operations */
+struct dm_regulator_ops {
+	/**
+	 * The regulator output value function calls operates on a micro Volts.
+	 *
+	 * get/set_value - get/set output value of the given output number
+	 * @dev          - regulator device
+	 * Sets:
+	 * @uV           - set the output value [micro Volts]
+	 * Returns: output value [uV] on success or negative errno if fail.
+	 */
+	int (*get_value)(struct udevice *dev);
+	int (*set_value)(struct udevice *dev, int uV);
+
+	/**
+	 * The regulator output current function calls operates on a micro Amps.
+	 *
+	 * get/set_current - get/set output current of the given output number
+	 * @dev            - regulator device
+	 * Sets:
+	 * @uA           - set the output current [micro Amps]
+	 * Returns: output value [uA] on success or negative errno if fail.
+	 */
+	int (*get_current)(struct udevice *dev);
+	int (*set_current)(struct udevice *dev, int uA);
+
+	/**
+	 * The most basic feature of the regulator output is its enable state.
+	 *
+	 * get/set_enable - get/set enable state of the given output number
+	 * @dev           - regulator device
+	 * Sets:
+	 * @enable         - set true - enable or false - disable
+	 * Returns: true/false for get; or 0 / -errno for set.
+	 */
+	bool (*get_enable)(struct udevice *dev);
+	int (*set_enable)(struct udevice *dev, bool enable);
+
+	/**
+	 * The 'get/set_mode()' function calls should operate on a driver
+	 * specific mode definitions, which should be found in:
+	 * field 'mode' of struct mode_desc.
+	 *
+	 * get/set_mode - get/set operation mode of the given output number
+	 * @dev         - regulator device
+	 * Sets
+	 * @mode_id     - set output mode id (struct dm_regulator_mode->id)
+	 * Returns: id/0 for get/set on success or negative errno if fail.
+	 * Note:
+	 * The field 'id' of struct type 'dm_regulator_mode', should be always
+	 * positive number, since the negative is reserved for the error.
+	 */
+	int (*get_mode)(struct udevice *dev);
+	int (*set_mode)(struct udevice *dev, int mode_id);
+};
+
+/**
+ * regulator_mode: returns a pointer to the array of regulator mode info
+ *
+ * @dev        - pointer to the regulator device
+ * @modep      - pointer to the returned mode info array
+ * Returns     - count of modep entries on success or negative errno if fail.
+ */
+int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep);
+
+/**
+ * regulator_get_value: get microvoltage voltage value of a given regulator
+ *
+ * @dev    - pointer to the regulator device
+ * Returns - positive output value [uV] on success or negative errno if fail.
+ */
+int regulator_get_value(struct udevice *dev);
+
+/**
+ * regulator_set_value: set the microvoltage value of a given regulator.
+ *
+ * @dev    - pointer to the regulator device
+ * @uV     - the output value to set [micro Volts]
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_value(struct udevice *dev, int uV);
+
+/**
+ * regulator_get_current: get microampere value of a given regulator
+ *
+ * @dev    - pointer to the regulator device
+ * Returns - positive output current [uA] on success or negative errno if fail.
+ */
+int regulator_get_current(struct udevice *dev);
+
+/**
+ * regulator_set_current: set the microampere value of a given regulator.
+ *
+ * @dev    - pointer to the regulator device
+ * @uA     - set the output current [micro Amps]
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_current(struct udevice *dev, int uA);
+
+/**
+ * regulator_get_enable: get regulator device enable state.
+ *
+ * @dev    - pointer to the regulator device
+ * Returns - true/false of enable state
+ */
+bool regulator_get_enable(struct udevice *dev);
+
+/**
+ * regulator_set_enable: set regulator enable state
+ *
+ * @dev    - pointer to the regulator device
+ * @enable - set true or false
+ * Returns - 0 on success or -errno val if fails
+ */
+int regulator_set_enable(struct udevice *dev, bool enable);
+
+/**
+ * regulator_get_mode: get mode of a given device regulator
+ *
+ * @dev    - pointer to the regulator device
+ * Returns - positive  mode number 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 'mode_desc' provides a field 'mode'
+ * for this purpose and register_value for a raw register value.
+ */
+int regulator_get_mode(struct udevice *dev);
+
+/**
+ * regulator_set_mode: set given regulator mode
+ *
+ * @dev    - pointer to the regulator device
+ * @mode   - mode type (field 'mode' of struct mode_desc)
+ * 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 mode);
+
+/**
+ * regulator_by_platname_autoset_and_enable: setup the regulator given by
+ * its uclass's platform data '.name'. The setup depends on constraints found
+ * in device's uclass's platform data (struct dm_regulator_uclass_platdata):
+ * - Voltage value - will set - if '.min_uV' and '.max_uV' values are equal
+ * - Current limit - will set - if '.min_uA' and '.max_uA' values are equal
+ * - Enable - will set - if '.always_on' or '.boot_on' are set to true
+ *
+ * The function returns on first encountered error.
+ *
+ * @platname - expected string for dm_regulator_uclass_platdata .name field
+ * @devp      - returned pointer to the regulator device - if non-NULL passed
+ * @verbose   - (true/false) print regulator setup info, or be quiet
+ * Returns: 0 on success or negative value of errno.
+ *
+ * The returned 'regulator' device can be used with:
+ * - regulator_get/set_*
+ * For shorter call name, the below macro regulator_autoset() can be used.
+ */
+int regulator_by_platname_autoset_and_enable(const char *platname,
+					     struct udevice **devp,
+					     bool verbose);
+
+#define regulator_autoset(platname, devp, verbose) \
+	regulator_by_platname_autoset_and_enable(platname, devp, verbose)
+
+/**
+ * regulator_by_platname_list_autoset_and_enable: setup the regulators given by
+ * list of its uclass's platform data '.name'. The setup depends on constraints
+ * found in device's uclass's platform data. The function loops with calls to:
+ * regulator_by_platname_autoset_and_enable() for each name of list.
+ *
+ * @list_platname - an array of expected strings for .name field of each
+ *                  regulator's uclass platdata
+ * @list_entries  - number of regulator's name list entries
+ * @list_devp     - an array of returned pointers to the successfully setup
+ *                  regulator devices if non-NULL passed
+ * @verbose       - (true/false) print each regulator setup info, or be quiet
+ * Returns: 0 on successfully setup of all list entries or 1 otwerwise.
+ *
+ * The returned 'regulator' devices can be used with:
+ * - regulator_get/set_*
+ * For shorter call name, the below macro regulator_list_autoset() can be used.
+ */
+int regulator_by_platname_list_autoset_and_enable(const char *list_platname[],
+						  int list_entries,
+						  struct udevice *list_devp[],
+						  bool verbose);
+
+#define regulator_list_autoset(namelist, entries, devlist, verbose)      \
+	regulator_by_platname_list_autoset_and_enable(namelist, entries, \
+						      devlist, verbose)
+
+/**
+ * regulator_by_devname: returns the pointer to the pmic regulator device.
+ *                       Search by name, found in regulator device's name.
+ *
+ * @devname - expected string for 'dev->name' of regulator device
+ * @devp     - returned pointer to the regulator device
+ * Returns: 0 on success or negative value of errno.
+ *
+ * The returned 'regulator' device can be used with:
+ * - regulator_get/set_*
+ */
+int regulator_by_devname(const char *devname, struct udevice **devp);
+
+/**
+ * regulator_by_platname: returns the pointer to the pmic regulator device.
+ *                        Search by name, found in regulator uclass platdata.
+ *
+ * @platname - expected string for dm_regulator_uclass_platdata .name field
+ * @devp     - returned pointer to the regulator device
+ * Returns: 0 on success or negative value of errno.
+ *
+ * The returned 'regulator' device can be used with:
+ * - regulator_get/set_*
+ */
+int regulator_by_platname(const char *platname, struct udevice **devp);
+
+#endif /* _INCLUDE_REGULATOR_H_ */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 06/16] dm: pmic: add pmic command
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (4 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 05/16] dm: regulator: add implementation of driver model regulator uclass Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:30         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command Przemyslaw Marczak
                         ` (10 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

This is new command for the PMIC devices based on driver model PMIC API.
Command features are unchanged:
- list UCLASS pmic devices
- show or [set] operating pmic device (NEW)
- dump registers
- read byte of register at address
- write byte to register at address

The only one change for this command is 'dev' subcommand.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes v3:
- new file
- add Kconfig

Changes V4:
- common/cmd_pmic.c: cleanup
- move config name: CONFIG_DM_PMIC_CMD to CONFIG_CMD_PMIC
---
 common/Kconfig    |  14 ++++
 common/Makefile   |   3 +
 common/cmd_pmic.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)
 create mode 100644 common/cmd_pmic.c

diff --git a/common/Kconfig b/common/Kconfig
index 17930a4..4666f8e 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -457,4 +457,18 @@ config BOOTSTAGE_STASH_SIZE
 
 endmenu
 
+menu "Power commands"
+config CMD_PMIC
+	bool "Enable Driver Model PMIC command"
+	depends on DM_PMIC
+	help
+	  This is the pmic command, based on a driver model pmic's API.
+	  Command features are unchanged:
+	  - list               - list pmic devices
+	  - pmic dev <id>      - show or [set] operating pmic device (NEW)
+	  - pmic dump          - dump registers
+	  - pmic read address  - read byte of register at address
+	  - pmic write address - write byte to register at address
+	  The only one change for this command is 'dev' subcommand.
+endmenu
 endmenu
diff --git a/common/Makefile b/common/Makefile
index 252fbf1..87a3efe 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -209,6 +209,9 @@ obj-$(CONFIG_UPDATE_TFTP) += update.o
 obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
 obj-$(CONFIG_CMD_DFU) += cmd_dfu.o
 obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
+
+# Power
+obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o
 endif
 
 ifdef CONFIG_SPL_BUILD
diff --git a/common/cmd_pmic.c b/common/cmd_pmic.c
new file mode 100644
index 0000000..bd88d68
--- /dev/null
+++ b/common/cmd_pmic.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/uclass-internal.h>
+#include <power/pmic.h>
+
+#define LIMIT_SEQ	3
+#define LIMIT_DEVNAME	20
+
+static struct udevice *currdev;
+
+static int failed(const char *getset, const char *thing,
+		  const char *for_dev, int ret)
+{
+	printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing, for_dev,
+						    ret, errno_str(ret));
+	return CMD_RET_FAILURE;
+}
+
+static int pmic_dev_get(bool list_only, int get_seq, struct udevice **devp)
+{
+	struct udevice *dev;
+	int ret;
+
+	if (devp)
+		*devp = NULL;
+
+	for (ret = uclass_first_device(UCLASS_PMIC, &dev); dev;
+	     ret = uclass_next_device(&dev)) {
+		if (list_only) {
+			printf("|%*d | %-*.*s| %-*.*s| %s @ %d\n",
+			       LIMIT_SEQ, dev->seq,
+			       LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
+			       LIMIT_DEVNAME, LIMIT_DEVNAME, dev->parent->name,
+			       dev_get_uclass_name(dev->parent),
+			       dev->parent->seq);
+			continue;
+		}
+
+		if (dev->seq == get_seq) {
+			if (devp)
+				*devp = dev;
+			else
+				return -EINVAL;
+
+			return 0;
+		}
+	}
+
+	if (list_only)
+		return ret;
+
+	return -ENODEV;
+}
+
+static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	int seq, ret = -ENODEV;
+
+	switch (argc) {
+	case 2:
+		seq = simple_strtoul(argv[1], NULL, 0);
+		ret = uclass_get_device_by_seq(UCLASS_PMIC, seq, &currdev);
+		if (ret && (ret = pmic_dev_get(false, seq, &currdev)))
+			goto failed;
+	case 1:
+		if (!currdev)
+			goto failed;
+
+		printf("dev: %d @ %s\n", currdev->seq, currdev->name);
+	}
+
+	return CMD_RET_SUCCESS;
+failed:
+	return failed("get", "the", "device", ret);
+}
+
+static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	int ret;
+
+	printf("|%*s | %-*.*s| %-*.*s| %s @ %s\n",
+	       LIMIT_SEQ, "Seq",
+	       LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
+	       LIMIT_DEVNAME, LIMIT_DEVNAME, "Parent name",
+	       "Parent uclass", "seq");
+
+	for (ret = uclass_first_device(UCLASS_PMIC, &dev); dev;
+	     ret = uclass_next_device(&dev)) {
+		printf("|%*d | %-*.*s| %-*.*s| %s @ %d\n",
+		       LIMIT_SEQ, dev->seq,
+		       LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
+		       LIMIT_DEVNAME, LIMIT_DEVNAME, dev->parent->name,
+		       dev_get_uclass_name(dev->parent), dev->parent->seq);
+	}
+
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_dump(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	uint8_t value;
+	uint reg;
+	int ret;
+
+	if (!currdev)
+		return failed("get", "current", "device", -ENODEV);
+
+	dev = currdev;
+
+	printf("Dump pmic: %s registers\n", dev->name);
+
+	for (reg = 0; reg < pmic_reg_count(dev); reg++) {
+		ret = pmic_read(dev, reg, &value, 1);
+		if (ret)
+			return failed("read", dev->name, "register", ret);
+
+		if (!(reg % 16))
+			printf("\n0x%02x: ", reg);
+
+		printf("%2.2x ", value);
+	}
+	printf("\n");
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_read(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	int regs, ret;
+	uint8_t value;
+	uint reg;
+
+	if (!currdev)
+		return failed("get", "current", "device", -ENODEV);
+
+	dev = currdev;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	reg = simple_strtoul(argv[1], NULL, 0);
+	regs = pmic_reg_count(dev);
+	if (reg > regs) {
+		printf("Pmic max reg: %d\n", regs);
+		return failed("read", "given", "address", -EFAULT);
+	}
+
+	ret = pmic_read(dev, reg, &value, 1);
+	if (ret)
+		return failed("read", dev->name, "register", ret);
+
+	printf("0x%02x: 0x%2.2x\n", reg, value);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	int regs, ret;
+	uint8_t value;
+	uint reg;
+
+	if (!currdev)
+		return failed("get", "current", "device", -ENODEV);
+
+	dev = currdev;
+
+	if (argc != 3)
+		return CMD_RET_USAGE;
+
+	reg = simple_strtoul(argv[1], NULL, 0);
+	regs = pmic_reg_count(dev);
+	if (reg > regs) {
+		printf("Pmic max reg: %d\n", regs);
+		return failed("write", "given", "address", -EFAULT);
+	}
+
+	value = simple_strtoul(argv[2], NULL, 0);
+
+	ret = pmic_write(dev, reg, &value, 1);
+	if (ret)
+		return failed("write", dev->name, "register", ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t subcmd[] = {
+	U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
+	U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
+	U_BOOT_CMD_MKENT(dump, 1, 1, do_dump, "", ""),
+	U_BOOT_CMD_MKENT(read, 2, 1, do_read, "", ""),
+	U_BOOT_CMD_MKENT(write, 3, 1, do_write, "", ""),
+};
+
+static int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc,
+			char * const argv[])
+{
+	cmd_tbl_t *cmd;
+
+	argc--;
+	argv++;
+
+	cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
+	if (cmd == NULL || argc > cmd->maxargs)
+		return CMD_RET_USAGE;
+
+	return cmd->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(pmic, CONFIG_SYS_MAXARGS, 1, do_pmic,
+	" operations",
+	"list          - list pmic devices\n"
+	"pmic dev [id]      - show or [set] operating pmic device\n"
+	"pmic dump          - dump registers\n"
+	"pmic read address  - read byte of register at address\n"
+	"pmic write address - write byte to register at address\n"
+);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (5 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 06/16] dm: pmic: add pmic command Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:30         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 08/16] pmic: max77686 set the same compatible as in the kernel Przemyslaw Marczak
                         ` (9 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

This command is based on driver model regulator's API.
The user interface provides:
- list UCLASS regulator devices
- show or [set] operating regulator device
- print constraints info
- print operating status
- print/[set] voltage value [uV] (force)
- print/[set] current value [uA]
- print/[set] operating mode id
- enable the regulator output
- disable the regulator output

The 'force' option can be used for setting the value which exceeds
the constraints min/max limits.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes v3:
- new file
- Kconfig entry

Changes V4:
- cmd regulator: move platdata to uc pdata
- cmd_regulator: includes cleanup
- cmd_regulator: add get_curr_dev_and_pl() check type
- move config name: CONFIG_DM_REGULATOR_CMD to CONFIG_CMD_REGULATOR
- common/Kconfig - cleanup
---
 common/Kconfig         |  22 +++
 common/Makefile        |   1 +
 common/cmd_regulator.c | 403 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 426 insertions(+)
 create mode 100644 common/cmd_regulator.c

diff --git a/common/Kconfig b/common/Kconfig
index 4666f8e..52f8bb1 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -470,5 +470,27 @@ config CMD_PMIC
 	  - pmic read address  - read byte of register at address
 	  - pmic write address - write byte to register at address
 	  The only one change for this command is 'dev' subcommand.
+
+config CMD_REGULATOR
+	bool "Enable Driver Model REGULATOR command"
+	depends on DM_REGULATOR
+	help
+	  This command is based on driver model regulator's API.
+	  User interface features:
+	  - list               - list regulator devices
+	  - regulator dev <id> - show or [set] operating regulator device
+	  - regulator info     - print constraints info
+	  - regulator status   - print operating status
+	  - regulator value <val] <-f> - print/[set] voltage value [uV]
+	  - regulator current <val>    - print/[set] current value [uA]
+	  - regulator mode <id>        - print/[set] operating mode id
+	  - regulator enable           - enable the regulator output
+	  - regulator disable          - disable the regulator output
+
+	  The '-f' (force) option can be used for set the value which exceeds
+	  the limits, which are found in device-tree and are kept in regulator's
+	  uclass platdata structure.
+
 endmenu
+
 endmenu
diff --git a/common/Makefile b/common/Makefile
index 87a3efe..93bded3 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -212,6 +212,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
 
 # Power
 obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o
+obj-$(CONFIG_CMD_REGULATOR) += cmd_regulator.o
 endif
 
 ifdef CONFIG_SPL_BUILD
diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
new file mode 100644
index 0000000..b1b9e87
--- /dev/null
+++ b/common/cmd_regulator.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/uclass-internal.h>
+#include <power/regulator.h>
+
+#define LIMIT_SEQ	3
+#define LIMIT_DEVNAME	20
+#define LIMIT_OFNAME	20
+#define LIMIT_INFO	16
+
+static struct udevice *currdev;
+
+static int failed(const char *getset, const char *thing,
+		  const char *for_dev, int ret)
+{
+	printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing, for_dev,
+						    ret, errno_str(ret));
+	return CMD_RET_FAILURE;
+}
+
+static int regulator_get(bool list_only, int get_seq, struct udevice **devp)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	struct udevice *dev;
+	int ret;
+
+	if (devp)
+		*devp = NULL;
+
+	for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
+	     ret = uclass_next_device(&dev)) {
+		if (list_only) {
+			uc_pdata = dev_get_uclass_platdata(dev);
+			printf("|%*d | %*.*s @ %-*.*s| %s @ %s\n",
+			       LIMIT_SEQ, dev->seq,
+			       LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
+			       LIMIT_OFNAME, LIMIT_OFNAME, uc_pdata->name,
+			       dev->parent->name,
+			       dev_get_uclass_name(dev->parent));
+			continue;
+		}
+
+		if (dev->seq == get_seq) {
+			if (devp)
+				*devp = dev;
+			else
+				return -EINVAL;
+
+			return 0;
+		}
+	}
+
+	if (list_only)
+		return ret;
+
+	return -ENODEV;
+}
+
+static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int seq, ret = -ENXIO;
+
+	switch (argc) {
+	case 2:
+		seq = simple_strtoul(argv[1], NULL, 0);
+		ret = uclass_get_device_by_seq(UCLASS_REGULATOR, seq, &currdev);
+		if (ret && (ret = regulator_get(false, seq, &currdev)))
+			goto failed;
+	case 1:
+		uc_pdata = dev_get_uclass_platdata(currdev);
+		if (!uc_pdata)
+			goto failed;
+
+		printf("dev: %d @ %s\n", currdev->seq, uc_pdata->name);
+	}
+
+	return CMD_RET_SUCCESS;
+failed:
+	return failed("get", "the", "device", ret);
+}
+
+static int get_curr_dev_and_pl(struct udevice **devp,
+			       struct dm_regulator_uclass_platdata **uc_pdata,
+			       bool allow_type_fixed)
+{
+	*devp = NULL;
+	*uc_pdata = NULL;
+
+	if (!currdev)
+		return failed("get", "current", "device", -ENODEV);
+
+	*devp = currdev;
+
+	*uc_pdata = dev_get_uclass_platdata(*devp);
+	if (!*uc_pdata)
+		return failed("get", "regulator", "platdata", -ENXIO);
+
+	if (!allow_type_fixed && (*uc_pdata)->type == REGULATOR_TYPE_FIXED) {
+		printf("Operation not allowed for fixed regulator!\n");
+		return CMD_RET_FAILURE;
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	int ret;
+
+	printf("|%*s | %*.*s @ %-*.*s| %s @ %s\n",
+	       LIMIT_SEQ, "Seq",
+	       LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
+	       LIMIT_OFNAME, LIMIT_OFNAME, "fdtname",
+	       "Parent", "uclass");
+
+	ret = regulator_get(true, 0, NULL);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	return CMD_RET_SUCCESS;
+}
+
+static int constraint(const char *name, int val, const char *val_name)
+{
+	printf("%-*s", LIMIT_INFO, name);
+	if (val < 0) {
+		printf(" %s (err: %d)\n", errno_str(val), val);
+		return val;
+	}
+
+	if (val_name)
+		printf(" %d (%s)\n", val, val_name);
+	else
+		printf(" %d\n", val);
+
+	return 0;
+}
+
+static const char *get_mode_name(struct dm_regulator_mode *mode,
+				 int mode_count,
+				 int mode_id)
+{
+	while (mode_count--) {
+		if (mode->id == mode_id)
+			return mode->name;
+		mode++;
+	}
+
+	return NULL;
+}
+
+static int do_info(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	struct dm_regulator_mode *modes;
+	const char *parent_uc;
+	int mode_count;
+	int ret;
+	int i;
+
+	ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
+	if (ret)
+		return ret;
+
+	parent_uc = dev_get_uclass_name(dev->parent);
+
+	printf("Uclass regulator dev %d info:\n", dev->seq);
+	printf("%-*s %s @ %s\n%-*s %s\n%-*s %s\n%-*s\n",
+	       LIMIT_INFO, "* parent:", dev->parent->name, parent_uc,
+	       LIMIT_INFO, "* dev name:", dev->name,
+	       LIMIT_INFO, "* fdt name:", uc_pdata->name,
+	       LIMIT_INFO, "* constraints:");
+
+	constraint("  - min uV:", uc_pdata->min_uV, NULL);
+	constraint("  - max uV:", uc_pdata->max_uV, NULL);
+	constraint("  - min uA:", uc_pdata->min_uA, NULL);
+	constraint("  - max uA:", uc_pdata->max_uA, NULL);
+	constraint("  - always on:", uc_pdata->always_on,
+		   uc_pdata->always_on ? "true" : "false");
+	constraint("  - boot on:", uc_pdata->boot_on,
+		   uc_pdata->boot_on ? "true" : "false");
+
+	mode_count = regulator_mode(dev, &modes);
+	constraint("* op modes:", mode_count, NULL);
+
+	for (i = 0; i < mode_count; i++, modes++)
+		constraint("  - mode id:", modes->id, modes->name);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int current, value, mode, ret;
+	const char *mode_name = NULL;
+	struct udevice *dev;
+	bool enabled;
+
+	ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
+	if (ret)
+		return ret;
+
+	enabled = regulator_get_enable(dev);
+	constraint(" * enable:", enabled, enabled ? "true" : "false");
+
+	value = regulator_get_value(dev);
+	constraint(" * value uV:", value, NULL);
+
+	current = regulator_get_current(dev);
+	constraint(" * current uA:", current, NULL);
+
+	mode = regulator_get_mode(dev);
+	mode_name = get_mode_name(uc_pdata->mode, uc_pdata->mode_count, mode);
+	constraint(" * mode id:", mode, mode_name);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_value(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int value;
+	int force;
+	int ret;
+
+	ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
+	if (ret)
+		return ret;
+
+	if (argc == 1) {
+		value = regulator_get_value(dev);
+		if (value < 0)
+			return failed("get", uc_pdata->name, "voltage", value);
+
+		printf("%d uV\n", value);
+		return CMD_RET_SUCCESS;
+	}
+
+	if (argc == 3)
+		force = !strcmp("-f", argv[2]);
+	else
+		force = 0;
+
+	value = simple_strtoul(argv[1], NULL, 0);
+	if ((value < uc_pdata->min_uV || value > uc_pdata->max_uV) && !force) {
+		printf("Value exceeds regulator constraint limits\n");
+		return CMD_RET_FAILURE;
+	}
+
+	ret = regulator_set_value(dev, value);
+	if (ret)
+		return failed("set", uc_pdata->name, "voltage value", ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_current(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int current;
+	int ret;
+
+	ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
+	if (ret)
+		return ret;
+
+	if (argc == 1) {
+		current = regulator_get_current(dev);
+		if (current < 0)
+			return failed("get", uc_pdata->name, "current", current);
+
+		printf("%d uA\n", current);
+		return CMD_RET_SUCCESS;
+	}
+
+	current = simple_strtoul(argv[1], NULL, 0);
+	if (current < uc_pdata->min_uA || current > uc_pdata->max_uA) {
+		printf("Current exceeds regulator constraint limits\n");
+		return CMD_RET_FAILURE;
+	}
+
+	ret = regulator_set_current(dev, current);
+	if (ret)
+		return failed("set", uc_pdata->name, "current value", ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int new_mode;
+	int mode;
+	int ret;
+
+	ret = get_curr_dev_and_pl(&dev, &uc_pdata, false);
+	if (ret)
+		return ret;
+
+	if (argc == 1) {
+		mode = regulator_get_mode(dev);
+		if (mode < 0)
+			return failed("get", uc_pdata->name, "mode", mode);
+
+		printf("mode id: %d\n", mode);
+		return CMD_RET_SUCCESS;
+	}
+
+	new_mode = simple_strtoul(argv[1], NULL, 0);
+
+	ret = regulator_set_mode(dev, new_mode);
+	if (ret)
+		return failed("set", uc_pdata->name, "mode", ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_enable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int ret;
+
+	ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
+	if (ret)
+		return ret;
+
+	ret = regulator_set_enable(dev, true);
+	if (ret)
+		return failed("enable", "regulator", uc_pdata->name, ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_disable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct udevice *dev;
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int ret;
+
+	ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
+	if (ret)
+		return ret;
+
+	ret = regulator_set_enable(dev, false);
+	if (ret)
+		return failed("disable", "regulator", uc_pdata->name, ret);
+
+	return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t subcmd[] = {
+	U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
+	U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
+	U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""),
+	U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
+	U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""),
+	U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""),
+	U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""),
+	U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""),
+	U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""),
+};
+
+static int do_regulator(cmd_tbl_t *cmdtp, int flag, int argc,
+			char * const argv[])
+{
+	cmd_tbl_t *cmd;
+
+	argc--;
+	argv++;
+
+	cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
+	if (cmd == NULL || argc > cmd->maxargs)
+		return CMD_RET_USAGE;
+
+	return cmd->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator,
+	"uclass operations",
+	"list         - list UCLASS regulator devices\n"
+	"regulator dev [id]     - show or [set] operating regulator device\n"
+	"regulator [info]       - print constraints info\n"
+	"regulator [status]     - print operating status\n"
+	"regulator [value] [-f] - print/[set] voltage value [uV] (force)\n"
+	"regulator [current]    - print/[set] current value [uA]\n"
+	"regulator [mode_id]    - print/[set] operating mode id\n"
+	"regulator [enable]     - enable the regulator output\n"
+	"regulator [disable]    - disable the regulator output\n"
+);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 08/16] pmic: max77686 set the same compatible as in the kernel
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (6 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:30         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 09/16] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
                         ` (8 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

This commit also updates the proper dts files.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 arch/arm/dts/exynos4412-odroid.dts   | 2 +-
 arch/arm/dts/exynos4412-trats2.dts   | 2 +-
 arch/arm/dts/exynos5250-smdk5250.dts | 2 +-
 arch/arm/dts/exynos5250-snow.dts     | 2 +-
 lib/fdtdec.c                         | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/arm/dts/exynos4412-odroid.dts b/arch/arm/dts/exynos4412-odroid.dts
index 582f6e5..5507d9a 100644
--- a/arch/arm/dts/exynos4412-odroid.dts
+++ b/arch/arm/dts/exynos4412-odroid.dts
@@ -36,7 +36,7 @@
 		status = "okay";
 
 		max77686_pmic at 09 {
-			compatible = "maxim,max77686_pmic";
+			compatible = "maxim,max77686";
 			interrupts = <7 0>;
 			reg = <0x09 0 0>;
 			#clock-cells = <1>;
diff --git a/arch/arm/dts/exynos4412-trats2.dts b/arch/arm/dts/exynos4412-trats2.dts
index dd238df..5c0bb91 100644
--- a/arch/arm/dts/exynos4412-trats2.dts
+++ b/arch/arm/dts/exynos4412-trats2.dts
@@ -41,7 +41,7 @@
 		status = "okay";
 
 		max77686_pmic at 09 {
-			compatible = "maxim,max77686_pmic";
+			compatible = "maxim,max77686";
 			interrupts = <7 0>;
 			reg = <0x09 0 0>;
 			#clock-cells = <1>;
diff --git a/arch/arm/dts/exynos5250-smdk5250.dts b/arch/arm/dts/exynos5250-smdk5250.dts
index 9273562..3cebfc2 100644
--- a/arch/arm/dts/exynos5250-smdk5250.dts
+++ b/arch/arm/dts/exynos5250-smdk5250.dts
@@ -68,7 +68,7 @@
 	i2c at 12c60000 {
 		pmic at 9 {
 			reg = <0x9>;
-			compatible = "maxim,max77686_pmic";
+			compatible = "maxim,max77686";
 		};
 	};
 
diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts
index e89a94f..e4b3dc2 100644
--- a/arch/arm/dts/exynos5250-snow.dts
+++ b/arch/arm/dts/exynos5250-snow.dts
@@ -108,7 +108,7 @@
 	i2c at 12c60000 {
 		pmic at 9 {
 			reg = <0x9>;
-			compatible = "maxim,max77686_pmic";
+			compatible = "maxim,max77686";
 		};
 	};
 
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 331eae2..244fd77 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -54,7 +54,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
 	COMPAT(SAMSUNG_EXYNOS_DWMMC, "samsung,exynos-dwmmc"),
 	COMPAT(SAMSUNG_EXYNOS_MMC, "samsung,exynos-mmc"),
 	COMPAT(SAMSUNG_EXYNOS_SERIAL, "samsung,exynos4210-uart"),
-	COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686_pmic"),
+	COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686"),
 	COMPAT(GENERIC_SPI_FLASH, "spi-flash"),
 	COMPAT(MAXIM_98095_CODEC, "maxim,max98095-codec"),
 	COMPAT(INFINEON_SLB9635_TPM, "infineon,slb9635-tpm"),
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 09/16] dm: pmic: add max77686 pmic driver
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (7 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 08/16] pmic: max77686 set the same compatible as in the kernel Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:30         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 10/16] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
                         ` (7 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

This is the implementation of driver model PMIC driver.
The max77686 PMIC driver implements read/write operations and driver
bind method - to bind its childs.

This driver will try to bind the regulator devices by using it's child
info array with regulator prefixes and driver names. This should succeed
when compatible regulator driver is compiled. If no regulator driver found,
then the pmic can still provide read/write operations, and can be used with
PMIC function calls.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- add implementation of pmic read/write
- max77686: add new operations
- max77686: header: change PMIC_NUM_OF_REGS to MAX77686_NUM_OF_REGS

Changes V3:
- pmic/max77686.c: call pmic_child_node_scan() to bind regulator device
- remove use of pmic platdata
- remove unused endian conversions
- Kconfig: add max77686 pmic entry

Changes V4:
- move DM_PMIC_MAX77686 Kconfig entry from: drivers/power/Kconfig to
  drivers/power/pmic/Kconfig
- pmic/max77686.c: cleanup
- pmic/max77686.c: includes cleanup
- max77686_pmic.h: define ldo and buck driver names
- power/Kconfig: cleanup
- add binding info
---
 doc/device-tree-bindings/pmic/max77686.txt | 36 +++++++++++++
 drivers/power/pmic/Kconfig                 |  7 +++
 drivers/power/pmic/Makefile                |  1 +
 drivers/power/pmic/max77686.c              | 87 ++++++++++++++++++++++++++++++
 drivers/power/pmic/pmic_max77686.c         |  2 +-
 include/power/max77686_pmic.h              | 10 +++-
 6 files changed, 140 insertions(+), 3 deletions(-)
 create mode 100644 doc/device-tree-bindings/pmic/max77686.txt
 create mode 100644 drivers/power/pmic/max77686.c

diff --git a/doc/device-tree-bindings/pmic/max77686.txt b/doc/device-tree-bindings/pmic/max77686.txt
new file mode 100644
index 0000000..54e1f69
--- /dev/null
+++ b/doc/device-tree-bindings/pmic/max77686.txt
@@ -0,0 +1,36 @@
+MAXIM, MAX77686 pmic
+
+This device uses two drivers:
+- drivers/power/pmic/max77686.c (for parent device)
+- drivers/power/regulator/max77686.c (for child regulators)
+
+This file describes the binding info for the PMIC driver.
+
+To bind the regulators, please read the additional binding info:
+- doc/device-tree-bindings/regulator/max77686.txt
+
+Required properties:
+- compatible: "maxim,max77686"
+- reg = 0x9
+
+With those two properties, the pmic device can be used for read/write only.
+To bind each regulator, the optional regulators subnode should exists.
+
+Optional subnode:
+- voltage-regulators: subnode list of each device's regulator
+  (see max77686.txt - regulator binding info)
+
+Example:
+
+max77686 at 09 {
+	compatible = "maxim,max77686";
+	reg = <0x09>;
+
+	voltage-regulators {
+		ldo1 {
+			...
+		};
+		...
+	};
+};
+
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
index d06d632..af68783 100644
--- a/drivers/power/pmic/Kconfig
+++ b/drivers/power/pmic/Kconfig
@@ -9,3 +9,10 @@ config DM_PMIC
 	for read/write. For detailed description, please refer to the files:
 	- 'drivers/power/pmic/pmic-uclass.c'
 	- 'include/power/pmic.h'
+
+config DM_PMIC_MAX77686
+	bool "Enable Driver Model for PMIC MAX77686"
+	depends on DM_PMIC
+	---help---
+	This config enables implementation of driver-model pmic uclass features
+	for PMIC MAX77686. The driver implements read/write operations.
\ No newline at end of file
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 594f620..8cb993d 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -11,6 +11,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
diff --git a/drivers/power/pmic/max77686.c b/drivers/power/pmic/max77686.c
new file mode 100644
index 0000000..e9503e2
--- /dev/null
+++ b/drivers/power/pmic/max77686.c
@@ -0,0 +1,87 @@
+/*
+ *  Copyright (C) 2014-2015 Samsung Electronics
+ *  Przemyslaw Marczak  <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/max77686_pmic.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct pmic_child_info pmic_childs_info[] = {
+	{ .prefix = "ldo", .driver = MAX77686_LDO_DRIVER },
+	{ .prefix = "buck", .driver = MAX77686_BUCK_DRIVER },
+	{ },
+};
+
+static int max77686_write(struct udevice *dev, uint reg, const uint8_t *buff,
+			  int len)
+{
+	if (dm_i2c_write(dev, reg, buff, len)) {
+		error("write error to device: %p register: %#x!", dev, reg);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int max77686_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+	if (dm_i2c_read(dev, reg, buff, len)) {
+		error("read error from device: %p register: %#x!", dev, reg);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int max77686_bind(struct udevice *dev)
+{
+	int regulators_node;
+	const void *blob = gd->fdt_blob;
+	int childs;
+
+	regulators_node = fdt_subnode_offset(blob, dev->of_offset,
+					     "voltage-regulators");
+	if (regulators_node <= 0) {
+		debug("%s: %s regulators subnode not found!", __func__,
+							     dev->name);
+		return -ENXIO;
+	}
+
+	debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
+
+	childs = pmic_bind_childs(dev, regulators_node, pmic_childs_info);
+	if (!childs)
+		debug("%s: %s - no child found\n", __func__, dev->name);
+
+	/* Always return success for this device */
+	return 0;
+}
+
+static struct dm_pmic_ops max77686_ops = {
+	.reg_count = MAX77686_NUM_OF_REGS,
+	.read = max77686_read,
+	.write = max77686_write,
+};
+
+static const struct udevice_id max77686_ids[] = {
+	{ .compatible = "maxim,max77686" },
+	{ }
+};
+
+U_BOOT_DRIVER(pmic_max77686) = {
+	.name = "max77686 pmic",
+	.id = UCLASS_PMIC,
+	.of_match = max77686_ids,
+	.bind = max77686_bind,
+	.ops = &max77686_ops,
+};
diff --git a/drivers/power/pmic/pmic_max77686.c b/drivers/power/pmic/pmic_max77686.c
index 95b1a57..1ad810a 100644
--- a/drivers/power/pmic/pmic_max77686.c
+++ b/drivers/power/pmic/pmic_max77686.c
@@ -295,7 +295,7 @@ int pmic_init(unsigned char bus)
 
 	p->name = name;
 	p->interface = PMIC_I2C;
-	p->number_of_regs = PMIC_NUM_OF_REGS;
+	p->number_of_regs = MAX77686_NUM_OF_REGS;
 	p->hw.i2c.tx_num = 1;
 
 	puts("Board PMIC init\n");
diff --git a/include/power/max77686_pmic.h b/include/power/max77686_pmic.h
index b0e4255..95597db 100644
--- a/include/power/max77686_pmic.h
+++ b/include/power/max77686_pmic.h
@@ -122,11 +122,17 @@ enum {
 	MAX77686_REG_PMIC_BBAT		= 0x7e,
 	MAX77686_REG_PMIC_32KHZ,
 
-	PMIC_NUM_OF_REGS,
+	MAX77686_NUM_OF_REGS,
 };
 
 /* 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
+
+/* Drivers name */
+#define MAX77686_LDO_DRIVER	"max77686_ldo"
+#define MAX77686_BUCK_DRIVER	"max77686_buck"
 
 enum {
 	REG_DISABLE = 0,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 10/16] dm: regulator: add max77686 regulator driver
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (8 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 09/16] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:31         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage " Przemyslaw Marczak
                         ` (6 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

This commit adds support to MAX77686 regulator driver,
based on a driver model regulator's API. It implements
almost all regulator operations, beside those for setting
and geting the Current value.
For proper bind and operation it requires the MAX77686 PMIC driver.

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- change debug() to error()
- code cleanup
- fix data types
- ldo/buck state implementation
- adjust to new uclass api

Changes V3:
- regulator/max77686.c:
  -- adjust to api changes
  -- add separeted drivers for buck and ldo
  -- bind regulators by its compatibles
- Kconfig: add regulator max77686 entry

Changes V4:
- move DM_REGULATOR_MAX77686 Kconfig entry from: drivers/power/Kconfig to
  drivers/power/regulator/Kconfig
- regulator/max77686.c: cleanup
- regulator/max77686.c: add missing break for switch
- regulator/max77686.c: includes cleanup
- regulator.h: comments cleanup
- add binding info

---
 doc/device-tree-bindings/regulator/max77686.txt |  70 ++
 drivers/power/Makefile                          |   1 -
 drivers/power/regulator/Kconfig                 |   8 +
 drivers/power/regulator/Makefile                |   1 +
 drivers/power/regulator/max77686.c              | 825 ++++++++++++++++++++++++
 include/power/max77686_pmic.h                   |  19 +-
 include/power/regulator.h                       |  42 +-
 7 files changed, 942 insertions(+), 24 deletions(-)
 create mode 100644 doc/device-tree-bindings/regulator/max77686.txt
 create mode 100644 drivers/power/regulator/max77686.c

diff --git a/doc/device-tree-bindings/regulator/max77686.txt b/doc/device-tree-bindings/regulator/max77686.txt
new file mode 100644
index 0000000..ae9b1b6
--- /dev/null
+++ b/doc/device-tree-bindings/regulator/max77686.txt
@@ -0,0 +1,70 @@
+MAXIM, MAX77686 regulators
+
+This device uses two drivers:
+- drivers/power/pmic/max77686.c (as parent I/O device)
+- drivers/power/regulator/max77686.c (for child regulators)
+
+This file describes the binding info for the REGULATOR driver.
+
+First, please read the binding info for the pmic:
+- doc/device-tree-bindings/pmic/max77686.txt
+
+Required subnode:
+- voltage-regulators: required for the PMIC driver
+
+Required properties:
+- regulator-name: used for regulator uclass platform data '.name'
+
+Optional:
+- regulator-min-microvolt: minimum allowed Voltage to set
+- regulator-max-microvolt: minimum allowed Voltage to set
+- regulator-always-on: regulator should be never disabled
+- regulator-boot-on: regulator should be enabled by the bootloader
+
+Example:
+(subnode of max77686 pmic node)
+voltage-regulators {
+	ldo1 {
+		regulator-name = "VDD_ALIVE_1.0V";
+		regulator-min-microvolt = <1000000>;
+		regulator-max-microvolt = <1000000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+
+	ldo2 {
+		regulator-name = "VDDQ_VM1M2_1.2V";
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+	.
+	.
+	.
+	ldo26 {
+		regulator-name = "nc";
+		regulator-min-microvolt = <3000000>;
+		regulator-max-microvolt = <3000000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+
+	buck1 {
+		regulator-compatible = "BUCK1";
+		regulator-name = "VDD_MIF_1.0V";
+		regulator-min-microvolt = <8500000>;
+		regulator-max-microvolt = <1100000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+	.
+	.
+	.
+	buck9 {
+		regulator-compatible = "BUCK9";
+		regulator-name = "nc";
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+	};
+};
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 2145652..a2d3c04 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -15,7 +15,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
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index cb15162..0cdfabc 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -15,3 +15,11 @@ config DM_REGULATOR
 	when binding the regulator devices. The pmic_bind_childs() can be used
 	for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_node()
 	otherwise. Detailed informations can be found in the header file.
+
+config DM_REGULATOR_MAX77686
+	bool "Enable Driver Model for REGULATOR MAX77686"
+	depends on DM_REGULATOR && DM_PMIC_MAX77686
+	---help---
+	This config enables implementation of driver-model regulator uclass
+	features for REGULATOR MAX77686. The driver implements get/set api for:
+	value, enable and mode.
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index 27c9006..f9c4e6d 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -6,3 +6,4 @@
 #
 
 obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
+obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c
new file mode 100644
index 0000000..37ebe94
--- /dev/null
+++ b/drivers/power/regulator/max77686.c
@@ -0,0 +1,825 @@
+/*
+ *  Copyright (C) 2012-2015 Samsung Electronics
+ *
+ *  Rajeshwari Shinde <rajeshwari.s@samsung.com>
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/max77686_pmic.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define MODE(_id, _val, _name) { \
+	.id = _id, \
+	.register_value = _val, \
+	.name = _name, \
+}
+
+/* LDO: 1,3,4,5,9,17,18,19,20,21,22,23,24,26,26,27 */
+static struct dm_regulator_mode max77686_ldo_mode_standby1[] = {
+	MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
+	MODE(OPMODE_LPM, MAX77686_LDO_MODE_LPM, "LPM"),
+	MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
+	MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
+};
+
+/* LDO: 2,6,7,8,10,11,12,14,15,16 */
+static struct dm_regulator_mode max77686_ldo_mode_standby2[] = {
+	MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_LDO_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
+	MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
+};
+
+/* Buck: 1 */
+static struct dm_regulator_mode max77686_buck_mode_standby[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+/* Buck: 2,3,4 */
+static struct dm_regulator_mode max77686_buck_mode_lpm[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
+	MODE(OPMODE_LPM, MAX77686_BUCK_MODE_LPM, "LPM"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+/* Buck: 5,6,7,8,9 */
+static struct dm_regulator_mode max77686_buck_mode_onoff[] = {
+	MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+	MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+static const char max77686_buck_addr[] = {
+	0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38
+};
+
+static int max77686_buck_volt2hex(int buck, int uV)
+{
+	unsigned int hex = 0;
+	unsigned int hex_max = 0;
+
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		/* hex = (uV - 600000) / 12500; */
+		hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP;
+		hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
+		/**
+		 * Those use voltage scaller - temporary not implemented
+		 * so return just 0
+		 */
+		return -ENOSYS;
+	default:
+		/* hex = (uV - 750000) / 50000; */
+		hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP;
+		hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
+		break;
+	}
+
+	if (hex >= 0 && hex <= hex_max)
+		return hex;
+
+	error("Value: %d uV is wrong for BUCK%d", uV, buck);
+	return -EINVAL;
+}
+
+static int max77686_buck_hex2volt(int buck, int hex)
+{
+	unsigned uV = 0;
+	unsigned int hex_max = 0;
+
+	if (hex < 0)
+		goto bad_hex;
+
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
+		if (hex > hex_max)
+			goto bad_hex;
+
+		/* uV = hex * 12500 + 600000; */
+		uV = hex * MAX77686_BUCK_UV_LSTEP + MAX77686_BUCK_UV_LMIN;
+		break;
+	default:
+		hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
+		if (hex > hex_max)
+			goto bad_hex;
+
+		/* uV = hex * 50000 + 750000; */
+		uV = hex * MAX77686_BUCK_UV_HSTEP + MAX77686_BUCK_UV_HMIN;
+		break;
+	}
+
+	return uV;
+
+bad_hex:
+	error("Value: %#x is wrong for BUCK%d", hex, buck);
+	return -EINVAL;
+}
+
+static int max77686_ldo_volt2hex(int ldo, int uV)
+{
+	unsigned int hex = 0;
+
+	switch (ldo) {
+	case 1:
+	case 2:
+	case 6:
+	case 7:
+	case 8:
+	case 15:
+		hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_LSTEP;
+		/* hex = (uV - 800000) / 25000; */
+		break;
+	default:
+		hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_HSTEP;
+		/* hex = (uV - 800000) / 50000; */
+	}
+
+	if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX)
+		return hex;
+
+	error("Value: %d uV is wrong for LDO%d", uV, ldo);
+	return -EINVAL;
+}
+
+static int max77686_ldo_hex2volt(int ldo, int hex)
+{
+	unsigned int uV = 0;
+
+	if (hex > MAX77686_LDO_VOLT_MAX_HEX)
+		goto bad_hex;
+
+	switch (ldo) {
+	case 1:
+	case 2:
+	case 6:
+	case 7:
+	case 8:
+	case 15:
+		/* uV = hex * 25000 + 800000; */
+		uV = hex * MAX77686_LDO_UV_LSTEP + MAX77686_LDO_UV_MIN;
+		break;
+	default:
+		/* uV = hex * 50000 + 800000; */
+		uV = hex * MAX77686_LDO_UV_HSTEP + MAX77686_LDO_UV_MIN;
+	}
+
+	return uV;
+
+bad_hex:
+	error("Value: %#x is wrong for ldo%d", hex, ldo);
+	return -EINVAL;
+}
+
+static int max77686_ldo_hex2mode(int ldo, int hex)
+{
+	if (hex > MAX77686_LDO_MODE_MASK)
+		return -EINVAL;
+
+	switch (hex) {
+	case MAX77686_LDO_MODE_OFF:
+		return OPMODE_OFF;
+	case MAX77686_LDO_MODE_LPM: /* == MAX77686_LDO_MODE_STANDBY: */
+		/* The same mode values but different meaning for each ldo */
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			return OPMODE_STANDBY;
+		default:
+			return OPMODE_LPM;
+		}
+	case MAX77686_LDO_MODE_STANDBY_LPM:
+		return OPMODE_STANDBY_LPM;
+	case MAX77686_LDO_MODE_ON:
+		return OPMODE_ON;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int max77686_buck_hex2mode(int buck, int hex)
+{
+	if (hex > MAX77686_BUCK_MODE_MASK)
+		return -EINVAL;
+
+	switch (hex) {
+	case MAX77686_BUCK_MODE_OFF:
+		return OPMODE_OFF;
+	case MAX77686_BUCK_MODE_ON:
+		return OPMODE_ON;
+	case MAX77686_BUCK_MODE_STANDBY:
+		switch (buck) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+			return OPMODE_STANDBY;
+		default:
+			return -EINVAL;
+		}
+	case MAX77686_BUCK_MODE_LPM:
+		switch (buck) {
+		case 2:
+		case 3:
+		case 4:
+			return OPMODE_LPM;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int max77686_buck_modes(int buck, struct dm_regulator_mode **modesp)
+{
+	int ret = -EINVAL;
+
+	if (buck < 1 || buck > MAX77686_BUCK_NUM)
+		return ret;
+
+	switch (buck) {
+	case 1:
+		*modesp = max77686_buck_mode_standby;
+		ret = ARRAY_SIZE(max77686_buck_mode_standby);
+		break;
+	case 2:
+	case 3:
+	case 4:
+		*modesp = max77686_buck_mode_lpm;
+		ret = ARRAY_SIZE(max77686_buck_mode_lpm);
+		break;
+	default:
+		*modesp = max77686_buck_mode_onoff;
+		ret = ARRAY_SIZE(max77686_buck_mode_onoff);
+	}
+
+	return ret;
+}
+
+static int max77686_ldo_modes(int ldo, struct dm_regulator_mode **modesp,
+				struct udevice *dev)
+{
+	int ret = -EINVAL;
+
+	if (ldo < 1 || ldo > MAX77686_LDO_NUM)
+		return ret;
+
+	switch (ldo) {
+	case 2:
+	case 6:
+	case 7:
+	case 8:
+	case 10:
+	case 11:
+	case 12:
+	case 14:
+	case 15:
+	case 16:
+		*modesp = max77686_ldo_mode_standby2;
+		ret = ARRAY_SIZE(max77686_ldo_mode_standby2);
+		break;
+	default:
+		*modesp = max77686_ldo_mode_standby1;
+		ret = ARRAY_SIZE(max77686_ldo_mode_standby1);
+	}
+
+	return ret;
+}
+
+static int max77686_ldo_val(struct udevice *dev, int op, int *uV)
+{
+	unsigned int ret, hex, adr;
+	unsigned char val;
+	int ldo;
+
+	if (op == PMIC_OP_GET)
+		*uV = 0;
+
+	ldo = dev->driver_data;
+	if (ldo < 1 || ldo > MAX77686_LDO_NUM) {
+		error("Wrong ldo number: %d", ldo);
+		return -EINVAL;
+	}
+
+	adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
+
+	ret = pmic_read(dev->parent, adr, &val, 1);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= MAX77686_LDO_VOLT_MASK;
+		ret = max77686_ldo_hex2volt(ldo, val);
+		if (ret < 0)
+			return ret;
+		*uV = ret;
+		return 0;
+	}
+
+	hex = max77686_ldo_volt2hex(ldo, *uV);
+	if (hex < 0)
+		return hex;
+
+	val &= ~MAX77686_LDO_VOLT_MASK;
+	val |= hex;
+	ret = pmic_write(dev->parent, adr, &val, 1);
+
+	return ret;
+}
+
+static int max77686_buck_val(struct udevice *dev, int op, int *uV)
+{
+	unsigned int hex, ret, mask, adr;
+	unsigned char val;
+	int buck;
+
+	buck = dev->driver_data;
+	if (buck < 1 || buck > MAX77686_BUCK_NUM) {
+		error("Wrong buck number: %d", buck);
+		return -EINVAL;
+	}
+
+	if (op == PMIC_OP_GET)
+		*uV = 0;
+
+	/* &buck_out = ctrl + 1 */
+	adr = max77686_buck_addr[buck] + 1;
+
+	/* mask */
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		/* Those use voltage scallers - will support in the future */
+		mask = MAX77686_BUCK234_VOLT_MASK;
+		return -ENOSYS;
+	default:
+		mask = MAX77686_BUCK_VOLT_MASK;
+	}
+
+	ret = pmic_read(dev->parent, adr, &val, 1);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= mask;
+		ret = max77686_buck_hex2volt(buck, val);
+		if (ret < 0)
+			return ret;
+		*uV = ret;
+		return 0;
+	}
+
+	hex = max77686_buck_volt2hex(buck, *uV);
+	if (hex < 0)
+		return hex;
+
+	val &= ~mask;
+	val |= hex;
+	ret = pmic_write(dev->parent, adr, &val, 1);
+
+	return ret;
+}
+
+static int max77686_ldo_mode(struct udevice *dev, int op, int *opmode)
+{
+	unsigned int ret, adr, mode;
+	unsigned char val;
+	int ldo;
+
+	if (op == PMIC_OP_GET)
+		*opmode = -EINVAL;
+
+	ldo = dev->driver_data;
+	if (ldo < 1 || ldo > MAX77686_LDO_NUM) {
+		error("Wrong ldo number: %d", ldo);
+		return -EINVAL;
+	}
+
+	adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
+
+	ret = pmic_read(dev->parent, adr, &val, 1);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= MAX77686_LDO_MODE_MASK;
+		ret = max77686_ldo_hex2mode(ldo, val);
+		if (ret < 0)
+			return ret;
+		*opmode = ret;
+		return 0;
+	}
+
+	/* mode */
+	switch (*opmode) {
+	case OPMODE_OFF:
+		mode = MAX77686_LDO_MODE_OFF;
+		break;
+	case OPMODE_LPM:
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			return -EINVAL;
+		default:
+			mode = MAX77686_LDO_MODE_LPM;
+		}
+		break;
+	case OPMODE_STANDBY:
+		switch (ldo) {
+		case 2:
+		case 6:
+		case 7:
+		case 8:
+		case 10:
+		case 11:
+		case 12:
+		case 14:
+		case 15:
+		case 16:
+			mode = MAX77686_LDO_MODE_STANDBY;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case OPMODE_STANDBY_LPM:
+		mode = MAX77686_LDO_MODE_STANDBY_LPM;
+		break;
+	case OPMODE_ON:
+		mode = MAX77686_LDO_MODE_ON;
+		break;
+	default:
+		mode = 0xff;
+	}
+
+	if (mode == 0xff) {
+		error("Wrong mode: %d for ldo%d", *opmode, ldo);
+		return -EINVAL;
+	}
+
+	val &= ~MAX77686_LDO_MODE_MASK;
+	val |= mode;
+	ret = pmic_write(dev->parent, adr, &val, 1);
+
+	return ret;
+}
+
+static int max77686_ldo_enable(struct udevice *dev, int op, bool *enable)
+{
+	int ret, on_off;
+
+	if (op == PMIC_OP_GET) {
+		ret = max77686_ldo_mode(dev, op, &on_off);
+		if (ret)
+			return ret;
+
+		switch (on_off) {
+		case OPMODE_OFF:
+			*enable = 0;
+			break;
+		case OPMODE_ON:
+			*enable = 1;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else if (op == PMIC_OP_SET) {
+		switch (*enable) {
+		case 0:
+			on_off = OPMODE_OFF;
+			break;
+		case 1:
+			on_off = OPMODE_ON;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = max77686_ldo_mode(dev, op, &on_off);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int max77686_buck_mode(struct udevice *dev, int op, int *opmode)
+{
+	unsigned int ret, mask, adr, mode, mode_shift;
+	unsigned char val;
+	int buck;
+
+	buck = dev->driver_data;
+	if (buck < 1 || buck > MAX77686_BUCK_NUM) {
+		error("Wrong buck number: %d", buck);
+		return -EINVAL;
+	}
+
+	adr = max77686_buck_addr[buck];
+
+	/* mask */
+	switch (buck) {
+	case 2:
+	case 3:
+	case 4:
+		mode_shift = MAX77686_BUCK_MODE_SHIFT_2;
+		break;
+	default:
+		mode_shift = MAX77686_BUCK_MODE_SHIFT_1;
+	}
+
+	mask = MAX77686_BUCK_MODE_MASK << mode_shift;
+
+	ret = pmic_read(dev->parent, adr, &val, 1);
+	if (ret)
+		return ret;
+
+	if (op == PMIC_OP_GET) {
+		val &= mask;
+		val >>= mode_shift;
+		ret = max77686_buck_hex2mode(buck, val);
+		if (ret < 0)
+			return ret;
+		*opmode = ret;
+		return 0;
+	}
+
+	/* mode */
+	switch (*opmode) {
+	case OPMODE_OFF:
+		mode = MAX77686_BUCK_MODE_OFF;
+		break;
+	case OPMODE_STANDBY:
+		switch (buck) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+			mode = MAX77686_BUCK_MODE_STANDBY << mode_shift;
+			break;
+		default:
+			mode = 0xff;
+		}
+		break;
+	case OPMODE_LPM:
+		switch (buck) {
+		case 2:
+		case 3:
+		case 4:
+			mode = MAX77686_BUCK_MODE_LPM << mode_shift;
+			break;
+		default:
+			mode = 0xff;
+		}
+		break;
+	case OPMODE_ON:
+		mode = MAX77686_BUCK_MODE_ON << mode_shift;
+		break;
+	default:
+		mode = 0xff;
+	}
+
+	if (mode == 0xff) {
+		error("Wrong mode: %d for buck: %d\n", *opmode, buck);
+		return -EINVAL;
+	}
+
+	val &= ~mask;
+	val |= mode;
+	ret = pmic_write(dev->parent, adr, &val, 1);
+
+	return ret;
+}
+
+static int max77686_buck_enable(struct udevice *dev, int op, bool *enable)
+{
+	int ret, on_off;
+
+	if (op == PMIC_OP_GET) {
+		ret = max77686_buck_mode(dev, op, &on_off);
+		if (ret)
+			return ret;
+
+		switch (on_off) {
+		case OPMODE_OFF:
+			*enable = false;
+			break;
+		case OPMODE_ON:
+			*enable = true;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else if (op == PMIC_OP_SET) {
+		switch (*enable) {
+		case 0:
+			on_off = OPMODE_OFF;
+			break;
+		case 1:
+			on_off = OPMODE_ON;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = max77686_buck_mode(dev, op, &on_off);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int max77686_ldo_probe(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+
+	uc_pdata->type = REGULATOR_TYPE_LDO;
+	uc_pdata->mode_count = max77686_ldo_modes(dev->driver_data,
+						  &uc_pdata->mode, dev);
+
+	return 0;
+}
+
+static int ldo_get_value(struct udevice *dev)
+{
+	int uV;
+	int ret;
+
+	ret = max77686_ldo_val(dev, PMIC_OP_GET, &uV);
+	if (ret)
+		return ret;
+
+	return uV;
+}
+
+static int ldo_set_value(struct udevice *dev, int uV)
+{
+	return max77686_ldo_val(dev, PMIC_OP_SET, &uV);
+}
+
+static bool ldo_get_enable(struct udevice *dev)
+{
+	bool enable = false;
+	int ret;
+
+	ret = max77686_ldo_enable(dev, PMIC_OP_GET, &enable);
+	if (ret)
+		return ret;
+
+	return enable;
+}
+
+static int ldo_set_enable(struct udevice *dev, bool enable)
+{
+	return max77686_ldo_enable(dev, PMIC_OP_SET, &enable);
+}
+
+static int ldo_get_mode(struct udevice *dev)
+{
+	int mode;
+	int ret;
+
+	ret = max77686_ldo_mode(dev, PMIC_OP_GET, &mode);
+	if (ret)
+		return ret;
+
+	return mode;
+}
+
+static int ldo_set_mode(struct udevice *dev, int mode)
+{
+	return max77686_ldo_mode(dev, PMIC_OP_SET, &mode);
+}
+
+static int max77686_buck_probe(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+
+	uc_pdata->type = REGULATOR_TYPE_BUCK;
+	uc_pdata->mode_count = max77686_buck_modes(dev->driver_data,
+						   &uc_pdata->mode);
+
+	return 0;
+}
+
+static int buck_get_value(struct udevice *dev)
+{
+	int uV;
+	int ret;
+
+	ret = max77686_buck_val(dev, PMIC_OP_GET, &uV);
+	if (ret)
+		return ret;
+
+	return uV;
+}
+
+static int buck_set_value(struct udevice *dev, int uV)
+{
+	return max77686_buck_val(dev, PMIC_OP_SET, &uV);
+}
+
+static bool buck_get_enable(struct udevice *dev)
+{
+	bool enable = false;
+	int ret;
+
+	ret = max77686_buck_enable(dev, PMIC_OP_GET, &enable);
+	if (ret)
+		return ret;
+
+	return enable;
+}
+
+static int buck_set_enable(struct udevice *dev, bool enable)
+{
+	return max77686_buck_enable(dev, PMIC_OP_SET, &enable);
+}
+
+static int buck_get_mode(struct udevice *dev)
+{
+	int mode;
+	int ret;
+
+	ret = max77686_buck_mode(dev, PMIC_OP_GET, &mode);
+	if (ret)
+		return ret;
+
+	return mode;
+}
+
+static int buck_set_mode(struct udevice *dev, int mode)
+{
+	return max77686_buck_mode(dev, PMIC_OP_SET, &mode);
+}
+
+static const struct dm_regulator_ops max77686_ldo_ops = {
+	.get_value  = ldo_get_value,
+	.set_value  = ldo_set_value,
+	.get_enable = ldo_get_enable,
+	.set_enable = ldo_set_enable,
+	.get_mode   = ldo_get_mode,
+	.set_mode   = ldo_set_mode,
+};
+
+U_BOOT_DRIVER(max77686_ldo) = {
+	.name = MAX77686_LDO_DRIVER,
+	.id = UCLASS_REGULATOR,
+	.ops = &max77686_ldo_ops,
+	.probe = max77686_ldo_probe,
+};
+
+static const struct dm_regulator_ops max77686_buck_ops = {
+	.get_value  = buck_get_value,
+	.set_value  = buck_set_value,
+	.get_enable = buck_get_enable,
+	.set_enable = buck_set_enable,
+	.get_mode   = buck_get_mode,
+	.set_mode   = buck_set_mode,
+};
+
+U_BOOT_DRIVER(max77686_buck) = {
+	.name = MAX77686_BUCK_DRIVER,
+	.id = UCLASS_REGULATOR,
+	.ops = &max77686_buck_ops,
+	.probe = max77686_buck_probe,
+};
diff --git a/include/power/max77686_pmic.h b/include/power/max77686_pmic.h
index 95597db..2300352 100644
--- a/include/power/max77686_pmic.h
+++ b/include/power/max77686_pmic.h
@@ -149,23 +149,29 @@ 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_voltage(struct pmic *p, int buck, ulong uV);
 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
@@ -176,6 +182,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 */
diff --git a/include/power/regulator.h b/include/power/regulator.h
index 0302c1d..6916660 100644
--- a/include/power/regulator.h
+++ b/include/power/regulator.h
@@ -170,7 +170,7 @@ struct dm_regulator_ops {
 	 * @dev          - regulator device
 	 * Sets:
 	 * @uV           - set the output value [micro Volts]
-	 * Returns: output value [uV] on success or negative errno if fail.
+	 * @return output value [uV] on success or negative errno if fail.
 	 */
 	int (*get_value)(struct udevice *dev);
 	int (*set_value)(struct udevice *dev, int uV);
@@ -182,7 +182,7 @@ struct dm_regulator_ops {
 	 * @dev            - regulator device
 	 * Sets:
 	 * @uA           - set the output current [micro Amps]
-	 * Returns: output value [uA] on success or negative errno if fail.
+	 * @return output value [uA] on success or negative errno if fail.
 	 */
 	int (*get_current)(struct udevice *dev);
 	int (*set_current)(struct udevice *dev, int uA);
@@ -194,13 +194,13 @@ struct dm_regulator_ops {
 	 * @dev           - regulator device
 	 * Sets:
 	 * @enable         - set true - enable or false - disable
-	 * Returns: true/false for get; or 0 / -errno for set.
+	 * @return true/false for get; or 0 / -errno for set.
 	 */
 	bool (*get_enable)(struct udevice *dev);
 	int (*set_enable)(struct udevice *dev, bool enable);
 
 	/**
-	 * The 'get/set_mode()' function calls should operate on a driver
+	 * The 'get/set_mode()' function calls should operate on a driver-
 	 * specific mode definitions, which should be found in:
 	 * field 'mode' of struct mode_desc.
 	 *
@@ -208,7 +208,7 @@ struct dm_regulator_ops {
 	 * @dev         - regulator device
 	 * Sets
 	 * @mode_id     - set output mode id (struct dm_regulator_mode->id)
-	 * Returns: id/0 for get/set on success or negative errno if fail.
+	 * @return id/0 for get/set on success or negative errno if fail.
 	 * Note:
 	 * The field 'id' of struct type 'dm_regulator_mode', should be always
 	 * positive number, since the negative is reserved for the error.
@@ -222,7 +222,7 @@ struct dm_regulator_ops {
  *
  * @dev        - pointer to the regulator device
  * @modep      - pointer to the returned mode info array
- * Returns     - count of modep entries on success or negative errno if fail.
+ * @return     - count of modep entries on success or negative errno if fail.
  */
 int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep);
 
@@ -230,7 +230,7 @@ int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep);
  * regulator_get_value: get microvoltage voltage value of a given regulator
  *
  * @dev    - pointer to the regulator device
- * Returns - positive output value [uV] on success or negative errno if fail.
+ * @return - positive output value [uV] on success or negative errno if fail.
  */
 int regulator_get_value(struct udevice *dev);
 
@@ -239,7 +239,7 @@ int regulator_get_value(struct udevice *dev);
  *
  * @dev    - pointer to the regulator device
  * @uV     - the output value to set [micro Volts]
- * Returns - 0 on success or -errno val if fails
+ * @return - 0 on success or -errno val if fails
  */
 int regulator_set_value(struct udevice *dev, int uV);
 
@@ -247,7 +247,7 @@ int regulator_set_value(struct udevice *dev, int uV);
  * regulator_get_current: get microampere value of a given regulator
  *
  * @dev    - pointer to the regulator device
- * Returns - positive output current [uA] on success or negative errno if fail.
+ * @return - positive output current [uA] on success or negative errno if fail.
  */
 int regulator_get_current(struct udevice *dev);
 
@@ -256,7 +256,7 @@ int regulator_get_current(struct udevice *dev);
  *
  * @dev    - pointer to the regulator device
  * @uA     - set the output current [micro Amps]
- * Returns - 0 on success or -errno val if fails
+ * @return - 0 on success or -errno val if fails
  */
 int regulator_set_current(struct udevice *dev, int uA);
 
@@ -264,7 +264,7 @@ int regulator_set_current(struct udevice *dev, int uA);
  * regulator_get_enable: get regulator device enable state.
  *
  * @dev    - pointer to the regulator device
- * Returns - true/false of enable state
+ * @return - true/false of enable state
  */
 bool regulator_get_enable(struct udevice *dev);
 
@@ -273,7 +273,7 @@ bool regulator_get_enable(struct udevice *dev);
  *
  * @dev    - pointer to the regulator device
  * @enable - set true or false
- * Returns - 0 on success or -errno val if fails
+ * @return - 0 on success or -errno val if fails
  */
 int regulator_set_enable(struct udevice *dev, bool enable);
 
@@ -281,7 +281,7 @@ int regulator_set_enable(struct udevice *dev, bool enable);
  * regulator_get_mode: get mode of a given device regulator
  *
  * @dev    - pointer to the regulator device
- * Returns - positive  mode number on success or -errno val if fails
+ * @return - positive  mode number 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 'mode_desc' provides a field 'mode'
@@ -294,7 +294,7 @@ int regulator_get_mode(struct udevice *dev);
  *
  * @dev    - pointer to the regulator device
  * @mode   - mode type (field 'mode' of struct mode_desc)
- * Returns - 0 on success or -errno value if fails
+ * @return - 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
@@ -308,14 +308,14 @@ int regulator_set_mode(struct udevice *dev, int mode);
  * in device's uclass's platform data (struct dm_regulator_uclass_platdata):
  * - Voltage value - will set - if '.min_uV' and '.max_uV' values are equal
  * - Current limit - will set - if '.min_uA' and '.max_uA' values are equal
- * - Enable - will set - if '.always_on' or '.boot_on' are set to true
+ * - Enable - will set - if any of: '.always_on' or '.boot_on', is set to true
  *
  * The function returns on first encountered error.
  *
  * @platname - expected string for dm_regulator_uclass_platdata .name field
  * @devp      - returned pointer to the regulator device - if non-NULL passed
  * @verbose   - (true/false) print regulator setup info, or be quiet
- * Returns: 0 on success or negative value of errno.
+ * @return: 0 on success or negative value of errno.
  *
  * The returned 'regulator' device can be used with:
  * - regulator_get/set_*
@@ -340,7 +340,7 @@ int regulator_by_platname_autoset_and_enable(const char *platname,
  * @list_devp     - an array of returned pointers to the successfully setup
  *                  regulator devices if non-NULL passed
  * @verbose       - (true/false) print each regulator setup info, or be quiet
- * Returns: 0 on successfully setup of all list entries or 1 otwerwise.
+ * @return 0 on successfully setup of all list entries or 1 otwerwise.
  *
  * The returned 'regulator' devices can be used with:
  * - regulator_get/set_*
@@ -360,8 +360,8 @@ int regulator_by_platname_list_autoset_and_enable(const char *list_platname[],
  *                       Search by name, found in regulator device's name.
  *
  * @devname - expected string for 'dev->name' of regulator device
- * @devp     - returned pointer to the regulator device
- * Returns: 0 on success or negative value of errno.
+ * @devp    - returned pointer to the regulator device
+ * @return 0 on success or negative value of errno.
  *
  * The returned 'regulator' device can be used with:
  * - regulator_get/set_*
@@ -372,9 +372,9 @@ int regulator_by_devname(const char *devname, struct udevice **devp);
  * regulator_by_platname: returns the pointer to the pmic regulator device.
  *                        Search by name, found in regulator uclass platdata.
  *
- * @platname - expected string for dm_regulator_uclass_platdata .name field
+ * @platname - expected string for uc_pdata->name of regulator uclass platdata
  * @devp     - returned pointer to the regulator device
- * Returns: 0 on success or negative value of errno.
+ * @return 0 on success or negative value of errno.
  *
  * The returned 'regulator' device can be used with:
  * - regulator_get/set_*
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage regulator driver
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (9 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 10/16] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:31         ` Simon Glass
  2015-04-23 12:31         ` Przemyslaw Marczak
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 12/16] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
                         ` (5 subsequent siblings)
  16 siblings, 2 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

This driver implements regulator operations for fixed Voltage/Current
value regulators. beside the standard regulator constraints, which are
put into the uclass platform data, a typical fixed regulator node provides
few additional properties like:
- gpio
- gpio-open-drain
- enable-active-high
- startup-delay-us
The only 'gpio' is used by this driver and is kept in structure of type
'fixed_regulator_platdata', as a device platform data (dev->platdata).

The driver implements:
- get_value
- get_current
- get_enable
- set_enable

The regulator calls and commands can be used for fixed-regulator devices,
and the proper error will be returned for prohibited.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>

Changes v3:
- new file
- Kconfig add fixed-regulator entry

Changes V4:
- move DM_REGULATOR_FIXED Kconfig entry from: drivers/power/Kconfig to
  drivers/power/regulator/Kconfig
- regulator/fixed.c: adjust to use of uclass platdata and device platdata
- regulator/fixed.c: includes cleanup
- regulator/fixed.c: fix gpio request
- add binding info
---
 doc/device-tree-bindings/regulator/fixed.txt |  38 ++++++++
 drivers/power/regulator/Kconfig              |   8 ++
 drivers/power/regulator/Makefile             |   1 +
 drivers/power/regulator/fixed.c              | 126 +++++++++++++++++++++++++++
 4 files changed, 173 insertions(+)
 create mode 100644 doc/device-tree-bindings/regulator/fixed.txt
 create mode 100644 drivers/power/regulator/fixed.c

diff --git a/doc/device-tree-bindings/regulator/fixed.txt b/doc/device-tree-bindings/regulator/fixed.txt
new file mode 100644
index 0000000..4ff39b8
--- /dev/null
+++ b/doc/device-tree-bindings/regulator/fixed.txt
@@ -0,0 +1,38 @@
+Fixed Voltage regulator
+
+Binding:
+The binding is done by the property "compatible" - this is different, than for
+binding by the node prefix (doc/device-tree-bindings/regulator/regulator.txt).
+
+Required properties:
+- compatible: "regulator-fixed"
+- regulator-name: this is required by the regulator uclass
+
+Optional properties:
+- gpio: GPIO to use for enable control
+- regulator constraints (binding info: regulator.txt)
+
+Other kernel-style properties, are currently not used.
+
+Note:
+For the regulator constraints, driver expects that:
+- regulator-min-microvolt is equal to regulator-max-microvolt
+- regulator-min-microamp is equal to regulator-max-microamp
+
+Example:
+fixed_regulator at 0 {
+	/* Mandatory */
+	compatible = "regulator-fixed";
+	regulator-name = "LED_3.3V";
+
+	/* Optional: */
+	gpio = <&gpc1 0 GPIO_ACTIVE_LOW>;
+
+	/* Optional for regulator uclass */
+	regulator-min-microvolt = <3300000>;
+	regulator-max-microvolt = <3300000>;
+	regulator-min-microamp = <15000>;
+	regulator-max-microamp = <15000>;
+	regulator-always-on;
+	regulator-boot-on;
+};
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index 0cdfabc..54ce188 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -23,3 +23,11 @@ config DM_REGULATOR_MAX77686
 	This config enables implementation of driver-model regulator uclass
 	features for REGULATOR MAX77686. The driver implements get/set api for:
 	value, enable and mode.
+
+config DM_REGULATOR_FIXED
+	bool "Enable Driver Model for REGULATOR Fixed value"
+	depends on DM_REGULATOR
+	---help---
+	This config enables implementation of driver-model regulator uclass
+	features for fixed value regulators. The driver implements get/set api
+	for enable and get only for voltage value.
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index f9c4e6d..cc8326d 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -7,3 +7,4 @@
 
 obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
 obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
+obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o
diff --git a/drivers/power/regulator/fixed.c b/drivers/power/regulator/fixed.c
new file mode 100644
index 0000000..d053817
--- /dev/null
+++ b/drivers/power/regulator/fixed.c
@@ -0,0 +1,126 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics
+ *
+ *  Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm/gpio.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct fixed_regulator_platdata {
+	struct gpio_desc gpio; /* GPIO for regulator enable control */
+};
+
+static int fixed_regulator_ofdata_to_platdata(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	struct fixed_regulator_platdata *dev_pdata;
+	struct gpio_desc *gpio;
+	int ret;
+
+	dev_pdata = dev_get_platdata(dev);
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata)
+		return -ENXIO;
+
+	/* Set type to fixed */
+	uc_pdata->type = REGULATOR_TYPE_FIXED;
+
+	/* Get fixed regulator gpio desc */
+	gpio = &dev_pdata->gpio;
+	ret = gpio_request_by_name(dev, "gpio", 0, gpio, GPIOD_IS_OUT);
+	if (ret)
+		debug("Fixed regulator gpio - not found! Error: %d", ret);
+
+	return 0;
+}
+
+static int fixed_regulator_get_value(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata)
+		return -ENXIO;
+
+	if (uc_pdata->min_uV != uc_pdata->max_uV) {
+		debug("Invalid constraints for: %s\n", uc_pdata->name);
+		return -EINVAL;
+	}
+
+	return uc_pdata->min_uV;
+}
+
+static int fixed_regulator_get_current(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata)
+		return -ENXIO;
+
+	if (uc_pdata->min_uA != uc_pdata->max_uA) {
+		debug("Invalid constraints for: %s\n", uc_pdata->name);
+		return -EINVAL;
+	}
+
+	return uc_pdata->min_uA;
+}
+
+static bool fixed_regulator_get_enable(struct udevice *dev)
+{
+	struct fixed_regulator_platdata *dev_pdata = dev_get_platdata(dev);
+
+	if (!dev_pdata->gpio.dev)
+		return false;
+
+	return dm_gpio_get_value(&dev_pdata->gpio);
+}
+
+static int fixed_regulator_set_enable(struct udevice *dev, bool enable)
+{
+	struct fixed_regulator_platdata *dev_pdata = dev_get_platdata(dev);
+	int ret;
+
+	if (!dev_pdata->gpio.dev)
+		return -ENOSYS;
+
+	ret = dm_gpio_set_value(&dev_pdata->gpio, enable);
+	if (ret) {
+		error("Can't set regulator : %s gpio to: %d\n", dev->name,
+		      enable);
+		return ret;
+	}
+	return 0;
+}
+
+static const struct dm_regulator_ops fixed_regulator_ops = {
+	.get_value	= fixed_regulator_get_value,
+	.get_current	= fixed_regulator_get_current,
+	.get_enable	= fixed_regulator_get_enable,
+	.set_enable	= fixed_regulator_set_enable,
+};
+
+static const struct udevice_id fixed_regulator_ids[] = {
+	{ .compatible = "regulator-fixed" },
+	{ },
+};
+
+U_BOOT_DRIVER(fixed_regulator) = {
+	.name = "fixed regulator",
+	.id = UCLASS_REGULATOR,
+	.ops = &fixed_regulator_ops,
+	.of_match = fixed_regulator_ids,
+	.ofdata_to_platdata = fixed_regulator_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct fixed_regulator_platdata),
+};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 12/16] doc: driver-model: pmic and regulator uclass documentation
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (10 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage " Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:31         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 13/16] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
                         ` (4 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

Since this framework is still under the construction, the main
documentation is kept in the header files.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes v2, V3:
- update documentation with the framework api changes
- remove doc file name 'dm' prefix

Changes V4:
- move the description to the headers and leave only general info

---
 doc/driver-model/pmic-framework.txt | 142 ++++++++++++++++++++++++++++++++++++
 1 file changed, 142 insertions(+)
 create mode 100644 doc/driver-model/pmic-framework.txt

diff --git a/doc/driver-model/pmic-framework.txt b/doc/driver-model/pmic-framework.txt
new file mode 100644
index 0000000..cc82236
--- /dev/null
+++ b/doc/driver-model/pmic-framework.txt
@@ -0,0 +1,142 @@
+#
+# (C) Copyright 2014-2015 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+PMIC framework based on Driver Model
+====================================
+TOC:
+1. Introduction
+2. How does it work
+3. Pmic uclass
+4. Regulator uclass
+
+1. Introduction
+===============
+This is an introduction to driver-model multi uclass PMIC IC's support.
+At present it's based on two uclass types:
+- UCLASS_PMIC      - basic uclass type for PMIC I/O, which provides common
+                     read/write interface.
+- UCLASS_REGULATOR - additional uclass type for specific PMIC features,
+                     which are Voltage/Current regulators.
+
+New files:
+UCLASS_PMIC:
+- drivers/power/pmic/pmic-uclass.c
+- include/power/pmic.h
+UCLASS_REGULATOR:
+- drivers/power/regulator/regulator-uclass.c
+- include/power/regulator.h
+
+Commands:
+- common/cmd_pmic.c
+- common/cmd_regulator.c
+
+2. How doees it work
+====================
+The Power Management Integrated Circuits (PMIC) are used in embedded systems
+to provide stable, precise and specific voltage power source with over-voltage
+and thermal protection circuits.
+
+The single PMIC can provide various functions by single or multiple interfaces,
+like in the example below.
+
+-- SoC
+ |
+ |            ______________________________________
+ | BUS 0     |       Multi interface PMIC IC        |--> LDO out 1
+ | e.g.I2C0  |                                      |--> LDO out N
+ |-----------|---- PMIC device 0 (READ/WRITE ops)   |
+ | or SPI0   |    |_ REGULATOR device (ldo/... ops) |--> BUCK out 1
+ |           |    |_ CHARGER device (charger ops)   |--> BUCK out M
+ |           |    |_ MUIC device (microUSB con ops) |
+ | BUS 1     |    |_ ...                            |---> BATTERY
+ | e.g.I2C1  |                                      |
+ |-----------|---- PMIC device 1 (READ/WRITE ops)   |---> USB in 1
+ . or SPI1   |    |_ RTC device (rtc ops)           |---> USB in 2
+ .           |______________________________________|---> USB out
+ .
+
+Since U-Boot provides driver model features for I2C and SPI bus drivers,
+the PMIC devices should also support this. By the pmic and regulator API's,
+PMIC drivers can simply provide a common functions, for multi-interface and
+and multi-instance device support.
+
+Basic design assumptions:
+
+- Common I/O API - UCLASS_PMIC
+For the multi-function PMIC devices, this can be used as parent I/O device
+for each IC's interface. Then, each children uses the same dev for read/write.
+
+- Common regulator API - UCLASS_REGULATOR
+For driving the regulator attributes, auto setting function or command line
+interface, based on kernel-style regulator device tree constraints.
+
+For simple implementations, regulator drivers are not required, so the code can
+use pmic read/write directly.
+
+3. Pmic uclass
+==============
+The basic informations:
+* Uclass:   'UCLASS_PMIC'
+* Header:   'include/power/pmic.h'
+* Core:     'drivers/power/pmic/pmic-uclass.c'
+  config:   'CONFIG_DM_PMIC'
+* Command:  'common/cmd_pmic.c'
+  config:   'CONFIG_CMD_PMIC'
+* Example:  'drivers/power/pmic/max77686.c'
+
+This is still under the construction. So for the API description, please refer
+to the header file.
+
+As an example of the pmic driver, please refer to the MAX77686 driver.
+
+Please pay attention for the driver's '.bind' method. Exactly the function call:
+'pmic_bind_childs()', which is used to bind the regulators by using the array of
+regulator's node, compatible prefixes.
+
+The 'pmic; command also supports the new API. So the pmic command can be enabled
+by adding CONFIG_CMD_PMIC.
+The new pmic command allows to:
+- list pmic devices
+- choose the current device (like the mmc command)
+- read or write the pmic register
+- dump all pmic registers
+
+This command can use only UCLASS_PMIC devices, since this uclass is designed
+for pmic I/O operations only.
+
+For more informations, please refer to the file: 'common/cmd_pmic.c'.
+
+4. Regulator uclass
+===================
+The basic informations:
+* Uclass:  'UCLASS_REGULATOR'
+* Header:  'include/power/regulator.h'
+* Core:    'drivers/power/regulator/regulator-uclass.c'
+  config:  'CONFIG_DM_REGULATOR'
+  binding: 'doc/device-tree-bindings/regulator/regulator.txt'
+* Command: 'common/cmd_regulator.c'
+  config:  'CONFIG_CMD_REGULATOR'
+* Example: 'drivers/power/regulator/max77686.c'
+           'drivers/power/pmic/max77686.c' (required I/O driver for the above)
+* Example: 'drivers/power/regulator/fixed.c'
+  config"  'CONFIG_DM_REGULATOR_FIXED'
+
+This is still under the construction. So for the API description, please refer
+to the header file.
+
+For the example regulator driver, please refer to the MAX77686 regulator driver,
+but this driver can't operate without pmic's example driver, which provides an
+I/O interface for MAX77686 regulator.
+
+The second example is a fixed Voltage/Current regulator for a common use.
+
+The 'regulator' command also supports the new API. The command allow:
+- list regulator devices
+- choose the current device (like the mmc command)
+- do all regulator-specific operations
+
+For more informations, please refer to the file: 'common/cmd_regulator.c'
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 13/16] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (11 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 12/16] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:31         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 14/16] odroid: board: add support to dm pmic api Przemyslaw Marczak
                         ` (3 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
 board/samsung/common/board.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/board/samsung/common/board.c b/board/samsung/common/board.c
index 9be2950..20dd75c 100644
--- a/board/samsung/common/board.c
+++ b/board/samsung/common/board.c
@@ -21,9 +21,9 @@
 #include <asm/arch/pinmux.h>
 #include <asm/arch/power.h>
 #include <asm/arch/system.h>
-#include <power/pmic.h>
 #include <asm/arch/sromc.h>
 #include <lcd.h>
+#include <i2c.h>
 #include <samsung/misc.h>
 #include <usb.h>
 
@@ -169,7 +169,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();
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 14/16] odroid: board: add support to dm pmic api
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (12 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 13/16] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:31         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 15/16] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
                         ` (2 subsequent siblings)
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

This commit change the old pmic framework calls to the new ones.

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes v2:
- remove board_init_i2c() call
- update regulator calls
- update headers
- samsung/misc.c: include required header

Changes v3:
- adjust regulator calls to new api

Changes V4:
- use regulator_list_autoset() for mmc regulators

---
 board/samsung/common/misc.c   |  1 +
 board/samsung/odroid/odroid.c | 77 +++++++++++++++++++++++++------------------
 2 files changed, 46 insertions(+), 32 deletions(-)

diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
index 1a77c82..f0d69d4 100644
--- a/board/samsung/common/misc.c
+++ b/board/samsung/common/misc.c
@@ -16,6 +16,7 @@
 #include <asm/arch/cpu.h>
 #include <asm/gpio.h>
 #include <linux/input.h>
+#include <dm.h>
 #include <power/pmic.h>
 #include <mmc.h>
 
diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
index ae41c29..29de325 100644
--- a/board/samsung/odroid/odroid.c
+++ b/board/samsung/odroid/odroid.c
@@ -12,7 +12,9 @@
 #include <asm/arch/gpio.h>
 #include <asm/gpio.h>
 #include <asm/arch/cpu.h>
+#include <dm.h>
 #include <power/pmic.h>
+#include <power/regulator.h>
 #include <power/max77686_pmic.h>
 #include <errno.h>
 #include <mmc.h>
@@ -31,6 +33,12 @@ enum {
 	ODROID_TYPES,
 };
 
+static const char *mmc_regulators[] = {
+	"VDDQ_EMMC_1.8V",
+	"VDDQ_EMMC_2.8V",
+	"TFLASH_2.8V",
+};
+
 void set_board_type(void)
 {
 	/* Set GPA1 pin 1 to HI - enable XCL205 output */
@@ -403,21 +411,6 @@ static void board_gpio_init(void)
 #endif
 }
 
-static int pmic_init_max77686(void)
-{
-	struct pmic *p = pmic_get("MAX77686_PMIC");
-
-	if (pmic_probe(p))
-		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 */
-
-	return 0;
-}
-
 int exynos_early_init_f(void)
 {
 	board_clock_init();
@@ -434,8 +427,10 @@ int exynos_init(void)
 
 int exynos_power_init(void)
 {
-	pmic_init(0);
-	pmic_init_max77686();
+	int list_count = ARRAY_SIZE(mmc_regulators);
+
+	if (regulator_list_autoset(mmc_regulators, list_count, NULL, true))
+		error("Unable to init all mmc regulators");
 
 	return 0;
 }
@@ -443,19 +438,20 @@ int exynos_power_init(void)
 #ifdef CONFIG_USB_GADGET
 static int s5pc210_phy_control(int on)
 {
-	struct pmic *p_pmic;
+	struct udevice *dev;
+	int ret;
 
-	p_pmic = pmic_get("MAX77686_PMIC");
-	if (!p_pmic)
-		return -ENODEV;
-
-	if (pmic_probe(p_pmic))
-		return -1;
+	ret = regulator_by_platname("VDD_UOTG_3.0V", &dev);
+	if (ret) {
+		error("Regulator get error: %d", ret);
+		return ret;
+	}
 
 	if (on)
-		return max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
+		return regulator_set_mode(dev, OPMODE_ON);
 	else
-		return max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
+		return regulator_set_mode(dev, OPMODE_LPM);
+
 }
 
 struct s3c_plat_otg_data s5pc210_otg_data = {
@@ -472,7 +468,8 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
 int board_usb_init(int index, enum usb_init_type init)
 {
 #ifdef CONFIG_CMD_USB
-	struct pmic *p_pmic;
+	struct udevice *dev;
+	int ret;
 
 	/* Set Ref freq 0 => 24MHz, 1 => 26MHz*/
 	/* Odroid Us have it at 24MHz, Odroid Xs at 26MHz */
@@ -490,14 +487,30 @@ int board_usb_init(int index, enum usb_init_type init)
 	/* Power off and on BUCK8 for LAN9730 */
 	debug("LAN9730 - Turning power buck 8 OFF and ON.\n");
 
-	p_pmic = pmic_get("MAX77686_PMIC");
-	if (p_pmic && !pmic_probe(p_pmic)) {
-		max77686_set_buck_voltage(p_pmic, 8, 750000);
-		max77686_set_buck_voltage(p_pmic, 8, 3300000);
+	ret = regulator_by_platname("VCC_P3V3_2.85V", &dev);
+	if (ret) {
+		error("Regulator get error: %d", ret);
+		return ret;
 	}
 
-#endif
+	ret = regulator_set_enable(dev, true);
+	if (ret) {
+		error("Regulator %s enable setting error: %d", dev->name, ret);
+		return ret;
+	}
 
+	ret = regulator_set_value(dev, 750000);
+	if (ret) {
+		error("Regulator %s value setting error: %d", dev->name, ret);
+		return ret;
+	}
+
+	ret = regulator_set_value(dev, 3300000);
+	if (ret) {
+		error("Regulator %s value setting error: %d", dev->name, ret);
+		return ret;
+	}
+#endif
 	debug("USB_udc_probe\n");
 	return s3c_udc_probe(&s5pc210_otg_data);
 }
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 15/16] odroid: dts: add 'voltage-regulators' description to max77686 node
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (13 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 14/16] odroid: board: add support to dm pmic api Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:31         ` Simon Glass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 16/16] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
  2015-04-22 16:29       ` [U-Boot] [PATCH v4 00/16] Power(full) framework based on Driver Model Simon Glass
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

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

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- odroid: dts: remove pmic alias

Changes V3:
- none

Changes V4:
- add constraints for mmc regulators auto enable

---
 arch/arm/dts/exynos4412-odroid.dts | 253 +++++++++++++++++++++++++++++++++++++
 1 file changed, 253 insertions(+)

diff --git a/arch/arm/dts/exynos4412-odroid.dts b/arch/arm/dts/exynos4412-odroid.dts
index 5507d9a..415dfea 100644
--- a/arch/arm/dts/exynos4412-odroid.dts
+++ b/arch/arm/dts/exynos4412-odroid.dts
@@ -40,6 +40,259 @@
 			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>;
+					regulator-always-on;
+					regulator-boot-on;
+				};
+
+				ldo21_reg: ldo21 {
+					regulator-compatible = "LDO21";
+					regulator-name = "TFLASH_2.8V";
+					regulator-min-microvolt = <2800000>;
+					regulator-max-microvolt = <2800000>;
+					regulator-always-on;
+					regulator-boot-on;
+				};
+
+				ldo22_reg: ldo22 {
+					regulator-compatible = "LDO22";
+					regulator-name = "VDDQ_EMMC_2.8V";
+					regulator-min-microvolt = <2800000>;
+					regulator-max-microvolt = <2800000>;
+					regulator-always-on;
+					regulator-boot-on;
+				};
+
+				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: buck at 1 {
+					regulator-compatible = "BUCK1";
+					regulator-name = "VDD_MIF_1.0V";
+					regulator-min-microvolt = <8500000>;
+					regulator-max-microvolt = <1100000>;
+				};
+
+				buck2_reg: buck at 2 {
+					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>;
+				};
+			};
 		};
 	};
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 16/16] odroid: config: enable dm pmic, dm regulator and max77686 driver
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (14 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 15/16] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
@ 2015-04-20 18:07       ` Przemyslaw Marczak
  2015-04-22 16:31         ` Simon Glass
  2015-04-22 16:29       ` [U-Boot] [PATCH v4 00/16] Power(full) framework based on Driver Model Simon Glass
  16 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-20 18:07 UTC (permalink / raw)
  To: u-boot

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

This commits enables:
- CONFIG_ERRNO_STR
- CONFIG_DM_PMIC
- CONFIG_DM_PMIC_CMD
- CONFIG_DM_PMIC_MAX77686
- CONFIG_DM_REGULATOR
- CONFIG_DM_REGULATOR_CMD
- CONFIG_DM_REGULATOR_MAX77686

And removes the unused:
- CONFIG_DM_I2C_COMPAT
- CONFIG_POWER
- CONFIG_POWER_I2C
- CONFIG_POWER_MAX77686

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
Changes V2:
- config: enable dm i2c; cleanup
- remove CONFIG_DM_I2C_COMPAT
- enable regulator command

Changes V3:
- move options to defconfig

Changes V4:
- update commands config names

---
 configs/odroid_defconfig | 8 +++++++-
 include/configs/odroid.h | 5 -----
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/configs/odroid_defconfig b/configs/odroid_defconfig
index d32b5b5..6ca9586 100644
--- a/configs/odroid_defconfig
+++ b/configs/odroid_defconfig
@@ -4,5 +4,11 @@ CONFIG_TARGET_ODROID=y
 CONFIG_OF_CONTROL=y
 CONFIG_DEFAULT_DEVICE_TREE="exynos4412-odroid"
 CONFIG_DM_I2C=y
-CONFIG_DM_I2C_COMPAT=y
 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_ERRNO_STR=y
+CONFIG_DM_PMIC=y
+CONFIG_CMD_PMIC=y
+CONFIG_DM_PMIC_MAX77686=y
+CONFIG_DM_REGULATOR=y
+CONFIG_CMD_REGULATOR=y
+CONFIG_DM_REGULATOR_MAX77686=y
diff --git a/include/configs/odroid.h b/include/configs/odroid.h
index 5ee0abe..3874baa 100644
--- a/include/configs/odroid.h
+++ b/include/configs/odroid.h
@@ -182,11 +182,6 @@
 #define CONFIG_SYS_I2C_S3C24X0_SPEED	100000
 #define CONFIG_SYS_I2C_S3C24X0_SLAVE	0
 
-/* POWER */
-#define CONFIG_POWER
-#define CONFIG_POWER_I2C
-#define CONFIG_POWER_MAX77686
-
 /* GPT */
 #define CONFIG_RANDOM_UUID
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 00/16] Power(full) framework based on Driver Model
  2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
                         ` (15 preceding siblings ...)
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 16/16] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
@ 2015-04-22 16:29       ` Simon Glass
  2015-04-23 11:33         ` Przemyslaw Marczak
  16 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:29 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello,
> Again the next version. The changes are described below each commit message.
> This is rebased on last u-boot-dm/master after apply this patchset:
> https://patchwork.ozlabs.org/patch/462775/
> https://patchwork.ozlabs.org/patch/462777/
> https://patchwork.ozlabs.org/patch/462776/
>
> This all can be found in here:
> https://github.com/bobenstein/u-boot/tree/dm-pmic-v4
>
> Best regards,
>
> Przemyslaw Marczak (16):
>   exynos5: fix build break by adding CONFIG_POWER
>   exynos4-common: remove the unsued CONFIG_CMD_PMIC
>   lib: Kconfig: add entry for errno_str() function
>   dm: pmic: add implementation of driver model pmic uclass
>   dm: regulator: add implementation of driver model regulator uclass
>   dm: pmic: add pmic command
>   dm: regulator: add regulator command
>   pmic: max77686 set the same compatible as in the kernel
>   dm: pmic: add max77686 pmic driver
>   dm: regulator: add max77686 regulator driver
>   dm: regulator: add fixed voltage 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                                         |   3 +-
>  arch/arm/dts/exynos4412-odroid.dts               | 255 ++++++-
>  arch/arm/dts/exynos4412-trats2.dts               |   2 +-
>  arch/arm/dts/exynos5250-smdk5250.dts             |   2 +-
>  arch/arm/dts/exynos5250-snow.dts                 |   2 +-
>  board/samsung/common/board.c                     |   4 +-
>  board/samsung/common/misc.c                      |   1 +
>  board/samsung/odroid/odroid.c                    |  77 ++-
>  common/Kconfig                                   |  36 +
>  common/Makefile                                  |   4 +
>  common/cmd_pmic.c                                | 231 +++++++
>  common/cmd_regulator.c                           | 403 +++++++++++
>  configs/odroid_defconfig                         |   8 +-
>  doc/device-tree-bindings/pmic/max77686.txt       |  36 +
>  doc/device-tree-bindings/regulator/fixed.txt     |  38 ++
>  doc/device-tree-bindings/regulator/max77686.txt  |  70 ++
>  doc/device-tree-bindings/regulator/regulator.txt |  55 ++
>  doc/driver-model/pmic-framework.txt              | 142 ++++
>  drivers/power/Kconfig                            |   8 +
>  drivers/power/Makefile                           |   1 -
>  drivers/power/pmic/Kconfig                       |  18 +
>  drivers/power/pmic/Makefile                      |   2 +
>  drivers/power/pmic/max77686.c                    |  87 +++
>  drivers/power/pmic/pmic-uclass.c                 | 158 +++++
>  drivers/power/pmic/pmic_max77686.c               |   2 +-
>  drivers/power/regulator/Kconfig                  |  33 +
>  drivers/power/regulator/Makefile                 |  10 +
>  drivers/power/regulator/fixed.c                  | 126 ++++
>  drivers/power/regulator/max77686.c               | 825 +++++++++++++++++++++++
>  drivers/power/regulator/regulator-uclass.c       | 300 +++++++++
>  include/configs/exynos4-common.h                 |   1 -
>  include/configs/exynos5-common.h                 |   4 +
>  include/configs/odroid.h                         |   5 -
>  include/dm/uclass-id.h                           |   4 +
>  include/power/max77686_pmic.h                    |  29 +-
>  include/power/pmic.h                             | 189 ++++++
>  include/power/regulator.h                        | 384 +++++++++++
>  lib/Kconfig                                      |   8 +
>  lib/fdtdec.c                                     |   2 +-
>  39 files changed, 3512 insertions(+), 53 deletions(-)
>  create mode 100644 common/cmd_pmic.c
>  create mode 100644 common/cmd_regulator.c
>  create mode 100644 doc/device-tree-bindings/pmic/max77686.txt
>  create mode 100644 doc/device-tree-bindings/regulator/fixed.txt
>  create mode 100644 doc/device-tree-bindings/regulator/max77686.txt
>  create mode 100644 doc/device-tree-bindings/regulator/regulator.txt
>  create mode 100644 doc/driver-model/pmic-framework.txt
>  create mode 100644 drivers/power/pmic/Kconfig
>  create mode 100644 drivers/power/pmic/max77686.c
>  create mode 100644 drivers/power/pmic/pmic-uclass.c
>  create mode 100644 drivers/power/regulator/Kconfig
>  create mode 100644 drivers/power/regulator/Makefile
>  create mode 100644 drivers/power/regulator/fixed.c
>  create mode 100644 drivers/power/regulator/max77686.c
>  create mode 100644 drivers/power/regulator/regulator-uclass.c
>  create mode 100644 include/power/regulator.h
>
> --
> 1.9.1
>

I'm going to test this and apply to u-boot-dm/next while we wait for
you to finish the sandbox test code. I've made a few comments on the
series. They are minor so I don't want to do another whole spin of the
series (it takes time to review!), but you could do a fix-up patch or
two if you like. I could perhaps squash them in if you prefer that,
but honestly they are nits.

It will be great to get this into mainline soon!

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 01/16] exynos5: fix build break by adding CONFIG_POWER
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 01/16] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
@ 2015-04-22 16:29         ` Simon Glass
  2015-04-22 17:08           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:29 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>
> 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.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  include/configs/exynos5-common.h | 4 ++++
>  1 file changed, 4 insertions(+)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 02/16] exynos4-common: remove the unsued CONFIG_CMD_PMIC
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 02/16] exynos4-common: remove the unsued CONFIG_CMD_PMIC Przemyslaw Marczak
@ 2015-04-22 16:29         ` Simon Glass
  2015-04-22 17:09           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:29 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This config name was never used, because the present pmic command
> was precompiled for the CONFIG_POWER.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  include/configs/exynos4-common.h | 1 -
>  1 file changed, 1 deletion(-)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 03/16] lib: Kconfig: add entry for errno_str() function
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 03/16] lib: Kconfig: add entry for errno_str() function Przemyslaw Marczak
@ 2015-04-22 16:29         ` Simon Glass
  2015-04-22 17:09           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:29 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  lib/Kconfig | 8 ++++++++
>  1 file changed, 8 insertions(+)

(please make sure to always add a commit message)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 04/16] dm: pmic: add implementation of driver model pmic uclass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 04/16] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
@ 2015-04-22 16:30         ` Simon Glass
  2015-04-22 17:09           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  0 siblings, 2 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:30 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This commit introduces the PMIC uclass implementation.
> It allows providing the basic I/O interface for PMIC devices.
> For the multi-function PMIC devices, this can be used as I/O
> parent device, for each IC's interface. Then, each PMIC particular
> function can be provided by the child device's operations, and the
> child devices will use its parent for read/write by the common API.
>
> Core files:
> - 'include/power/pmic.h'
> - 'drivers/power/pmic/pmic-uclass.c'
>
> The old pmic framework is still kept and is independent.
>
> For more detailed informations, please look into the header file.
>
> Changes:
> - new uclass-id: UCLASS_PMIC
> - new config: CONFIG_DM_PMIC
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> 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
>
> Changes V3:
> - pmic-uclass.c and pmic.h:
>   -- remove  pmic_if_* functions
>   -- add new function pmic_child_node_scan()
> - add Kconfig entry
>
> Changes V4:
> - move drivers/power/pmic-uclass.c to drivers/power/pmic/pmic-uclass.c
> - move DM_PMIC Kconfig entry: drivers/power/Kconfig to drivers/power/pmic/Kconfig
> - drivers/power/Kconfig: Add menu "Power" and include pmic Kconfig
> - Kconfig: provide only the general information about the PMIC
> - pmic-uclass.c: add pmic_bind_childs()
> - pmic-uclass.c: add debug
> - pmic-uclass.c: cleanup includes
> - pmic-uclass.c: remove pmic_get_uclass_ops() and use of dev_get_driver_ops()
> - pmic-uclass.c: use of uclass_get_device_by_name()
> - pmic-uclass.c: add str_get_num() - for get number from string
> - include/power/pmic.h - start comments rewording
> - power/pmic.h: comments update
> ---
>  drivers/power/Kconfig            |   6 ++
>  drivers/power/pmic/Kconfig       |  11 +++
>  drivers/power/pmic/Makefile      |   1 +
>  drivers/power/pmic/pmic-uclass.c | 158 ++++++++++++++++++++++++++++++++
>  include/dm/uclass-id.h           |   3 +
>  include/power/pmic.h             | 189 +++++++++++++++++++++++++++++++++++++++
>  6 files changed, 368 insertions(+)
>  create mode 100644 drivers/power/pmic/Kconfig
>  create mode 100644 drivers/power/pmic/pmic-uclass.c

Acked-by: Simon Glass <sjg@chromium.org>

I have a few nits below - perhaps they can be targeted in a follow-up
patch or two? I'd like to merge this soon and it is not worth holding
up the series for nits.

>
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index f8f0239..d03626e 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -1,3 +1,7 @@
> +menu "Power"
> +
> +source "drivers/power/pmic/Kconfig"
> +
>  config AXP221_POWER
>         boolean "axp221 / axp223 pmic support"
>         depends on MACH_SUN6I || MACH_SUN8I
> @@ -73,3 +77,5 @@ config AXP221_ELDO3_VOLT
>         disable eldo3. On some A31(s) tablets it might be used to supply
>         1.2V for the SSD2828 chip (converter of parallel LCD interface
>         into MIPI DSI).
> +
> +endmenu
> diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
> new file mode 100644
> index 0000000..d06d632
> --- /dev/null
> +++ b/drivers/power/pmic/Kconfig
> @@ -0,0 +1,11 @@
> +config DM_PMIC
> +       bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC)"
> +       depends on DM
> +       ---help---
> +       This config enables the driver-model PMIC support.
> +       UCLASS_PMIC - designed to provide an I/O interface for PMIC devices.
> +       For the multi-function PMIC devices, this can be used as parent I/O
> +       device for each IC's interface. Then, each children uses its parent
> +       for read/write. For detailed description, please refer to the files:
> +       - 'drivers/power/pmic/pmic-uclass.c'
> +       - 'include/power/pmic.h'
> diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
> index 985cfdb..594f620 100644
> --- a/drivers/power/pmic/Makefile
> +++ b/drivers/power/pmic/Makefile
> @@ -5,6 +5,7 @@
>  # SPDX-License-Identifier:     GPL-2.0+
>  #
>
> +obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
>  obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o
>  obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o
>  obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o
> diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c
> new file mode 100644
> index 0000000..d82d3da
> --- /dev/null
> +++ b/drivers/power/pmic/pmic-uclass.c
> @@ -0,0 +1,158 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <fdtdec.h>
> +#include <errno.h>
> +#include <dm.h>
> +#include <dm/lists.h>
> +#include <dm/device-internal.h>
> +#include <dm/uclass-internal.h>
> +#include <power/pmic.h>
> +#include <linux/ctype.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +static ulong str_get_num(const char *ptr, const char *maxptr)
> +{
> +       if (!ptr || !maxptr)
> +               return 0;
> +
> +       while (!isdigit(*ptr) && ptr++ < maxptr);
> +
> +       return simple_strtoul(ptr, NULL, 0);
> +}
> +
> +int pmic_bind_childs(struct udevice *pmic, int offset,
> +                    const struct pmic_child_info *child_info)
> +{
> +       const struct pmic_child_info *info;
> +       const void *blob = gd->fdt_blob;
> +       struct driver *drv;
> +       struct udevice *child;
> +       const char *node_name;
> +       int node_name_len;
> +       int bind_count = 0;
> +       int node;
> +       int prefix_len;
> +       int ret;
> +
> +       debug("%s for '%s' at node offset: %d\n", __func__, pmic->name,
> +             pmic->of_offset);
> +
> +       for (node = fdt_first_subnode(blob, offset);
> +            node > 0;
> +            node = fdt_next_subnode(blob, node)) {
> +               node_name = fdt_get_name(blob, node, &node_name_len);
> +
> +               debug("* Found child node: '%s' at offset:%d\n", node_name,
> +                                                                node);
> +
> +               child = NULL;
> +               info = child_info;
> +               while (info->prefix) {
> +                       prefix_len = strlen(info->prefix);
> +                       if (strncasecmp(info->prefix, node_name, prefix_len) ||
> +                           !info->driver) {
> +                               info++;
> +                               continue;
> +                       }
> +
> +                       debug("  - compatible prefix: '%s'\n", info->prefix);
> +
> +                       drv = lists_driver_lookup_name(info->driver);
> +                       if (!drv) {
> +                               debug("  - driver: '%s' not found!\n",
> +                                     info->driver);
> +                               continue;
> +                       }
> +
> +                       debug("  - found child driver: '%s'\n", drv->name);
> +
> +                       ret = device_bind(pmic, drv, node_name, NULL,
> +                                         node, &child);
> +                       if (ret) {
> +                               debug("  - child binding error: %d\n", ret);
> +                               continue;
> +                       }
> +
> +                       debug("  - bound child device: '%s'\n", child->name);
> +
> +                       child->driver_data = str_get_num(node_name +
> +                                                        prefix_len,
> +                                                        node_name +
> +                                                        node_name_len);
> +
> +                       debug("  - set 'child->driver_data': %lu\n",
> +                             child->driver_data);
> +                       break;
> +               }
> +
> +               if (child)
> +                       bind_count++;
> +               else
> +                       debug("  - compatible prefix not found\n");
> +       }
> +
> +       debug("Bound: %d childs for PMIC: '%s'\n", bind_count, pmic->name);
> +       return bind_count;
> +}
> +
> +int pmic_get(const char *name, struct udevice **devp)
> +{
> +       return uclass_get_device_by_name(UCLASS_PMIC, name, devp);
> +}
> +
> +int pmic_reg_count(struct udevice *dev)
> +{
> +       const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);

blank line here

> +       if (!ops)
> +               return -ENOSYS;
> +
> +       return ops->reg_count;
> +}
> +
> +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len)
> +{
> +       const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
> +       int ret;
> +
> +       if (!buffer)
> +               return -EFAULT;
> +
> +       if (!ops || !ops->read)
> +               return -ENOSYS;
> +
> +       ret = ops->read(dev, reg, buffer, len);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len)
> +{
> +       const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
> +       int ret;
> +
> +       if (!buffer)
> +               return -EFAULT;
> +
> +       if (!ops || !ops->write)
> +               return -ENOSYS;
> +
> +       ret = ops->write(dev, reg, buffer, len);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +UCLASS_DRIVER(pmic) = {
> +       .id             = UCLASS_PMIC,
> +       .name           = "pmic",
> +};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index fddfd35..23b3eb9 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -46,6 +46,9 @@ enum uclass_id {
>         UCLASS_USB_DEV_GENERIC, /* USB generic device */
>         UCLASS_MASS_STORAGE,    /* Mass storage device */
>
> +       /* Power Management */
> +       UCLASS_PMIC,            /* PMIC I/O device */
> +
>         UCLASS_COUNT,
>         UCLASS_INVALID = -1,
>  };
> diff --git a/include/power/pmic.h b/include/power/pmic.h
> index afbc5aa..f7ae781 100644
> --- a/include/power/pmic.h
> +++ b/include/power/pmic.h
> @@ -1,4 +1,7 @@
>  /*
> + *  Copyright (C) 2014-2015 Samsung Electronics
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
>   *  Copyright (C) 2011-2012 Samsung Electronics
>   *  Lukasz Majewski <l.majewski@samsung.com>
>   *
> @@ -9,10 +12,13 @@
>  #define __CORE_PMIC_H_
>
>  #include <linux/list.h>
> +#include <spi.h>
>  #include <i2c.h>
>  #include <power/power_chrg.h>

At some point can you update the ordering of this lot? I think it should be:

i2c.g
spi.h
linux/
power/

>
>  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,189 @@ struct pmic {
>         struct pmic *parent;
>         struct list_head list;
>  };
> +#endif /* CONFIG_POWER */
> +
> +#ifdef CONFIG_DM_PMIC
> +/**
> + * U-Boot PMIC Framework
> + * =====================
> + *
> + * UCLASS_PMIC - The is designed to provide an I/O interface for PMIC devices.

This is designed

> + *
> + * For the multi-function PMIC devices, this can be used as parent I/O device
> + * for each IC's interface. Then, each children uses its parent for read/write.

each child

> + *
> + * The driver model tree could look 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_RTC     (in the future)
> + *
> + * We can find two PMIC cases in boards design:
> + * - single I/O interface
> + * - multiple I/O interfaces
> + * We bind single PMIC device for each interface, to provide an I/O as a parent,

a single

> + * of proper child devices. Each child usually implements a different function,
> + * controlled by the same interface.
> + *
> + * The binding should be done automatically. If 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 device nodes, for multi-interface PMIC.
> + *
> + * Note:
> + * Each PMIC interface driver should use a different compatible string.
> + *
> + * If each pmic child device driver need access the PMIC-specific registers,

If a pmic child device driver needs

> + * it need know only the register address and the access can be 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 the example below. It should be done by call

by calling pmic_bind_childs()

(which you should rename to pmic_bind_children() I think)

> + * the 'pmic_bind_childs()' - please refer to the description of this function
> + * in this header file. This function, should be called in the driver's '.bind'
> + * method.
> + *
> + * For the example driver, please refer the MAX77686 driver:
> + * - 'drivers/power/pmic/max77686.c'
> + */
> +
> +/**
> + * 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.
> + *
> + * @reg_count: devices register count
> + * @read:      read 'len' bytes at "reg" and store it into the 'buffer'
> + * @write:     write 'len' bytes from the 'buffer' to the register at 'reg' address
> + */
> +struct dm_pmic_ops {
> +       int reg_count;

This should not be in ops as it is not an operation. Perhaps add a
function for it, or put it in the uclass platform data?

> +       int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int len);
> +       int (*write)(struct udevice *dev, uint reg, const uint8_t *buffer,
> +                    int len);
> +};
> +
> +/* enum pmic_op_type - used for various pmic devices operation calls,

/**
 * enum pmic_op_type -

> + * 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,
> +};
> +
> +/**
> + * struct pmic_child_info - basic device's child info for bind child nodes with
> + * the driver by the node name prefix and driver name. This is a helper struct
> + * for function: pmic_bind_childs().
> + *
> + * @prefix - child node name prefix (or its name if is unique or single)
> + * @driver - driver name for the sub-node with prefix
> + */
> +struct pmic_child_info {
> +       const char *prefix;
> +       const char *driver;
> +};
> +
> +/* drivers/power/pmic-uclass.c */
> +
> +/**
> + * pmic_bind_childs() - bind drivers for given parent pmic, using child info
> + * found in 'child_info' array.
> + *
> + * @pmic       - pmic device - the parent of found child's
> + * @child_info - N-childs info array
> + * @return a positive number of childs, or 0 if no child found (error)
> + *
> + * Note: For N-childs the child_info array should have N+1 entries and the last
> + * entry prefix should be NULL - the same as for drivers compatible.
> + *
> + * For example, a single prefix info (N=1):
> + * static const struct pmic_child_info bind_info[] = {
> + *     { .prefix = "ldo", .driver = "ldo_driver" },
> + *     { },
> + * };
> + *
> + * This function is useful for regulator sub-nodes:
> + * my_regulator at 0xa {
> + *     reg = <0xa>;
> + *     (pmic - bind automatically by compatible)
> + *     compatible = "my_pmic";
> + *     ...
> + *     (pmic's childs - bind by pmic_bind_childs())
> + *     (nodes prefix: "ldo", driver: "my_regulator_ldo")
> + *     ldo1 { ... };
> + *     ldo2 { ... };
> + *
> + *     (nodes prefix: "buck", driver: "my_regulator_buck")
> + *     buck1 { ... };
> + *     buck2 { ... };
> + * };
> + */
> +int pmic_bind_childs(struct udevice *pmic, int offset,
> +                    const struct pmic_child_info *child_info);

pmic_bind_children

> +
> +/**
> + * pmic_get: get the pmic device using its name
> + *
> + * @name - device name
> + * @devp - returned pointer to the pmic device
> + * @return 0 on success or negative value of errno.
> + *
> + * The returned devp device can be used with pmic_read/write calls
> + */
> +int pmic_get(const char *name, struct udevice **devp);
> +
> +/**
> + * pmic_reg_count: get the pmic register count
> + *
> + * The required pmic device can be obtained by 'pmic_get()'
> + *
> + * @dev - pointer to the UCLASS_PMIC device
> + * @return register count value on success or negative value of errno.
> + */
> +int pmic_reg_count(struct udevice *dev);
> +
> +/**
> + * pmic_read/write: read/write to the UCLASS_PMIC device
> + *
> + * The required pmic device can be obtained by 'pmic_get()'
> + *
> + * @pmic   - pointer to the UCLASS_PMIC device
> + * @reg    - device register offset
> + * @buffer - pointer to read/write buffer
> + * @len    - byte count for read/write
> + * @return 0 on success or negative value of errno.
> + */
> +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len);
> +int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len);
> +#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 +276,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)
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 05/16] dm: regulator: add implementation of driver model regulator uclass
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 05/16] dm: regulator: add implementation of driver model regulator uclass Przemyslaw Marczak
@ 2015-04-22 16:30         ` Simon Glass
  2015-04-22 16:54           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  0 siblings, 2 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:30 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This commit introduces the implementation of dm regulator API.
> Device tree support allows for auto binding. And by the basic
> uclass operations, it allows to driving the devices in a common
> way. For detailed informations, please look into the header file.
>
> Core 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_REGULATOR
> - new config: CONFIG_DM_REGULATOR
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> 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
>
> Changes V3:
> - regulator-uclass.c and regulator.h:
>   -- api cleanup
>   -- new function regulator_ofdata_to_platdata()
>   -- update of comments
>   -- add Kconfig
>
> Changes V4:
> - move file drivers/power/regulator-uclass.c to
>   drivers/power/regulator/regulator-uclass.c
> - move DM_REGULATOR Kconfig entry from: drivers/power/Kconfig to
>   drivers/power/regulator/Kconfig
> - drivers/power/Kconfig: include regulator Kconfig path
> - Kconfig: provide only general informations
> - regulator-uclass.c: cleanup
> - regulator-uclass.c: allow init regulator with name only
> - regulator-uclass.c: remove pmic_get_uclass_ops and use dev_get_driver_ops
> - regulator-uclass.c: add use of uclass_get_device_by_name()
> - regulator.h: add 'struct dm_regulator_uclass_platdata'
> - regulator.h: API documentation cleanup
> - regulator - add binding info
>
> ---
>  Makefile                                         |   3 +-
>  doc/device-tree-bindings/regulator/regulator.txt |  55 ++++
>  drivers/power/Kconfig                            |   2 +
>  drivers/power/regulator/Kconfig                  |  17 +
>  drivers/power/regulator/Makefile                 |   8 +
>  drivers/power/regulator/regulator-uclass.c       | 300 ++++++++++++++++++
>  include/dm/uclass-id.h                           |   1 +
>  include/power/regulator.h                        | 384 +++++++++++++++++++++++
>  8 files changed, 769 insertions(+), 1 deletion(-)
>  create mode 100644 doc/device-tree-bindings/regulator/regulator.txt
>  create mode 100644 drivers/power/regulator/Kconfig
>  create mode 100644 drivers/power/regulator/Makefile
>  create mode 100644 drivers/power/regulator/regulator-uclass.c
>  create mode 100644 include/power/regulator.h

A few more nits for later.

>
> diff --git a/Makefile b/Makefile
> index dc25f70..dfb0d56 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -645,7 +645,8 @@ libs-y += drivers/power/ \
>         drivers/power/fuel_gauge/ \
>         drivers/power/mfd/ \
>         drivers/power/pmic/ \
> -       drivers/power/battery/
> +       drivers/power/battery/ \
> +       drivers/power/regulator/
>  libs-y += drivers/spi/
>  libs-$(CONFIG_FMAN_ENET) += drivers/net/fm/
>  libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
> diff --git a/doc/device-tree-bindings/regulator/regulator.txt b/doc/device-tree-bindings/regulator/regulator.txt
> new file mode 100644
> index 0000000..249e0f5
> --- /dev/null
> +++ b/doc/device-tree-bindings/regulator/regulator.txt
> @@ -0,0 +1,55 @@
> +Voltage/Current regulator
> +
> +Binding:
> +The regulator devices don't use the "compatible" property. The binding is done
> +by the prefix of regulator node's name. Usually the pmic I/O driver will provide
> +the array of 'struct pmic_child_info' with the prefixes and compatible drivers.
> +The bind is done by calling function: pmic_bind_childs().
> +Example drivers:
> +pmic: drivers/power/pmic/max77686.c
> +regulator: drivers/power/regulator/max77686.c
> +
> +For the node name e.g.: "prefix[:alpha:]num { ... }":
> +- the driver prefix should be: "prefix" or "PREFIX" - case insensitive
> +- the node name's "num" is set as "dev->driver_data" on bind
> +
> +Example the prefix "ldo" will pass for: "ldo1", "ldo at 1", "LDO1", "LDOREG at 1"...
> +
> +Required properties:
> +- regulator-name: a string, required by the regulator uclass
> +
> +Note
> +The "regulator-name" constraint is used for setting the device's uclass
> +platform data '.name' field. And the regulator device name is set from
> +it's node name.
> +
> +Optional properties:
> +- regulator-min-microvolt: a minimum allowed Voltage value
> +- regulator-max-microvolt: a maximum allowed Voltage value
> +- regulator-min-microamp: a minimum allowed Current value
> +- regulator-max-microamp: a maximum allowed Current value
> +- regulator-always-on: regulator should never be disabled
> +- regulator-boot-on: enabled by bootloader/firmware
> +
> +Other kernel-style properties, are currently not used.
> +
> +Note:
> +For the regulator autoset from constraints, the framework expects that:
> +- regulator-min-microvolt is equal to regulator-max-microvolt
> +- regulator-min-microamp is equal to regulator-max-microamp
> +- regulator-always-on or regulator-boot-on is set
> +
> +Example:
> +ldo0 {
> +       /* Mandatory */
> +       regulator-name = "VDDQ_EMMC_1.8V";
> +
> +       /* Optional */
> +       regulator-min-microvolt = <1800000>;
> +       regulator-max-microvolt = <1800000>;
> +       regulator-min-microamp = <100000>;
> +       regulator-max-microamp = <100000>;
> +       regulator-always-on;
> +       regulator-boot-on;
> +};
> +
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index d03626e..23cdd71 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -2,6 +2,8 @@ menu "Power"
>
>  source "drivers/power/pmic/Kconfig"
>
> +source "drivers/power/regulator/Kconfig"
> +
>  config AXP221_POWER
>         boolean "axp221 / axp223 pmic support"
>         depends on MACH_SUN6I || MACH_SUN8I
> diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
> new file mode 100644
> index 0000000..cb15162
> --- /dev/null
> +++ b/drivers/power/regulator/Kconfig
> @@ -0,0 +1,17 @@
> +config DM_REGULATOR
> +       bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)"
> +       depends on DM
> +       ---help---
> +       This config enables the driver model regulator support.
> +       UCLASS_REGULATOR - designed to provide a common API for basic regulator's
> +       functions, like get/set Voltage or Current value, enable state, etc...
> +       Note:
> +       When enabling this, please read the description, found in the files:
> +       - 'include/power/pmic.h'
> +       - 'include/power/regulator.h'
> +       - 'drivers/power/pmic/pmic-uclass.c'
> +       - 'drivers/power/pmic/regulator-uclass.c'
> +       It's important to call the device_bind() with the proper node offset,
> +       when binding the regulator devices. The pmic_bind_childs() can be used
> +       for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_node()
> +       otherwise. Detailed informations can be found in the header file.
> diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
> new file mode 100644
> index 0000000..27c9006
> --- /dev/null
> +++ b/drivers/power/regulator/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# Copyright (C) 2015 Samsung Electronics
> +# Przemyslaw Marczak <p.marczak@samsung.com>
> +#
> +# SPDX-License-Identifier:     GPL-2.0+
> +#
> +
> +obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
> diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
> new file mode 100644
> index 0000000..07ce286
> --- /dev/null
> +++ b/drivers/power/regulator/regulator-uclass.c
> @@ -0,0 +1,300 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +#include <common.h>
> +#include <fdtdec.h>
> +#include <errno.h>
> +#include <dm.h>
> +#include <dm/uclass-internal.h>
> +#include <power/pmic.h>
> +#include <power/regulator.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep)
> +{
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +
> +       *modep = NULL;
> +
> +       uc_pdata = dev_get_uclass_platdata(dev);
> +       if (!uc_pdata)
> +               return -ENXIO;
> +
> +       *modep = uc_pdata->mode;
> +       return uc_pdata->mode_count;
> +}
> +
> +int regulator_get_value(struct udevice *dev)
> +{
> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
> +
> +       if (!ops || !ops->get_value)
> +               return -ENOSYS;
> +
> +       return ops->get_value(dev);
> +}
> +
> +int regulator_set_value(struct udevice *dev, int uV)
> +{
> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
> +
> +       if (!ops || !ops->set_value)
> +               return -ENOSYS;
> +
> +       return ops->set_value(dev, uV);
> +}
> +
> +int regulator_get_current(struct udevice *dev)
> +{
> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
> +
> +       if (!ops || !ops->get_current)
> +               return -ENOSYS;
> +
> +       return ops->get_current(dev);
> +}
> +
> +int regulator_set_current(struct udevice *dev, int uA)
> +{
> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
> +
> +       if (!ops || !ops->set_current)
> +               return -ENOSYS;
> +
> +       return ops->set_current(dev, uA);
> +}
> +
> +bool regulator_get_enable(struct udevice *dev)
> +{
> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
> +
> +       if (!ops || !ops->get_enable)
> +               return -ENOSYS;
> +
> +       return ops->get_enable(dev);
> +}
> +
> +int regulator_set_enable(struct udevice *dev, bool enable)
> +{
> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
> +
> +       if (!ops || !ops->set_enable)
> +               return -ENOSYS;
> +
> +       return ops->set_enable(dev, enable);
> +}
> +
> +int regulator_get_mode(struct udevice *dev)
> +{
> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
> +
> +       if (!ops || !ops->get_mode)
> +               return -ENOSYS;
> +
> +       return ops->get_mode(dev);
> +}
> +
> +int regulator_set_mode(struct udevice *dev, int mode)
> +{
> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
> +
> +       if (!ops || !ops->set_mode)
> +               return -ENOSYS;
> +
> +       return ops->set_mode(dev, mode);
> +}
> +
> +int regulator_by_platname(const char *plat_name, struct udevice **devp)

regulator_get_by_platname()

since it does probe the device

> +{
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       struct udevice *dev;
> +
> +       *devp = NULL;
> +
> +       for (uclass_find_first_device(UCLASS_REGULATOR, &dev);
> +            dev;
> +            uclass_find_next_device(&dev)) {
> +               uc_pdata = dev_get_uclass_platdata(dev);
> +               if (!uc_pdata || strcmp(plat_name, uc_pdata->name))
> +                       continue;
> +
> +               return uclass_get_device_tail(dev, 0, devp);
> +       }
> +
> +       debug("%s: can't find: %s\n", __func__, plat_name);
> +
> +       return -ENODEV;
> +}
> +
> +int regulator_by_devname(const char *devname, struct udevice **devp)

regulator_get_by_devname

> +{
> +       return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp);
> +}
> +
> +static int setting_failed(int ret, bool verbose, const char *fmt, ...)
> +{
> +       va_list args;
> +       char buf[64];
> +
> +       if (verbose == false)
> +               return ret;
> +
> +       va_start(args, fmt);
> +       vscnprintf(buf, sizeof(buf), fmt, args);
> +       va_end(args);
> +
> +       printf(buf);

I wonder if we should have a vprintf() in U-Boot?

> +
> +       if (!ret)
> +               return 0;
> +
> +       printf(" (ret: %d)", ret);
> +
> +       return ret;
> +}
> +
> +int regulator_by_platname_autoset_and_enable(const char *platname,
> +                                            struct udevice **devp,
> +                                            bool verbose)
> +{
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       struct udevice *dev;
> +       bool v = verbose;

Can we drop this local var and just use 'verbose'

> +       int ret;
> +
> +       if (devp)
> +               *devp = NULL;
> +
> +       ret = regulator_by_platname(platname, &dev);
> +       if (ret) {
> +               error("Can get the regulator: %s!", platname);
> +               return ret;
> +       }
> +
> +       uc_pdata = dev_get_uclass_platdata(dev);
> +       if (!uc_pdata) {
> +               error("Can get the regulator %s uclass platdata!", platname);
> +               return -ENXIO;
> +       }
> +
> +       if (v)
> +               printf("%s@%s: ", dev->name, uc_pdata->name);
> +
> +       /* Those values are optional (-ENODATA if unset) */
> +       if ((uc_pdata->min_uV != -ENODATA) &&
> +           (uc_pdata->max_uV != -ENODATA) &&
> +           (uc_pdata->min_uV == uc_pdata->max_uV)) {
> +               ret = regulator_set_value(dev, uc_pdata->min_uV);
> +               if (setting_failed(ret, v, "set %d uV", uc_pdata->min_uV))
> +                       goto exit;
> +       }
> +
> +       /* Those values are optional (-ENODATA if unset) */
> +       if ((uc_pdata->min_uA != -ENODATA) &&
> +           (uc_pdata->max_uA != -ENODATA) &&
> +           (uc_pdata->min_uA == uc_pdata->max_uA)) {
> +               ret = regulator_set_current(dev, uc_pdata->min_uA);
> +               if (setting_failed(ret, v, "; set %d uA", uc_pdata->min_uA))
> +                       goto exit;
> +       }
> +
> +       if (!uc_pdata->always_on && !uc_pdata->boot_on)
> +               goto retdev;
> +
> +       ret = regulator_set_enable(dev, true);
> +       if (setting_failed(ret, v, "; enabling", uc_pdata->min_uA))
> +               goto exit;
> +
> +retdev:
> +       if (devp)
> +               *devp = dev;
> +exit:
> +       if (v)
> +               printf("\n");
> +       return ret;
> +}
> +
> +int regulator_by_platname_list_autoset_and_enable(const char *list_platname[],
> +                                                 int list_entries,
> +                                                 struct udevice *list_devp[],
> +                                                 bool verbose)

I wonder if you could shorten this (e.g. to regulator_list_autoset())?

> +{
> +       struct udevice *dev;
> +       int i, ret, success = 0;
> +
> +       for (i = 0; i < list_entries; i++) {
> +               ret = regulator_autoset(list_platname[i], &dev, verbose);
> +               if (!ret)
> +                       success++;
> +
> +               if (!list_devp)
> +                       continue;
> +
> +               if (ret)
> +                       list_devp[i] = NULL;
> +               else
> +                       list_devp[i] = dev;

Shouldn't dev be NULL if ret is non-zero anyway?

> +       }
> +
> +       return (success != list_entries);

return success == list_entries ? 0 : -EIO

or something. Better would be to record the first error you get and return that.

> +}
> +
> +static int regulator_post_bind(struct udevice *dev)
> +{
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       int offset = dev->of_offset;
> +       const void *blob = gd->fdt_blob;
> +
> +       uc_pdata = dev_get_uclass_platdata(dev);
> +       if (!uc_pdata)
> +               return -ENXIO;
> +
> +       /* Regulator's mandatory constraint */
> +       uc_pdata->name = fdt_getprop(blob, offset, "regulator-name", NULL);
> +       if (!uc_pdata->name) {
> +               debug("%s: dev: %s has no property 'regulator-name'\n",
> +                     __func__, dev->name);
> +               return -ENXIO;

-EINVAL as it indicates invalid device tree data.

> +       }
> +
> +       return 0;
> +}
> +
> +static int regulator_pre_probe(struct udevice *dev)
> +{
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       int offset = dev->of_offset;
> +
> +       uc_pdata = dev_get_uclass_platdata(dev);
> +       if (!uc_pdata)
> +               return -ENXIO;
> +
> +       /* Regulator's optional constraints */
> +       uc_pdata->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
> +                                         "regulator-min-microvolt", -ENODATA);
> +       uc_pdata->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
> +                                         "regulator-max-microvolt", -ENODATA);
> +       uc_pdata->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
> +                                         "regulator-min-microamp", -ENODATA);
> +       uc_pdata->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
> +                                         "regulator-max-microamp", -ENODATA);
> +       uc_pdata->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
> +                                             "regulator-always-on");
> +       uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
> +                                           "regulator-boot-on");
> +
> +       return 0;
> +}
> +
> +UCLASS_DRIVER(regulator) = {
> +       .id             = UCLASS_REGULATOR,
> +       .name           = "regulator",
> +       .post_bind      = regulator_post_bind,
> +       .pre_probe      = regulator_pre_probe,
> +       .per_device_platdata_auto_alloc_size =
> +                               sizeof(struct dm_regulator_uclass_platdata),
> +};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index 23b3eb9..3c572d7 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -48,6 +48,7 @@ enum uclass_id {
>
>         /* Power Management */
>         UCLASS_PMIC,            /* PMIC I/O device */
> +       UCLASS_REGULATOR,       /* REGULATOR device */
>
>         UCLASS_COUNT,
>         UCLASS_INVALID = -1,
> diff --git a/include/power/regulator.h b/include/power/regulator.h
> new file mode 100644
> index 0000000..0302c1d
> --- /dev/null
> +++ b/include/power/regulator.h
> @@ -0,0 +1,384 @@
> +/*
> + *  Copyright (C) 2014-2015 Samsung Electronics
> + *  Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#ifndef _INCLUDE_REGULATOR_H_
> +#define _INCLUDE_REGULATOR_H_
> +
> +/**
> + * U-Boot Voltage/Current Regulator
> + * ================================
> + *
> + * The regulator API is based on a driver model, with the device tree support.
> + * And this header describes the functions and data types for the uclass id:
> + * 'UCLASS_REGULATOR' and the regulator driver API.
> + *
> + * The regulator uclass - is based on uclass platform data which is allocated,
> + * automatically for each regulator device on bind and 'dev->uclass_platdata'
> + * points to it. The data type is: 'struct dm_regulator_uclass_platdata'.
> + * The uclass file: 'drivers/power/regulator/regulator-uclass.c'
> + *
> + * The regulator device - is based on driver's model 'struct udevice'.
> + * The API can use regulator name in two meanings:
> + * - devname  - the regulator device's name: 'dev->name'
> + * - platname - the device's platdata's name. So in the code it looks like:
> + *              'uc_pdata = dev->uclass_platdata'; 'name = uc_pdata->name'.
> + *
> + * The regulator device driver - provide an implementation of uclass operations
> + * pointed by 'dev->driver->ops' as a struct of type 'struct dm_regulator_ops'.
> + *
> + * To proper bind the regulator device, the device tree node should provide
> + * regulator constraints, like in the example below:
> + *
> + * ldo1 {
> + *      regulator-name = "VDD_MMC_1.8V";     (mandatory for bind)
> + *      regulator-min-microvolt = <1000000>; (optional)
> + *      regulator-max-microvolt = <1000000>; (optional)
> + *      regulator-min-microamp = <1000>;     (optional)
> + *      regulator-max-microamp = <1000>;     (optional)
> + *      regulator-always-on;                 (optional)
> + *      regulator-boot-on;                   (optional)
> + * };
> + *
> + * Please take a notice, that for the proper operation at least name constraint

Please note that for the proper

or

Note: For the proper

> + * is needed, e.g. for call the device_by_platname(...).
> + *
> + * Regulator bind:
> + * For each regulator device, the device_bind() should be called with passed
> + * device tree offset. This is required for this uclass's '.post_bind' method,
> + * which do the scan on the device node, for the 'regulator-name' constraint.

s/do/does/

> + * If the parent is not a PMIC device, and the child is not bind by function:
> + * 'pmic_bind_childs()', then it's recommended to bind the device by call to
> + * dm_scan_fdt_node() - this is usually done automatically for bus devices,
> + * as a post bind method.
> + * Having the device's name constraint, we can call regulator_by_platname(),
> + * to find interesting regulator. Before return, the regulator is probed,

How about:

s/to find interesting regulator/to find the required regulator/

> + * and the rest of its constraints are put into the device's uclass platform
> + * data, by the uclass regulator '.pre_probe' method.
> + *
> + * For more info about PMIC bind, please refer to file: 'include/power/pmic.h'
> + *
> + * Note:
> + * Please do not use the device_bind_by_name() function, since it pass '-1' as
> + * device node offset - and the bind will fail on uclass .post_bind method,
> + * because of missing 'regulator-name' constraint.

Indeed. BTW I think i'm going to add a similar function which allows
the node to be passed.

> + *
> + *
> + * Fixed Voltage/Current Regulator
> + * ===============================
> + *
> + * When fixed voltage regulator is needed, then enable the config:
> + * - CONFIG_DM_REGULATOR_FIXED
> + *
> + * The driver file: 'drivers/power/regulator/fixed.c', provides basic support
> + * for control the GPIO, and return the device tree constraint values.
> + *
> + * To bind the fixed voltage regulator device, we usually use a 'simple-bus'
> + * node as a parent. And 'regulator-fixed' for the driver compatible. This is
> + * the same as in the kernel. The example node of fixed regulator:
> + *
> + * simple-bus {
> + *     compatible = "simple-bus";
> + *     #address-cells = <1>;
> + *     #size-cells = <0>;
> + *
> + *     blue_led {
> + *         compatible = "regulator-fixed";
> + *         regulator-name = "VDD_LED_3.3V";
> + *         regulator-min-microvolt = <3300000>;
> + *         regulator-max-microvolt = <3300000>;
> + *         gpio = <&gpc1 0 GPIO_ACTIVE_LOW>;
> + *     };
> + * };
> + *
> + * The fixed regulator devices also provide regulator uclass platform data. And
> + * devices bound from such node, can use the regulator drivers API.
> +*/
> +
> +/* enum regulator_type - used for regulator_*() variant calls */
> +enum regulator_type {
> +       REGULATOR_TYPE_LDO = 0,
> +       REGULATOR_TYPE_BUCK,
> +       REGULATOR_TYPE_DVS,
> +       REGULATOR_TYPE_FIXED,
> +       REGULATOR_TYPE_OTHER,
> +};
> +
> +/**
> + * struct dm_regulator_mode - this structure holds an information about
> + * each regulator operation mode. Probably in most cases - an array.
> + * This will be probably a driver-static data, since it is device-specific.
> + *
> + * @id             - a driver-specific mode id
> + * @register_value - a driver-specific value for its mode id
> + * @name           - the name of mode - used for regulator command
> + * Note:
> + * The field 'id', should be always a positive number, since the negative values
> + * are reserved for the errno numbers when returns the mode id.
> + */
> +struct dm_regulator_mode {
> +       int id; /* Set only as >= 0 (negative value is reserved for errno) */
> +       int register_value;
> +       const char *name;
> +};
> +
> +/**
> + * struct dm_regulator_uclass_platdata - pointed by dev->uclass_platdata, and
> + * allocated on each regulator bind. This structure holds an information
> + * about each regulator's constraints and supported operation modes.
> + * There is no "step" voltage value - so driver should take care of this.
> + *
> + * @type       - one of 'enum regulator_type'
> + * @mode       - pointer to the regulator mode (array if more than one)
> + * @mode_count - number of '.mode' entries
> + * @min_uV*    - minimum voltage (micro Volts)
> + * @max_uV*    - maximum voltage (micro Volts)
> + * @min_uA*    - minimum amperage (micro Amps)
> + * @max_uA*    - maximum amperage (micro Amps)
> + * @always_on* - bool type, true or false
> + * @boot_on*   - bool type, true or false
> + * @name**     - fdt regulator name - should be taken from the device tree
> + *
> + * Note:
> + * *  - set automatically on device probe by the uclass's '.pre_probe' method.
> + * ** - set automatically on device bind by the uclass's '.post_bind' method.
> + * The constraints: type, mode, mode_count, can be set by device driver, e.g.
> + * by the driver '.probe' method.
> + */
> +struct dm_regulator_uclass_platdata {
> +       enum regulator_type type;
> +       struct dm_regulator_mode *mode;
> +       int mode_count;
> +       int min_uV;
> +       int max_uV;
> +       int min_uA;
> +       int max_uA;
> +       bool always_on;
> +       bool boot_on;
> +       const char *name;
> +};
> +
> +/* Regulator device operations */
> +struct dm_regulator_ops {
> +       /**
> +        * The regulator output value function calls operates on a micro Volts.
> +        *
> +        * get/set_value - get/set output value of the given output number
> +        * @dev          - regulator device
> +        * Sets:
> +        * @uV           - set the output value [micro Volts]
> +        * Returns: output value [uV] on success or negative errno if fail.
> +        */
> +       int (*get_value)(struct udevice *dev);
> +       int (*set_value)(struct udevice *dev, int uV);
> +
> +       /**
> +        * The regulator output current function calls operates on a micro Amps.
> +        *
> +        * get/set_current - get/set output current of the given output number
> +        * @dev            - regulator device
> +        * Sets:
> +        * @uA           - set the output current [micro Amps]
> +        * Returns: output value [uA] on success or negative errno if fail.
> +        */
> +       int (*get_current)(struct udevice *dev);
> +       int (*set_current)(struct udevice *dev, int uA);
> +
> +       /**
> +        * The most basic feature of the regulator output is its enable state.
> +        *
> +        * get/set_enable - get/set enable state of the given output number
> +        * @dev           - regulator device
> +        * Sets:
> +        * @enable         - set true - enable or false - disable
> +        * Returns: true/false for get; or 0 / -errno for set.
> +        */
> +       bool (*get_enable)(struct udevice *dev);
> +       int (*set_enable)(struct udevice *dev, bool enable);
> +
> +       /**
> +        * The 'get/set_mode()' function calls should operate on a driver
> +        * specific mode definitions, which should be found in:
> +        * field 'mode' of struct mode_desc.
> +        *
> +        * get/set_mode - get/set operation mode of the given output number
> +        * @dev         - regulator device
> +        * Sets
> +        * @mode_id     - set output mode id (struct dm_regulator_mode->id)
> +        * Returns: id/0 for get/set on success or negative errno if fail.
> +        * Note:
> +        * The field 'id' of struct type 'dm_regulator_mode', should be always
> +        * positive number, since the negative is reserved for the error.
> +        */
> +       int (*get_mode)(struct udevice *dev);
> +       int (*set_mode)(struct udevice *dev, int mode_id);
> +};
> +
> +/**
> + * regulator_mode: returns a pointer to the array of regulator mode info
> + *
> + * @dev        - pointer to the regulator device
> + * @modep      - pointer to the returned mode info array
> + * Returns     - count of modep entries on success or negative errno if fail.
> + */
> +int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep);
> +
> +/**
> + * regulator_get_value: get microvoltage voltage value of a given regulator
> + *
> + * @dev    - pointer to the regulator device
> + * Returns - positive output value [uV] on success or negative errno if fail.
> + */
> +int regulator_get_value(struct udevice *dev);
> +
> +/**
> + * regulator_set_value: set the microvoltage value of a given regulator.
> + *
> + * @dev    - pointer to the regulator device
> + * @uV     - the output value to set [micro Volts]
> + * Returns - 0 on success or -errno val if fails
> + */
> +int regulator_set_value(struct udevice *dev, int uV);
> +
> +/**
> + * regulator_get_current: get microampere value of a given regulator
> + *
> + * @dev    - pointer to the regulator device
> + * Returns - positive output current [uA] on success or negative errno if fail.
> + */
> +int regulator_get_current(struct udevice *dev);
> +
> +/**
> + * regulator_set_current: set the microampere value of a given regulator.
> + *
> + * @dev    - pointer to the regulator device
> + * @uA     - set the output current [micro Amps]
> + * Returns - 0 on success or -errno val if fails
> + */
> +int regulator_set_current(struct udevice *dev, int uA);
> +
> +/**
> + * regulator_get_enable: get regulator device enable state.
> + *
> + * @dev    - pointer to the regulator device
> + * Returns - true/false of enable state
> + */
> +bool regulator_get_enable(struct udevice *dev);
> +
> +/**
> + * regulator_set_enable: set regulator enable state
> + *
> + * @dev    - pointer to the regulator device
> + * @enable - set true or false
> + * Returns - 0 on success or -errno val if fails
> + */
> +int regulator_set_enable(struct udevice *dev, bool enable);
> +
> +/**
> + * regulator_get_mode: get mode of a given device regulator
> + *
> + * @dev    - pointer to the regulator device
> + * Returns - positive  mode number 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 'mode_desc' provides a field 'mode'
> + * for this purpose and register_value for a raw register value.

Can you please reword the first sentence? I don't understand what it
is trying to say. Similar below.

> + */
> +int regulator_get_mode(struct udevice *dev);
> +
> +/**
> + * regulator_set_mode: set given regulator mode
> + *
> + * @dev    - pointer to the regulator device
> + * @mode   - mode type (field 'mode' of struct mode_desc)
> + * 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 mode);
> +
> +/**
> + * regulator_by_platname_autoset_and_enable: setup the regulator given by
> + * its uclass's platform data '.name'. The setup depends on constraints found
> + * in device's uclass's platform data (struct dm_regulator_uclass_platdata):
> + * - Voltage value - will set - if '.min_uV' and '.max_uV' values are equal
> + * - Current limit - will set - if '.min_uA' and '.max_uA' values are equal
> + * - Enable - will set - if '.always_on' or '.boot_on' are set to true
> + *
> + * The function returns on first encountered error.
> + *
> + * @platname - expected string for dm_regulator_uclass_platdata .name field
> + * @devp      - returned pointer to the regulator device - if non-NULL passed
> + * @verbose   - (true/false) print regulator setup info, or be quiet
> + * Returns: 0 on success or negative value of errno.
> + *
> + * The returned 'regulator' device can be used with:
> + * - regulator_get/set_*
> + * For shorter call name, the below macro regulator_autoset() can be used.
> + */
> +int regulator_by_platname_autoset_and_enable(const char *platname,
> +                                            struct udevice **devp,
> +                                            bool verbose);
> +
> +#define regulator_autoset(platname, devp, verbose) \
> +       regulator_by_platname_autoset_and_enable(platname, devp, verbose)
> +

Can we just use the shorter name for the function and avoid this #ifdef?

> +/**
> + * regulator_by_platname_list_autoset_and_enable: setup the regulators given by
> + * list of its uclass's platform data '.name'. The setup depends on constraints
> + * found in device's uclass's platform data. The function loops with calls to:
> + * regulator_by_platname_autoset_and_enable() for each name of list.
> + *
> + * @list_platname - an array of expected strings for .name field of each
> + *                  regulator's uclass platdata
> + * @list_entries  - number of regulator's name list entries
> + * @list_devp     - an array of returned pointers to the successfully setup
> + *                  regulator devices if non-NULL passed
> + * @verbose       - (true/false) print each regulator setup info, or be quiet
> + * Returns: 0 on successfully setup of all list entries or 1 otwerwise.

otherwise

> + *
> + * The returned 'regulator' devices can be used with:
> + * - regulator_get/set_*
> + * For shorter call name, the below macro regulator_list_autoset() can be used.
> + */
> +int regulator_by_platname_list_autoset_and_enable(const char *list_platname[],
> +                                                 int list_entries,
> +                                                 struct udevice *list_devp[],
> +                                                 bool verbose);
> +
> +#define regulator_list_autoset(namelist, entries, devlist, verbose)      \
> +       regulator_by_platname_list_autoset_and_enable(namelist, entries, \
> +                                                     devlist, verbose)

As above. With #defines it changes the name in the map output or
debugger, which confuses people. An inline function is better, but in
this case it may be better to just rename the function.

> +
> +/**
> + * regulator_by_devname: returns the pointer to the pmic regulator device.
> + *                       Search by name, found in regulator device's name.
> + *
> + * @devname - expected string for 'dev->name' of regulator device
> + * @devp     - returned pointer to the regulator device
> + * Returns: 0 on success or negative value of errno.
> + *
> + * The returned 'regulator' device can be used with:
> + * - regulator_get/set_*
> + */
> +int regulator_by_devname(const char *devname, struct udevice **devp);

regulator_get_by_devname

> +
> +/**
> + * regulator_by_platname: returns the pointer to the pmic regulator device.
> + *                        Search by name, found in regulator uclass platdata.
> + *
> + * @platname - expected string for dm_regulator_uclass_platdata .name field
> + * @devp     - returned pointer to the regulator device
> + * Returns: 0 on success or negative value of errno.
> + *
> + * The returned 'regulator' device can be used with:
> + * - regulator_get/set_*
> + */
> +int regulator_by_platname(const char *platname, struct udevice **devp);

regulator_get_by_platname

> +
> +#endif /* _INCLUDE_REGULATOR_H_ */
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 06/16] dm: pmic: add pmic command
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 06/16] dm: pmic: add pmic command Przemyslaw Marczak
@ 2015-04-22 16:30         ` Simon Glass
  2015-04-22 17:09           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:30 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This is new command for the PMIC devices based on driver model PMIC API.
> Command features are unchanged:
> - list UCLASS pmic devices
> - show or [set] operating pmic device (NEW)
> - dump registers
> - read byte of register at address
> - write byte to register at address
>
> The only one change for this command is 'dev' subcommand.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes v3:
> - new file
> - add Kconfig
>
> Changes V4:
> - common/cmd_pmic.c: cleanup
> - move config name: CONFIG_DM_PMIC_CMD to CONFIG_CMD_PMIC
> ---
>  common/Kconfig    |  14 ++++
>  common/Makefile   |   3 +
>  common/cmd_pmic.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 248 insertions(+)
>  create mode 100644 common/cmd_pmic.c

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command Przemyslaw Marczak
@ 2015-04-22 16:30         ` Simon Glass
  2015-04-22 17:09           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  0 siblings, 2 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:30 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This command is based on driver model regulator's API.
> The user interface provides:
> - list UCLASS regulator devices
> - show or [set] operating regulator device
> - print constraints info
> - print operating status
> - print/[set] voltage value [uV] (force)
> - print/[set] current value [uA]
> - print/[set] operating mode id
> - enable the regulator output
> - disable the regulator output
>
> The 'force' option can be used for setting the value which exceeds
> the constraints min/max limits.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes v3:
> - new file
> - Kconfig entry
>
> Changes V4:
> - cmd regulator: move platdata to uc pdata
> - cmd_regulator: includes cleanup
> - cmd_regulator: add get_curr_dev_and_pl() check type
> - move config name: CONFIG_DM_REGULATOR_CMD to CONFIG_CMD_REGULATOR
> - common/Kconfig - cleanup
> ---
>  common/Kconfig         |  22 +++
>  common/Makefile        |   1 +
>  common/cmd_regulator.c | 403 +++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 426 insertions(+)
>  create mode 100644 common/cmd_regulator.c

Acked-by: Simon Glass <sjg@chromium.org>

I have a few nits that could be dealt with by a follow-on patch.

>
> diff --git a/common/Kconfig b/common/Kconfig
> index 4666f8e..52f8bb1 100644
> --- a/common/Kconfig
> +++ b/common/Kconfig
> @@ -470,5 +470,27 @@ config CMD_PMIC
>           - pmic read address  - read byte of register at address
>           - pmic write address - write byte to register at address
>           The only one change for this command is 'dev' subcommand.
> +
> +config CMD_REGULATOR
> +       bool "Enable Driver Model REGULATOR command"
> +       depends on DM_REGULATOR
> +       help
> +         This command is based on driver model regulator's API.
> +         User interface features:
> +         - list               - list regulator devices
> +         - regulator dev <id> - show or [set] operating regulator device
> +         - regulator info     - print constraints info
> +         - regulator status   - print operating status
> +         - regulator value <val] <-f> - print/[set] voltage value [uV]
> +         - regulator current <val>    - print/[set] current value [uA]
> +         - regulator mode <id>        - print/[set] operating mode id
> +         - regulator enable           - enable the regulator output
> +         - regulator disable          - disable the regulator output
> +
> +         The '-f' (force) option can be used for set the value which exceeds
> +         the limits, which are found in device-tree and are kept in regulator's
> +         uclass platdata structure.
> +
>  endmenu
> +
>  endmenu
> diff --git a/common/Makefile b/common/Makefile
> index 87a3efe..93bded3 100644
> --- a/common/Makefile
> +++ b/common/Makefile
> @@ -212,6 +212,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
>
>  # Power
>  obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o
> +obj-$(CONFIG_CMD_REGULATOR) += cmd_regulator.o
>  endif
>
>  ifdef CONFIG_SPL_BUILD
> diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
> new file mode 100644
> index 0000000..b1b9e87
> --- /dev/null
> +++ b/common/cmd_regulator.c
> @@ -0,0 +1,403 @@
> +/*
> + * Copyright (C) 2014-2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +#include <common.h>
> +#include <errno.h>
> +#include <dm.h>
> +#include <dm/uclass-internal.h>
> +#include <power/regulator.h>
> +
> +#define LIMIT_SEQ      3
> +#define LIMIT_DEVNAME  20
> +#define LIMIT_OFNAME   20
> +#define LIMIT_INFO     16
> +
> +static struct udevice *currdev;
> +
> +static int failed(const char *getset, const char *thing,
> +                 const char *for_dev, int ret)
> +{
> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing, for_dev,
> +                                                   ret, errno_str(ret));

blank line here.

I worry that if someone gets one of these messages they will not be
able to find it in the source code. How about passing in the full
printf() string in each case, or just using printf() in situ? I don't
think the code space saving is significant.

> +       return CMD_RET_FAILURE;
> +}
> +
> +static int regulator_get(bool list_only, int get_seq, struct udevice **devp)

This function seems to do multiple things (find and list). Should we
split it into two?

> +{
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       struct udevice *dev;
> +       int ret;
> +
> +       if (devp)
> +               *devp = NULL;
> +
> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
> +            ret = uclass_next_device(&dev)) {

This will probe all regulators that it checks. I think it should avoid
that. Do you mean to use

> +               if (list_only) {
> +                       uc_pdata = dev_get_uclass_platdata(dev);
> +                       printf("|%*d | %*.*s @ %-*.*s| %s @ %s\n",
> +                              LIMIT_SEQ, dev->seq,
> +                              LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
> +                              LIMIT_OFNAME, LIMIT_OFNAME, uc_pdata->name,
> +                              dev->parent->name,
> +                              dev_get_uclass_name(dev->parent));
> +                       continue;
> +               }
> +
> +               if (dev->seq == get_seq) {
> +                       if (devp)
> +                               *devp = dev;
> +                       else
> +                               return -EINVAL;
> +
> +                       return 0;
> +               }
> +       }
> +
> +       if (list_only)
> +               return ret;
> +
> +       return -ENODEV;
> +}
> +
> +static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       int seq, ret = -ENXIO;
> +
> +       switch (argc) {
> +       case 2:
> +               seq = simple_strtoul(argv[1], NULL, 0);
> +               ret = uclass_get_device_by_seq(UCLASS_REGULATOR, seq, &currdev);
> +               if (ret && (ret = regulator_get(false, seq, &currdev)))
> +                       goto failed;
> +       case 1:
> +               uc_pdata = dev_get_uclass_platdata(currdev);
> +               if (!uc_pdata)
> +                       goto failed;
> +
> +               printf("dev: %d @ %s\n", currdev->seq, uc_pdata->name);
> +       }
> +
> +       return CMD_RET_SUCCESS;
> +failed:
> +       return failed("get", "the", "device", ret);
> +}
> +
> +static int get_curr_dev_and_pl(struct udevice **devp,

What is pl? The name does not seem very meaningful to me.

> +                              struct dm_regulator_uclass_platdata **uc_pdata,
> +                              bool allow_type_fixed)
> +{
> +       *devp = NULL;
> +       *uc_pdata = NULL;
> +
> +       if (!currdev)
> +               return failed("get", "current", "device", -ENODEV);
> +
> +       *devp = currdev;
> +
> +       *uc_pdata = dev_get_uclass_platdata(*devp);
> +       if (!*uc_pdata)
> +               return failed("get", "regulator", "platdata", -ENXIO);
> +
> +       if (!allow_type_fixed && (*uc_pdata)->type == REGULATOR_TYPE_FIXED) {
> +               printf("Operation not allowed for fixed regulator!\n");
> +               return CMD_RET_FAILURE;
> +       }
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       int ret;
> +
> +       printf("|%*s | %*.*s @ %-*.*s| %s @ %s\n",
> +              LIMIT_SEQ, "Seq",
> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
> +              LIMIT_OFNAME, LIMIT_OFNAME, "fdtname",
> +              "Parent", "uclass");
> +
> +       ret = regulator_get(true, 0, NULL);
> +       if (ret)
> +               return CMD_RET_FAILURE;
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int constraint(const char *name, int val, const char *val_name)
> +{
> +       printf("%-*s", LIMIT_INFO, name);
> +       if (val < 0) {
> +               printf(" %s (err: %d)\n", errno_str(val), val);
> +               return val;
> +       }
> +
> +       if (val_name)
> +               printf(" %d (%s)\n", val, val_name);
> +       else
> +               printf(" %d\n", val);
> +
> +       return 0;
> +}
> +
> +static const char *get_mode_name(struct dm_regulator_mode *mode,
> +                                int mode_count,
> +                                int mode_id)
> +{
> +       while (mode_count--) {
> +               if (mode->id == mode_id)
> +                       return mode->name;
> +               mode++;
> +       }
> +
> +       return NULL;
> +}
> +
> +static int do_info(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       struct dm_regulator_mode *modes;
> +       const char *parent_uc;
> +       int mode_count;
> +       int ret;
> +       int i;
> +
> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
> +       if (ret)
> +               return ret;
> +
> +       parent_uc = dev_get_uclass_name(dev->parent);
> +
> +       printf("Uclass regulator dev %d info:\n", dev->seq);
> +       printf("%-*s %s @ %s\n%-*s %s\n%-*s %s\n%-*s\n",
> +              LIMIT_INFO, "* parent:", dev->parent->name, parent_uc,
> +              LIMIT_INFO, "* dev name:", dev->name,
> +              LIMIT_INFO, "* fdt name:", uc_pdata->name,
> +              LIMIT_INFO, "* constraints:");
> +
> +       constraint("  - min uV:", uc_pdata->min_uV, NULL);
> +       constraint("  - max uV:", uc_pdata->max_uV, NULL);
> +       constraint("  - min uA:", uc_pdata->min_uA, NULL);
> +       constraint("  - max uA:", uc_pdata->max_uA, NULL);
> +       constraint("  - always on:", uc_pdata->always_on,
> +                  uc_pdata->always_on ? "true" : "false");
> +       constraint("  - boot on:", uc_pdata->boot_on,
> +                  uc_pdata->boot_on ? "true" : "false");
> +
> +       mode_count = regulator_mode(dev, &modes);
> +       constraint("* op modes:", mode_count, NULL);
> +
> +       for (i = 0; i < mode_count; i++, modes++)
> +               constraint("  - mode id:", modes->id, modes->name);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       int current, value, mode, ret;
> +       const char *mode_name = NULL;
> +       struct udevice *dev;
> +       bool enabled;
> +
> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
> +       if (ret)
> +               return ret;
> +
> +       enabled = regulator_get_enable(dev);
> +       constraint(" * enable:", enabled, enabled ? "true" : "false");
> +
> +       value = regulator_get_value(dev);
> +       constraint(" * value uV:", value, NULL);
> +
> +       current = regulator_get_current(dev);
> +       constraint(" * current uA:", current, NULL);
> +
> +       mode = regulator_get_mode(dev);
> +       mode_name = get_mode_name(uc_pdata->mode, uc_pdata->mode_count, mode);
> +       constraint(" * mode id:", mode, mode_name);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_value(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       int value;
> +       int force;
> +       int ret;
> +
> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
> +       if (ret)
> +               return ret;
> +
> +       if (argc == 1) {
> +               value = regulator_get_value(dev);
> +               if (value < 0)
> +                       return failed("get", uc_pdata->name, "voltage", value);
> +
> +               printf("%d uV\n", value);
> +               return CMD_RET_SUCCESS;
> +       }
> +
> +       if (argc == 3)
> +               force = !strcmp("-f", argv[2]);
> +       else
> +               force = 0;
> +
> +       value = simple_strtoul(argv[1], NULL, 0);
> +       if ((value < uc_pdata->min_uV || value > uc_pdata->max_uV) && !force) {
> +               printf("Value exceeds regulator constraint limits\n");
> +               return CMD_RET_FAILURE;
> +       }
> +
> +       ret = regulator_set_value(dev, value);
> +       if (ret)
> +               return failed("set", uc_pdata->name, "voltage value", ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_current(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       int current;
> +       int ret;
> +
> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
> +       if (ret)
> +               return ret;
> +
> +       if (argc == 1) {
> +               current = regulator_get_current(dev);
> +               if (current < 0)
> +                       return failed("get", uc_pdata->name, "current", current);
> +
> +               printf("%d uA\n", current);
> +               return CMD_RET_SUCCESS;
> +       }
> +
> +       current = simple_strtoul(argv[1], NULL, 0);
> +       if (current < uc_pdata->min_uA || current > uc_pdata->max_uA) {
> +               printf("Current exceeds regulator constraint limits\n");
> +               return CMD_RET_FAILURE;
> +       }
> +
> +       ret = regulator_set_current(dev, current);
> +       if (ret)
> +               return failed("set", uc_pdata->name, "current value", ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       int new_mode;
> +       int mode;
> +       int ret;
> +
> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, false);
> +       if (ret)
> +               return ret;
> +
> +       if (argc == 1) {
> +               mode = regulator_get_mode(dev);
> +               if (mode < 0)
> +                       return failed("get", uc_pdata->name, "mode", mode);
> +
> +               printf("mode id: %d\n", mode);
> +               return CMD_RET_SUCCESS;
> +       }
> +
> +       new_mode = simple_strtoul(argv[1], NULL, 0);
> +
> +       ret = regulator_set_mode(dev, new_mode);
> +       if (ret)
> +               return failed("set", uc_pdata->name, "mode", ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_enable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       int ret;
> +
> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
> +       if (ret)
> +               return ret;
> +
> +       ret = regulator_set_enable(dev, true);
> +       if (ret)
> +               return failed("enable", "regulator", uc_pdata->name, ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static int do_disable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       struct udevice *dev;
> +       struct dm_regulator_uclass_platdata *uc_pdata;
> +       int ret;
> +
> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
> +       if (ret)
> +               return ret;
> +
> +       ret = regulator_set_enable(dev, false);
> +       if (ret)
> +               return failed("disable", "regulator", uc_pdata->name, ret);
> +
> +       return CMD_RET_SUCCESS;
> +}
> +
> +static cmd_tbl_t subcmd[] = {
> +       U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
> +       U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
> +       U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""),
> +       U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
> +       U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""),
> +       U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""),
> +       U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""),
> +       U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""),
> +       U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""),
> +};
> +
> +static int do_regulator(cmd_tbl_t *cmdtp, int flag, int argc,
> +                       char * const argv[])
> +{
> +       cmd_tbl_t *cmd;
> +
> +       argc--;
> +       argv++;
> +
> +       cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
> +       if (cmd == NULL || argc > cmd->maxargs)
> +               return CMD_RET_USAGE;
> +
> +       return cmd->cmd(cmdtp, flag, argc, argv);
> +}
> +
> +U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator,
> +       "uclass operations",
> +       "list         - list UCLASS regulator devices\n"
> +       "regulator dev [id]     - show or [set] operating regulator device\n"
> +       "regulator [info]       - print constraints info\n"
> +       "regulator [status]     - print operating status\n"
> +       "regulator [value] [-f] - print/[set] voltage value [uV] (force)\n"
> +       "regulator [current]    - print/[set] current value [uA]\n"
> +       "regulator [mode_id]    - print/[set] operating mode id\n"
> +       "regulator [enable]     - enable the regulator output\n"
> +       "regulator [disable]    - disable the regulator output\n"
> +);
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 08/16] pmic: max77686 set the same compatible as in the kernel
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 08/16] pmic: max77686 set the same compatible as in the kernel Przemyslaw Marczak
@ 2015-04-22 16:30         ` Simon Glass
  2015-04-22 17:09           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:30 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This commit also updates the proper dts files.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  arch/arm/dts/exynos4412-odroid.dts   | 2 +-
>  arch/arm/dts/exynos4412-trats2.dts   | 2 +-
>  arch/arm/dts/exynos5250-smdk5250.dts | 2 +-
>  arch/arm/dts/exynos5250-snow.dts     | 2 +-
>  lib/fdtdec.c                         | 2 +-
>  5 files changed, 5 insertions(+), 5 deletions(-)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 09/16] dm: pmic: add max77686 pmic driver
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 09/16] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
@ 2015-04-22 16:30         ` Simon Glass
  2015-04-22 17:10           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:30 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This is the implementation of driver model PMIC driver.
> The max77686 PMIC driver implements read/write operations and driver
> bind method - to bind its childs.
>
> This driver will try to bind the regulator devices by using it's child
> info array with regulator prefixes and driver names. This should succeed
> when compatible regulator driver is compiled. If no regulator driver found,
> then the pmic can still provide read/write operations, and can be used with
> PMIC function calls.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes V2:
> - add implementation of pmic read/write
> - max77686: add new operations
> - max77686: header: change PMIC_NUM_OF_REGS to MAX77686_NUM_OF_REGS
>
> Changes V3:
> - pmic/max77686.c: call pmic_child_node_scan() to bind regulator device
> - remove use of pmic platdata
> - remove unused endian conversions
> - Kconfig: add max77686 pmic entry
>
> Changes V4:
> - move DM_PMIC_MAX77686 Kconfig entry from: drivers/power/Kconfig to
>   drivers/power/pmic/Kconfig
> - pmic/max77686.c: cleanup
> - pmic/max77686.c: includes cleanup
> - max77686_pmic.h: define ldo and buck driver names
> - power/Kconfig: cleanup
> - add binding info
> ---
>  doc/device-tree-bindings/pmic/max77686.txt | 36 +++++++++++++
>  drivers/power/pmic/Kconfig                 |  7 +++
>  drivers/power/pmic/Makefile                |  1 +
>  drivers/power/pmic/max77686.c              | 87 ++++++++++++++++++++++++++++++
>  drivers/power/pmic/pmic_max77686.c         |  2 +-
>  include/power/max77686_pmic.h              | 10 +++-
>  6 files changed, 140 insertions(+), 3 deletions(-)
>  create mode 100644 doc/device-tree-bindings/pmic/max77686.txt
>  create mode 100644 drivers/power/pmic/max77686.c

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 10/16] dm: regulator: add max77686 regulator driver
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 10/16] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
@ 2015-04-22 16:31         ` Simon Glass
  2015-04-22 17:10           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:31 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This commit adds support to MAX77686 regulator driver,
> based on a driver model regulator's API. It implements
> almost all regulator operations, beside those for setting
> and geting the Current value.
> For proper bind and operation it requires the MAX77686 PMIC driver.
>
> New file: drivers/power/regulator/max77686.c
> New config: CONFIG_DM_REGULATOR_MAX77686
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes V2:
> - change debug() to error()
> - code cleanup
> - fix data types
> - ldo/buck state implementation
> - adjust to new uclass api
>
> Changes V3:
> - regulator/max77686.c:
>   -- adjust to api changes
>   -- add separeted drivers for buck and ldo
>   -- bind regulators by its compatibles
> - Kconfig: add regulator max77686 entry
>
> Changes V4:
> - move DM_REGULATOR_MAX77686 Kconfig entry from: drivers/power/Kconfig to
>   drivers/power/regulator/Kconfig
> - regulator/max77686.c: cleanup
> - regulator/max77686.c: add missing break for switch
> - regulator/max77686.c: includes cleanup
> - regulator.h: comments cleanup
> - add binding info
>
> ---
>  doc/device-tree-bindings/regulator/max77686.txt |  70 ++
>  drivers/power/Makefile                          |   1 -
>  drivers/power/regulator/Kconfig                 |   8 +
>  drivers/power/regulator/Makefile                |   1 +
>  drivers/power/regulator/max77686.c              | 825 ++++++++++++++++++++++++
>  include/power/max77686_pmic.h                   |  19 +-
>  include/power/regulator.h                       |  42 +-
>  7 files changed, 942 insertions(+), 24 deletions(-)
>  create mode 100644 doc/device-tree-bindings/regulator/max77686.txt
>  create mode 100644 drivers/power/regulator/max77686.c

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage regulator driver
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage " Przemyslaw Marczak
@ 2015-04-22 16:31         ` Simon Glass
  2015-04-22 17:10           ` Simon Glass
  2015-04-23 12:31         ` Przemyslaw Marczak
  1 sibling, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:31 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This driver implements regulator operations for fixed Voltage/Current
> value regulators. beside the standard regulator constraints, which are
> put into the uclass platform data, a typical fixed regulator node provides
> few additional properties like:
> - gpio
> - gpio-open-drain
> - enable-active-high
> - startup-delay-us
> The only 'gpio' is used by this driver and is kept in structure of type
> 'fixed_regulator_platdata', as a device platform data (dev->platdata).
>
> The driver implements:
> - get_value
> - get_current
> - get_enable
> - set_enable
>
> The regulator calls and commands can be used for fixed-regulator devices,
> and the proper error will be returned for prohibited.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>
> Changes v3:
> - new file
> - Kconfig add fixed-regulator entry
>
> Changes V4:
> - move DM_REGULATOR_FIXED Kconfig entry from: drivers/power/Kconfig to
>   drivers/power/regulator/Kconfig
> - regulator/fixed.c: adjust to use of uclass platdata and device platdata
> - regulator/fixed.c: includes cleanup
> - regulator/fixed.c: fix gpio request
> - add binding info
> ---
>  doc/device-tree-bindings/regulator/fixed.txt |  38 ++++++++
>  drivers/power/regulator/Kconfig              |   8 ++
>  drivers/power/regulator/Makefile             |   1 +
>  drivers/power/regulator/fixed.c              | 126 +++++++++++++++++++++++++++
>  4 files changed, 173 insertions(+)
>  create mode 100644 doc/device-tree-bindings/regulator/fixed.txt
>  create mode 100644 drivers/power/regulator/fixed.c

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 12/16] doc: driver-model: pmic and regulator uclass documentation
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 12/16] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
@ 2015-04-22 16:31         ` Simon Glass
  2015-04-22 17:10           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  0 siblings, 2 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:31 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Since this framework is still under the construction, the main
> documentation is kept in the header files.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes v2, V3:
> - update documentation with the framework api changes
> - remove doc file name 'dm' prefix
>
> Changes V4:
> - move the description to the headers and leave only general info
>
> ---
>  doc/driver-model/pmic-framework.txt | 142 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 142 insertions(+)
>  create mode 100644 doc/driver-model/pmic-framework.txt

Acked-by: Simon Glass <sjg@chromium.org>

>
> diff --git a/doc/driver-model/pmic-framework.txt b/doc/driver-model/pmic-framework.txt
> new file mode 100644
> index 0000000..cc82236
> --- /dev/null
> +++ b/doc/driver-model/pmic-framework.txt
> @@ -0,0 +1,142 @@
> +#
> +# (C) Copyright 2014-2015 Samsung Electronics
> +# Przemyslaw Marczak <p.marczak@samsung.com>
> +#
> +# SPDX-License-Identifier:      GPL-2.0+
> +#
> +
> +PMIC framework based on Driver Model
> +====================================
> +TOC:
> +1. Introduction
> +2. How does it work
> +3. Pmic uclass
> +4. Regulator uclass
> +
> +1. Introduction
> +===============
> +This is an introduction to driver-model multi uclass PMIC IC's support.
> +At present it's based on two uclass types:
> +- UCLASS_PMIC      - basic uclass type for PMIC I/O, which provides common
> +                     read/write interface.
> +- UCLASS_REGULATOR - additional uclass type for specific PMIC features,
> +                     which are Voltage/Current regulators.
> +
> +New files:
> +UCLASS_PMIC:
> +- drivers/power/pmic/pmic-uclass.c
> +- include/power/pmic.h
> +UCLASS_REGULATOR:
> +- drivers/power/regulator/regulator-uclass.c
> +- include/power/regulator.h
> +
> +Commands:
> +- common/cmd_pmic.c
> +- common/cmd_regulator.c
> +
> +2. How doees it work
> +====================
> +The Power Management Integrated Circuits (PMIC) are used in embedded systems
> +to provide stable, precise and specific voltage power source with over-voltage
> +and thermal protection circuits.
> +
> +The single PMIC can provide various functions by single or multiple interfaces,
> +like in the example below.
> +
> +-- SoC
> + |
> + |            ______________________________________
> + | BUS 0     |       Multi interface PMIC IC        |--> LDO out 1
> + | e.g.I2C0  |                                      |--> LDO out N
> + |-----------|---- PMIC device 0 (READ/WRITE ops)   |
> + | or SPI0   |    |_ REGULATOR device (ldo/... ops) |--> BUCK out 1
> + |           |    |_ CHARGER device (charger ops)   |--> BUCK out M
> + |           |    |_ MUIC device (microUSB con ops) |
> + | BUS 1     |    |_ ...                            |---> BATTERY
> + | e.g.I2C1  |                                      |
> + |-----------|---- PMIC device 1 (READ/WRITE ops)   |---> USB in 1
> + . or SPI1   |    |_ RTC device (rtc ops)           |---> USB in 2
> + .           |______________________________________|---> USB out
> + .
> +
> +Since U-Boot provides driver model features for I2C and SPI bus drivers,
> +the PMIC devices should also support this. By the pmic and regulator API's,
> +PMIC drivers can simply provide a common functions, for multi-interface and
> +and multi-instance device support.
> +
> +Basic design assumptions:
> +
> +- Common I/O API - UCLASS_PMIC
> +For the multi-function PMIC devices, this can be used as parent I/O device
> +for each IC's interface. Then, each children uses the same dev for read/write.
> +
> +- Common regulator API - UCLASS_REGULATOR
> +For driving the regulator attributes, auto setting function or command line
> +interface, based on kernel-style regulator device tree constraints.
> +
> +For simple implementations, regulator drivers are not required, so the code can
> +use pmic read/write directly.
> +
> +3. Pmic uclass
> +==============
> +The basic informations:
> +* Uclass:   'UCLASS_PMIC'
> +* Header:   'include/power/pmic.h'
> +* Core:     'drivers/power/pmic/pmic-uclass.c'
> +  config:   'CONFIG_DM_PMIC'
> +* Command:  'common/cmd_pmic.c'
> +  config:   'CONFIG_CMD_PMIC'
> +* Example:  'drivers/power/pmic/max77686.c'
> +
> +This is still under the construction. So for the API description, please refer
> +to the header file.
> +
> +As an example of the pmic driver, please refer to the MAX77686 driver.
> +
> +Please pay attention for the driver's '.bind' method. Exactly the function call:

I think 'driver's bind() method' is better than a quoted '.bind'.

> +'pmic_bind_childs()', which is used to bind the regulators by using the array of
> +regulator's node, compatible prefixes.
> +
> +The 'pmic; command also supports the new API. So the pmic command can be enabled
> +by adding CONFIG_CMD_PMIC.
> +The new pmic command allows to:
> +- list pmic devices
> +- choose the current device (like the mmc command)
> +- read or write the pmic register
> +- dump all pmic registers
> +
> +This command can use only UCLASS_PMIC devices, since this uclass is designed
> +for pmic I/O operations only.
> +
> +For more informations, please refer to the file: 'common/cmd_pmic.c'.

information

> +
> +4. Regulator uclass
> +===================
> +The basic informations:

information

> +* Uclass:  'UCLASS_REGULATOR'
> +* Header:  'include/power/regulator.h'
> +* Core:    'drivers/power/regulator/regulator-uclass.c'
> +  config:  'CONFIG_DM_REGULATOR'
> +  binding: 'doc/device-tree-bindings/regulator/regulator.txt'
> +* Command: 'common/cmd_regulator.c'
> +  config:  'CONFIG_CMD_REGULATOR'
> +* Example: 'drivers/power/regulator/max77686.c'
> +           'drivers/power/pmic/max77686.c' (required I/O driver for the above)
> +* Example: 'drivers/power/regulator/fixed.c'
> +  config"  'CONFIG_DM_REGULATOR_FIXED'
> +
> +This is still under the construction. So for the API description, please refer
> +to the header file.

What is still under construction?

> +
> +For the example regulator driver, please refer to the MAX77686 regulator driver,
> +but this driver can't operate without pmic's example driver, which provides an
> +I/O interface for MAX77686 regulator.
> +
> +The second example is a fixed Voltage/Current regulator for a common use.
> +
> +The 'regulator' command also supports the new API. The command allow:

allows, or maybe s/allow/allows you to/

> +- list regulator devices
> +- choose the current device (like the mmc command)
> +- do all regulator-specific operations
> +
> +For more informations, please refer to the file: 'common/cmd_regulator.c'
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 13/16] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 13/16] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
@ 2015-04-22 16:31         ` Simon Glass
  2015-04-22 17:10           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  0 siblings, 2 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:31 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> In the power_init_board function call, regulator driver init is called,
> so before compile, make sure that any power framework is defined.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
>  board/samsung/common/board.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)

Acked-by: Simon Glass <sjg@chromium.org>

Would be good to sort the includes correctly (unrelated to your patch).

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 14/16] odroid: board: add support to dm pmic api
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 14/16] odroid: board: add support to dm pmic api Przemyslaw Marczak
@ 2015-04-22 16:31         ` Simon Glass
  2015-04-22 17:11           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  0 siblings, 2 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:31 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This commit change the old pmic framework calls to the new ones.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes v2:
> - remove board_init_i2c() call
> - update regulator calls
> - update headers
> - samsung/misc.c: include required header
>
> Changes v3:
> - adjust regulator calls to new api
>
> Changes V4:
> - use regulator_list_autoset() for mmc regulators
>
> ---
>  board/samsung/common/misc.c   |  1 +
>  board/samsung/odroid/odroid.c | 77 +++++++++++++++++++++++++------------------
>  2 files changed, 46 insertions(+), 32 deletions(-)

Acked-by: Simon Glass <sjg@chromium.org>

>
> diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
> index 1a77c82..f0d69d4 100644
> --- a/board/samsung/common/misc.c
> +++ b/board/samsung/common/misc.c
> @@ -16,6 +16,7 @@
>  #include <asm/arch/cpu.h>
>  #include <asm/gpio.h>
>  #include <linux/input.h>
> +#include <dm.h>
>  #include <power/pmic.h>
>  #include <mmc.h>
>
> diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
> index ae41c29..29de325 100644
> --- a/board/samsung/odroid/odroid.c
> +++ b/board/samsung/odroid/odroid.c
> @@ -12,7 +12,9 @@
>  #include <asm/arch/gpio.h>
>  #include <asm/gpio.h>
>  #include <asm/arch/cpu.h>
> +#include <dm.h>
>  #include <power/pmic.h>
> +#include <power/regulator.h>
>  #include <power/max77686_pmic.h>
>  #include <errno.h>
>  #include <mmc.h>
> @@ -31,6 +33,12 @@ enum {
>         ODROID_TYPES,
>  };
>
> +static const char *mmc_regulators[] = {
> +       "VDDQ_EMMC_1.8V",
> +       "VDDQ_EMMC_2.8V",
> +       "TFLASH_2.8V",

I wonder if it would be better to terminate with NULL instead of
requiring an ARRAY_SIZE?

> +};
> +
>  void set_board_type(void)
>  {
>         /* Set GPA1 pin 1 to HI - enable XCL205 output */
> @@ -403,21 +411,6 @@ static void board_gpio_init(void)
>  #endif
>  }
>
> -static int pmic_init_max77686(void)
> -{
> -       struct pmic *p = pmic_get("MAX77686_PMIC");
> -
> -       if (pmic_probe(p))
> -               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 */
> -
> -       return 0;
> -}
> -
>  int exynos_early_init_f(void)
>  {
>         board_clock_init();
> @@ -434,8 +427,10 @@ int exynos_init(void)
>
>  int exynos_power_init(void)
>  {
> -       pmic_init(0);
> -       pmic_init_max77686();
> +       int list_count = ARRAY_SIZE(mmc_regulators);
> +
> +       if (regulator_list_autoset(mmc_regulators, list_count, NULL, true))
> +               error("Unable to init all mmc regulators");
>
>         return 0;
>  }
> @@ -443,19 +438,20 @@ int exynos_power_init(void)
>  #ifdef CONFIG_USB_GADGET
>  static int s5pc210_phy_control(int on)
>  {
> -       struct pmic *p_pmic;
> +       struct udevice *dev;
> +       int ret;
>
> -       p_pmic = pmic_get("MAX77686_PMIC");
> -       if (!p_pmic)
> -               return -ENODEV;
> -
> -       if (pmic_probe(p_pmic))
> -               return -1;
> +       ret = regulator_by_platname("VDD_UOTG_3.0V", &dev);
> +       if (ret) {
> +               error("Regulator get error: %d", ret);
> +               return ret;
> +       }
>
>         if (on)
> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
> +               return regulator_set_mode(dev, OPMODE_ON);
>         else
> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
> +               return regulator_set_mode(dev, OPMODE_LPM);
> +
>  }
>
>  struct s3c_plat_otg_data s5pc210_otg_data = {
> @@ -472,7 +468,8 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
>  int board_usb_init(int index, enum usb_init_type init)
>  {
>  #ifdef CONFIG_CMD_USB
> -       struct pmic *p_pmic;
> +       struct udevice *dev;
> +       int ret;
>
>         /* Set Ref freq 0 => 24MHz, 1 => 26MHz*/
>         /* Odroid Us have it at 24MHz, Odroid Xs at 26MHz */
> @@ -490,14 +487,30 @@ int board_usb_init(int index, enum usb_init_type init)
>         /* Power off and on BUCK8 for LAN9730 */
>         debug("LAN9730 - Turning power buck 8 OFF and ON.\n");
>
> -       p_pmic = pmic_get("MAX77686_PMIC");
> -       if (p_pmic && !pmic_probe(p_pmic)) {
> -               max77686_set_buck_voltage(p_pmic, 8, 750000);
> -               max77686_set_buck_voltage(p_pmic, 8, 3300000);
> +       ret = regulator_by_platname("VCC_P3V3_2.85V", &dev);
> +       if (ret) {
> +               error("Regulator get error: %d", ret);
> +               return ret;
>         }
>
> -#endif
> +       ret = regulator_set_enable(dev, true);
> +       if (ret) {
> +               error("Regulator %s enable setting error: %d", dev->name, ret);
> +               return ret;
> +       }
>
> +       ret = regulator_set_value(dev, 750000);
> +       if (ret) {
> +               error("Regulator %s value setting error: %d", dev->name, ret);
> +               return ret;
> +       }
> +
> +       ret = regulator_set_value(dev, 3300000);
> +       if (ret) {
> +               error("Regulator %s value setting error: %d", dev->name, ret);
> +               return ret;
> +       }
> +#endif
>         debug("USB_udc_probe\n");
>         return s3c_udc_probe(&s5pc210_otg_data);
>  }
> --
> 1.9.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 15/16] odroid: dts: add 'voltage-regulators' description to max77686 node
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 15/16] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
@ 2015-04-22 16:31         ` Simon Glass
  2015-04-22 17:11           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:31 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Adding regulators subnode to fdt max77686 node, allows properly init
> regulators by the max77686 regulator driver. This enables the complete
> functionality of the regulator command.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes V2:
> - odroid: dts: remove pmic alias
>
> Changes V3:
> - none
>
> Changes V4:
> - add constraints for mmc regulators auto enable
>
> ---
>  arch/arm/dts/exynos4412-odroid.dts | 253 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 253 insertions(+)
>

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 16/16] odroid: config: enable dm pmic, dm regulator and max77686 driver
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 16/16] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
@ 2015-04-22 16:31         ` Simon Glass
  2015-04-22 17:11           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:31 UTC (permalink / raw)
  To: u-boot

On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This change enables the configs required to init and setup max77686
> regulator driver, using the new driver model pmic and regulator API.
>
> This commits enables:
> - CONFIG_ERRNO_STR
> - CONFIG_DM_PMIC
> - CONFIG_DM_PMIC_CMD
> - CONFIG_DM_PMIC_MAX77686
> - CONFIG_DM_REGULATOR
> - CONFIG_DM_REGULATOR_CMD
> - CONFIG_DM_REGULATOR_MAX77686
>
> And removes the unused:
> - CONFIG_DM_I2C_COMPAT
> - CONFIG_POWER
> - CONFIG_POWER_I2C
> - CONFIG_POWER_MAX77686
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> Changes V2:
> - config: enable dm i2c; cleanup
> - remove CONFIG_DM_I2C_COMPAT
> - enable regulator command
>
> Changes V3:
> - move options to defconfig
>
> Changes V4:
> - update commands config names
>
> ---
>  configs/odroid_defconfig | 8 +++++++-
>  include/configs/odroid.h | 5 -----
>  2 files changed, 7 insertions(+), 6 deletions(-)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 05/16] dm: regulator: add implementation of driver model regulator uclass
  2015-04-22 16:30         ` Simon Glass
@ 2015-04-22 16:54           ` Simon Glass
  2015-04-22 17:09             ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  1 sibling, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-22 16:54 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:30, Simon Glass <sjg@chromium.org> wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This commit introduces the implementation of dm regulator API.
>> Device tree support allows for auto binding. And by the basic
>> uclass operations, it allows to driving the devices in a common
>> way. For detailed informations, please look into the header file.
>>
>> Core 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_REGULATOR
>> - new config: CONFIG_DM_REGULATOR
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> 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
>>
>> Changes V3:
>> - regulator-uclass.c and regulator.h:
>>   -- api cleanup
>>   -- new function regulator_ofdata_to_platdata()
>>   -- update of comments
>>   -- add Kconfig
>>
>> Changes V4:
>> - move file drivers/power/regulator-uclass.c to
>>   drivers/power/regulator/regulator-uclass.c
>> - move DM_REGULATOR Kconfig entry from: drivers/power/Kconfig to
>>   drivers/power/regulator/Kconfig
>> - drivers/power/Kconfig: include regulator Kconfig path
>> - Kconfig: provide only general informations
>> - regulator-uclass.c: cleanup
>> - regulator-uclass.c: allow init regulator with name only
>> - regulator-uclass.c: remove pmic_get_uclass_ops and use dev_get_driver_ops
>> - regulator-uclass.c: add use of uclass_get_device_by_name()
>> - regulator.h: add 'struct dm_regulator_uclass_platdata'
>> - regulator.h: API documentation cleanup
>> - regulator - add binding info
>>

Acked-by: Simon Glass <sjg@chromium.org>


>> ---
>>  Makefile                                         |   3 +-
>>  doc/device-tree-bindings/regulator/regulator.txt |  55 ++++
>>  drivers/power/Kconfig                            |   2 +
>>  drivers/power/regulator/Kconfig                  |  17 +
>>  drivers/power/regulator/Makefile                 |   8 +
>>  drivers/power/regulator/regulator-uclass.c       | 300 ++++++++++++++++++
>>  include/dm/uclass-id.h                           |   1 +
>>  include/power/regulator.h                        | 384 +++++++++++++++++++++++
>>  8 files changed, 769 insertions(+), 1 deletion(-)
>>  create mode 100644 doc/device-tree-bindings/regulator/regulator.txt
>>  create mode 100644 drivers/power/regulator/Kconfig
>>  create mode 100644 drivers/power/regulator/Makefile
>>  create mode 100644 drivers/power/regulator/regulator-uclass.c
>>  create mode 100644 include/power/regulator.h

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 01/16] exynos5: fix build break by adding CONFIG_POWER
  2015-04-22 16:29         ` Simon Glass
@ 2015-04-22 17:08           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:08 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:29, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>>
>> 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.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>  include/configs/exynos5-common.h | 4 ++++
>>  1 file changed, 4 insertions(+)
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 02/16] exynos4-common: remove the unsued CONFIG_CMD_PMIC
  2015-04-22 16:29         ` Simon Glass
@ 2015-04-22 17:09           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:09 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:29, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This config name was never used, because the present pmic command
>> was precompiled for the CONFIG_POWER.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>  include/configs/exynos4-common.h | 1 -
>>  1 file changed, 1 deletion(-)
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 03/16] lib: Kconfig: add entry for errno_str() function
  2015-04-22 16:29         ` Simon Glass
@ 2015-04-22 17:09           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:09 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:29, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>  lib/Kconfig | 8 ++++++++
>>  1 file changed, 8 insertions(+)
>
> (please make sure to always add a commit message)
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 04/16] dm: pmic: add implementation of driver model pmic uclass
  2015-04-22 16:30         ` Simon Glass
@ 2015-04-22 17:09           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:09 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:30, Simon Glass <sjg@chromium.org> wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This commit introduces the PMIC uclass implementation.
>> It allows providing the basic I/O interface for PMIC devices.
>> For the multi-function PMIC devices, this can be used as I/O
>> parent device, for each IC's interface. Then, each PMIC particular
>> function can be provided by the child device's operations, and the
>> child devices will use its parent for read/write by the common API.
>>
>> Core files:
>> - 'include/power/pmic.h'
>> - 'drivers/power/pmic/pmic-uclass.c'
>>
>> The old pmic framework is still kept and is independent.
>>
>> For more detailed informations, please look into the header file.
>>
>> Changes:
>> - new uclass-id: UCLASS_PMIC
>> - new config: CONFIG_DM_PMIC
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> 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
>>
>> Changes V3:
>> - pmic-uclass.c and pmic.h:
>>   -- remove  pmic_if_* functions
>>   -- add new function pmic_child_node_scan()
>> - add Kconfig entry
>>
>> Changes V4:
>> - move drivers/power/pmic-uclass.c to drivers/power/pmic/pmic-uclass.c
>> - move DM_PMIC Kconfig entry: drivers/power/Kconfig to drivers/power/pmic/Kconfig
>> - drivers/power/Kconfig: Add menu "Power" and include pmic Kconfig
>> - Kconfig: provide only the general information about the PMIC
>> - pmic-uclass.c: add pmic_bind_childs()
>> - pmic-uclass.c: add debug
>> - pmic-uclass.c: cleanup includes
>> - pmic-uclass.c: remove pmic_get_uclass_ops() and use of dev_get_driver_ops()
>> - pmic-uclass.c: use of uclass_get_device_by_name()
>> - pmic-uclass.c: add str_get_num() - for get number from string
>> - include/power/pmic.h - start comments rewording
>> - power/pmic.h: comments update
>> ---
>>  drivers/power/Kconfig            |   6 ++
>>  drivers/power/pmic/Kconfig       |  11 +++
>>  drivers/power/pmic/Makefile      |   1 +
>>  drivers/power/pmic/pmic-uclass.c | 158 ++++++++++++++++++++++++++++++++
>>  include/dm/uclass-id.h           |   3 +
>>  include/power/pmic.h             | 189 +++++++++++++++++++++++++++++++++++++++
>>  6 files changed, 368 insertions(+)
>>  create mode 100644 drivers/power/pmic/Kconfig
>>  create mode 100644 drivers/power/pmic/pmic-uclass.c
>
> Acked-by: Simon Glass <sjg@chromium.org>
>
> I have a few nits below - perhaps they can be targeted in a follow-up
> patch or two? I'd like to merge this soon and it is not worth holding
> up the series for nits.

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 05/16] dm: regulator: add implementation of driver model regulator uclass
  2015-04-22 16:54           ` Simon Glass
@ 2015-04-22 17:09             ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:09 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:54, Simon Glass <sjg@chromium.org> wrote:
> On 22 April 2015 at 10:30, Simon Glass <sjg@chromium.org> wrote:
>> Hi Przemyslaw,
>>
>> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>>> This commit introduces the implementation of dm regulator API.
>>> Device tree support allows for auto binding. And by the basic
>>> uclass operations, it allows to driving the devices in a common
>>> way. For detailed informations, please look into the header file.
>>>
>>> Core 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_REGULATOR
>>> - new config: CONFIG_DM_REGULATOR
>>>
>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>> ---
>>> 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
>>>
>>> Changes V3:
>>> - regulator-uclass.c and regulator.h:
>>>   -- api cleanup
>>>   -- new function regulator_ofdata_to_platdata()
>>>   -- update of comments
>>>   -- add Kconfig
>>>
>>> Changes V4:
>>> - move file drivers/power/regulator-uclass.c to
>>>   drivers/power/regulator/regulator-uclass.c
>>> - move DM_REGULATOR Kconfig entry from: drivers/power/Kconfig to
>>>   drivers/power/regulator/Kconfig
>>> - drivers/power/Kconfig: include regulator Kconfig path
>>> - Kconfig: provide only general informations
>>> - regulator-uclass.c: cleanup
>>> - regulator-uclass.c: allow init regulator with name only
>>> - regulator-uclass.c: remove pmic_get_uclass_ops and use dev_get_driver_ops
>>> - regulator-uclass.c: add use of uclass_get_device_by_name()
>>> - regulator.h: add 'struct dm_regulator_uclass_platdata'
>>> - regulator.h: API documentation cleanup
>>> - regulator - add binding info
>>>
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 06/16] dm: pmic: add pmic command
  2015-04-22 16:30         ` Simon Glass
@ 2015-04-22 17:09           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:09 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:30, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This is new command for the PMIC devices based on driver model PMIC API.
>> Command features are unchanged:
>> - list UCLASS pmic devices
>> - show or [set] operating pmic device (NEW)
>> - dump registers
>> - read byte of register at address
>> - write byte to register at address
>>
>> The only one change for this command is 'dev' subcommand.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes v3:
>> - new file
>> - add Kconfig
>>
>> Changes V4:
>> - common/cmd_pmic.c: cleanup
>> - move config name: CONFIG_DM_PMIC_CMD to CONFIG_CMD_PMIC
>> ---
>>  common/Kconfig    |  14 ++++
>>  common/Makefile   |   3 +
>>  common/cmd_pmic.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 248 insertions(+)
>>  create mode 100644 common/cmd_pmic.c
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command
  2015-04-22 16:30         ` Simon Glass
@ 2015-04-22 17:09           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:09 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:30, Simon Glass <sjg@chromium.org> wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This command is based on driver model regulator's API.
>> The user interface provides:
>> - list UCLASS regulator devices
>> - show or [set] operating regulator device
>> - print constraints info
>> - print operating status
>> - print/[set] voltage value [uV] (force)
>> - print/[set] current value [uA]
>> - print/[set] operating mode id
>> - enable the regulator output
>> - disable the regulator output
>>
>> The 'force' option can be used for setting the value which exceeds
>> the constraints min/max limits.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes v3:
>> - new file
>> - Kconfig entry
>>
>> Changes V4:
>> - cmd regulator: move platdata to uc pdata
>> - cmd_regulator: includes cleanup
>> - cmd_regulator: add get_curr_dev_and_pl() check type
>> - move config name: CONFIG_DM_REGULATOR_CMD to CONFIG_CMD_REGULATOR
>> - common/Kconfig - cleanup
>> ---
>>  common/Kconfig         |  22 +++
>>  common/Makefile        |   1 +
>>  common/cmd_regulator.c | 403 +++++++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 426 insertions(+)
>>  create mode 100644 common/cmd_regulator.c
>
> Acked-by: Simon Glass <sjg@chromium.org>
>
> I have a few nits that could be dealt with by a follow-on patch.

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 08/16] pmic: max77686 set the same compatible as in the kernel
  2015-04-22 16:30         ` Simon Glass
@ 2015-04-22 17:09           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:09 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:30, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This commit also updates the proper dts files.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>  arch/arm/dts/exynos4412-odroid.dts   | 2 +-
>>  arch/arm/dts/exynos4412-trats2.dts   | 2 +-
>>  arch/arm/dts/exynos5250-smdk5250.dts | 2 +-
>>  arch/arm/dts/exynos5250-snow.dts     | 2 +-
>>  lib/fdtdec.c                         | 2 +-
>>  5 files changed, 5 insertions(+), 5 deletions(-)
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 09/16] dm: pmic: add max77686 pmic driver
  2015-04-22 16:30         ` Simon Glass
@ 2015-04-22 17:10           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:10 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:30, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This is the implementation of driver model PMIC driver.
>> The max77686 PMIC driver implements read/write operations and driver
>> bind method - to bind its childs.
>>
>> This driver will try to bind the regulator devices by using it's child
>> info array with regulator prefixes and driver names. This should succeed
>> when compatible regulator driver is compiled. If no regulator driver found,
>> then the pmic can still provide read/write operations, and can be used with
>> PMIC function calls.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes V2:
>> - add implementation of pmic read/write
>> - max77686: add new operations
>> - max77686: header: change PMIC_NUM_OF_REGS to MAX77686_NUM_OF_REGS
>>
>> Changes V3:
>> - pmic/max77686.c: call pmic_child_node_scan() to bind regulator device
>> - remove use of pmic platdata
>> - remove unused endian conversions
>> - Kconfig: add max77686 pmic entry
>>
>> Changes V4:
>> - move DM_PMIC_MAX77686 Kconfig entry from: drivers/power/Kconfig to
>>   drivers/power/pmic/Kconfig
>> - pmic/max77686.c: cleanup
>> - pmic/max77686.c: includes cleanup
>> - max77686_pmic.h: define ldo and buck driver names
>> - power/Kconfig: cleanup
>> - add binding info
>> ---
>>  doc/device-tree-bindings/pmic/max77686.txt | 36 +++++++++++++
>>  drivers/power/pmic/Kconfig                 |  7 +++
>>  drivers/power/pmic/Makefile                |  1 +
>>  drivers/power/pmic/max77686.c              | 87 ++++++++++++++++++++++++++++++
>>  drivers/power/pmic/pmic_max77686.c         |  2 +-
>>  include/power/max77686_pmic.h              | 10 +++-
>>  6 files changed, 140 insertions(+), 3 deletions(-)
>>  create mode 100644 doc/device-tree-bindings/pmic/max77686.txt
>>  create mode 100644 drivers/power/pmic/max77686.c
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 10/16] dm: regulator: add max77686 regulator driver
  2015-04-22 16:31         ` Simon Glass
@ 2015-04-22 17:10           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:10 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:31, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This commit adds support to MAX77686 regulator driver,
>> based on a driver model regulator's API. It implements
>> almost all regulator operations, beside those for setting
>> and geting the Current value.
>> For proper bind and operation it requires the MAX77686 PMIC driver.
>>
>> New file: drivers/power/regulator/max77686.c
>> New config: CONFIG_DM_REGULATOR_MAX77686
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes V2:
>> - change debug() to error()
>> - code cleanup
>> - fix data types
>> - ldo/buck state implementation
>> - adjust to new uclass api
>>
>> Changes V3:
>> - regulator/max77686.c:
>>   -- adjust to api changes
>>   -- add separeted drivers for buck and ldo
>>   -- bind regulators by its compatibles
>> - Kconfig: add regulator max77686 entry
>>
>> Changes V4:
>> - move DM_REGULATOR_MAX77686 Kconfig entry from: drivers/power/Kconfig to
>>   drivers/power/regulator/Kconfig
>> - regulator/max77686.c: cleanup
>> - regulator/max77686.c: add missing break for switch
>> - regulator/max77686.c: includes cleanup
>> - regulator.h: comments cleanup
>> - add binding info
>>
>> ---
>>  doc/device-tree-bindings/regulator/max77686.txt |  70 ++
>>  drivers/power/Makefile                          |   1 -
>>  drivers/power/regulator/Kconfig                 |   8 +
>>  drivers/power/regulator/Makefile                |   1 +
>>  drivers/power/regulator/max77686.c              | 825 ++++++++++++++++++++++++
>>  include/power/max77686_pmic.h                   |  19 +-
>>  include/power/regulator.h                       |  42 +-
>>  7 files changed, 942 insertions(+), 24 deletions(-)
>>  create mode 100644 doc/device-tree-bindings/regulator/max77686.txt
>>  create mode 100644 drivers/power/regulator/max77686.c
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage regulator driver
  2015-04-22 16:31         ` Simon Glass
@ 2015-04-22 17:10           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:10 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:31, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This driver implements regulator operations for fixed Voltage/Current
>> value regulators. beside the standard regulator constraints, which are
>> put into the uclass platform data, a typical fixed regulator node provides
>> few additional properties like:
>> - gpio
>> - gpio-open-drain
>> - enable-active-high
>> - startup-delay-us
>> The only 'gpio' is used by this driver and is kept in structure of type
>> 'fixed_regulator_platdata', as a device platform data (dev->platdata).
>>
>> The driver implements:
>> - get_value
>> - get_current
>> - get_enable
>> - set_enable
>>
>> The regulator calls and commands can be used for fixed-regulator devices,
>> and the proper error will be returned for prohibited.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>
>> Changes v3:
>> - new file
>> - Kconfig add fixed-regulator entry
>>
>> Changes V4:
>> - move DM_REGULATOR_FIXED Kconfig entry from: drivers/power/Kconfig to
>>   drivers/power/regulator/Kconfig
>> - regulator/fixed.c: adjust to use of uclass platdata and device platdata
>> - regulator/fixed.c: includes cleanup
>> - regulator/fixed.c: fix gpio request
>> - add binding info
>> ---
>>  doc/device-tree-bindings/regulator/fixed.txt |  38 ++++++++
>>  drivers/power/regulator/Kconfig              |   8 ++
>>  drivers/power/regulator/Makefile             |   1 +
>>  drivers/power/regulator/fixed.c              | 126 +++++++++++++++++++++++++++
>>  4 files changed, 173 insertions(+)
>>  create mode 100644 doc/device-tree-bindings/regulator/fixed.txt
>>  create mode 100644 drivers/power/regulator/fixed.c
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 12/16] doc: driver-model: pmic and regulator uclass documentation
  2015-04-22 16:31         ` Simon Glass
@ 2015-04-22 17:10           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:10 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:31, Simon Glass <sjg@chromium.org> wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Since this framework is still under the construction, the main
>> documentation is kept in the header files.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes v2, V3:
>> - update documentation with the framework api changes
>> - remove doc file name 'dm' prefix
>>
>> Changes V4:
>> - move the description to the headers and leave only general info
>>
>> ---
>>  doc/driver-model/pmic-framework.txt | 142 ++++++++++++++++++++++++++++++++++++
>>  1 file changed, 142 insertions(+)
>>  create mode 100644 doc/driver-model/pmic-framework.txt
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 13/16] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
  2015-04-22 16:31         ` Simon Glass
@ 2015-04-22 17:10           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:10 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:31, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> In the power_init_board function call, regulator driver init is called,
>> so before compile, make sure that any power framework is defined.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>  board/samsung/common/board.c | 4 ++--
>>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> Acked-by: Simon Glass <sjg@chromium.org>
>
> Would be good to sort the includes correctly (unrelated to your patch).

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 14/16] odroid: board: add support to dm pmic api
  2015-04-22 16:31         ` Simon Glass
@ 2015-04-22 17:11           ` Simon Glass
  2015-04-23 11:33           ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:11 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:31, Simon Glass <sjg@chromium.org> wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This commit change the old pmic framework calls to the new ones.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes v2:
>> - remove board_init_i2c() call
>> - update regulator calls
>> - update headers
>> - samsung/misc.c: include required header
>>
>> Changes v3:
>> - adjust regulator calls to new api
>>
>> Changes V4:
>> - use regulator_list_autoset() for mmc regulators
>>
>> ---
>>  board/samsung/common/misc.c   |  1 +
>>  board/samsung/odroid/odroid.c | 77 +++++++++++++++++++++++++------------------
>>  2 files changed, 46 insertions(+), 32 deletions(-)
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 15/16] odroid: dts: add 'voltage-regulators' description to max77686 node
  2015-04-22 16:31         ` Simon Glass
@ 2015-04-22 17:11           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:11 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:31, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Adding regulators subnode to fdt max77686 node, allows properly init
>> regulators by the max77686 regulator driver. This enables the complete
>> functionality of the regulator command.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes V2:
>> - odroid: dts: remove pmic alias
>>
>> Changes V3:
>> - none
>>
>> Changes V4:
>> - add constraints for mmc regulators auto enable
>>
>> ---
>>  arch/arm/dts/exynos4412-odroid.dts | 253 +++++++++++++++++++++++++++++++++++++
>>  1 file changed, 253 insertions(+)
>>
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 16/16] odroid: config: enable dm pmic, dm regulator and max77686 driver
  2015-04-22 16:31         ` Simon Glass
@ 2015-04-22 17:11           ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-22 17:11 UTC (permalink / raw)
  To: u-boot

On 22 April 2015 at 10:31, Simon Glass <sjg@chromium.org> wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This change enables the configs required to init and setup max77686
>> regulator driver, using the new driver model pmic and regulator API.
>>
>> This commits enables:
>> - CONFIG_ERRNO_STR
>> - CONFIG_DM_PMIC
>> - CONFIG_DM_PMIC_CMD
>> - CONFIG_DM_PMIC_MAX77686
>> - CONFIG_DM_REGULATOR
>> - CONFIG_DM_REGULATOR_CMD
>> - CONFIG_DM_REGULATOR_MAX77686
>>
>> And removes the unused:
>> - CONFIG_DM_I2C_COMPAT
>> - CONFIG_POWER
>> - CONFIG_POWER_I2C
>> - CONFIG_POWER_MAX77686
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes V2:
>> - config: enable dm i2c; cleanup
>> - remove CONFIG_DM_I2C_COMPAT
>> - enable regulator command
>>
>> Changes V3:
>> - move options to defconfig
>>
>> Changes V4:
>> - update commands config names
>>
>> ---
>>  configs/odroid_defconfig | 8 +++++++-
>>  include/configs/odroid.h | 5 -----
>>  2 files changed, 7 insertions(+), 6 deletions(-)
>
> Acked-by: Simon Glass <sjg@chromium.org>

Applied to u-boot-dm/next, thanks!

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 04/16] dm: pmic: add implementation of driver model pmic uclass
  2015-04-22 16:30         ` Simon Glass
  2015-04-22 17:09           ` Simon Glass
@ 2015-04-23 11:33           ` Przemyslaw Marczak
  2015-04-24  4:51             ` Simon Glass
  1 sibling, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-23 11:33 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/22/2015 06:30 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This commit introduces the PMIC uclass implementation.
>> It allows providing the basic I/O interface for PMIC devices.
>> For the multi-function PMIC devices, this can be used as I/O
>> parent device, for each IC's interface. Then, each PMIC particular
>> function can be provided by the child device's operations, and the
>> child devices will use its parent for read/write by the common API.
>>
>> Core files:
>> - 'include/power/pmic.h'
>> - 'drivers/power/pmic/pmic-uclass.c'
>>
>> The old pmic framework is still kept and is independent.
>>
>> For more detailed informations, please look into the header file.
>>
>> Changes:
>> - new uclass-id: UCLASS_PMIC
>> - new config: CONFIG_DM_PMIC
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> 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
>>
>> Changes V3:
>> - pmic-uclass.c and pmic.h:
>>    -- remove  pmic_if_* functions
>>    -- add new function pmic_child_node_scan()
>> - add Kconfig entry
>>
>> Changes V4:
>> - move drivers/power/pmic-uclass.c to drivers/power/pmic/pmic-uclass.c
>> - move DM_PMIC Kconfig entry: drivers/power/Kconfig to drivers/power/pmic/Kconfig
>> - drivers/power/Kconfig: Add menu "Power" and include pmic Kconfig
>> - Kconfig: provide only the general information about the PMIC
>> - pmic-uclass.c: add pmic_bind_childs()
>> - pmic-uclass.c: add debug
>> - pmic-uclass.c: cleanup includes
>> - pmic-uclass.c: remove pmic_get_uclass_ops() and use of dev_get_driver_ops()
>> - pmic-uclass.c: use of uclass_get_device_by_name()
>> - pmic-uclass.c: add str_get_num() - for get number from string
>> - include/power/pmic.h - start comments rewording
>> - power/pmic.h: comments update
>> ---
>>   drivers/power/Kconfig            |   6 ++
>>   drivers/power/pmic/Kconfig       |  11 +++
>>   drivers/power/pmic/Makefile      |   1 +
>>   drivers/power/pmic/pmic-uclass.c | 158 ++++++++++++++++++++++++++++++++
>>   include/dm/uclass-id.h           |   3 +
>>   include/power/pmic.h             | 189 +++++++++++++++++++++++++++++++++++++++
>>   6 files changed, 368 insertions(+)
>>   create mode 100644 drivers/power/pmic/Kconfig
>>   create mode 100644 drivers/power/pmic/pmic-uclass.c
>
> Acked-by: Simon Glass <sjg@chromium.org>
>
> I have a few nits below - perhaps they can be targeted in a follow-up
> patch or two? I'd like to merge this soon and it is not worth holding
> up the series for nits.
>

That's good information. I will fix it all and resend ASAP.

>>
>> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
>> index f8f0239..d03626e 100644
>> --- a/drivers/power/Kconfig
>> +++ b/drivers/power/Kconfig
>> @@ -1,3 +1,7 @@
>> +menu "Power"
>> +
>> +source "drivers/power/pmic/Kconfig"
>> +
>>   config AXP221_POWER
>>          boolean "axp221 / axp223 pmic support"
>>          depends on MACH_SUN6I || MACH_SUN8I
>> @@ -73,3 +77,5 @@ config AXP221_ELDO3_VOLT
>>          disable eldo3. On some A31(s) tablets it might be used to supply
>>          1.2V for the SSD2828 chip (converter of parallel LCD interface
>>          into MIPI DSI).
>> +
>> +endmenu
>> diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
>> new file mode 100644
>> index 0000000..d06d632
>> --- /dev/null
>> +++ b/drivers/power/pmic/Kconfig
>> @@ -0,0 +1,11 @@
>> +config DM_PMIC
>> +       bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC)"
>> +       depends on DM
>> +       ---help---
>> +       This config enables the driver-model PMIC support.
>> +       UCLASS_PMIC - designed to provide an I/O interface for PMIC devices.
>> +       For the multi-function PMIC devices, this can be used as parent I/O
>> +       device for each IC's interface. Then, each children uses its parent
>> +       for read/write. For detailed description, please refer to the files:
>> +       - 'drivers/power/pmic/pmic-uclass.c'
>> +       - 'include/power/pmic.h'
>> diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
>> index 985cfdb..594f620 100644
>> --- a/drivers/power/pmic/Makefile
>> +++ b/drivers/power/pmic/Makefile
>> @@ -5,6 +5,7 @@
>>   # SPDX-License-Identifier:     GPL-2.0+
>>   #
>>
>> +obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
>>   obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o
>>   obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o
>>   obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o
>> diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c
>> new file mode 100644
>> index 0000000..d82d3da
>> --- /dev/null
>> +++ b/drivers/power/pmic/pmic-uclass.c
>> @@ -0,0 +1,158 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#include <common.h>
>> +#include <fdtdec.h>
>> +#include <errno.h>
>> +#include <dm.h>
>> +#include <dm/lists.h>
>> +#include <dm/device-internal.h>
>> +#include <dm/uclass-internal.h>
>> +#include <power/pmic.h>
>> +#include <linux/ctype.h>
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +static ulong str_get_num(const char *ptr, const char *maxptr)
>> +{
>> +       if (!ptr || !maxptr)
>> +               return 0;
>> +
>> +       while (!isdigit(*ptr) && ptr++ < maxptr);
>> +
>> +       return simple_strtoul(ptr, NULL, 0);
>> +}
>> +
>> +int pmic_bind_childs(struct udevice *pmic, int offset,
>> +                    const struct pmic_child_info *child_info)
>> +{
>> +       const struct pmic_child_info *info;
>> +       const void *blob = gd->fdt_blob;
>> +       struct driver *drv;
>> +       struct udevice *child;
>> +       const char *node_name;
>> +       int node_name_len;
>> +       int bind_count = 0;
>> +       int node;
>> +       int prefix_len;
>> +       int ret;
>> +
>> +       debug("%s for '%s' at node offset: %d\n", __func__, pmic->name,
>> +             pmic->of_offset);
>> +
>> +       for (node = fdt_first_subnode(blob, offset);
>> +            node > 0;
>> +            node = fdt_next_subnode(blob, node)) {
>> +               node_name = fdt_get_name(blob, node, &node_name_len);
>> +
>> +               debug("* Found child node: '%s' at offset:%d\n", node_name,
>> +                                                                node);
>> +
>> +               child = NULL;
>> +               info = child_info;
>> +               while (info->prefix) {
>> +                       prefix_len = strlen(info->prefix);
>> +                       if (strncasecmp(info->prefix, node_name, prefix_len) ||
>> +                           !info->driver) {
>> +                               info++;
>> +                               continue;
>> +                       }
>> +
>> +                       debug("  - compatible prefix: '%s'\n", info->prefix);
>> +
>> +                       drv = lists_driver_lookup_name(info->driver);
>> +                       if (!drv) {
>> +                               debug("  - driver: '%s' not found!\n",
>> +                                     info->driver);
>> +                               continue;
>> +                       }
>> +
>> +                       debug("  - found child driver: '%s'\n", drv->name);
>> +
>> +                       ret = device_bind(pmic, drv, node_name, NULL,
>> +                                         node, &child);
>> +                       if (ret) {
>> +                               debug("  - child binding error: %d\n", ret);
>> +                               continue;
>> +                       }
>> +
>> +                       debug("  - bound child device: '%s'\n", child->name);
>> +
>> +                       child->driver_data = str_get_num(node_name +
>> +                                                        prefix_len,
>> +                                                        node_name +
>> +                                                        node_name_len);
>> +
>> +                       debug("  - set 'child->driver_data': %lu\n",
>> +                             child->driver_data);
>> +                       break;
>> +               }
>> +
>> +               if (child)
>> +                       bind_count++;
>> +               else
>> +                       debug("  - compatible prefix not found\n");
>> +       }
>> +
>> +       debug("Bound: %d childs for PMIC: '%s'\n", bind_count, pmic->name);
>> +       return bind_count;
>> +}
>> +
>> +int pmic_get(const char *name, struct udevice **devp)
>> +{
>> +       return uclass_get_device_by_name(UCLASS_PMIC, name, devp);
>> +}
>> +
>> +int pmic_reg_count(struct udevice *dev)
>> +{
>> +       const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
>
> blank line here
>
>> +       if (!ops)
>> +               return -ENOSYS;
>> +
>> +       return ops->reg_count;
>> +}
>> +
>> +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len)
>> +{
>> +       const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
>> +       int ret;
>> +
>> +       if (!buffer)
>> +               return -EFAULT;
>> +
>> +       if (!ops || !ops->read)
>> +               return -ENOSYS;
>> +
>> +       ret = ops->read(dev, reg, buffer, len);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return 0;
>> +}
>> +
>> +int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len)
>> +{
>> +       const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
>> +       int ret;
>> +
>> +       if (!buffer)
>> +               return -EFAULT;
>> +
>> +       if (!ops || !ops->write)
>> +               return -ENOSYS;
>> +
>> +       ret = ops->write(dev, reg, buffer, len);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return 0;
>> +}
>> +
>> +UCLASS_DRIVER(pmic) = {
>> +       .id             = UCLASS_PMIC,
>> +       .name           = "pmic",
>> +};
>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>> index fddfd35..23b3eb9 100644
>> --- a/include/dm/uclass-id.h
>> +++ b/include/dm/uclass-id.h
>> @@ -46,6 +46,9 @@ enum uclass_id {
>>          UCLASS_USB_DEV_GENERIC, /* USB generic device */
>>          UCLASS_MASS_STORAGE,    /* Mass storage device */
>>
>> +       /* Power Management */
>> +       UCLASS_PMIC,            /* PMIC I/O device */
>> +
>>          UCLASS_COUNT,
>>          UCLASS_INVALID = -1,
>>   };
>> diff --git a/include/power/pmic.h b/include/power/pmic.h
>> index afbc5aa..f7ae781 100644
>> --- a/include/power/pmic.h
>> +++ b/include/power/pmic.h
>> @@ -1,4 +1,7 @@
>>   /*
>> + *  Copyright (C) 2014-2015 Samsung Electronics
>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>>    *  Copyright (C) 2011-2012 Samsung Electronics
>>    *  Lukasz Majewski <l.majewski@samsung.com>
>>    *
>> @@ -9,10 +12,13 @@
>>   #define __CORE_PMIC_H_
>>
>>   #include <linux/list.h>
>> +#include <spi.h>
>>   #include <i2c.h>
>>   #include <power/power_chrg.h>
>
> At some point can you update the ordering of this lot? I think it should be:
>
> i2c.g
> spi.h
> linux/
> power/
>
>>
>>   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,189 @@ struct pmic {
>>          struct pmic *parent;
>>          struct list_head list;
>>   };
>> +#endif /* CONFIG_POWER */
>> +
>> +#ifdef CONFIG_DM_PMIC
>> +/**
>> + * U-Boot PMIC Framework
>> + * =====================
>> + *
>> + * UCLASS_PMIC - The is designed to provide an I/O interface for PMIC devices.
>
> This is designed
>
>> + *
>> + * For the multi-function PMIC devices, this can be used as parent I/O device
>> + * for each IC's interface. Then, each children uses its parent for read/write.
>
> each child
>
>> + *
>> + * The driver model tree could look 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_RTC     (in the future)
>> + *
>> + * We can find two PMIC cases in boards design:
>> + * - single I/O interface
>> + * - multiple I/O interfaces
>> + * We bind single PMIC device for each interface, to provide an I/O as a parent,
>
> a single
>
>> + * of proper child devices. Each child usually implements a different function,
>> + * controlled by the same interface.
>> + *
>> + * The binding should be done automatically. If 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 device nodes, for multi-interface PMIC.
>> + *
>> + * Note:
>> + * Each PMIC interface driver should use a different compatible string.
>> + *
>> + * If each pmic child device driver need access the PMIC-specific registers,
>
> If a pmic child device driver needs
>
>> + * it need know only the register address and the access can be 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 the example below. It should be done by call
>
> by calling pmic_bind_childs()
>
> (which you should rename to pmic_bind_children() I think)
>
>> + * the 'pmic_bind_childs()' - please refer to the description of this function
>> + * in this header file. This function, should be called in the driver's '.bind'
>> + * method.
>> + *
>> + * For the example driver, please refer the MAX77686 driver:
>> + * - 'drivers/power/pmic/max77686.c'
>> + */
>> +
>> +/**
>> + * 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.
>> + *
>> + * @reg_count: devices register count
>> + * @read:      read 'len' bytes at "reg" and store it into the 'buffer'
>> + * @write:     write 'len' bytes from the 'buffer' to the register at 'reg' address
>> + */
>> +struct dm_pmic_ops {
>> +       int reg_count;
>
> This should not be in ops as it is not an operation. Perhaps add a
> function for it, or put it in the uclass platform data?
>

Adding this to uclass platform data will add another problem - choosing 
the right place for setting this value, because it's usually not given 
in dts, however the device node "reg" property could also provide the 
address length as it is for the memory.
But probably you will agree, that this is a job for I2C/SPI subsystem, 
and moreover has no sense, since usually the dts files don't provide the 
"reg" property as "<reg length>" for i2c/spi devices.

So maybe putting here an 'ops' function is better idea.
int (*get_reg_count)(struct udevice *dev);

This is used only for the PMIC command, so if return -ENOSYS, then just 
"dump" command will no available for the device.

>> +       int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int len);
>> +       int (*write)(struct udevice *dev, uint reg, const uint8_t *buffer,
>> +                    int len);
>> +};
>> +
>> +/* enum pmic_op_type - used for various pmic devices operation calls,
>
> /**
>   * enum pmic_op_type -
>
>> + * 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,
>> +};
>> +
>> +/**
>> + * struct pmic_child_info - basic device's child info for bind child nodes with
>> + * the driver by the node name prefix and driver name. This is a helper struct
>> + * for function: pmic_bind_childs().
>> + *
>> + * @prefix - child node name prefix (or its name if is unique or single)
>> + * @driver - driver name for the sub-node with prefix
>> + */
>> +struct pmic_child_info {
>> +       const char *prefix;
>> +       const char *driver;
>> +};
>> +
>> +/* drivers/power/pmic-uclass.c */
>> +
>> +/**
>> + * pmic_bind_childs() - bind drivers for given parent pmic, using child info
>> + * found in 'child_info' array.
>> + *
>> + * @pmic       - pmic device - the parent of found child's
>> + * @child_info - N-childs info array
>> + * @return a positive number of childs, or 0 if no child found (error)
>> + *
>> + * Note: For N-childs the child_info array should have N+1 entries and the last
>> + * entry prefix should be NULL - the same as for drivers compatible.
>> + *
>> + * For example, a single prefix info (N=1):
>> + * static const struct pmic_child_info bind_info[] = {
>> + *     { .prefix = "ldo", .driver = "ldo_driver" },
>> + *     { },
>> + * };
>> + *
>> + * This function is useful for regulator sub-nodes:
>> + * my_regulator at 0xa {
>> + *     reg = <0xa>;
>> + *     (pmic - bind automatically by compatible)
>> + *     compatible = "my_pmic";
>> + *     ...
>> + *     (pmic's childs - bind by pmic_bind_childs())
>> + *     (nodes prefix: "ldo", driver: "my_regulator_ldo")
>> + *     ldo1 { ... };
>> + *     ldo2 { ... };
>> + *
>> + *     (nodes prefix: "buck", driver: "my_regulator_buck")
>> + *     buck1 { ... };
>> + *     buck2 { ... };
>> + * };
>> + */
>> +int pmic_bind_childs(struct udevice *pmic, int offset,
>> +                    const struct pmic_child_info *child_info);
>
> pmic_bind_children
>
>> +
>> +/**
>> + * pmic_get: get the pmic device using its name
>> + *
>> + * @name - device name
>> + * @devp - returned pointer to the pmic device
>> + * @return 0 on success or negative value of errno.
>> + *
>> + * The returned devp device can be used with pmic_read/write calls
>> + */
>> +int pmic_get(const char *name, struct udevice **devp);
>> +
>> +/**
>> + * pmic_reg_count: get the pmic register count
>> + *
>> + * The required pmic device can be obtained by 'pmic_get()'
>> + *
>> + * @dev - pointer to the UCLASS_PMIC device
>> + * @return register count value on success or negative value of errno.
>> + */
>> +int pmic_reg_count(struct udevice *dev);
>> +
>> +/**
>> + * pmic_read/write: read/write to the UCLASS_PMIC device
>> + *
>> + * The required pmic device can be obtained by 'pmic_get()'
>> + *
>> + * @pmic   - pointer to the UCLASS_PMIC device
>> + * @reg    - device register offset
>> + * @buffer - pointer to read/write buffer
>> + * @len    - byte count for read/write
>> + * @return 0 on success or negative value of errno.
>> + */
>> +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len);
>> +int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len);
>> +#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 +276,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)
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thanks for the review.

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 05/16] dm: regulator: add implementation of driver model regulator uclass
  2015-04-22 16:30         ` Simon Glass
  2015-04-22 16:54           ` Simon Glass
@ 2015-04-23 11:33           ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-23 11:33 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/22/2015 06:30 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This commit introduces the implementation of dm regulator API.
>> Device tree support allows for auto binding. And by the basic
>> uclass operations, it allows to driving the devices in a common
>> way. For detailed informations, please look into the header file.
>>
>> Core 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_REGULATOR
>> - new config: CONFIG_DM_REGULATOR
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> 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
>>
>> Changes V3:
>> - regulator-uclass.c and regulator.h:
>>    -- api cleanup
>>    -- new function regulator_ofdata_to_platdata()
>>    -- update of comments
>>    -- add Kconfig
>>
>> Changes V4:
>> - move file drivers/power/regulator-uclass.c to
>>    drivers/power/regulator/regulator-uclass.c
>> - move DM_REGULATOR Kconfig entry from: drivers/power/Kconfig to
>>    drivers/power/regulator/Kconfig
>> - drivers/power/Kconfig: include regulator Kconfig path
>> - Kconfig: provide only general informations
>> - regulator-uclass.c: cleanup
>> - regulator-uclass.c: allow init regulator with name only
>> - regulator-uclass.c: remove pmic_get_uclass_ops and use dev_get_driver_ops
>> - regulator-uclass.c: add use of uclass_get_device_by_name()
>> - regulator.h: add 'struct dm_regulator_uclass_platdata'
>> - regulator.h: API documentation cleanup
>> - regulator - add binding info
>>
>> ---
>>   Makefile                                         |   3 +-
>>   doc/device-tree-bindings/regulator/regulator.txt |  55 ++++
>>   drivers/power/Kconfig                            |   2 +
>>   drivers/power/regulator/Kconfig                  |  17 +
>>   drivers/power/regulator/Makefile                 |   8 +
>>   drivers/power/regulator/regulator-uclass.c       | 300 ++++++++++++++++++
>>   include/dm/uclass-id.h                           |   1 +
>>   include/power/regulator.h                        | 384 +++++++++++++++++++++++
>>   8 files changed, 769 insertions(+), 1 deletion(-)
>>   create mode 100644 doc/device-tree-bindings/regulator/regulator.txt
>>   create mode 100644 drivers/power/regulator/Kconfig
>>   create mode 100644 drivers/power/regulator/Makefile
>>   create mode 100644 drivers/power/regulator/regulator-uclass.c
>>   create mode 100644 include/power/regulator.h
>
> A few more nits for later.
>
>>
>> diff --git a/Makefile b/Makefile
>> index dc25f70..dfb0d56 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -645,7 +645,8 @@ libs-y += drivers/power/ \
>>          drivers/power/fuel_gauge/ \
>>          drivers/power/mfd/ \
>>          drivers/power/pmic/ \
>> -       drivers/power/battery/
>> +       drivers/power/battery/ \
>> +       drivers/power/regulator/
>>   libs-y += drivers/spi/
>>   libs-$(CONFIG_FMAN_ENET) += drivers/net/fm/
>>   libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
>> diff --git a/doc/device-tree-bindings/regulator/regulator.txt b/doc/device-tree-bindings/regulator/regulator.txt
>> new file mode 100644
>> index 0000000..249e0f5
>> --- /dev/null
>> +++ b/doc/device-tree-bindings/regulator/regulator.txt
>> @@ -0,0 +1,55 @@
>> +Voltage/Current regulator
>> +
>> +Binding:
>> +The regulator devices don't use the "compatible" property. The binding is done
>> +by the prefix of regulator node's name. Usually the pmic I/O driver will provide
>> +the array of 'struct pmic_child_info' with the prefixes and compatible drivers.
>> +The bind is done by calling function: pmic_bind_childs().
>> +Example drivers:
>> +pmic: drivers/power/pmic/max77686.c
>> +regulator: drivers/power/regulator/max77686.c
>> +
>> +For the node name e.g.: "prefix[:alpha:]num { ... }":
>> +- the driver prefix should be: "prefix" or "PREFIX" - case insensitive
>> +- the node name's "num" is set as "dev->driver_data" on bind
>> +
>> +Example the prefix "ldo" will pass for: "ldo1", "ldo at 1", "LDO1", "LDOREG at 1"...
>> +
>> +Required properties:
>> +- regulator-name: a string, required by the regulator uclass
>> +
>> +Note
>> +The "regulator-name" constraint is used for setting the device's uclass
>> +platform data '.name' field. And the regulator device name is set from
>> +it's node name.
>> +
>> +Optional properties:
>> +- regulator-min-microvolt: a minimum allowed Voltage value
>> +- regulator-max-microvolt: a maximum allowed Voltage value
>> +- regulator-min-microamp: a minimum allowed Current value
>> +- regulator-max-microamp: a maximum allowed Current value
>> +- regulator-always-on: regulator should never be disabled
>> +- regulator-boot-on: enabled by bootloader/firmware
>> +
>> +Other kernel-style properties, are currently not used.
>> +
>> +Note:
>> +For the regulator autoset from constraints, the framework expects that:
>> +- regulator-min-microvolt is equal to regulator-max-microvolt
>> +- regulator-min-microamp is equal to regulator-max-microamp
>> +- regulator-always-on or regulator-boot-on is set
>> +
>> +Example:
>> +ldo0 {
>> +       /* Mandatory */
>> +       regulator-name = "VDDQ_EMMC_1.8V";
>> +
>> +       /* Optional */
>> +       regulator-min-microvolt = <1800000>;
>> +       regulator-max-microvolt = <1800000>;
>> +       regulator-min-microamp = <100000>;
>> +       regulator-max-microamp = <100000>;
>> +       regulator-always-on;
>> +       regulator-boot-on;
>> +};
>> +
>> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
>> index d03626e..23cdd71 100644
>> --- a/drivers/power/Kconfig
>> +++ b/drivers/power/Kconfig
>> @@ -2,6 +2,8 @@ menu "Power"
>>
>>   source "drivers/power/pmic/Kconfig"
>>
>> +source "drivers/power/regulator/Kconfig"
>> +
>>   config AXP221_POWER
>>          boolean "axp221 / axp223 pmic support"
>>          depends on MACH_SUN6I || MACH_SUN8I
>> diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
>> new file mode 100644
>> index 0000000..cb15162
>> --- /dev/null
>> +++ b/drivers/power/regulator/Kconfig
>> @@ -0,0 +1,17 @@
>> +config DM_REGULATOR
>> +       bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)"
>> +       depends on DM
>> +       ---help---
>> +       This config enables the driver model regulator support.
>> +       UCLASS_REGULATOR - designed to provide a common API for basic regulator's
>> +       functions, like get/set Voltage or Current value, enable state, etc...
>> +       Note:
>> +       When enabling this, please read the description, found in the files:
>> +       - 'include/power/pmic.h'
>> +       - 'include/power/regulator.h'
>> +       - 'drivers/power/pmic/pmic-uclass.c'
>> +       - 'drivers/power/pmic/regulator-uclass.c'
>> +       It's important to call the device_bind() with the proper node offset,
>> +       when binding the regulator devices. The pmic_bind_childs() can be used
>> +       for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_node()
>> +       otherwise. Detailed informations can be found in the header file.
>> diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
>> new file mode 100644
>> index 0000000..27c9006
>> --- /dev/null
>> +++ b/drivers/power/regulator/Makefile
>> @@ -0,0 +1,8 @@
>> +#
>> +# Copyright (C) 2015 Samsung Electronics
>> +# Przemyslaw Marczak <p.marczak@samsung.com>
>> +#
>> +# SPDX-License-Identifier:     GPL-2.0+
>> +#
>> +
>> +obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
>> diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
>> new file mode 100644
>> index 0000000..07ce286
>> --- /dev/null
>> +++ b/drivers/power/regulator/regulator-uclass.c
>> @@ -0,0 +1,300 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +#include <common.h>
>> +#include <fdtdec.h>
>> +#include <errno.h>
>> +#include <dm.h>
>> +#include <dm/uclass-internal.h>
>> +#include <power/pmic.h>
>> +#include <power/regulator.h>
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep)
>> +{
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +
>> +       *modep = NULL;
>> +
>> +       uc_pdata = dev_get_uclass_platdata(dev);
>> +       if (!uc_pdata)
>> +               return -ENXIO;
>> +
>> +       *modep = uc_pdata->mode;
>> +       return uc_pdata->mode_count;
>> +}
>> +
>> +int regulator_get_value(struct udevice *dev)
>> +{
>> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
>> +
>> +       if (!ops || !ops->get_value)
>> +               return -ENOSYS;
>> +
>> +       return ops->get_value(dev);
>> +}
>> +
>> +int regulator_set_value(struct udevice *dev, int uV)
>> +{
>> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
>> +
>> +       if (!ops || !ops->set_value)
>> +               return -ENOSYS;
>> +
>> +       return ops->set_value(dev, uV);
>> +}
>> +
>> +int regulator_get_current(struct udevice *dev)
>> +{
>> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
>> +
>> +       if (!ops || !ops->get_current)
>> +               return -ENOSYS;
>> +
>> +       return ops->get_current(dev);
>> +}
>> +
>> +int regulator_set_current(struct udevice *dev, int uA)
>> +{
>> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
>> +
>> +       if (!ops || !ops->set_current)
>> +               return -ENOSYS;
>> +
>> +       return ops->set_current(dev, uA);
>> +}
>> +
>> +bool regulator_get_enable(struct udevice *dev)
>> +{
>> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
>> +
>> +       if (!ops || !ops->get_enable)
>> +               return -ENOSYS;
>> +
>> +       return ops->get_enable(dev);
>> +}
>> +
>> +int regulator_set_enable(struct udevice *dev, bool enable)
>> +{
>> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
>> +
>> +       if (!ops || !ops->set_enable)
>> +               return -ENOSYS;
>> +
>> +       return ops->set_enable(dev, enable);
>> +}
>> +
>> +int regulator_get_mode(struct udevice *dev)
>> +{
>> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
>> +
>> +       if (!ops || !ops->get_mode)
>> +               return -ENOSYS;
>> +
>> +       return ops->get_mode(dev);
>> +}
>> +
>> +int regulator_set_mode(struct udevice *dev, int mode)
>> +{
>> +       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
>> +
>> +       if (!ops || !ops->set_mode)
>> +               return -ENOSYS;
>> +
>> +       return ops->set_mode(dev, mode);
>> +}
>> +
>> +int regulator_by_platname(const char *plat_name, struct udevice **devp)
>
> regulator_get_by_platname()
>
> since it does probe the device
>

Ok.

>> +{
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       struct udevice *dev;
>> +
>> +       *devp = NULL;
>> +
>> +       for (uclass_find_first_device(UCLASS_REGULATOR, &dev);
>> +            dev;
>> +            uclass_find_next_device(&dev)) {
>> +               uc_pdata = dev_get_uclass_platdata(dev);
>> +               if (!uc_pdata || strcmp(plat_name, uc_pdata->name))
>> +                       continue;
>> +
>> +               return uclass_get_device_tail(dev, 0, devp);
>> +       }
>> +
>> +       debug("%s: can't find: %s\n", __func__, plat_name);
>> +
>> +       return -ENODEV;
>> +}
>> +
>> +int regulator_by_devname(const char *devname, struct udevice **devp)
>
> regulator_get_by_devname
>

Ok.

>> +{
>> +       return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp);
>> +}
>> +
>> +static int setting_failed(int ret, bool verbose, const char *fmt, ...)
>> +{
>> +       va_list args;
>> +       char buf[64];
>> +
>> +       if (verbose == false)
>> +               return ret;
>> +
>> +       va_start(args, fmt);
>> +       vscnprintf(buf, sizeof(buf), fmt, args);
>> +       va_end(args);
>> +
>> +       printf(buf);
>
> I wonder if we should have a vprintf() in U-Boot?
>

Yes, it could be useful.

>> +
>> +       if (!ret)
>> +               return 0;
>> +
>> +       printf(" (ret: %d)", ret);
>> +
>> +       return ret;
>> +}
>> +
>> +int regulator_by_platname_autoset_and_enable(const char *platname,
>> +                                            struct udevice **devp,
>> +                                            bool verbose)
>> +{
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       struct udevice *dev;
>> +       bool v = verbose;
>
> Can we drop this local var and just use 'verbose'
>

I added this to avoid breaking the line of call to setting_failed().
So I will use just failed(), and then can put the verbose with no line 
breaking inside 'if' brackets.

>> +       int ret;
>> +
>> +       if (devp)
>> +               *devp = NULL;
>> +
>> +       ret = regulator_by_platname(platname, &dev);
>> +       if (ret) {
>> +               error("Can get the regulator: %s!", platname);
>> +               return ret;
>> +       }
>> +
>> +       uc_pdata = dev_get_uclass_platdata(dev);
>> +       if (!uc_pdata) {
>> +               error("Can get the regulator %s uclass platdata!", platname);
>> +               return -ENXIO;
>> +       }
>> +
>> +       if (v)
>> +               printf("%s@%s: ", dev->name, uc_pdata->name);
>> +
>> +       /* Those values are optional (-ENODATA if unset) */
>> +       if ((uc_pdata->min_uV != -ENODATA) &&
>> +           (uc_pdata->max_uV != -ENODATA) &&
>> +           (uc_pdata->min_uV == uc_pdata->max_uV)) {
>> +               ret = regulator_set_value(dev, uc_pdata->min_uV);
>> +               if (setting_failed(ret, v, "set %d uV", uc_pdata->min_uV))
>> +                       goto exit;
>> +       }
>> +
>> +       /* Those values are optional (-ENODATA if unset) */
>> +       if ((uc_pdata->min_uA != -ENODATA) &&
>> +           (uc_pdata->max_uA != -ENODATA) &&
>> +           (uc_pdata->min_uA == uc_pdata->max_uA)) {
>> +               ret = regulator_set_current(dev, uc_pdata->min_uA);
>> +               if (setting_failed(ret, v, "; set %d uA", uc_pdata->min_uA))
>> +                       goto exit;
>> +       }
>> +
>> +       if (!uc_pdata->always_on && !uc_pdata->boot_on)
>> +               goto retdev;
>> +
>> +       ret = regulator_set_enable(dev, true);
>> +       if (setting_failed(ret, v, "; enabling", uc_pdata->min_uA))
>> +               goto exit;
>> +
>> +retdev:
>> +       if (devp)
>> +               *devp = dev;
>> +exit:
>> +       if (v)
>> +               printf("\n");
>> +       return ret;
>> +}
>> +
>> +int regulator_by_platname_list_autoset_and_enable(const char *list_platname[],
>> +                                                 int list_entries,
>> +                                                 struct udevice *list_devp[],
>> +                                                 bool verbose)
>
> I wonder if you could shorten this (e.g. to regulator_list_autoset())?
>

I introduced macro: "regulator_list_autoset()" in 
include/power/regulator.h. Is it good?

>> +{
>> +       struct udevice *dev;
>> +       int i, ret, success = 0;
>> +
>> +       for (i = 0; i < list_entries; i++) {
>> +               ret = regulator_autoset(list_platname[i], &dev, verbose);
>> +               if (!ret)
>> +                       success++;
>> +
>> +               if (!list_devp)
>> +                       continue;
>> +
>> +               if (ret)
>> +                       list_devp[i] = NULL;
>> +               else
>> +                       list_devp[i] = dev;
>
> Shouldn't dev be NULL if ret is non-zero anyway?
>

Right, it always be if given. Will fix.

>> +       }
>> +
>> +       return (success != list_entries);
>
> return success == list_entries ? 0 : -EIO
>
> or something. Better would be to record the first error you get and return that.
>

Ok, I will tune it.

>> +}
>> +
>> +static int regulator_post_bind(struct udevice *dev)
>> +{
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       int offset = dev->of_offset;
>> +       const void *blob = gd->fdt_blob;
>> +
>> +       uc_pdata = dev_get_uclass_platdata(dev);
>> +       if (!uc_pdata)
>> +               return -ENXIO;
>> +
>> +       /* Regulator's mandatory constraint */
>> +       uc_pdata->name = fdt_getprop(blob, offset, "regulator-name", NULL);
>> +       if (!uc_pdata->name) {
>> +               debug("%s: dev: %s has no property 'regulator-name'\n",
>> +                     __func__, dev->name);
>> +               return -ENXIO;
>
> -EINVAL as it indicates invalid device tree data.
>

Ok.

>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int regulator_pre_probe(struct udevice *dev)
>> +{
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       int offset = dev->of_offset;
>> +
>> +       uc_pdata = dev_get_uclass_platdata(dev);
>> +       if (!uc_pdata)
>> +               return -ENXIO;
>> +
>> +       /* Regulator's optional constraints */
>> +       uc_pdata->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
>> +                                         "regulator-min-microvolt", -ENODATA);
>> +       uc_pdata->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
>> +                                         "regulator-max-microvolt", -ENODATA);
>> +       uc_pdata->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
>> +                                         "regulator-min-microamp", -ENODATA);
>> +       uc_pdata->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
>> +                                         "regulator-max-microamp", -ENODATA);
>> +       uc_pdata->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
>> +                                             "regulator-always-on");
>> +       uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
>> +                                           "regulator-boot-on");
>> +
>> +       return 0;
>> +}
>> +
>> +UCLASS_DRIVER(regulator) = {
>> +       .id             = UCLASS_REGULATOR,
>> +       .name           = "regulator",
>> +       .post_bind      = regulator_post_bind,
>> +       .pre_probe      = regulator_pre_probe,
>> +       .per_device_platdata_auto_alloc_size =
>> +                               sizeof(struct dm_regulator_uclass_platdata),
>> +};
>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>> index 23b3eb9..3c572d7 100644
>> --- a/include/dm/uclass-id.h
>> +++ b/include/dm/uclass-id.h
>> @@ -48,6 +48,7 @@ enum uclass_id {
>>
>>          /* Power Management */
>>          UCLASS_PMIC,            /* PMIC I/O device */
>> +       UCLASS_REGULATOR,       /* REGULATOR device */
>>
>>          UCLASS_COUNT,
>>          UCLASS_INVALID = -1,
>> diff --git a/include/power/regulator.h b/include/power/regulator.h
>> new file mode 100644
>> index 0000000..0302c1d
>> --- /dev/null
>> +++ b/include/power/regulator.h
>> @@ -0,0 +1,384 @@
>> +/*
>> + *  Copyright (C) 2014-2015 Samsung Electronics
>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#ifndef _INCLUDE_REGULATOR_H_
>> +#define _INCLUDE_REGULATOR_H_
>> +
>> +/**
>> + * U-Boot Voltage/Current Regulator
>> + * ================================
>> + *
>> + * The regulator API is based on a driver model, with the device tree support.
>> + * And this header describes the functions and data types for the uclass id:
>> + * 'UCLASS_REGULATOR' and the regulator driver API.
>> + *
>> + * The regulator uclass - is based on uclass platform data which is allocated,
>> + * automatically for each regulator device on bind and 'dev->uclass_platdata'
>> + * points to it. The data type is: 'struct dm_regulator_uclass_platdata'.
>> + * The uclass file: 'drivers/power/regulator/regulator-uclass.c'
>> + *
>> + * The regulator device - is based on driver's model 'struct udevice'.
>> + * The API can use regulator name in two meanings:
>> + * - devname  - the regulator device's name: 'dev->name'
>> + * - platname - the device's platdata's name. So in the code it looks like:
>> + *              'uc_pdata = dev->uclass_platdata'; 'name = uc_pdata->name'.
>> + *
>> + * The regulator device driver - provide an implementation of uclass operations
>> + * pointed by 'dev->driver->ops' as a struct of type 'struct dm_regulator_ops'.
>> + *
>> + * To proper bind the regulator device, the device tree node should provide
>> + * regulator constraints, like in the example below:
>> + *
>> + * ldo1 {
>> + *      regulator-name = "VDD_MMC_1.8V";     (mandatory for bind)
>> + *      regulator-min-microvolt = <1000000>; (optional)
>> + *      regulator-max-microvolt = <1000000>; (optional)
>> + *      regulator-min-microamp = <1000>;     (optional)
>> + *      regulator-max-microamp = <1000>;     (optional)
>> + *      regulator-always-on;                 (optional)
>> + *      regulator-boot-on;                   (optional)
>> + * };
>> + *
>> + * Please take a notice, that for the proper operation at least name constraint
>
> Please note that for the proper
>
> or
>
> Note: For the proper
>

Ok.

>> + * is needed, e.g. for call the device_by_platname(...).
>> + *
>> + * Regulator bind:
>> + * For each regulator device, the device_bind() should be called with passed
>> + * device tree offset. This is required for this uclass's '.post_bind' method,
>> + * which do the scan on the device node, for the 'regulator-name' constraint.
>
> s/do/does/
>

Ok.

>> + * If the parent is not a PMIC device, and the child is not bind by function:
>> + * 'pmic_bind_childs()', then it's recommended to bind the device by call to
>> + * dm_scan_fdt_node() - this is usually done automatically for bus devices,
>> + * as a post bind method.
>> + * Having the device's name constraint, we can call regulator_by_platname(),
>> + * to find interesting regulator. Before return, the regulator is probed,
>
> How about:
>
> s/to find interesting regulator/to find the required regulator/
>

Ok.

>> + * and the rest of its constraints are put into the device's uclass platform
>> + * data, by the uclass regulator '.pre_probe' method.
>> + *
>> + * For more info about PMIC bind, please refer to file: 'include/power/pmic.h'
>> + *
>> + * Note:
>> + * Please do not use the device_bind_by_name() function, since it pass '-1' as
>> + * device node offset - and the bind will fail on uclass .post_bind method,
>> + * because of missing 'regulator-name' constraint.
>
> Indeed. BTW I think i'm going to add a similar function which allows
> the node to be passed.
>

That's great, would be usefull.

>> + *
>> + *
>> + * Fixed Voltage/Current Regulator
>> + * ===============================
>> + *
>> + * When fixed voltage regulator is needed, then enable the config:
>> + * - CONFIG_DM_REGULATOR_FIXED
>> + *
>> + * The driver file: 'drivers/power/regulator/fixed.c', provides basic support
>> + * for control the GPIO, and return the device tree constraint values.
>> + *
>> + * To bind the fixed voltage regulator device, we usually use a 'simple-bus'
>> + * node as a parent. And 'regulator-fixed' for the driver compatible. This is
>> + * the same as in the kernel. The example node of fixed regulator:
>> + *
>> + * simple-bus {
>> + *     compatible = "simple-bus";
>> + *     #address-cells = <1>;
>> + *     #size-cells = <0>;
>> + *
>> + *     blue_led {
>> + *         compatible = "regulator-fixed";
>> + *         regulator-name = "VDD_LED_3.3V";
>> + *         regulator-min-microvolt = <3300000>;
>> + *         regulator-max-microvolt = <3300000>;
>> + *         gpio = <&gpc1 0 GPIO_ACTIVE_LOW>;
>> + *     };
>> + * };
>> + *
>> + * The fixed regulator devices also provide regulator uclass platform data. And
>> + * devices bound from such node, can use the regulator drivers API.
>> +*/
>> +
>> +/* enum regulator_type - used for regulator_*() variant calls */
>> +enum regulator_type {
>> +       REGULATOR_TYPE_LDO = 0,
>> +       REGULATOR_TYPE_BUCK,
>> +       REGULATOR_TYPE_DVS,
>> +       REGULATOR_TYPE_FIXED,
>> +       REGULATOR_TYPE_OTHER,
>> +};
>> +
>> +/**
>> + * struct dm_regulator_mode - this structure holds an information about
>> + * each regulator operation mode. Probably in most cases - an array.
>> + * This will be probably a driver-static data, since it is device-specific.
>> + *
>> + * @id             - a driver-specific mode id
>> + * @register_value - a driver-specific value for its mode id
>> + * @name           - the name of mode - used for regulator command
>> + * Note:
>> + * The field 'id', should be always a positive number, since the negative values
>> + * are reserved for the errno numbers when returns the mode id.
>> + */
>> +struct dm_regulator_mode {
>> +       int id; /* Set only as >= 0 (negative value is reserved for errno) */
>> +       int register_value;
>> +       const char *name;
>> +};
>> +
>> +/**
>> + * struct dm_regulator_uclass_platdata - pointed by dev->uclass_platdata, and
>> + * allocated on each regulator bind. This structure holds an information
>> + * about each regulator's constraints and supported operation modes.
>> + * There is no "step" voltage value - so driver should take care of this.
>> + *
>> + * @type       - one of 'enum regulator_type'
>> + * @mode       - pointer to the regulator mode (array if more than one)
>> + * @mode_count - number of '.mode' entries
>> + * @min_uV*    - minimum voltage (micro Volts)
>> + * @max_uV*    - maximum voltage (micro Volts)
>> + * @min_uA*    - minimum amperage (micro Amps)
>> + * @max_uA*    - maximum amperage (micro Amps)
>> + * @always_on* - bool type, true or false
>> + * @boot_on*   - bool type, true or false
>> + * @name**     - fdt regulator name - should be taken from the device tree
>> + *
>> + * Note:
>> + * *  - set automatically on device probe by the uclass's '.pre_probe' method.
>> + * ** - set automatically on device bind by the uclass's '.post_bind' method.
>> + * The constraints: type, mode, mode_count, can be set by device driver, e.g.
>> + * by the driver '.probe' method.
>> + */
>> +struct dm_regulator_uclass_platdata {
>> +       enum regulator_type type;
>> +       struct dm_regulator_mode *mode;
>> +       int mode_count;
>> +       int min_uV;
>> +       int max_uV;
>> +       int min_uA;
>> +       int max_uA;
>> +       bool always_on;
>> +       bool boot_on;
>> +       const char *name;
>> +};
>> +
>> +/* Regulator device operations */
>> +struct dm_regulator_ops {
>> +       /**
>> +        * The regulator output value function calls operates on a micro Volts.
>> +        *
>> +        * get/set_value - get/set output value of the given output number
>> +        * @dev          - regulator device
>> +        * Sets:
>> +        * @uV           - set the output value [micro Volts]
>> +        * Returns: output value [uV] on success or negative errno if fail.
>> +        */
>> +       int (*get_value)(struct udevice *dev);
>> +       int (*set_value)(struct udevice *dev, int uV);
>> +
>> +       /**
>> +        * The regulator output current function calls operates on a micro Amps.
>> +        *
>> +        * get/set_current - get/set output current of the given output number
>> +        * @dev            - regulator device
>> +        * Sets:
>> +        * @uA           - set the output current [micro Amps]
>> +        * Returns: output value [uA] on success or negative errno if fail.
>> +        */
>> +       int (*get_current)(struct udevice *dev);
>> +       int (*set_current)(struct udevice *dev, int uA);
>> +
>> +       /**
>> +        * The most basic feature of the regulator output is its enable state.
>> +        *
>> +        * get/set_enable - get/set enable state of the given output number
>> +        * @dev           - regulator device
>> +        * Sets:
>> +        * @enable         - set true - enable or false - disable
>> +        * Returns: true/false for get; or 0 / -errno for set.
>> +        */
>> +       bool (*get_enable)(struct udevice *dev);
>> +       int (*set_enable)(struct udevice *dev, bool enable);
>> +
>> +       /**
>> +        * The 'get/set_mode()' function calls should operate on a driver
>> +        * specific mode definitions, which should be found in:
>> +        * field 'mode' of struct mode_desc.
>> +        *
>> +        * get/set_mode - get/set operation mode of the given output number
>> +        * @dev         - regulator device
>> +        * Sets
>> +        * @mode_id     - set output mode id (struct dm_regulator_mode->id)
>> +        * Returns: id/0 for get/set on success or negative errno if fail.
>> +        * Note:
>> +        * The field 'id' of struct type 'dm_regulator_mode', should be always
>> +        * positive number, since the negative is reserved for the error.
>> +        */
>> +       int (*get_mode)(struct udevice *dev);
>> +       int (*set_mode)(struct udevice *dev, int mode_id);
>> +};
>> +
>> +/**
>> + * regulator_mode: returns a pointer to the array of regulator mode info
>> + *
>> + * @dev        - pointer to the regulator device
>> + * @modep      - pointer to the returned mode info array
>> + * Returns     - count of modep entries on success or negative errno if fail.
>> + */
>> +int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep);
>> +
>> +/**
>> + * regulator_get_value: get microvoltage voltage value of a given regulator
>> + *
>> + * @dev    - pointer to the regulator device
>> + * Returns - positive output value [uV] on success or negative errno if fail.
>> + */
>> +int regulator_get_value(struct udevice *dev);
>> +
>> +/**
>> + * regulator_set_value: set the microvoltage value of a given regulator.
>> + *
>> + * @dev    - pointer to the regulator device
>> + * @uV     - the output value to set [micro Volts]
>> + * Returns - 0 on success or -errno val if fails
>> + */
>> +int regulator_set_value(struct udevice *dev, int uV);
>> +
>> +/**
>> + * regulator_get_current: get microampere value of a given regulator
>> + *
>> + * @dev    - pointer to the regulator device
>> + * Returns - positive output current [uA] on success or negative errno if fail.
>> + */
>> +int regulator_get_current(struct udevice *dev);
>> +
>> +/**
>> + * regulator_set_current: set the microampere value of a given regulator.
>> + *
>> + * @dev    - pointer to the regulator device
>> + * @uA     - set the output current [micro Amps]
>> + * Returns - 0 on success or -errno val if fails
>> + */
>> +int regulator_set_current(struct udevice *dev, int uA);
>> +
>> +/**
>> + * regulator_get_enable: get regulator device enable state.
>> + *
>> + * @dev    - pointer to the regulator device
>> + * Returns - true/false of enable state
>> + */
>> +bool regulator_get_enable(struct udevice *dev);
>> +
>> +/**
>> + * regulator_set_enable: set regulator enable state
>> + *
>> + * @dev    - pointer to the regulator device
>> + * @enable - set true or false
>> + * Returns - 0 on success or -errno val if fails
>> + */
>> +int regulator_set_enable(struct udevice *dev, bool enable);
>> +
>> +/**
>> + * regulator_get_mode: get mode of a given device regulator
>> + *
>> + * @dev    - pointer to the regulator device
>> + * Returns - positive  mode number 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 'mode_desc' provides a field 'mode'
>> + * for this purpose and register_value for a raw register value.
>
> Can you please reword the first sentence? I don't understand what it
> is trying to say. Similar below.
>

Ok, will fix this.

>> + */
>> +int regulator_get_mode(struct udevice *dev);
>> +
>> +/**
>> + * regulator_set_mode: set given regulator mode
>> + *
>> + * @dev    - pointer to the regulator device
>> + * @mode   - mode type (field 'mode' of struct mode_desc)
>> + * 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 mode);
>> +
>> +/**
>> + * regulator_by_platname_autoset_and_enable: setup the regulator given by
>> + * its uclass's platform data '.name'. The setup depends on constraints found
>> + * in device's uclass's platform data (struct dm_regulator_uclass_platdata):
>> + * - Voltage value - will set - if '.min_uV' and '.max_uV' values are equal
>> + * - Current limit - will set - if '.min_uA' and '.max_uA' values are equal
>> + * - Enable - will set - if '.always_on' or '.boot_on' are set to true
>> + *
>> + * The function returns on first encountered error.
>> + *
>> + * @platname - expected string for dm_regulator_uclass_platdata .name field
>> + * @devp      - returned pointer to the regulator device - if non-NULL passed
>> + * @verbose   - (true/false) print regulator setup info, or be quiet
>> + * Returns: 0 on success or negative value of errno.
>> + *
>> + * The returned 'regulator' device can be used with:
>> + * - regulator_get/set_*
>> + * For shorter call name, the below macro regulator_autoset() can be used.
>> + */
>> +int regulator_by_platname_autoset_and_enable(const char *platname,
>> +                                            struct udevice **devp,
>> +                                            bool verbose);
>> +
>> +#define regulator_autoset(platname, devp, verbose) \
>> +       regulator_by_platname_autoset_and_enable(platname, devp, verbose)
>> +
>
> Can we just use the shorter name for the function and avoid this #ifdef?
>

Ok.

>> +/**
>> + * regulator_by_platname_list_autoset_and_enable: setup the regulators given by
>> + * list of its uclass's platform data '.name'. The setup depends on constraints
>> + * found in device's uclass's platform data. The function loops with calls to:
>> + * regulator_by_platname_autoset_and_enable() for each name of list.
>> + *
>> + * @list_platname - an array of expected strings for .name field of each
>> + *                  regulator's uclass platdata
>> + * @list_entries  - number of regulator's name list entries
>> + * @list_devp     - an array of returned pointers to the successfully setup
>> + *                  regulator devices if non-NULL passed
>> + * @verbose       - (true/false) print each regulator setup info, or be quiet
>> + * Returns: 0 on successfully setup of all list entries or 1 otwerwise.
>
> otherwise
>
>> + *
>> + * The returned 'regulator' devices can be used with:
>> + * - regulator_get/set_*
>> + * For shorter call name, the below macro regulator_list_autoset() can be used.
>> + */
>> +int regulator_by_platname_list_autoset_and_enable(const char *list_platname[],
>> +                                                 int list_entries,
>> +                                                 struct udevice *list_devp[],
>> +                                                 bool verbose);
>> +
>> +#define regulator_list_autoset(namelist, entries, devlist, verbose)      \
>> +       regulator_by_platname_list_autoset_and_enable(namelist, entries, \
>> +                                                     devlist, verbose)
>
> As above. With #defines it changes the name in the map output or
> debugger, which confuses people. An inline function is better, but in
> this case it may be better to just rename the function.
>

Okay, will fix both.

>> +
>> +/**
>> + * regulator_by_devname: returns the pointer to the pmic regulator device.
>> + *                       Search by name, found in regulator device's name.
>> + *
>> + * @devname - expected string for 'dev->name' of regulator device
>> + * @devp     - returned pointer to the regulator device
>> + * Returns: 0 on success or negative value of errno.
>> + *
>> + * The returned 'regulator' device can be used with:
>> + * - regulator_get/set_*
>> + */
>> +int regulator_by_devname(const char *devname, struct udevice **devp);
>
> regulator_get_by_devname
>
>> +
>> +/**
>> + * regulator_by_platname: returns the pointer to the pmic regulator device.
>> + *                        Search by name, found in regulator uclass platdata.
>> + *
>> + * @platname - expected string for dm_regulator_uclass_platdata .name field
>> + * @devp     - returned pointer to the regulator device
>> + * Returns: 0 on success or negative value of errno.
>> + *
>> + * The returned 'regulator' device can be used with:
>> + * - regulator_get/set_*
>> + */
>> +int regulator_by_platname(const char *platname, struct udevice **devp);
>
> regulator_get_by_platname
>

Also, will fix both.

>> +
>> +#endif /* _INCLUDE_REGULATOR_H_ */
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thank you again, will resend ASAP.

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command
  2015-04-22 16:30         ` Simon Glass
  2015-04-22 17:09           ` Simon Glass
@ 2015-04-23 11:33           ` Przemyslaw Marczak
  2015-04-24  4:51             ` Simon Glass
  1 sibling, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-23 11:33 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/22/2015 06:30 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This command is based on driver model regulator's API.
>> The user interface provides:
>> - list UCLASS regulator devices
>> - show or [set] operating regulator device
>> - print constraints info
>> - print operating status
>> - print/[set] voltage value [uV] (force)
>> - print/[set] current value [uA]
>> - print/[set] operating mode id
>> - enable the regulator output
>> - disable the regulator output
>>
>> The 'force' option can be used for setting the value which exceeds
>> the constraints min/max limits.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes v3:
>> - new file
>> - Kconfig entry
>>
>> Changes V4:
>> - cmd regulator: move platdata to uc pdata
>> - cmd_regulator: includes cleanup
>> - cmd_regulator: add get_curr_dev_and_pl() check type
>> - move config name: CONFIG_DM_REGULATOR_CMD to CONFIG_CMD_REGULATOR
>> - common/Kconfig - cleanup
>> ---
>>   common/Kconfig         |  22 +++
>>   common/Makefile        |   1 +
>>   common/cmd_regulator.c | 403 +++++++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 426 insertions(+)
>>   create mode 100644 common/cmd_regulator.c
>
> Acked-by: Simon Glass <sjg@chromium.org>
>
> I have a few nits that could be dealt with by a follow-on patch.
>

Ok.

>>
>> diff --git a/common/Kconfig b/common/Kconfig
>> index 4666f8e..52f8bb1 100644
>> --- a/common/Kconfig
>> +++ b/common/Kconfig
>> @@ -470,5 +470,27 @@ config CMD_PMIC
>>            - pmic read address  - read byte of register at address
>>            - pmic write address - write byte to register at address
>>            The only one change for this command is 'dev' subcommand.
>> +
>> +config CMD_REGULATOR
>> +       bool "Enable Driver Model REGULATOR command"
>> +       depends on DM_REGULATOR
>> +       help
>> +         This command is based on driver model regulator's API.
>> +         User interface features:
>> +         - list               - list regulator devices
>> +         - regulator dev <id> - show or [set] operating regulator device
>> +         - regulator info     - print constraints info
>> +         - regulator status   - print operating status
>> +         - regulator value <val] <-f> - print/[set] voltage value [uV]
>> +         - regulator current <val>    - print/[set] current value [uA]
>> +         - regulator mode <id>        - print/[set] operating mode id
>> +         - regulator enable           - enable the regulator output
>> +         - regulator disable          - disable the regulator output
>> +
>> +         The '-f' (force) option can be used for set the value which exceeds
>> +         the limits, which are found in device-tree and are kept in regulator's
>> +         uclass platdata structure.
>> +
>>   endmenu
>> +
>>   endmenu
>> diff --git a/common/Makefile b/common/Makefile
>> index 87a3efe..93bded3 100644
>> --- a/common/Makefile
>> +++ b/common/Makefile
>> @@ -212,6 +212,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
>>
>>   # Power
>>   obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o
>> +obj-$(CONFIG_CMD_REGULATOR) += cmd_regulator.o
>>   endif
>>
>>   ifdef CONFIG_SPL_BUILD
>> diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
>> new file mode 100644
>> index 0000000..b1b9e87
>> --- /dev/null
>> +++ b/common/cmd_regulator.c
>> @@ -0,0 +1,403 @@
>> +/*
>> + * Copyright (C) 2014-2015 Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +#include <common.h>
>> +#include <errno.h>
>> +#include <dm.h>
>> +#include <dm/uclass-internal.h>
>> +#include <power/regulator.h>
>> +
>> +#define LIMIT_SEQ      3
>> +#define LIMIT_DEVNAME  20
>> +#define LIMIT_OFNAME   20
>> +#define LIMIT_INFO     16
>> +
>> +static struct udevice *currdev;
>> +
>> +static int failed(const char *getset, const char *thing,
>> +                 const char *for_dev, int ret)
>> +{
>> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing, for_dev,
>> +                                                   ret, errno_str(ret));
>
> blank line here.

I don't see the blank line here in the patch, which I send.

>
> I worry that if someone gets one of these messages they will not be
> able to find it in the source code. How about passing in the full
> printf() string in each case, or just using printf() in situ? I don't
> think the code space saving is significant.
>

It's not a debug message. And each one is different, and easy to grep 
"failed". The code is a little cleaner with this. Also the command code 
is not complicated.

>> +       return CMD_RET_FAILURE;
>> +}
>> +
>> +static int regulator_get(bool list_only, int get_seq, struct udevice **devp)
>
> This function seems to do multiple things (find and list). Should we
> split it into two?
>
>> +{
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       struct udevice *dev;
>> +       int ret;
>> +
>> +       if (devp)
>> +               *devp = NULL;
>> +
>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
>> +            ret = uclass_next_device(&dev)) {
>
> This will probe all regulators that it checks. I think it should avoid
> that. Do you mean to use
>

Regarding the two above comments, we have two problems:

1. Getting the regulator by sequencial number (dev->seq).
I think it's required, because only this method returns the right 
device. Disadvantage: need to probe all devices.

2. Getting the regulator by "regulator-name" 
(regulator_uclass_platdata->name).
This would be clean, but unreliable if we have few regulators with the 
same name - I think we should keep this in mind. Advantage: can use for 
non-probed devices.

And about the doing multiple things by the regulator_get().
Following your comments about avoiding the code duplication, I put those 
things into one function, since both actually do the same - loops over 
the uclass's devices.

So we can threat it as a subcommands:
- regulator_get list
- regulator_get dev
Maybe the enum { GET_LIST, GET_DEV } would be better, than the bool.

Is that really bad?

>> +               if (list_only) {
>> +                       uc_pdata = dev_get_uclass_platdata(dev);
>> +                       printf("|%*d | %*.*s @ %-*.*s| %s @ %s\n",
>> +                              LIMIT_SEQ, dev->seq,
>> +                              LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
>> +                              LIMIT_OFNAME, LIMIT_OFNAME, uc_pdata->name,
>> +                              dev->parent->name,
>> +                              dev_get_uclass_name(dev->parent));
>> +                       continue;
>> +               }
>> +
>> +               if (dev->seq == get_seq) {
>> +                       if (devp)
>> +                               *devp = dev;
>> +                       else
>> +                               return -EINVAL;
>> +
>> +                       return 0;
>> +               }
>> +       }
>> +
>> +       if (list_only)
>> +               return ret;
>> +
>> +       return -ENODEV;
>> +}
>> +
>> +static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       int seq, ret = -ENXIO;
>> +
>> +       switch (argc) {
>> +       case 2:
>> +               seq = simple_strtoul(argv[1], NULL, 0);
>> +               ret = uclass_get_device_by_seq(UCLASS_REGULATOR, seq, &currdev);
>> +               if (ret && (ret = regulator_get(false, seq, &currdev)))
>> +                       goto failed;
>> +       case 1:
>> +               uc_pdata = dev_get_uclass_platdata(currdev);
>> +               if (!uc_pdata)
>> +                       goto failed;
>> +
>> +               printf("dev: %d @ %s\n", currdev->seq, uc_pdata->name);
>> +       }
>> +
>> +       return CMD_RET_SUCCESS;
>> +failed:
>> +       return failed("get", "the", "device", ret);
>> +}
>> +
>> +static int get_curr_dev_and_pl(struct udevice **devp,
>
> What is pl? The name does not seem very meaningful to me.
>

The platdata, ok I will tune it.

>> +                              struct dm_regulator_uclass_platdata **uc_pdata,
>> +                              bool allow_type_fixed)
>> +{
>> +       *devp = NULL;
>> +       *uc_pdata = NULL;
>> +
>> +       if (!currdev)
>> +               return failed("get", "current", "device", -ENODEV);
>> +
>> +       *devp = currdev;
>> +
>> +       *uc_pdata = dev_get_uclass_platdata(*devp);
>> +       if (!*uc_pdata)
>> +               return failed("get", "regulator", "platdata", -ENXIO);
>> +
>> +       if (!allow_type_fixed && (*uc_pdata)->type == REGULATOR_TYPE_FIXED) {
>> +               printf("Operation not allowed for fixed regulator!\n");
>> +               return CMD_RET_FAILURE;
>> +       }
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       int ret;
>> +
>> +       printf("|%*s | %*.*s @ %-*.*s| %s @ %s\n",
>> +              LIMIT_SEQ, "Seq",
>> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
>> +              LIMIT_OFNAME, LIMIT_OFNAME, "fdtname",
>> +              "Parent", "uclass");
>> +
>> +       ret = regulator_get(true, 0, NULL);
>> +       if (ret)
>> +               return CMD_RET_FAILURE;
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int constraint(const char *name, int val, const char *val_name)
>> +{
>> +       printf("%-*s", LIMIT_INFO, name);
>> +       if (val < 0) {
>> +               printf(" %s (err: %d)\n", errno_str(val), val);
>> +               return val;
>> +       }
>> +
>> +       if (val_name)
>> +               printf(" %d (%s)\n", val, val_name);
>> +       else
>> +               printf(" %d\n", val);
>> +
>> +       return 0;
>> +}
>> +
>> +static const char *get_mode_name(struct dm_regulator_mode *mode,
>> +                                int mode_count,
>> +                                int mode_id)
>> +{
>> +       while (mode_count--) {
>> +               if (mode->id == mode_id)
>> +                       return mode->name;
>> +               mode++;
>> +       }
>> +
>> +       return NULL;
>> +}
>> +
>> +static int do_info(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       struct dm_regulator_mode *modes;
>> +       const char *parent_uc;
>> +       int mode_count;
>> +       int ret;
>> +       int i;
>> +
>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>> +       if (ret)
>> +               return ret;
>> +
>> +       parent_uc = dev_get_uclass_name(dev->parent);
>> +
>> +       printf("Uclass regulator dev %d info:\n", dev->seq);
>> +       printf("%-*s %s @ %s\n%-*s %s\n%-*s %s\n%-*s\n",
>> +              LIMIT_INFO, "* parent:", dev->parent->name, parent_uc,
>> +              LIMIT_INFO, "* dev name:", dev->name,
>> +              LIMIT_INFO, "* fdt name:", uc_pdata->name,
>> +              LIMIT_INFO, "* constraints:");
>> +
>> +       constraint("  - min uV:", uc_pdata->min_uV, NULL);
>> +       constraint("  - max uV:", uc_pdata->max_uV, NULL);
>> +       constraint("  - min uA:", uc_pdata->min_uA, NULL);
>> +       constraint("  - max uA:", uc_pdata->max_uA, NULL);
>> +       constraint("  - always on:", uc_pdata->always_on,
>> +                  uc_pdata->always_on ? "true" : "false");
>> +       constraint("  - boot on:", uc_pdata->boot_on,
>> +                  uc_pdata->boot_on ? "true" : "false");
>> +
>> +       mode_count = regulator_mode(dev, &modes);
>> +       constraint("* op modes:", mode_count, NULL);
>> +
>> +       for (i = 0; i < mode_count; i++, modes++)
>> +               constraint("  - mode id:", modes->id, modes->name);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       int current, value, mode, ret;
>> +       const char *mode_name = NULL;
>> +       struct udevice *dev;
>> +       bool enabled;
>> +
>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>> +       if (ret)
>> +               return ret;
>> +
>> +       enabled = regulator_get_enable(dev);
>> +       constraint(" * enable:", enabled, enabled ? "true" : "false");
>> +
>> +       value = regulator_get_value(dev);
>> +       constraint(" * value uV:", value, NULL);
>> +
>> +       current = regulator_get_current(dev);
>> +       constraint(" * current uA:", current, NULL);
>> +
>> +       mode = regulator_get_mode(dev);
>> +       mode_name = get_mode_name(uc_pdata->mode, uc_pdata->mode_count, mode);
>> +       constraint(" * mode id:", mode, mode_name);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_value(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       int value;
>> +       int force;
>> +       int ret;
>> +
>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (argc == 1) {
>> +               value = regulator_get_value(dev);
>> +               if (value < 0)
>> +                       return failed("get", uc_pdata->name, "voltage", value);
>> +
>> +               printf("%d uV\n", value);
>> +               return CMD_RET_SUCCESS;
>> +       }
>> +
>> +       if (argc == 3)
>> +               force = !strcmp("-f", argv[2]);
>> +       else
>> +               force = 0;
>> +
>> +       value = simple_strtoul(argv[1], NULL, 0);
>> +       if ((value < uc_pdata->min_uV || value > uc_pdata->max_uV) && !force) {
>> +               printf("Value exceeds regulator constraint limits\n");
>> +               return CMD_RET_FAILURE;
>> +       }
>> +
>> +       ret = regulator_set_value(dev, value);
>> +       if (ret)
>> +               return failed("set", uc_pdata->name, "voltage value", ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_current(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       int current;
>> +       int ret;
>> +
>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (argc == 1) {
>> +               current = regulator_get_current(dev);
>> +               if (current < 0)
>> +                       return failed("get", uc_pdata->name, "current", current);
>> +
>> +               printf("%d uA\n", current);
>> +               return CMD_RET_SUCCESS;
>> +       }
>> +
>> +       current = simple_strtoul(argv[1], NULL, 0);
>> +       if (current < uc_pdata->min_uA || current > uc_pdata->max_uA) {
>> +               printf("Current exceeds regulator constraint limits\n");
>> +               return CMD_RET_FAILURE;
>> +       }
>> +
>> +       ret = regulator_set_current(dev, current);
>> +       if (ret)
>> +               return failed("set", uc_pdata->name, "current value", ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       int new_mode;
>> +       int mode;
>> +       int ret;
>> +
>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, false);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (argc == 1) {
>> +               mode = regulator_get_mode(dev);
>> +               if (mode < 0)
>> +                       return failed("get", uc_pdata->name, "mode", mode);
>> +
>> +               printf("mode id: %d\n", mode);
>> +               return CMD_RET_SUCCESS;
>> +       }
>> +
>> +       new_mode = simple_strtoul(argv[1], NULL, 0);
>> +
>> +       ret = regulator_set_mode(dev, new_mode);
>> +       if (ret)
>> +               return failed("set", uc_pdata->name, "mode", ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_enable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       int ret;
>> +
>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = regulator_set_enable(dev, true);
>> +       if (ret)
>> +               return failed("enable", "regulator", uc_pdata->name, ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static int do_disable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       struct udevice *dev;
>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>> +       int ret;
>> +
>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = regulator_set_enable(dev, false);
>> +       if (ret)
>> +               return failed("disable", "regulator", uc_pdata->name, ret);
>> +
>> +       return CMD_RET_SUCCESS;
>> +}
>> +
>> +static cmd_tbl_t subcmd[] = {
>> +       U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
>> +       U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
>> +       U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""),
>> +       U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
>> +       U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""),
>> +       U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""),
>> +       U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""),
>> +       U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""),
>> +       U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""),
>> +};
>> +
>> +static int do_regulator(cmd_tbl_t *cmdtp, int flag, int argc,
>> +                       char * const argv[])
>> +{
>> +       cmd_tbl_t *cmd;
>> +
>> +       argc--;
>> +       argv++;
>> +
>> +       cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
>> +       if (cmd == NULL || argc > cmd->maxargs)
>> +               return CMD_RET_USAGE;
>> +
>> +       return cmd->cmd(cmdtp, flag, argc, argv);
>> +}
>> +
>> +U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator,
>> +       "uclass operations",
>> +       "list         - list UCLASS regulator devices\n"
>> +       "regulator dev [id]     - show or [set] operating regulator device\n"
>> +       "regulator [info]       - print constraints info\n"
>> +       "regulator [status]     - print operating status\n"
>> +       "regulator [value] [-f] - print/[set] voltage value [uV] (force)\n"
>> +       "regulator [current]    - print/[set] current value [uA]\n"
>> +       "regulator [mode_id]    - print/[set] operating mode id\n"
>> +       "regulator [enable]     - enable the regulator output\n"
>> +       "regulator [disable]    - disable the regulator output\n"
>> +);
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 12/16] doc: driver-model: pmic and regulator uclass documentation
  2015-04-22 16:31         ` Simon Glass
  2015-04-22 17:10           ` Simon Glass
@ 2015-04-23 11:33           ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-23 11:33 UTC (permalink / raw)
  To: u-boot

Hello,

On 04/22/2015 06:31 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Since this framework is still under the construction, the main
>> documentation is kept in the header files.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes v2, V3:
>> - update documentation with the framework api changes
>> - remove doc file name 'dm' prefix
>>
>> Changes V4:
>> - move the description to the headers and leave only general info
>>
>> ---
>>   doc/driver-model/pmic-framework.txt | 142 ++++++++++++++++++++++++++++++++++++
>>   1 file changed, 142 insertions(+)
>>   create mode 100644 doc/driver-model/pmic-framework.txt
>
> Acked-by: Simon Glass <sjg@chromium.org>
>
>>
>> diff --git a/doc/driver-model/pmic-framework.txt b/doc/driver-model/pmic-framework.txt
>> new file mode 100644
>> index 0000000..cc82236
>> --- /dev/null
>> +++ b/doc/driver-model/pmic-framework.txt
>> @@ -0,0 +1,142 @@
>> +#
>> +# (C) Copyright 2014-2015 Samsung Electronics
>> +# Przemyslaw Marczak <p.marczak@samsung.com>
>> +#
>> +# SPDX-License-Identifier:      GPL-2.0+
>> +#
>> +
>> +PMIC framework based on Driver Model
>> +====================================
>> +TOC:
>> +1. Introduction
>> +2. How does it work
>> +3. Pmic uclass
>> +4. Regulator uclass
>> +
>> +1. Introduction
>> +===============
>> +This is an introduction to driver-model multi uclass PMIC IC's support.
>> +At present it's based on two uclass types:
>> +- UCLASS_PMIC      - basic uclass type for PMIC I/O, which provides common
>> +                     read/write interface.
>> +- UCLASS_REGULATOR - additional uclass type for specific PMIC features,
>> +                     which are Voltage/Current regulators.
>> +
>> +New files:
>> +UCLASS_PMIC:
>> +- drivers/power/pmic/pmic-uclass.c
>> +- include/power/pmic.h
>> +UCLASS_REGULATOR:
>> +- drivers/power/regulator/regulator-uclass.c
>> +- include/power/regulator.h
>> +
>> +Commands:
>> +- common/cmd_pmic.c
>> +- common/cmd_regulator.c
>> +
>> +2. How doees it work
>> +====================
>> +The Power Management Integrated Circuits (PMIC) are used in embedded systems
>> +to provide stable, precise and specific voltage power source with over-voltage
>> +and thermal protection circuits.
>> +
>> +The single PMIC can provide various functions by single or multiple interfaces,
>> +like in the example below.
>> +
>> +-- SoC
>> + |
>> + |            ______________________________________
>> + | BUS 0     |       Multi interface PMIC IC        |--> LDO out 1
>> + | e.g.I2C0  |                                      |--> LDO out N
>> + |-----------|---- PMIC device 0 (READ/WRITE ops)   |
>> + | or SPI0   |    |_ REGULATOR device (ldo/... ops) |--> BUCK out 1
>> + |           |    |_ CHARGER device (charger ops)   |--> BUCK out M
>> + |           |    |_ MUIC device (microUSB con ops) |
>> + | BUS 1     |    |_ ...                            |---> BATTERY
>> + | e.g.I2C1  |                                      |
>> + |-----------|---- PMIC device 1 (READ/WRITE ops)   |---> USB in 1
>> + . or SPI1   |    |_ RTC device (rtc ops)           |---> USB in 2
>> + .           |______________________________________|---> USB out
>> + .
>> +
>> +Since U-Boot provides driver model features for I2C and SPI bus drivers,
>> +the PMIC devices should also support this. By the pmic and regulator API's,
>> +PMIC drivers can simply provide a common functions, for multi-interface and
>> +and multi-instance device support.
>> +
>> +Basic design assumptions:
>> +
>> +- Common I/O API - UCLASS_PMIC
>> +For the multi-function PMIC devices, this can be used as parent I/O device
>> +for each IC's interface. Then, each children uses the same dev for read/write.
>> +
>> +- Common regulator API - UCLASS_REGULATOR
>> +For driving the regulator attributes, auto setting function or command line
>> +interface, based on kernel-style regulator device tree constraints.
>> +
>> +For simple implementations, regulator drivers are not required, so the code can
>> +use pmic read/write directly.
>> +
>> +3. Pmic uclass
>> +==============
>> +The basic informations:
>> +* Uclass:   'UCLASS_PMIC'
>> +* Header:   'include/power/pmic.h'
>> +* Core:     'drivers/power/pmic/pmic-uclass.c'
>> +  config:   'CONFIG_DM_PMIC'
>> +* Command:  'common/cmd_pmic.c'
>> +  config:   'CONFIG_CMD_PMIC'
>> +* Example:  'drivers/power/pmic/max77686.c'
>> +
>> +This is still under the construction. So for the API description, please refer
>> +to the header file.
>> +
>> +As an example of the pmic driver, please refer to the MAX77686 driver.
>> +
>> +Please pay attention for the driver's '.bind' method. Exactly the function call:
>
> I think 'driver's bind() method' is better than a quoted '.bind'.
>
>> +'pmic_bind_childs()', which is used to bind the regulators by using the array of
>> +regulator's node, compatible prefixes.
>> +
>> +The 'pmic; command also supports the new API. So the pmic command can be enabled
>> +by adding CONFIG_CMD_PMIC.
>> +The new pmic command allows to:
>> +- list pmic devices
>> +- choose the current device (like the mmc command)
>> +- read or write the pmic register
>> +- dump all pmic registers
>> +
>> +This command can use only UCLASS_PMIC devices, since this uclass is designed
>> +for pmic I/O operations only.
>> +
>> +For more informations, please refer to the file: 'common/cmd_pmic.c'.
>
> information
>
>> +
>> +4. Regulator uclass
>> +===================
>> +The basic informations:
>
> information
>
>> +* Uclass:  'UCLASS_REGULATOR'
>> +* Header:  'include/power/regulator.h'
>> +* Core:    'drivers/power/regulator/regulator-uclass.c'
>> +  config:  'CONFIG_DM_REGULATOR'
>> +  binding: 'doc/device-tree-bindings/regulator/regulator.txt'
>> +* Command: 'common/cmd_regulator.c'
>> +  config:  'CONFIG_CMD_REGULATOR'
>> +* Example: 'drivers/power/regulator/max77686.c'
>> +           'drivers/power/pmic/max77686.c' (required I/O driver for the above)
>> +* Example: 'drivers/power/regulator/fixed.c'
>> +  config"  'CONFIG_DM_REGULATOR_FIXED'
>> +
>> +This is still under the construction. So for the API description, please refer
>> +to the header file.
>
> What is still under construction?
>

We need add muic, charger, etc., so the whole framework isn't finished yet.

>> +
>> +For the example regulator driver, please refer to the MAX77686 regulator driver,
>> +but this driver can't operate without pmic's example driver, which provides an
>> +I/O interface for MAX77686 regulator.
>> +
>> +The second example is a fixed Voltage/Current regulator for a common use.
>> +
>> +The 'regulator' command also supports the new API. The command allow:
>
> allows, or maybe s/allow/allows you to/
>
>> +- list regulator devices
>> +- choose the current device (like the mmc command)
>> +- do all regulator-specific operations
>> +
>> +For more informations, please refer to the file: 'common/cmd_regulator.c'
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Thank you, I will fix the mistakes.

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 13/16] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC
  2015-04-22 16:31         ` Simon Glass
  2015-04-22 17:10           ` Simon Glass
@ 2015-04-23 11:33           ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-23 11:33 UTC (permalink / raw)
  To: u-boot

Hello,

On 04/22/2015 06:31 PM, Simon Glass wrote:
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> In the power_init_board function call, regulator driver init is called,
>> so before compile, make sure that any power framework is defined.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>>   board/samsung/common/board.c | 4 ++--
>>   1 file changed, 2 insertions(+), 2 deletions(-)
>
> Acked-by: Simon Glass <sjg@chromium.org>
>
> Would be good to sort the includes correctly (unrelated to your patch).
>

Ok, will do that.

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 14/16] odroid: board: add support to dm pmic api
  2015-04-22 16:31         ` Simon Glass
  2015-04-22 17:11           ` Simon Glass
@ 2015-04-23 11:33           ` Przemyslaw Marczak
  1 sibling, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-23 11:33 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/22/2015 06:31 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This commit change the old pmic framework calls to the new ones.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> ---
>> Changes v2:
>> - remove board_init_i2c() call
>> - update regulator calls
>> - update headers
>> - samsung/misc.c: include required header
>>
>> Changes v3:
>> - adjust regulator calls to new api
>>
>> Changes V4:
>> - use regulator_list_autoset() for mmc regulators
>>
>> ---
>>   board/samsung/common/misc.c   |  1 +
>>   board/samsung/odroid/odroid.c | 77 +++++++++++++++++++++++++------------------
>>   2 files changed, 46 insertions(+), 32 deletions(-)
>
> Acked-by: Simon Glass <sjg@chromium.org>
>
>>
>> diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
>> index 1a77c82..f0d69d4 100644
>> --- a/board/samsung/common/misc.c
>> +++ b/board/samsung/common/misc.c
>> @@ -16,6 +16,7 @@
>>   #include <asm/arch/cpu.h>
>>   #include <asm/gpio.h>
>>   #include <linux/input.h>
>> +#include <dm.h>
>>   #include <power/pmic.h>
>>   #include <mmc.h>
>>
>> diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
>> index ae41c29..29de325 100644
>> --- a/board/samsung/odroid/odroid.c
>> +++ b/board/samsung/odroid/odroid.c
>> @@ -12,7 +12,9 @@
>>   #include <asm/arch/gpio.h>
>>   #include <asm/gpio.h>
>>   #include <asm/arch/cpu.h>
>> +#include <dm.h>
>>   #include <power/pmic.h>
>> +#include <power/regulator.h>
>>   #include <power/max77686_pmic.h>
>>   #include <errno.h>
>>   #include <mmc.h>
>> @@ -31,6 +33,12 @@ enum {
>>          ODROID_TYPES,
>>   };
>>
>> +static const char *mmc_regulators[] = {
>> +       "VDDQ_EMMC_1.8V",
>> +       "VDDQ_EMMC_2.8V",
>> +       "TFLASH_2.8V",
>
> I wonder if it would be better to terminate with NULL instead of
> requiring an ARRAY_SIZE?
>

Yes, I should do this, will fix.

>> +};
>> +
>>   void set_board_type(void)
>>   {
>>          /* Set GPA1 pin 1 to HI - enable XCL205 output */
>> @@ -403,21 +411,6 @@ static void board_gpio_init(void)
>>   #endif
>>   }
>>
>> -static int pmic_init_max77686(void)
>> -{
>> -       struct pmic *p = pmic_get("MAX77686_PMIC");
>> -
>> -       if (pmic_probe(p))
>> -               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 */
>> -
>> -       return 0;
>> -}
>> -
>>   int exynos_early_init_f(void)
>>   {
>>          board_clock_init();
>> @@ -434,8 +427,10 @@ int exynos_init(void)
>>
>>   int exynos_power_init(void)
>>   {
>> -       pmic_init(0);
>> -       pmic_init_max77686();
>> +       int list_count = ARRAY_SIZE(mmc_regulators);
>> +
>> +       if (regulator_list_autoset(mmc_regulators, list_count, NULL, true))
>> +               error("Unable to init all mmc regulators");
>>
>>          return 0;
>>   }
>> @@ -443,19 +438,20 @@ int exynos_power_init(void)
>>   #ifdef CONFIG_USB_GADGET
>>   static int s5pc210_phy_control(int on)
>>   {
>> -       struct pmic *p_pmic;
>> +       struct udevice *dev;
>> +       int ret;
>>
>> -       p_pmic = pmic_get("MAX77686_PMIC");
>> -       if (!p_pmic)
>> -               return -ENODEV;
>> -
>> -       if (pmic_probe(p_pmic))
>> -               return -1;
>> +       ret = regulator_by_platname("VDD_UOTG_3.0V", &dev);
>> +       if (ret) {
>> +               error("Regulator get error: %d", ret);
>> +               return ret;
>> +       }
>>
>>          if (on)
>> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_ON);
>> +               return regulator_set_mode(dev, OPMODE_ON);
>>          else
>> -               return max77686_set_ldo_mode(p_pmic, 12, OPMODE_LPM);
>> +               return regulator_set_mode(dev, OPMODE_LPM);
>> +
>>   }
>>
>>   struct s3c_plat_otg_data s5pc210_otg_data = {
>> @@ -472,7 +468,8 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
>>   int board_usb_init(int index, enum usb_init_type init)
>>   {
>>   #ifdef CONFIG_CMD_USB
>> -       struct pmic *p_pmic;
>> +       struct udevice *dev;
>> +       int ret;
>>
>>          /* Set Ref freq 0 => 24MHz, 1 => 26MHz*/
>>          /* Odroid Us have it at 24MHz, Odroid Xs at 26MHz */
>> @@ -490,14 +487,30 @@ int board_usb_init(int index, enum usb_init_type init)
>>          /* Power off and on BUCK8 for LAN9730 */
>>          debug("LAN9730 - Turning power buck 8 OFF and ON.\n");
>>
>> -       p_pmic = pmic_get("MAX77686_PMIC");
>> -       if (p_pmic && !pmic_probe(p_pmic)) {
>> -               max77686_set_buck_voltage(p_pmic, 8, 750000);
>> -               max77686_set_buck_voltage(p_pmic, 8, 3300000);
>> +       ret = regulator_by_platname("VCC_P3V3_2.85V", &dev);
>> +       if (ret) {
>> +               error("Regulator get error: %d", ret);
>> +               return ret;
>>          }
>>
>> -#endif
>> +       ret = regulator_set_enable(dev, true);
>> +       if (ret) {
>> +               error("Regulator %s enable setting error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>>
>> +       ret = regulator_set_value(dev, 750000);
>> +       if (ret) {
>> +               error("Regulator %s value setting error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = regulator_set_value(dev, 3300000);
>> +       if (ret) {
>> +               error("Regulator %s value setting error: %d", dev->name, ret);
>> +               return ret;
>> +       }
>> +#endif
>>          debug("USB_udc_probe\n");
>>          return s3c_udc_probe(&s5pc210_otg_data);
>>   }
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 00/16] Power(full) framework based on Driver Model
  2015-04-22 16:29       ` [U-Boot] [PATCH v4 00/16] Power(full) framework based on Driver Model Simon Glass
@ 2015-04-23 11:33         ` Przemyslaw Marczak
  2015-04-24  4:48           ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-23 11:33 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/22/2015 06:29 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Hello,
>> Again the next version. The changes are described below each commit message.
>> This is rebased on last u-boot-dm/master after apply this patchset:
>> https://patchwork.ozlabs.org/patch/462775/
>> https://patchwork.ozlabs.org/patch/462777/
>> https://patchwork.ozlabs.org/patch/462776/
>>
>> This all can be found in here:
>> https://github.com/bobenstein/u-boot/tree/dm-pmic-v4
>>
>> Best regards,
>>
>> Przemyslaw Marczak (16):
>>    exynos5: fix build break by adding CONFIG_POWER
>>    exynos4-common: remove the unsued CONFIG_CMD_PMIC
>>    lib: Kconfig: add entry for errno_str() function
>>    dm: pmic: add implementation of driver model pmic uclass
>>    dm: regulator: add implementation of driver model regulator uclass
>>    dm: pmic: add pmic command
>>    dm: regulator: add regulator command
>>    pmic: max77686 set the same compatible as in the kernel
>>    dm: pmic: add max77686 pmic driver
>>    dm: regulator: add max77686 regulator driver
>>    dm: regulator: add fixed voltage 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                                         |   3 +-
>>   arch/arm/dts/exynos4412-odroid.dts               | 255 ++++++-
>>   arch/arm/dts/exynos4412-trats2.dts               |   2 +-
>>   arch/arm/dts/exynos5250-smdk5250.dts             |   2 +-
>>   arch/arm/dts/exynos5250-snow.dts                 |   2 +-
>>   board/samsung/common/board.c                     |   4 +-
>>   board/samsung/common/misc.c                      |   1 +
>>   board/samsung/odroid/odroid.c                    |  77 ++-
>>   common/Kconfig                                   |  36 +
>>   common/Makefile                                  |   4 +
>>   common/cmd_pmic.c                                | 231 +++++++
>>   common/cmd_regulator.c                           | 403 +++++++++++
>>   configs/odroid_defconfig                         |   8 +-
>>   doc/device-tree-bindings/pmic/max77686.txt       |  36 +
>>   doc/device-tree-bindings/regulator/fixed.txt     |  38 ++
>>   doc/device-tree-bindings/regulator/max77686.txt  |  70 ++
>>   doc/device-tree-bindings/regulator/regulator.txt |  55 ++
>>   doc/driver-model/pmic-framework.txt              | 142 ++++
>>   drivers/power/Kconfig                            |   8 +
>>   drivers/power/Makefile                           |   1 -
>>   drivers/power/pmic/Kconfig                       |  18 +
>>   drivers/power/pmic/Makefile                      |   2 +
>>   drivers/power/pmic/max77686.c                    |  87 +++
>>   drivers/power/pmic/pmic-uclass.c                 | 158 +++++
>>   drivers/power/pmic/pmic_max77686.c               |   2 +-
>>   drivers/power/regulator/Kconfig                  |  33 +
>>   drivers/power/regulator/Makefile                 |  10 +
>>   drivers/power/regulator/fixed.c                  | 126 ++++
>>   drivers/power/regulator/max77686.c               | 825 +++++++++++++++++++++++
>>   drivers/power/regulator/regulator-uclass.c       | 300 +++++++++
>>   include/configs/exynos4-common.h                 |   1 -
>>   include/configs/exynos5-common.h                 |   4 +
>>   include/configs/odroid.h                         |   5 -
>>   include/dm/uclass-id.h                           |   4 +
>>   include/power/max77686_pmic.h                    |  29 +-
>>   include/power/pmic.h                             | 189 ++++++
>>   include/power/regulator.h                        | 384 +++++++++++
>>   lib/Kconfig                                      |   8 +
>>   lib/fdtdec.c                                     |   2 +-
>>   39 files changed, 3512 insertions(+), 53 deletions(-)
>>   create mode 100644 common/cmd_pmic.c
>>   create mode 100644 common/cmd_regulator.c
>>   create mode 100644 doc/device-tree-bindings/pmic/max77686.txt
>>   create mode 100644 doc/device-tree-bindings/regulator/fixed.txt
>>   create mode 100644 doc/device-tree-bindings/regulator/max77686.txt
>>   create mode 100644 doc/device-tree-bindings/regulator/regulator.txt
>>   create mode 100644 doc/driver-model/pmic-framework.txt
>>   create mode 100644 drivers/power/pmic/Kconfig
>>   create mode 100644 drivers/power/pmic/max77686.c
>>   create mode 100644 drivers/power/pmic/pmic-uclass.c
>>   create mode 100644 drivers/power/regulator/Kconfig
>>   create mode 100644 drivers/power/regulator/Makefile
>>   create mode 100644 drivers/power/regulator/fixed.c
>>   create mode 100644 drivers/power/regulator/max77686.c
>>   create mode 100644 drivers/power/regulator/regulator-uclass.c
>>   create mode 100644 include/power/regulator.h
>>
>> --
>> 1.9.1
>>
>
> I'm going to test this and apply to u-boot-dm/next while we wait for
> you to finish the sandbox test code. I've made a few comments on the
> series. They are minor so I don't want to do another whole spin of the
> series (it takes time to review!), but you could do a fix-up patch or
> two if you like. I could perhaps squash them in if you prefer that,
> but honestly they are nits.
>
> It will be great to get this into mainline soon!
>
> Regards,
> Simon
>

Thank you again for the review. I know, that it takes a time, especially 
when things are fully reworked.
I have some other things to do now. So I think, that the end of the next 
week is reliable time to have the sandbox tests ready.

I will try to resend the fixes today or tomorrow.

One more question about the sandbox.

What do you think about adding: "drivers/power/pmic/max77686-sandbox.c"?
The read/write could be done for the file as in the sandbox SPI driver.
Then it's no problem to enable the max77686 regulator for the sandbox. 
This could probably work without dump the real device, when keep the set 
-> get I/O order.

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage regulator driver
  2015-04-20 18:07       ` [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage " Przemyslaw Marczak
  2015-04-22 16:31         ` Simon Glass
@ 2015-04-23 12:31         ` Przemyslaw Marczak
  2015-04-23 12:36           ` Przemyslaw Marczak
  2015-04-24  4:50           ` Simon Glass
  1 sibling, 2 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-23 12:31 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/20/2015 08:07 PM, Przemyslaw Marczak wrote:
> This driver implements regulator operations for fixed Voltage/Current
> value regulators. beside the standard regulator constraints, which are
> put into the uclass platform data, a typical fixed regulator node provides
> few additional properties like:
> - gpio
> - gpio-open-drain
> - enable-active-high
> - startup-delay-us
> The only 'gpio' is used by this driver and is kept in structure of type
> 'fixed_regulator_platdata', as a device platform data (dev->platdata).
>
> The driver implements:
> - get_value
> - get_current
> - get_enable
> - set_enable
>
> The regulator calls and commands can be used for fixed-regulator devices,
> and the proper error will be returned for prohibited.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>

I missed the separation (---) here and I see that you add ACK below the 
changes.
Could you remove them from the tree?

> Changes v3:
> - new file
> - Kconfig add fixed-regulator entry
>
> Changes V4:
> - move DM_REGULATOR_FIXED Kconfig entry from: drivers/power/Kconfig to
>    drivers/power/regulator/Kconfig
> - regulator/fixed.c: adjust to use of uclass platdata and device platdata
> - regulator/fixed.c: includes cleanup
> - regulator/fixed.c: fix gpio request
> - add binding info
> ---
>   doc/device-tree-bindings/regulator/fixed.txt |  38 ++++++++
>   drivers/power/regulator/Kconfig              |   8 ++
>   drivers/power/regulator/Makefile             |   1 +
>   drivers/power/regulator/fixed.c              | 126 +++++++++++++++++++++++++++
>   4 files changed, 173 insertions(+)
>   create mode 100644 doc/device-tree-bindings/regulator/fixed.txt
>   create mode 100644 drivers/power/regulator/fixed.c

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage regulator driver
  2015-04-23 12:31         ` Przemyslaw Marczak
@ 2015-04-23 12:36           ` Przemyslaw Marczak
  2015-04-24  4:50           ` Simon Glass
  1 sibling, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-23 12:36 UTC (permalink / raw)
  To: u-boot

+CC Simon

On 04/23/2015 02:31 PM, Przemyslaw Marczak wrote:
> Hello Simon,
>
> On 04/20/2015 08:07 PM, Przemyslaw Marczak wrote:
>> This driver implements regulator operations for fixed Voltage/Current
>> value regulators. beside the standard regulator constraints, which are
>> put into the uclass platform data, a typical fixed regulator node
>> provides
>> few additional properties like:
>> - gpio
>> - gpio-open-drain
>> - enable-active-high
>> - startup-delay-us
>> The only 'gpio' is used by this driver and is kept in structure of type
>> 'fixed_regulator_platdata', as a device platform data (dev->platdata).
>>
>> The driver implements:
>> - get_value
>> - get_current
>> - get_enable
>> - set_enable
>>
>> The regulator calls and commands can be used for fixed-regulator devices,
>> and the proper error will be returned for prohibited.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>
>
> I missed the separation (---) here and I see that you add ACK below the
> changes.
> Could you remove them from the tree?
>

[...]

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 00/16] Power(full) framework based on Driver Model
  2015-04-23 11:33         ` Przemyslaw Marczak
@ 2015-04-24  4:48           ` Simon Glass
  2015-04-24 12:18             ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-24  4:48 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 23 April 2015 at 05:33, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 04/22/2015 06:29 PM, Simon Glass wrote:
>>
>> Hi Przemyslaw,
>>
>> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>>> Hello,
>>> Again the next version. The changes are described below each commit
>>> message.
>>> This is rebased on last u-boot-dm/master after apply this patchset:
>>> https://patchwork.ozlabs.org/patch/462775/
>>> https://patchwork.ozlabs.org/patch/462777/
>>> https://patchwork.ozlabs.org/patch/462776/
>>>
>>> This all can be found in here:
>>> https://github.com/bobenstein/u-boot/tree/dm-pmic-v4
>>>
>>> Best regards,
>>>
>>> Przemyslaw Marczak (16):
>>>    exynos5: fix build break by adding CONFIG_POWER
>>>    exynos4-common: remove the unsued CONFIG_CMD_PMIC
>>>    lib: Kconfig: add entry for errno_str() function
>>>    dm: pmic: add implementation of driver model pmic uclass
>>>    dm: regulator: add implementation of driver model regulator uclass
>>>    dm: pmic: add pmic command
>>>    dm: regulator: add regulator command
>>>    pmic: max77686 set the same compatible as in the kernel
>>>    dm: pmic: add max77686 pmic driver
>>>    dm: regulator: add max77686 regulator driver
>>>    dm: regulator: add fixed voltage 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                                         |   3 +-
>>>   arch/arm/dts/exynos4412-odroid.dts               | 255 ++++++-
>>>   arch/arm/dts/exynos4412-trats2.dts               |   2 +-
>>>   arch/arm/dts/exynos5250-smdk5250.dts             |   2 +-
>>>   arch/arm/dts/exynos5250-snow.dts                 |   2 +-
>>>   board/samsung/common/board.c                     |   4 +-
>>>   board/samsung/common/misc.c                      |   1 +
>>>   board/samsung/odroid/odroid.c                    |  77 ++-
>>>   common/Kconfig                                   |  36 +
>>>   common/Makefile                                  |   4 +
>>>   common/cmd_pmic.c                                | 231 +++++++
>>>   common/cmd_regulator.c                           | 403 +++++++++++
>>>   configs/odroid_defconfig                         |   8 +-
>>>   doc/device-tree-bindings/pmic/max77686.txt       |  36 +
>>>   doc/device-tree-bindings/regulator/fixed.txt     |  38 ++
>>>   doc/device-tree-bindings/regulator/max77686.txt  |  70 ++
>>>   doc/device-tree-bindings/regulator/regulator.txt |  55 ++
>>>   doc/driver-model/pmic-framework.txt              | 142 ++++
>>>   drivers/power/Kconfig                            |   8 +
>>>   drivers/power/Makefile                           |   1 -
>>>   drivers/power/pmic/Kconfig                       |  18 +
>>>   drivers/power/pmic/Makefile                      |   2 +
>>>   drivers/power/pmic/max77686.c                    |  87 +++
>>>   drivers/power/pmic/pmic-uclass.c                 | 158 +++++
>>>   drivers/power/pmic/pmic_max77686.c               |   2 +-
>>>   drivers/power/regulator/Kconfig                  |  33 +
>>>   drivers/power/regulator/Makefile                 |  10 +
>>>   drivers/power/regulator/fixed.c                  | 126 ++++
>>>   drivers/power/regulator/max77686.c               | 825
>>> +++++++++++++++++++++++
>>>   drivers/power/regulator/regulator-uclass.c       | 300 +++++++++
>>>   include/configs/exynos4-common.h                 |   1 -
>>>   include/configs/exynos5-common.h                 |   4 +
>>>   include/configs/odroid.h                         |   5 -
>>>   include/dm/uclass-id.h                           |   4 +
>>>   include/power/max77686_pmic.h                    |  29 +-
>>>   include/power/pmic.h                             | 189 ++++++
>>>   include/power/regulator.h                        | 384 +++++++++++
>>>   lib/Kconfig                                      |   8 +
>>>   lib/fdtdec.c                                     |   2 +-
>>>   39 files changed, 3512 insertions(+), 53 deletions(-)
>>>   create mode 100644 common/cmd_pmic.c
>>>   create mode 100644 common/cmd_regulator.c
>>>   create mode 100644 doc/device-tree-bindings/pmic/max77686.txt
>>>   create mode 100644 doc/device-tree-bindings/regulator/fixed.txt
>>>   create mode 100644 doc/device-tree-bindings/regulator/max77686.txt
>>>   create mode 100644 doc/device-tree-bindings/regulator/regulator.txt
>>>   create mode 100644 doc/driver-model/pmic-framework.txt
>>>   create mode 100644 drivers/power/pmic/Kconfig
>>>   create mode 100644 drivers/power/pmic/max77686.c
>>>   create mode 100644 drivers/power/pmic/pmic-uclass.c
>>>   create mode 100644 drivers/power/regulator/Kconfig
>>>   create mode 100644 drivers/power/regulator/Makefile
>>>   create mode 100644 drivers/power/regulator/fixed.c
>>>   create mode 100644 drivers/power/regulator/max77686.c
>>>   create mode 100644 drivers/power/regulator/regulator-uclass.c
>>>   create mode 100644 include/power/regulator.h
>>>
>>> --
>>> 1.9.1
>>>
>>
>> I'm going to test this and apply to u-boot-dm/next while we wait for
>> you to finish the sandbox test code. I've made a few comments on the
>> series. They are minor so I don't want to do another whole spin of the
>> series (it takes time to review!), but you could do a fix-up patch or
>> two if you like. I could perhaps squash them in if you prefer that,
>> but honestly they are nits.
>>
>> It will be great to get this into mainline soon!
>>
>> Regards,
>> Simon
>>
>
> Thank you again for the review. I know, that it takes a time, especially
> when things are fully reworked.
> I have some other things to do now. So I think, that the end of the next
> week is reliable time to have the sandbox tests ready.
>
> I will try to resend the fixes today or tomorrow.
>
> One more question about the sandbox.
>
> What do you think about adding: "drivers/power/pmic/max77686-sandbox.c"?
> The read/write could be done for the file as in the sandbox SPI driver.
> Then it's no problem to enable the max77686 regulator for the sandbox. This
> could probably work without dump the real device, when keep the set -> get
> I/O order.

I'd be more comfortable with a new pmic, a really simple 'fake' one
with just a few LDOs, maybe one BUCK. Enough to support some
reasonable tests.

The problem with max77686 is that is it quite complicated, and when
writing and expanding tests, we really want things to be simple.

Also for the tests, we really don't need anything elaborate. Try to
test the main functions.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage regulator driver
  2015-04-23 12:31         ` Przemyslaw Marczak
  2015-04-23 12:36           ` Przemyslaw Marczak
@ 2015-04-24  4:50           ` Simon Glass
  1 sibling, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-24  4:50 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 23 April 2015 at 06:31, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
> On 04/20/2015 08:07 PM, Przemyslaw Marczak wrote:
>>
>> This driver implements regulator operations for fixed Voltage/Current
>> value regulators. beside the standard regulator constraints, which are
>> put into the uclass platform data, a typical fixed regulator node provides
>> few additional properties like:
>> - gpio
>> - gpio-open-drain
>> - enable-active-high
>> - startup-delay-us
>> The only 'gpio' is used by this driver and is kept in structure of type
>> 'fixed_regulator_platdata', as a device platform data (dev->platdata).
>>
>> The driver implements:
>> - get_value
>> - get_current
>> - get_enable
>> - set_enable
>>
>> The regulator calls and commands can be used for fixed-regulator devices,
>> and the proper error will be returned for prohibited.
>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>
>
> I missed the separation (---) here and I see that you add ACK below the
> changes.
> Could you remove them from the tree?

OK, done. You could try patman which handles this automatically.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 04/16] dm: pmic: add implementation of driver model pmic uclass
  2015-04-23 11:33           ` Przemyslaw Marczak
@ 2015-04-24  4:51             ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-24  4:51 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 23 April 2015 at 05:33, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 04/22/2015 06:30 PM, Simon Glass wrote:
>>
>> Hi Przemyslaw,
>>
>> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>>> This commit introduces the PMIC uclass implementation.
>>> It allows providing the basic I/O interface for PMIC devices.
>>> For the multi-function PMIC devices, this can be used as I/O
>>> parent device, for each IC's interface. Then, each PMIC particular
>>> function can be provided by the child device's operations, and the
>>> child devices will use its parent for read/write by the common API.
>>>
>>> Core files:
>>> - 'include/power/pmic.h'
>>> - 'drivers/power/pmic/pmic-uclass.c'
>>>
>>> The old pmic framework is still kept and is independent.
>>>
>>> For more detailed informations, please look into the header file.
>>>
>>> Changes:
>>> - new uclass-id: UCLASS_PMIC
>>> - new config: CONFIG_DM_PMIC
>>>
>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>> ---
>>> 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
>>>
>>> Changes V3:
>>> - pmic-uclass.c and pmic.h:
>>>    -- remove  pmic_if_* functions
>>>    -- add new function pmic_child_node_scan()
>>> - add Kconfig entry
>>>
>>> Changes V4:
>>> - move drivers/power/pmic-uclass.c to drivers/power/pmic/pmic-uclass.c
>>> - move DM_PMIC Kconfig entry: drivers/power/Kconfig to
>>> drivers/power/pmic/Kconfig
>>> - drivers/power/Kconfig: Add menu "Power" and include pmic Kconfig
>>> - Kconfig: provide only the general information about the PMIC
>>> - pmic-uclass.c: add pmic_bind_childs()
>>> - pmic-uclass.c: add debug
>>> - pmic-uclass.c: cleanup includes
>>> - pmic-uclass.c: remove pmic_get_uclass_ops() and use of
>>> dev_get_driver_ops()
>>> - pmic-uclass.c: use of uclass_get_device_by_name()
>>> - pmic-uclass.c: add str_get_num() - for get number from string
>>> - include/power/pmic.h - start comments rewording
>>> - power/pmic.h: comments update
>>> ---
>>>   drivers/power/Kconfig            |   6 ++
>>>   drivers/power/pmic/Kconfig       |  11 +++
>>>   drivers/power/pmic/Makefile      |   1 +
>>>   drivers/power/pmic/pmic-uclass.c | 158 ++++++++++++++++++++++++++++++++
>>>   include/dm/uclass-id.h           |   3 +
>>>   include/power/pmic.h             | 189
>>> +++++++++++++++++++++++++++++++++++++++
>>>   6 files changed, 368 insertions(+)
>>>   create mode 100644 drivers/power/pmic/Kconfig
>>>   create mode 100644 drivers/power/pmic/pmic-uclass.c
>>
>>
>> Acked-by: Simon Glass <sjg@chromium.org>
>>
>> I have a few nits below - perhaps they can be targeted in a follow-up
>> patch or two? I'd like to merge this soon and it is not worth holding
>> up the series for nits.
>>
>
> That's good information. I will fix it all and resend ASAP.
>
>
>>>
>>> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
>>> index f8f0239..d03626e 100644
>>> --- a/drivers/power/Kconfig
>>> +++ b/drivers/power/Kconfig
>>> @@ -1,3 +1,7 @@
>>> +menu "Power"
>>> +
>>> +source "drivers/power/pmic/Kconfig"
>>> +
>>>   config AXP221_POWER
>>>          boolean "axp221 / axp223 pmic support"
>>>          depends on MACH_SUN6I || MACH_SUN8I
>>> @@ -73,3 +77,5 @@ config AXP221_ELDO3_VOLT
>>>          disable eldo3. On some A31(s) tablets it might be used to supply
>>>          1.2V for the SSD2828 chip (converter of parallel LCD interface
>>>          into MIPI DSI).
>>> +
>>> +endmenu
>>> diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
>>> new file mode 100644
>>> index 0000000..d06d632
>>> --- /dev/null
>>> +++ b/drivers/power/pmic/Kconfig
>>> @@ -0,0 +1,11 @@
>>> +config DM_PMIC
>>> +       bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC)"
>>> +       depends on DM
>>> +       ---help---
>>> +       This config enables the driver-model PMIC support.
>>> +       UCLASS_PMIC - designed to provide an I/O interface for PMIC
>>> devices.
>>> +       For the multi-function PMIC devices, this can be used as parent
>>> I/O
>>> +       device for each IC's interface. Then, each children uses its
>>> parent
>>> +       for read/write. For detailed description, please refer to the
>>> files:
>>> +       - 'drivers/power/pmic/pmic-uclass.c'
>>> +       - 'include/power/pmic.h'
>>> diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
>>> index 985cfdb..594f620 100644
>>> --- a/drivers/power/pmic/Makefile
>>> +++ b/drivers/power/pmic/Makefile
>>> @@ -5,6 +5,7 @@
>>>   # SPDX-License-Identifier:     GPL-2.0+
>>>   #
>>>
>>> +obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
>>>   obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o
>>>   obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o
>>>   obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o
>>> diff --git a/drivers/power/pmic/pmic-uclass.c
>>> b/drivers/power/pmic/pmic-uclass.c
>>> new file mode 100644
>>> index 0000000..d82d3da
>>> --- /dev/null
>>> +++ b/drivers/power/pmic/pmic-uclass.c
>>> @@ -0,0 +1,158 @@
>>> +/*
>>> + * Copyright (C) 2014-2015 Samsung Electronics
>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>> + *
>>> + * SPDX-License-Identifier:    GPL-2.0+
>>> + */
>>> +
>>> +#include <common.h>
>>> +#include <fdtdec.h>
>>> +#include <errno.h>
>>> +#include <dm.h>
>>> +#include <dm/lists.h>
>>> +#include <dm/device-internal.h>
>>> +#include <dm/uclass-internal.h>
>>> +#include <power/pmic.h>
>>> +#include <linux/ctype.h>
>>> +
>>> +DECLARE_GLOBAL_DATA_PTR;
>>> +
>>> +static ulong str_get_num(const char *ptr, const char *maxptr)
>>> +{
>>> +       if (!ptr || !maxptr)
>>> +               return 0;
>>> +
>>> +       while (!isdigit(*ptr) && ptr++ < maxptr);
>>> +
>>> +       return simple_strtoul(ptr, NULL, 0);
>>> +}
>>> +
>>> +int pmic_bind_childs(struct udevice *pmic, int offset,
>>> +                    const struct pmic_child_info *child_info)
>>> +{
>>> +       const struct pmic_child_info *info;
>>> +       const void *blob = gd->fdt_blob;
>>> +       struct driver *drv;
>>> +       struct udevice *child;
>>> +       const char *node_name;
>>> +       int node_name_len;
>>> +       int bind_count = 0;
>>> +       int node;
>>> +       int prefix_len;
>>> +       int ret;
>>> +
>>> +       debug("%s for '%s' at node offset: %d\n", __func__, pmic->name,
>>> +             pmic->of_offset);
>>> +
>>> +       for (node = fdt_first_subnode(blob, offset);
>>> +            node > 0;
>>> +            node = fdt_next_subnode(blob, node)) {
>>> +               node_name = fdt_get_name(blob, node, &node_name_len);
>>> +
>>> +               debug("* Found child node: '%s' at offset:%d\n",
>>> node_name,
>>> +                                                                node);
>>> +
>>> +               child = NULL;
>>> +               info = child_info;
>>> +               while (info->prefix) {
>>> +                       prefix_len = strlen(info->prefix);
>>> +                       if (strncasecmp(info->prefix, node_name,
>>> prefix_len) ||
>>> +                           !info->driver) {
>>> +                               info++;
>>> +                               continue;
>>> +                       }
>>> +
>>> +                       debug("  - compatible prefix: '%s'\n",
>>> info->prefix);
>>> +
>>> +                       drv = lists_driver_lookup_name(info->driver);
>>> +                       if (!drv) {
>>> +                               debug("  - driver: '%s' not found!\n",
>>> +                                     info->driver);
>>> +                               continue;
>>> +                       }
>>> +
>>> +                       debug("  - found child driver: '%s'\n",
>>> drv->name);
>>> +
>>> +                       ret = device_bind(pmic, drv, node_name, NULL,
>>> +                                         node, &child);
>>> +                       if (ret) {
>>> +                               debug("  - child binding error: %d\n",
>>> ret);
>>> +                               continue;
>>> +                       }
>>> +
>>> +                       debug("  - bound child device: '%s'\n",
>>> child->name);
>>> +
>>> +                       child->driver_data = str_get_num(node_name +
>>> +                                                        prefix_len,
>>> +                                                        node_name +
>>> +                                                        node_name_len);
>>> +
>>> +                       debug("  - set 'child->driver_data': %lu\n",
>>> +                             child->driver_data);
>>> +                       break;
>>> +               }
>>> +
>>> +               if (child)
>>> +                       bind_count++;
>>> +               else
>>> +                       debug("  - compatible prefix not found\n");
>>> +       }
>>> +
>>> +       debug("Bound: %d childs for PMIC: '%s'\n", bind_count,
>>> pmic->name);
>>> +       return bind_count;
>>> +}
>>> +
>>> +int pmic_get(const char *name, struct udevice **devp)
>>> +{
>>> +       return uclass_get_device_by_name(UCLASS_PMIC, name, devp);
>>> +}
>>> +
>>> +int pmic_reg_count(struct udevice *dev)
>>> +{
>>> +       const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
>>
>>
>> blank line here
>>
>>> +       if (!ops)
>>> +               return -ENOSYS;
>>> +
>>> +       return ops->reg_count;
>>> +}
>>> +
>>> +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len)
>>> +{
>>> +       const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
>>> +       int ret;
>>> +
>>> +       if (!buffer)
>>> +               return -EFAULT;
>>> +
>>> +       if (!ops || !ops->read)
>>> +               return -ENOSYS;
>>> +
>>> +       ret = ops->read(dev, reg, buffer, len);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int
>>> len)
>>> +{
>>> +       const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
>>> +       int ret;
>>> +
>>> +       if (!buffer)
>>> +               return -EFAULT;
>>> +
>>> +       if (!ops || !ops->write)
>>> +               return -ENOSYS;
>>> +
>>> +       ret = ops->write(dev, reg, buffer, len);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +UCLASS_DRIVER(pmic) = {
>>> +       .id             = UCLASS_PMIC,
>>> +       .name           = "pmic",
>>> +};
>>> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
>>> index fddfd35..23b3eb9 100644
>>> --- a/include/dm/uclass-id.h
>>> +++ b/include/dm/uclass-id.h
>>> @@ -46,6 +46,9 @@ enum uclass_id {
>>>          UCLASS_USB_DEV_GENERIC, /* USB generic device */
>>>          UCLASS_MASS_STORAGE,    /* Mass storage device */
>>>
>>> +       /* Power Management */
>>> +       UCLASS_PMIC,            /* PMIC I/O device */
>>> +
>>>          UCLASS_COUNT,
>>>          UCLASS_INVALID = -1,
>>>   };
>>> diff --git a/include/power/pmic.h b/include/power/pmic.h
>>> index afbc5aa..f7ae781 100644
>>> --- a/include/power/pmic.h
>>> +++ b/include/power/pmic.h
>>> @@ -1,4 +1,7 @@
>>>   /*
>>> + *  Copyright (C) 2014-2015 Samsung Electronics
>>> + *  Przemyslaw Marczak <p.marczak@samsung.com>
>>> + *
>>>    *  Copyright (C) 2011-2012 Samsung Electronics
>>>    *  Lukasz Majewski <l.majewski@samsung.com>
>>>    *
>>> @@ -9,10 +12,13 @@
>>>   #define __CORE_PMIC_H_
>>>
>>>   #include <linux/list.h>
>>> +#include <spi.h>
>>>   #include <i2c.h>
>>>   #include <power/power_chrg.h>
>>
>>
>> At some point can you update the ordering of this lot? I think it should
>> be:
>>
>> i2c.g
>> spi.h
>> linux/
>> power/
>>
>>>
>>>   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,189 @@ struct pmic {
>>>          struct pmic *parent;
>>>          struct list_head list;
>>>   };
>>> +#endif /* CONFIG_POWER */
>>> +
>>> +#ifdef CONFIG_DM_PMIC
>>> +/**
>>> + * U-Boot PMIC Framework
>>> + * =====================
>>> + *
>>> + * UCLASS_PMIC - The is designed to provide an I/O interface for PMIC
>>> devices.
>>
>>
>> This is designed
>>
>>> + *
>>> + * For the multi-function PMIC devices, this can be used as parent I/O
>>> device
>>> + * for each IC's interface. Then, each children uses its parent for
>>> read/write.
>>
>>
>> each child
>>
>>> + *
>>> + * The driver model tree could look 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_RTC     (in the
>>> future)
>>> + *
>>> + * We can find two PMIC cases in boards design:
>>> + * - single I/O interface
>>> + * - multiple I/O interfaces
>>> + * We bind single PMIC device for each interface, to provide an I/O as a
>>> parent,
>>
>>
>> a single
>>
>>> + * of proper child devices. Each child usually implements a different
>>> function,
>>> + * controlled by the same interface.
>>> + *
>>> + * The binding should be done automatically. If 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 device nodes, for multi-interface PMIC.
>>> + *
>>> + * Note:
>>> + * Each PMIC interface driver should use a different compatible string.
>>> + *
>>> + * If each pmic child device driver need access the PMIC-specific
>>> registers,
>>
>>
>> If a pmic child device driver needs
>>
>>> + * it need know only the register address and the access can be 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 the example below. It should be done
>>> by call
>>
>>
>> by calling pmic_bind_childs()
>>
>> (which you should rename to pmic_bind_children() I think)
>>
>>> + * the 'pmic_bind_childs()' - please refer to the description of this
>>> function
>>> + * in this header file. This function, should be called in the driver's
>>> '.bind'
>>> + * method.
>>> + *
>>> + * For the example driver, please refer the MAX77686 driver:
>>> + * - 'drivers/power/pmic/max77686.c'
>>> + */
>>> +
>>> +/**
>>> + * 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.
>>> + *
>>> + * @reg_count: devices register count
>>> + * @read:      read 'len' bytes at "reg" and store it into the 'buffer'
>>> + * @write:     write 'len' bytes from the 'buffer' to the register at
>>> 'reg' address
>>> + */
>>> +struct dm_pmic_ops {
>>> +       int reg_count;
>>
>>
>> This should not be in ops as it is not an operation. Perhaps add a
>> function for it, or put it in the uclass platform data?
>>
>
> Adding this to uclass platform data will add another problem - choosing the
> right place for setting this value, because it's usually not given in dts,
> however the device node "reg" property could also provide the address length
> as it is for the memory.
> But probably you will agree, that this is a job for I2C/SPI subsystem, and
> moreover has no sense, since usually the dts files don't provide the "reg"
> property as "<reg length>" for i2c/spi devices.
>
> So maybe putting here an 'ops' function is better idea.
> int (*get_reg_count)(struct udevice *dev);
>
> This is used only for the PMIC command, so if return -ENOSYS, then just
> "dump" command will no available for the device.

Yes I think turning this value into a function is fine. I just don't
think we should have values in the operations struct.

>
>
>>> +       int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int
>>> len);
>>> +       int (*write)(struct udevice *dev, uint reg, const uint8_t
>>> *buffer,
>>> +                    int len);
>>> +};
>>> +
>>> +/* enum pmic_op_type - used for various pmic devices operation calls,
>>
>>
>> /**
>>   * enum pmic_op_type -
>>
>>> + * 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,
>>> +};
>>> +
>>> +/**
>>> + * struct pmic_child_info - basic device's child info for bind child
>>> nodes with
>>> + * the driver by the node name prefix and driver name. This is a helper
>>> struct
>>> + * for function: pmic_bind_childs().
>>> + *
>>> + * @prefix - child node name prefix (or its name if is unique or single)
>>> + * @driver - driver name for the sub-node with prefix
>>> + */
>>> +struct pmic_child_info {
>>> +       const char *prefix;
>>> +       const char *driver;
>>> +};
>>> +
>>> +/* drivers/power/pmic-uclass.c */
>>> +
>>> +/**
>>> + * pmic_bind_childs() - bind drivers for given parent pmic, using child
>>> info
>>> + * found in 'child_info' array.
>>> + *
>>> + * @pmic       - pmic device - the parent of found child's
>>> + * @child_info - N-childs info array
>>> + * @return a positive number of childs, or 0 if no child found (error)
>>> + *
>>> + * Note: For N-childs the child_info array should have N+1 entries and
>>> the last
>>> + * entry prefix should be NULL - the same as for drivers compatible.
>>> + *
>>> + * For example, a single prefix info (N=1):
>>> + * static const struct pmic_child_info bind_info[] = {
>>> + *     { .prefix = "ldo", .driver = "ldo_driver" },
>>> + *     { },
>>> + * };
>>> + *
>>> + * This function is useful for regulator sub-nodes:
>>> + * my_regulator at 0xa {
>>> + *     reg = <0xa>;
>>> + *     (pmic - bind automatically by compatible)
>>> + *     compatible = "my_pmic";
>>> + *     ...
>>> + *     (pmic's childs - bind by pmic_bind_childs())
>>> + *     (nodes prefix: "ldo", driver: "my_regulator_ldo")
>>> + *     ldo1 { ... };
>>> + *     ldo2 { ... };
>>> + *
>>> + *     (nodes prefix: "buck", driver: "my_regulator_buck")
>>> + *     buck1 { ... };
>>> + *     buck2 { ... };
>>> + * };
>>> + */
>>> +int pmic_bind_childs(struct udevice *pmic, int offset,
>>> +                    const struct pmic_child_info *child_info);
>>
>>
>> pmic_bind_children
>>
>>> +
>>> +/**
>>> + * pmic_get: get the pmic device using its name
>>> + *
>>> + * @name - device name
>>> + * @devp - returned pointer to the pmic device
>>> + * @return 0 on success or negative value of errno.
>>> + *
>>> + * The returned devp device can be used with pmic_read/write calls
>>> + */
>>> +int pmic_get(const char *name, struct udevice **devp);
>>> +
>>> +/**
>>> + * pmic_reg_count: get the pmic register count
>>> + *
>>> + * The required pmic device can be obtained by 'pmic_get()'
>>> + *
>>> + * @dev - pointer to the UCLASS_PMIC device
>>> + * @return register count value on success or negative value of errno.
>>> + */
>>> +int pmic_reg_count(struct udevice *dev);
>>> +
>>> +/**
>>> + * pmic_read/write: read/write to the UCLASS_PMIC device
>>> + *
>>> + * The required pmic device can be obtained by 'pmic_get()'
>>> + *
>>> + * @pmic   - pointer to the UCLASS_PMIC device
>>> + * @reg    - device register offset
>>> + * @buffer - pointer to read/write buffer
>>> + * @len    - byte count for read/write
>>> + * @return 0 on success or negative value of errno.
>>> + */
>>> +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len);
>>> +int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int
>>> len);
>>> +#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 +276,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)
>>> --
>>> 1.9.1

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command
  2015-04-23 11:33           ` Przemyslaw Marczak
@ 2015-04-24  4:51             ` Simon Glass
  2015-04-24 12:18               ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-24  4:51 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 23 April 2015 at 05:33, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 04/22/2015 06:30 PM, Simon Glass wrote:
>>
>> Hi Przemyslaw,
>>
>> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>>> This command is based on driver model regulator's API.
>>> The user interface provides:
>>> - list UCLASS regulator devices
>>> - show or [set] operating regulator device
>>> - print constraints info
>>> - print operating status
>>> - print/[set] voltage value [uV] (force)
>>> - print/[set] current value [uA]
>>> - print/[set] operating mode id
>>> - enable the regulator output
>>> - disable the regulator output
>>>
>>> The 'force' option can be used for setting the value which exceeds
>>> the constraints min/max limits.
>>>
>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>> ---
>>> Changes v3:
>>> - new file
>>> - Kconfig entry
>>>
>>> Changes V4:
>>> - cmd regulator: move platdata to uc pdata
>>> - cmd_regulator: includes cleanup
>>> - cmd_regulator: add get_curr_dev_and_pl() check type
>>> - move config name: CONFIG_DM_REGULATOR_CMD to CONFIG_CMD_REGULATOR
>>> - common/Kconfig - cleanup
>>> ---
>>>   common/Kconfig         |  22 +++
>>>   common/Makefile        |   1 +
>>>   common/cmd_regulator.c | 403
>>> +++++++++++++++++++++++++++++++++++++++++++++++++
>>>   3 files changed, 426 insertions(+)
>>>   create mode 100644 common/cmd_regulator.c
>>
>>
>> Acked-by: Simon Glass <sjg@chromium.org>
>>
>> I have a few nits that could be dealt with by a follow-on patch.
>>
>
> Ok.
>
>
>>>
>>> diff --git a/common/Kconfig b/common/Kconfig
>>> index 4666f8e..52f8bb1 100644
>>> --- a/common/Kconfig
>>> +++ b/common/Kconfig
>>> @@ -470,5 +470,27 @@ config CMD_PMIC
>>>            - pmic read address  - read byte of register at address
>>>            - pmic write address - write byte to register at address
>>>            The only one change for this command is 'dev' subcommand.
>>> +
>>> +config CMD_REGULATOR
>>> +       bool "Enable Driver Model REGULATOR command"
>>> +       depends on DM_REGULATOR
>>> +       help
>>> +         This command is based on driver model regulator's API.
>>> +         User interface features:
>>> +         - list               - list regulator devices
>>> +         - regulator dev <id> - show or [set] operating regulator device
>>> +         - regulator info     - print constraints info
>>> +         - regulator status   - print operating status
>>> +         - regulator value <val] <-f> - print/[set] voltage value [uV]
>>> +         - regulator current <val>    - print/[set] current value [uA]
>>> +         - regulator mode <id>        - print/[set] operating mode id
>>> +         - regulator enable           - enable the regulator output
>>> +         - regulator disable          - disable the regulator output
>>> +
>>> +         The '-f' (force) option can be used for set the value which
>>> exceeds
>>> +         the limits, which are found in device-tree and are kept in
>>> regulator's
>>> +         uclass platdata structure.
>>> +
>>>   endmenu
>>> +
>>>   endmenu
>>> diff --git a/common/Makefile b/common/Makefile
>>> index 87a3efe..93bded3 100644
>>> --- a/common/Makefile
>>> +++ b/common/Makefile
>>> @@ -212,6 +212,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
>>>
>>>   # Power
>>>   obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o
>>> +obj-$(CONFIG_CMD_REGULATOR) += cmd_regulator.o
>>>   endif
>>>
>>>   ifdef CONFIG_SPL_BUILD
>>> diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
>>> new file mode 100644
>>> index 0000000..b1b9e87
>>> --- /dev/null
>>> +++ b/common/cmd_regulator.c
>>> @@ -0,0 +1,403 @@
>>> +/*
>>> + * Copyright (C) 2014-2015 Samsung Electronics
>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>> + *
>>> + * SPDX-License-Identifier:    GPL-2.0+
>>> + */
>>> +#include <common.h>
>>> +#include <errno.h>
>>> +#include <dm.h>
>>> +#include <dm/uclass-internal.h>
>>> +#include <power/regulator.h>
>>> +
>>> +#define LIMIT_SEQ      3
>>> +#define LIMIT_DEVNAME  20
>>> +#define LIMIT_OFNAME   20
>>> +#define LIMIT_INFO     16
>>> +
>>> +static struct udevice *currdev;
>>> +
>>> +static int failed(const char *getset, const char *thing,
>>> +                 const char *for_dev, int ret)
>>> +{
>>> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing,
>>> for_dev,
>>> +                                                   ret, errno_str(ret));
>>
>>
>> blank line here.
>
>
> I don't see the blank line here in the patch, which I send.

Odd, there seem to be two blank lines there, and we only need one.

>
>>
>> I worry that if someone gets one of these messages they will not be
>> able to find it in the source code. How about passing in the full
>> printf() string in each case, or just using printf() in situ? I don't
>> think the code space saving is significant.
>>
>
> It's not a debug message. And each one is different, and easy to grep
> "failed". The code is a little cleaner with this. Also the command code is
> not complicated.

git grep -i  failed |wc -l
2089

Is there some way to know it is a PMIC error message, and find it that way?

>
>>> +       return CMD_RET_FAILURE;
>>> +}
>>> +
>>> +static int regulator_get(bool list_only, int get_seq, struct udevice
>>> **devp)
>>
>>
>> This function seems to do multiple things (find and list). Should we
>> split it into two?
>>
>>> +{
>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>> +       struct udevice *dev;
>>> +       int ret;
>>> +
>>> +       if (devp)
>>> +               *devp = NULL;
>>> +
>>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
>>> +            ret = uclass_next_device(&dev)) {
>>
>>
>> This will probe all regulators that it checks. I think it should avoid
>> that. Do you mean to use
>>
>
> Regarding the two above comments, we have two problems:
>
> 1. Getting the regulator by sequencial number (dev->seq).
> I think it's required, because only this method returns the right device.
> Disadvantage: need to probe all devices.

But you can use req_seq, or if you have platform data, check that.

>
> 2. Getting the regulator by "regulator-name"
> (regulator_uclass_platdata->name).
> This would be clean, but unreliable if we have few regulators with the same
> name - I think we should keep this in mind. Advantage: can use for
> non-probed devices.

We could probably refuse to bind regulators with the same name. That's
just going to lead to confusion.

>
> And about the doing multiple things by the regulator_get().
> Following your comments about avoiding the code duplication, I put those
> things into one function, since both actually do the same - loops over the
> uclass's devices.
>
> So we can threat it as a subcommands:
> - regulator_get list
> - regulator_get dev
> Maybe the enum { GET_LIST, GET_DEV } would be better, than the bool.
>
> Is that really bad?

I suspect it would be better as two completely separate functions, and
probably not much more code size.

>
>
>>> +               if (list_only) {
>>> +                       uc_pdata = dev_get_uclass_platdata(dev);
>>> +                       printf("|%*d | %*.*s @ %-*.*s| %s @ %s\n",
>>> +                              LIMIT_SEQ, dev->seq,
>>> +                              LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
>>> +                              LIMIT_OFNAME, LIMIT_OFNAME,
>>> uc_pdata->name,
>>> +                              dev->parent->name,
>>> +                              dev_get_uclass_name(dev->parent));
>>> +                       continue;
>>> +               }
>>> +
>>> +               if (dev->seq == get_seq) {
>>> +                       if (devp)
>>> +                               *devp = dev;
>>> +                       else
>>> +                               return -EINVAL;
>>> +
>>> +                       return 0;
>>> +               }
>>> +       }
>>> +
>>> +       if (list_only)
>>> +               return ret;
>>> +
>>> +       return -ENODEV;
>>> +}
>>> +
>>> +static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>> argv[])
>>> +{
>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>> +       int seq, ret = -ENXIO;
>>> +
>>> +       switch (argc) {
>>> +       case 2:
>>> +               seq = simple_strtoul(argv[1], NULL, 0);
>>> +               ret = uclass_get_device_by_seq(UCLASS_REGULATOR, seq,
>>> &currdev);
>>> +               if (ret && (ret = regulator_get(false, seq, &currdev)))
>>> +                       goto failed;
>>> +       case 1:
>>> +               uc_pdata = dev_get_uclass_platdata(currdev);
>>> +               if (!uc_pdata)
>>> +                       goto failed;
>>> +
>>> +               printf("dev: %d @ %s\n", currdev->seq, uc_pdata->name);
>>> +       }
>>> +
>>> +       return CMD_RET_SUCCESS;
>>> +failed:
>>> +       return failed("get", "the", "device", ret);
>>> +}
>>> +
>>> +static int get_curr_dev_and_pl(struct udevice **devp,
>>
>>
>> What is pl? The name does not seem very meaningful to me.
>>
>
> The platdata, ok I will tune it.
>
>
>>> +                              struct dm_regulator_uclass_platdata
>>> **uc_pdata,
>>> +                              bool allow_type_fixed)
>>> +{
>>> +       *devp = NULL;
>>> +       *uc_pdata = NULL;
>>> +
>>> +       if (!currdev)
>>> +               return failed("get", "current", "device", -ENODEV);
>>> +
>>> +       *devp = currdev;
>>> +
>>> +       *uc_pdata = dev_get_uclass_platdata(*devp);
>>> +       if (!*uc_pdata)
>>> +               return failed("get", "regulator", "platdata", -ENXIO);
>>> +
>>> +       if (!allow_type_fixed && (*uc_pdata)->type ==
>>> REGULATOR_TYPE_FIXED) {
>>> +               printf("Operation not allowed for fixed regulator!\n");
>>> +               return CMD_RET_FAILURE;
>>> +       }
>>> +
>>> +       return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>> argv[])
>>> +{
>>> +       int ret;
>>> +
>>> +       printf("|%*s | %*.*s @ %-*.*s| %s @ %s\n",
>>> +              LIMIT_SEQ, "Seq",
>>> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
>>> +              LIMIT_OFNAME, LIMIT_OFNAME, "fdtname",
>>> +              "Parent", "uclass");
>>> +
>>> +       ret = regulator_get(true, 0, NULL);
>>> +       if (ret)
>>> +               return CMD_RET_FAILURE;
>>> +
>>> +       return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +static int constraint(const char *name, int val, const char *val_name)
>>> +{
>>> +       printf("%-*s", LIMIT_INFO, name);
>>> +       if (val < 0) {
>>> +               printf(" %s (err: %d)\n", errno_str(val), val);
>>> +               return val;
>>> +       }
>>> +
>>> +       if (val_name)
>>> +               printf(" %d (%s)\n", val, val_name);
>>> +       else
>>> +               printf(" %d\n", val);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static const char *get_mode_name(struct dm_regulator_mode *mode,
>>> +                                int mode_count,
>>> +                                int mode_id)
>>> +{
>>> +       while (mode_count--) {
>>> +               if (mode->id == mode_id)
>>> +                       return mode->name;
>>> +               mode++;
>>> +       }
>>> +
>>> +       return NULL;
>>> +}
>>> +
>>> +static int do_info(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>> argv[])
>>> +{
>>> +       struct udevice *dev;
>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>> +       struct dm_regulator_mode *modes;
>>> +       const char *parent_uc;
>>> +       int mode_count;
>>> +       int ret;
>>> +       int i;
>>> +
>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       parent_uc = dev_get_uclass_name(dev->parent);
>>> +
>>> +       printf("Uclass regulator dev %d info:\n", dev->seq);
>>> +       printf("%-*s %s @ %s\n%-*s %s\n%-*s %s\n%-*s\n",
>>> +              LIMIT_INFO, "* parent:", dev->parent->name, parent_uc,
>>> +              LIMIT_INFO, "* dev name:", dev->name,
>>> +              LIMIT_INFO, "* fdt name:", uc_pdata->name,
>>> +              LIMIT_INFO, "* constraints:");
>>> +
>>> +       constraint("  - min uV:", uc_pdata->min_uV, NULL);
>>> +       constraint("  - max uV:", uc_pdata->max_uV, NULL);
>>> +       constraint("  - min uA:", uc_pdata->min_uA, NULL);
>>> +       constraint("  - max uA:", uc_pdata->max_uA, NULL);
>>> +       constraint("  - always on:", uc_pdata->always_on,
>>> +                  uc_pdata->always_on ? "true" : "false");
>>> +       constraint("  - boot on:", uc_pdata->boot_on,
>>> +                  uc_pdata->boot_on ? "true" : "false");
>>> +
>>> +       mode_count = regulator_mode(dev, &modes);
>>> +       constraint("* op modes:", mode_count, NULL);
>>> +
>>> +       for (i = 0; i < mode_count; i++, modes++)
>>> +               constraint("  - mode id:", modes->id, modes->name);
>>> +
>>> +       return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +static int do_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>> argv[])
>>> +{
>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>> +       int current, value, mode, ret;
>>> +       const char *mode_name = NULL;
>>> +       struct udevice *dev;
>>> +       bool enabled;
>>> +
>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       enabled = regulator_get_enable(dev);
>>> +       constraint(" * enable:", enabled, enabled ? "true" : "false");
>>> +
>>> +       value = regulator_get_value(dev);
>>> +       constraint(" * value uV:", value, NULL);
>>> +
>>> +       current = regulator_get_current(dev);
>>> +       constraint(" * current uA:", current, NULL);
>>> +
>>> +       mode = regulator_get_mode(dev);
>>> +       mode_name = get_mode_name(uc_pdata->mode, uc_pdata->mode_count,
>>> mode);
>>> +       constraint(" * mode id:", mode, mode_name);
>>> +
>>> +       return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +static int do_value(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>> argv[])
>>> +{
>>> +       struct udevice *dev;
>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>> +       int value;
>>> +       int force;
>>> +       int ret;
>>> +
>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       if (argc == 1) {
>>> +               value = regulator_get_value(dev);
>>> +               if (value < 0)
>>> +                       return failed("get", uc_pdata->name, "voltage",
>>> value);
>>> +
>>> +               printf("%d uV\n", value);
>>> +               return CMD_RET_SUCCESS;
>>> +       }
>>> +
>>> +       if (argc == 3)
>>> +               force = !strcmp("-f", argv[2]);
>>> +       else
>>> +               force = 0;
>>> +
>>> +       value = simple_strtoul(argv[1], NULL, 0);
>>> +       if ((value < uc_pdata->min_uV || value > uc_pdata->max_uV) &&
>>> !force) {
>>> +               printf("Value exceeds regulator constraint limits\n");
>>> +               return CMD_RET_FAILURE;
>>> +       }
>>> +
>>> +       ret = regulator_set_value(dev, value);
>>> +       if (ret)
>>> +               return failed("set", uc_pdata->name, "voltage value",
>>> ret);
>>> +
>>> +       return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +static int do_current(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>> argv[])
>>> +{
>>> +       struct udevice *dev;
>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>> +       int current;
>>> +       int ret;
>>> +
>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       if (argc == 1) {
>>> +               current = regulator_get_current(dev);
>>> +               if (current < 0)
>>> +                       return failed("get", uc_pdata->name, "current",
>>> current);
>>> +
>>> +               printf("%d uA\n", current);
>>> +               return CMD_RET_SUCCESS;
>>> +       }
>>> +
>>> +       current = simple_strtoul(argv[1], NULL, 0);
>>> +       if (current < uc_pdata->min_uA || current > uc_pdata->max_uA) {
>>> +               printf("Current exceeds regulator constraint limits\n");
>>> +               return CMD_RET_FAILURE;
>>> +       }
>>> +
>>> +       ret = regulator_set_current(dev, current);
>>> +       if (ret)
>>> +               return failed("set", uc_pdata->name, "current value",
>>> ret);
>>> +
>>> +       return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +static int do_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>> argv[])
>>> +{
>>> +       struct udevice *dev;
>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>> +       int new_mode;
>>> +       int mode;
>>> +       int ret;
>>> +
>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, false);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       if (argc == 1) {
>>> +               mode = regulator_get_mode(dev);
>>> +               if (mode < 0)
>>> +                       return failed("get", uc_pdata->name, "mode",
>>> mode);
>>> +
>>> +               printf("mode id: %d\n", mode);
>>> +               return CMD_RET_SUCCESS;
>>> +       }
>>> +
>>> +       new_mode = simple_strtoul(argv[1], NULL, 0);
>>> +
>>> +       ret = regulator_set_mode(dev, new_mode);
>>> +       if (ret)
>>> +               return failed("set", uc_pdata->name, "mode", ret);
>>> +
>>> +       return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +static int do_enable(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>> argv[])
>>> +{
>>> +       struct udevice *dev;
>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>> +       int ret;
>>> +
>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = regulator_set_enable(dev, true);
>>> +       if (ret)
>>> +               return failed("enable", "regulator", uc_pdata->name,
>>> ret);
>>> +
>>> +       return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +static int do_disable(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>> argv[])
>>> +{
>>> +       struct udevice *dev;
>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>> +       int ret;
>>> +
>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = regulator_set_enable(dev, false);
>>> +       if (ret)
>>> +               return failed("disable", "regulator", uc_pdata->name,
>>> ret);
>>> +
>>> +       return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +static cmd_tbl_t subcmd[] = {
>>> +       U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
>>> +       U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
>>> +       U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""),
>>> +       U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
>>> +       U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""),
>>> +       U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""),
>>> +       U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""),
>>> +       U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""),
>>> +       U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""),
>>> +};
>>> +
>>> +static int do_regulator(cmd_tbl_t *cmdtp, int flag, int argc,
>>> +                       char * const argv[])
>>> +{
>>> +       cmd_tbl_t *cmd;
>>> +
>>> +       argc--;
>>> +       argv++;
>>> +
>>> +       cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
>>> +       if (cmd == NULL || argc > cmd->maxargs)
>>> +               return CMD_RET_USAGE;
>>> +
>>> +       return cmd->cmd(cmdtp, flag, argc, argv);
>>> +}
>>> +
>>> +U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator,
>>> +       "uclass operations",
>>> +       "list         - list UCLASS regulator devices\n"
>>> +       "regulator dev [id]     - show or [set] operating regulator
>>> device\n"
>>> +       "regulator [info]       - print constraints info\n"
>>> +       "regulator [status]     - print operating status\n"
>>> +       "regulator [value] [-f] - print/[set] voltage value [uV]
>>> (force)\n"
>>> +       "regulator [current]    - print/[set] current value [uA]\n"
>>> +       "regulator [mode_id]    - print/[set] operating mode id\n"
>>> +       "regulator [enable]     - enable the regulator output\n"
>>> +       "regulator [disable]    - disable the regulator output\n"
>>> +);
>>> --
>>> 1.9.1
>>>

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command
  2015-04-24  4:51             ` Simon Glass
@ 2015-04-24 12:18               ` Przemyslaw Marczak
  2015-04-24 12:34                 ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-24 12:18 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/24/2015 06:51 AM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 23 April 2015 at 05:33, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Hello Simon,
>>
>>
>> On 04/22/2015 06:30 PM, Simon Glass wrote:
>>>
>>> Hi Przemyslaw,
>>>
>>> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com>
>>> wrote:
>>>>
>>>> This command is based on driver model regulator's API.
>>>> The user interface provides:
>>>> - list UCLASS regulator devices
>>>> - show or [set] operating regulator device
>>>> - print constraints info
>>>> - print operating status
>>>> - print/[set] voltage value [uV] (force)
>>>> - print/[set] current value [uA]
>>>> - print/[set] operating mode id
>>>> - enable the regulator output
>>>> - disable the regulator output
>>>>
>>>> The 'force' option can be used for setting the value which exceeds
>>>> the constraints min/max limits.
>>>>
>>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>>> ---
>>>> Changes v3:
>>>> - new file
>>>> - Kconfig entry
>>>>
>>>> Changes V4:
>>>> - cmd regulator: move platdata to uc pdata
>>>> - cmd_regulator: includes cleanup
>>>> - cmd_regulator: add get_curr_dev_and_pl() check type
>>>> - move config name: CONFIG_DM_REGULATOR_CMD to CONFIG_CMD_REGULATOR
>>>> - common/Kconfig - cleanup
>>>> ---
>>>>    common/Kconfig         |  22 +++
>>>>    common/Makefile        |   1 +
>>>>    common/cmd_regulator.c | 403
>>>> +++++++++++++++++++++++++++++++++++++++++++++++++
>>>>    3 files changed, 426 insertions(+)
>>>>    create mode 100644 common/cmd_regulator.c
>>>
>>>
>>> Acked-by: Simon Glass <sjg@chromium.org>
>>>
>>> I have a few nits that could be dealt with by a follow-on patch.
>>>
>>
>> Ok.
>>
>>
>>>>
>>>> diff --git a/common/Kconfig b/common/Kconfig
>>>> index 4666f8e..52f8bb1 100644
>>>> --- a/common/Kconfig
>>>> +++ b/common/Kconfig
>>>> @@ -470,5 +470,27 @@ config CMD_PMIC
>>>>             - pmic read address  - read byte of register at address
>>>>             - pmic write address - write byte to register at address
>>>>             The only one change for this command is 'dev' subcommand.
>>>> +
>>>> +config CMD_REGULATOR
>>>> +       bool "Enable Driver Model REGULATOR command"
>>>> +       depends on DM_REGULATOR
>>>> +       help
>>>> +         This command is based on driver model regulator's API.
>>>> +         User interface features:
>>>> +         - list               - list regulator devices
>>>> +         - regulator dev <id> - show or [set] operating regulator device
>>>> +         - regulator info     - print constraints info
>>>> +         - regulator status   - print operating status
>>>> +         - regulator value <val] <-f> - print/[set] voltage value [uV]
>>>> +         - regulator current <val>    - print/[set] current value [uA]
>>>> +         - regulator mode <id>        - print/[set] operating mode id
>>>> +         - regulator enable           - enable the regulator output
>>>> +         - regulator disable          - disable the regulator output
>>>> +
>>>> +         The '-f' (force) option can be used for set the value which
>>>> exceeds
>>>> +         the limits, which are found in device-tree and are kept in
>>>> regulator's
>>>> +         uclass platdata structure.
>>>> +
>>>>    endmenu
>>>> +
>>>>    endmenu
>>>> diff --git a/common/Makefile b/common/Makefile
>>>> index 87a3efe..93bded3 100644
>>>> --- a/common/Makefile
>>>> +++ b/common/Makefile
>>>> @@ -212,6 +212,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
>>>>
>>>>    # Power
>>>>    obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o
>>>> +obj-$(CONFIG_CMD_REGULATOR) += cmd_regulator.o
>>>>    endif
>>>>
>>>>    ifdef CONFIG_SPL_BUILD
>>>> diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
>>>> new file mode 100644
>>>> index 0000000..b1b9e87
>>>> --- /dev/null
>>>> +++ b/common/cmd_regulator.c
>>>> @@ -0,0 +1,403 @@
>>>> +/*
>>>> + * Copyright (C) 2014-2015 Samsung Electronics
>>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>>> + *
>>>> + * SPDX-License-Identifier:    GPL-2.0+
>>>> + */
>>>> +#include <common.h>
>>>> +#include <errno.h>
>>>> +#include <dm.h>
>>>> +#include <dm/uclass-internal.h>
>>>> +#include <power/regulator.h>
>>>> +
>>>> +#define LIMIT_SEQ      3
>>>> +#define LIMIT_DEVNAME  20
>>>> +#define LIMIT_OFNAME   20
>>>> +#define LIMIT_INFO     16
>>>> +
>>>> +static struct udevice *currdev;
>>>> +
>>>> +static int failed(const char *getset, const char *thing,
>>>> +                 const char *for_dev, int ret)
>>>> +{
>>>> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing,
>>>> for_dev,
>>>> +                                                   ret, errno_str(ret));
>>>
>>>
>>> blank line here.
>>
>>
>> I don't see the blank line here in the patch, which I send.
>
> Odd, there seem to be two blank lines there, and we only need one.
>

Ah, sorry. You mean, that there should be added a blank line.
Ok, will add one.

>>
>>>
>>> I worry that if someone gets one of these messages they will not be
>>> able to find it in the source code. How about passing in the full
>>> printf() string in each case, or just using printf() in situ? I don't
>>> think the code space saving is significant.
>>>
>>
>> It's not a debug message. And each one is different, and easy to grep
>> "failed". The code is a little cleaner with this. Also the command code is
>> not complicated.
>
> git grep -i  failed |wc -l
> 2089
>
> Is there some way to know it is a PMIC error message, and find it that way?
>

Ok, I assumed that you know which command you called, and where to find 
it, so you could use:
grep -i "failed" common/cmd_regulator.c | wc -l
15

But this was only the function name, not a useful text for grep.
Now I see that this should use the printf instead of the helper funcion.

>>
>>>> +       return CMD_RET_FAILURE;
>>>> +}
>>>> +
>>>> +static int regulator_get(bool list_only, int get_seq, struct udevice
>>>> **devp)
>>>
>>>
>>> This function seems to do multiple things (find and list). Should we
>>> split it into two?
>>>
>>>> +{
>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>> +       struct udevice *dev;
>>>> +       int ret;
>>>> +
>>>> +       if (devp)
>>>> +               *devp = NULL;
>>>> +
>>>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
>>>> +            ret = uclass_next_device(&dev)) {
>>>
>>>
>>> This will probe all regulators that it checks. I think it should avoid
>>> that. Do you mean to use
>>>
>>
>> Regarding the two above comments, we have two problems:
>>
>> 1. Getting the regulator by sequencial number (dev->seq).
>> I think it's required, because only this method returns the right device.
>> Disadvantage: need to probe all devices.
>
> But you can use req_seq, or if you have platform data, check that.
>

Ok, we could use the req_seq for the PMIC uclass, it's natural that 
interface, has its address and <reg> property - but this can repeat,
if we have two PMICs on a different busses. This is probably possible.

We also shouldn't set the req_seq as the number found in node name, 
because those numbers can repeat: ldo1 {}; buck1 {}; regulator1 { }.

I think that, using the req_seq is bad idea, since we can't be sure that 
those values are unique.

I understand that, the probe is not ideal here? But from the other side,
if we call "pmic list", then we are sure, that listed devices are ready 
to use. Shouldn't we expect this?

>>
>> 2. Getting the regulator by "regulator-name"
>> (regulator_uclass_platdata->name).
>> This would be clean, but unreliable if we have few regulators with the same
>> name - I think we should keep this in mind. Advantage: can use for
>> non-probed devices.
>
> We could probably refuse to bind regulators with the same name. That's
> just going to lead to confusion.
>

This could be good, because at present we can get only the first 
matching device, for the given "regulator-name" constraint.
Ok, I need add the checking routine to regulator's post_bind() method.
And then refuse the bind of the repeated one.

>>
>> And about the doing multiple things by the regulator_get().
>> Following your comments about avoiding the code duplication, I put those
>> things into one function, since both actually do the same - loops over the
>> uclass's devices.
>>
>> So we can threat it as a subcommands:
>> - regulator_get list
>> - regulator_get dev
>> Maybe the enum { GET_LIST, GET_DEV } would be better, than the bool.
>>
>> Is that really bad?
>
> I suspect it would be better as two completely separate functions, and
> probably not much more code size.
>

Ok.

>>
>>
>>>> +               if (list_only) {
>>>> +                       uc_pdata = dev_get_uclass_platdata(dev);
>>>> +                       printf("|%*d | %*.*s @ %-*.*s| %s @ %s\n",
>>>> +                              LIMIT_SEQ, dev->seq,
>>>> +                              LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
>>>> +                              LIMIT_OFNAME, LIMIT_OFNAME,
>>>> uc_pdata->name,
>>>> +                              dev->parent->name,
>>>> +                              dev_get_uclass_name(dev->parent));
>>>> +                       continue;
>>>> +               }
>>>> +
>>>> +               if (dev->seq == get_seq) {
>>>> +                       if (devp)
>>>> +                               *devp = dev;
>>>> +                       else
>>>> +                               return -EINVAL;
>>>> +
>>>> +                       return 0;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       if (list_only)
>>>> +               return ret;
>>>> +
>>>> +       return -ENODEV;
>>>> +}
>>>> +
>>>> +static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>> argv[])
>>>> +{
>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>> +       int seq, ret = -ENXIO;
>>>> +
>>>> +       switch (argc) {
>>>> +       case 2:
>>>> +               seq = simple_strtoul(argv[1], NULL, 0);
>>>> +               ret = uclass_get_device_by_seq(UCLASS_REGULATOR, seq,
>>>> &currdev);
>>>> +               if (ret && (ret = regulator_get(false, seq, &currdev)))
>>>> +                       goto failed;
>>>> +       case 1:
>>>> +               uc_pdata = dev_get_uclass_platdata(currdev);
>>>> +               if (!uc_pdata)
>>>> +                       goto failed;
>>>> +
>>>> +               printf("dev: %d @ %s\n", currdev->seq, uc_pdata->name);
>>>> +       }
>>>> +
>>>> +       return CMD_RET_SUCCESS;
>>>> +failed:
>>>> +       return failed("get", "the", "device", ret);
>>>> +}
>>>> +
>>>> +static int get_curr_dev_and_pl(struct udevice **devp,
>>>
>>>
>>> What is pl? The name does not seem very meaningful to me.
>>>
>>
>> The platdata, ok I will tune it.
>>
>>
>>>> +                              struct dm_regulator_uclass_platdata
>>>> **uc_pdata,
>>>> +                              bool allow_type_fixed)
>>>> +{
>>>> +       *devp = NULL;
>>>> +       *uc_pdata = NULL;
>>>> +
>>>> +       if (!currdev)
>>>> +               return failed("get", "current", "device", -ENODEV);
>>>> +
>>>> +       *devp = currdev;
>>>> +
>>>> +       *uc_pdata = dev_get_uclass_platdata(*devp);
>>>> +       if (!*uc_pdata)
>>>> +               return failed("get", "regulator", "platdata", -ENXIO);
>>>> +
>>>> +       if (!allow_type_fixed && (*uc_pdata)->type ==
>>>> REGULATOR_TYPE_FIXED) {
>>>> +               printf("Operation not allowed for fixed regulator!\n");
>>>> +               return CMD_RET_FAILURE;
>>>> +       }
>>>> +
>>>> +       return CMD_RET_SUCCESS;
>>>> +}
>>>> +
>>>> +static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>> argv[])
>>>> +{
>>>> +       int ret;
>>>> +
>>>> +       printf("|%*s | %*.*s @ %-*.*s| %s @ %s\n",
>>>> +              LIMIT_SEQ, "Seq",
>>>> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
>>>> +              LIMIT_OFNAME, LIMIT_OFNAME, "fdtname",
>>>> +              "Parent", "uclass");
>>>> +
>>>> +       ret = regulator_get(true, 0, NULL);
>>>> +       if (ret)
>>>> +               return CMD_RET_FAILURE;
>>>> +
>>>> +       return CMD_RET_SUCCESS;
>>>> +}
>>>> +
>>>> +static int constraint(const char *name, int val, const char *val_name)
>>>> +{
>>>> +       printf("%-*s", LIMIT_INFO, name);
>>>> +       if (val < 0) {
>>>> +               printf(" %s (err: %d)\n", errno_str(val), val);
>>>> +               return val;
>>>> +       }
>>>> +
>>>> +       if (val_name)
>>>> +               printf(" %d (%s)\n", val, val_name);
>>>> +       else
>>>> +               printf(" %d\n", val);
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static const char *get_mode_name(struct dm_regulator_mode *mode,
>>>> +                                int mode_count,
>>>> +                                int mode_id)
>>>> +{
>>>> +       while (mode_count--) {
>>>> +               if (mode->id == mode_id)
>>>> +                       return mode->name;
>>>> +               mode++;
>>>> +       }
>>>> +
>>>> +       return NULL;
>>>> +}
>>>> +
>>>> +static int do_info(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>> argv[])
>>>> +{
>>>> +       struct udevice *dev;
>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>> +       struct dm_regulator_mode *modes;
>>>> +       const char *parent_uc;
>>>> +       int mode_count;
>>>> +       int ret;
>>>> +       int i;
>>>> +
>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       parent_uc = dev_get_uclass_name(dev->parent);
>>>> +
>>>> +       printf("Uclass regulator dev %d info:\n", dev->seq);
>>>> +       printf("%-*s %s @ %s\n%-*s %s\n%-*s %s\n%-*s\n",
>>>> +              LIMIT_INFO, "* parent:", dev->parent->name, parent_uc,
>>>> +              LIMIT_INFO, "* dev name:", dev->name,
>>>> +              LIMIT_INFO, "* fdt name:", uc_pdata->name,
>>>> +              LIMIT_INFO, "* constraints:");
>>>> +
>>>> +       constraint("  - min uV:", uc_pdata->min_uV, NULL);
>>>> +       constraint("  - max uV:", uc_pdata->max_uV, NULL);
>>>> +       constraint("  - min uA:", uc_pdata->min_uA, NULL);
>>>> +       constraint("  - max uA:", uc_pdata->max_uA, NULL);
>>>> +       constraint("  - always on:", uc_pdata->always_on,
>>>> +                  uc_pdata->always_on ? "true" : "false");
>>>> +       constraint("  - boot on:", uc_pdata->boot_on,
>>>> +                  uc_pdata->boot_on ? "true" : "false");
>>>> +
>>>> +       mode_count = regulator_mode(dev, &modes);
>>>> +       constraint("* op modes:", mode_count, NULL);
>>>> +
>>>> +       for (i = 0; i < mode_count; i++, modes++)
>>>> +               constraint("  - mode id:", modes->id, modes->name);
>>>> +
>>>> +       return CMD_RET_SUCCESS;
>>>> +}
>>>> +
>>>> +static int do_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>> argv[])
>>>> +{
>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>> +       int current, value, mode, ret;
>>>> +       const char *mode_name = NULL;
>>>> +       struct udevice *dev;
>>>> +       bool enabled;
>>>> +
>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       enabled = regulator_get_enable(dev);
>>>> +       constraint(" * enable:", enabled, enabled ? "true" : "false");
>>>> +
>>>> +       value = regulator_get_value(dev);
>>>> +       constraint(" * value uV:", value, NULL);
>>>> +
>>>> +       current = regulator_get_current(dev);
>>>> +       constraint(" * current uA:", current, NULL);
>>>> +
>>>> +       mode = regulator_get_mode(dev);
>>>> +       mode_name = get_mode_name(uc_pdata->mode, uc_pdata->mode_count,
>>>> mode);
>>>> +       constraint(" * mode id:", mode, mode_name);
>>>> +
>>>> +       return CMD_RET_SUCCESS;
>>>> +}
>>>> +
>>>> +static int do_value(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>> argv[])
>>>> +{
>>>> +       struct udevice *dev;
>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>> +       int value;
>>>> +       int force;
>>>> +       int ret;
>>>> +
>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       if (argc == 1) {
>>>> +               value = regulator_get_value(dev);
>>>> +               if (value < 0)
>>>> +                       return failed("get", uc_pdata->name, "voltage",
>>>> value);
>>>> +
>>>> +               printf("%d uV\n", value);
>>>> +               return CMD_RET_SUCCESS;
>>>> +       }
>>>> +
>>>> +       if (argc == 3)
>>>> +               force = !strcmp("-f", argv[2]);
>>>> +       else
>>>> +               force = 0;
>>>> +
>>>> +       value = simple_strtoul(argv[1], NULL, 0);
>>>> +       if ((value < uc_pdata->min_uV || value > uc_pdata->max_uV) &&
>>>> !force) {
>>>> +               printf("Value exceeds regulator constraint limits\n");
>>>> +               return CMD_RET_FAILURE;
>>>> +       }
>>>> +
>>>> +       ret = regulator_set_value(dev, value);
>>>> +       if (ret)
>>>> +               return failed("set", uc_pdata->name, "voltage value",
>>>> ret);
>>>> +
>>>> +       return CMD_RET_SUCCESS;
>>>> +}
>>>> +
>>>> +static int do_current(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>> argv[])
>>>> +{
>>>> +       struct udevice *dev;
>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>> +       int current;
>>>> +       int ret;
>>>> +
>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       if (argc == 1) {
>>>> +               current = regulator_get_current(dev);
>>>> +               if (current < 0)
>>>> +                       return failed("get", uc_pdata->name, "current",
>>>> current);
>>>> +
>>>> +               printf("%d uA\n", current);
>>>> +               return CMD_RET_SUCCESS;
>>>> +       }
>>>> +
>>>> +       current = simple_strtoul(argv[1], NULL, 0);
>>>> +       if (current < uc_pdata->min_uA || current > uc_pdata->max_uA) {
>>>> +               printf("Current exceeds regulator constraint limits\n");
>>>> +               return CMD_RET_FAILURE;
>>>> +       }
>>>> +
>>>> +       ret = regulator_set_current(dev, current);
>>>> +       if (ret)
>>>> +               return failed("set", uc_pdata->name, "current value",
>>>> ret);
>>>> +
>>>> +       return CMD_RET_SUCCESS;
>>>> +}
>>>> +
>>>> +static int do_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>> argv[])
>>>> +{
>>>> +       struct udevice *dev;
>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>> +       int new_mode;
>>>> +       int mode;
>>>> +       int ret;
>>>> +
>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, false);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       if (argc == 1) {
>>>> +               mode = regulator_get_mode(dev);
>>>> +               if (mode < 0)
>>>> +                       return failed("get", uc_pdata->name, "mode",
>>>> mode);
>>>> +
>>>> +               printf("mode id: %d\n", mode);
>>>> +               return CMD_RET_SUCCESS;
>>>> +       }
>>>> +
>>>> +       new_mode = simple_strtoul(argv[1], NULL, 0);
>>>> +
>>>> +       ret = regulator_set_mode(dev, new_mode);
>>>> +       if (ret)
>>>> +               return failed("set", uc_pdata->name, "mode", ret);
>>>> +
>>>> +       return CMD_RET_SUCCESS;
>>>> +}
>>>> +
>>>> +static int do_enable(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>> argv[])
>>>> +{
>>>> +       struct udevice *dev;
>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>> +       int ret;
>>>> +
>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       ret = regulator_set_enable(dev, true);
>>>> +       if (ret)
>>>> +               return failed("enable", "regulator", uc_pdata->name,
>>>> ret);
>>>> +
>>>> +       return CMD_RET_SUCCESS;
>>>> +}
>>>> +
>>>> +static int do_disable(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>> argv[])
>>>> +{
>>>> +       struct udevice *dev;
>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>> +       int ret;
>>>> +
>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       ret = regulator_set_enable(dev, false);
>>>> +       if (ret)
>>>> +               return failed("disable", "regulator", uc_pdata->name,
>>>> ret);
>>>> +
>>>> +       return CMD_RET_SUCCESS;
>>>> +}
>>>> +
>>>> +static cmd_tbl_t subcmd[] = {
>>>> +       U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
>>>> +       U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
>>>> +       U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""),
>>>> +       U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
>>>> +       U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""),
>>>> +       U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""),
>>>> +       U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""),
>>>> +       U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""),
>>>> +       U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""),
>>>> +};
>>>> +
>>>> +static int do_regulator(cmd_tbl_t *cmdtp, int flag, int argc,
>>>> +                       char * const argv[])
>>>> +{
>>>> +       cmd_tbl_t *cmd;
>>>> +
>>>> +       argc--;
>>>> +       argv++;
>>>> +
>>>> +       cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
>>>> +       if (cmd == NULL || argc > cmd->maxargs)
>>>> +               return CMD_RET_USAGE;
>>>> +
>>>> +       return cmd->cmd(cmdtp, flag, argc, argv);
>>>> +}
>>>> +
>>>> +U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator,
>>>> +       "uclass operations",
>>>> +       "list         - list UCLASS regulator devices\n"
>>>> +       "regulator dev [id]     - show or [set] operating regulator
>>>> device\n"
>>>> +       "regulator [info]       - print constraints info\n"
>>>> +       "regulator [status]     - print operating status\n"
>>>> +       "regulator [value] [-f] - print/[set] voltage value [uV]
>>>> (force)\n"
>>>> +       "regulator [current]    - print/[set] current value [uA]\n"
>>>> +       "regulator [mode_id]    - print/[set] operating mode id\n"
>>>> +       "regulator [enable]     - enable the regulator output\n"
>>>> +       "regulator [disable]    - disable the regulator output\n"
>>>> +);
>>>> --
>>>> 1.9.1
>>>>
>
> Regards,
> Simon
>

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 00/16] Power(full) framework based on Driver Model
  2015-04-24  4:48           ` Simon Glass
@ 2015-04-24 12:18             ` Przemyslaw Marczak
  0 siblings, 0 replies; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-24 12:18 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/24/2015 06:48 AM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 23 April 2015 at 05:33, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Hello Simon,
>>
>>
>> On 04/22/2015 06:29 PM, Simon Glass wrote:
>>>
>>> Hi Przemyslaw,
>>>
>>> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com>
>>> wrote:
>>>>
>>>> Hello,
>>>> Again the next version. The changes are described below each commit
>>>> message.
>>>> This is rebased on last u-boot-dm/master after apply this patchset:
>>>> https://patchwork.ozlabs.org/patch/462775/
>>>> https://patchwork.ozlabs.org/patch/462777/
>>>> https://patchwork.ozlabs.org/patch/462776/
>>>>
>>>> This all can be found in here:
>>>> https://github.com/bobenstein/u-boot/tree/dm-pmic-v4
>>>>
>>>> Best regards,
>>>>
>>>> Przemyslaw Marczak (16):
>>>>     exynos5: fix build break by adding CONFIG_POWER
>>>>     exynos4-common: remove the unsued CONFIG_CMD_PMIC
>>>>     lib: Kconfig: add entry for errno_str() function
>>>>     dm: pmic: add implementation of driver model pmic uclass
>>>>     dm: regulator: add implementation of driver model regulator uclass
>>>>     dm: pmic: add pmic command
>>>>     dm: regulator: add regulator command
>>>>     pmic: max77686 set the same compatible as in the kernel
>>>>     dm: pmic: add max77686 pmic driver
>>>>     dm: regulator: add max77686 regulator driver
>>>>     dm: regulator: add fixed voltage 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                                         |   3 +-
>>>>    arch/arm/dts/exynos4412-odroid.dts               | 255 ++++++-
>>>>    arch/arm/dts/exynos4412-trats2.dts               |   2 +-
>>>>    arch/arm/dts/exynos5250-smdk5250.dts             |   2 +-
>>>>    arch/arm/dts/exynos5250-snow.dts                 |   2 +-
>>>>    board/samsung/common/board.c                     |   4 +-
>>>>    board/samsung/common/misc.c                      |   1 +
>>>>    board/samsung/odroid/odroid.c                    |  77 ++-
>>>>    common/Kconfig                                   |  36 +
>>>>    common/Makefile                                  |   4 +
>>>>    common/cmd_pmic.c                                | 231 +++++++
>>>>    common/cmd_regulator.c                           | 403 +++++++++++
>>>>    configs/odroid_defconfig                         |   8 +-
>>>>    doc/device-tree-bindings/pmic/max77686.txt       |  36 +
>>>>    doc/device-tree-bindings/regulator/fixed.txt     |  38 ++
>>>>    doc/device-tree-bindings/regulator/max77686.txt  |  70 ++
>>>>    doc/device-tree-bindings/regulator/regulator.txt |  55 ++
>>>>    doc/driver-model/pmic-framework.txt              | 142 ++++
>>>>    drivers/power/Kconfig                            |   8 +
>>>>    drivers/power/Makefile                           |   1 -
>>>>    drivers/power/pmic/Kconfig                       |  18 +
>>>>    drivers/power/pmic/Makefile                      |   2 +
>>>>    drivers/power/pmic/max77686.c                    |  87 +++
>>>>    drivers/power/pmic/pmic-uclass.c                 | 158 +++++
>>>>    drivers/power/pmic/pmic_max77686.c               |   2 +-
>>>>    drivers/power/regulator/Kconfig                  |  33 +
>>>>    drivers/power/regulator/Makefile                 |  10 +
>>>>    drivers/power/regulator/fixed.c                  | 126 ++++
>>>>    drivers/power/regulator/max77686.c               | 825
>>>> +++++++++++++++++++++++
>>>>    drivers/power/regulator/regulator-uclass.c       | 300 +++++++++
>>>>    include/configs/exynos4-common.h                 |   1 -
>>>>    include/configs/exynos5-common.h                 |   4 +
>>>>    include/configs/odroid.h                         |   5 -
>>>>    include/dm/uclass-id.h                           |   4 +
>>>>    include/power/max77686_pmic.h                    |  29 +-
>>>>    include/power/pmic.h                             | 189 ++++++
>>>>    include/power/regulator.h                        | 384 +++++++++++
>>>>    lib/Kconfig                                      |   8 +
>>>>    lib/fdtdec.c                                     |   2 +-
>>>>    39 files changed, 3512 insertions(+), 53 deletions(-)
>>>>    create mode 100644 common/cmd_pmic.c
>>>>    create mode 100644 common/cmd_regulator.c
>>>>    create mode 100644 doc/device-tree-bindings/pmic/max77686.txt
>>>>    create mode 100644 doc/device-tree-bindings/regulator/fixed.txt
>>>>    create mode 100644 doc/device-tree-bindings/regulator/max77686.txt
>>>>    create mode 100644 doc/device-tree-bindings/regulator/regulator.txt
>>>>    create mode 100644 doc/driver-model/pmic-framework.txt
>>>>    create mode 100644 drivers/power/pmic/Kconfig
>>>>    create mode 100644 drivers/power/pmic/max77686.c
>>>>    create mode 100644 drivers/power/pmic/pmic-uclass.c
>>>>    create mode 100644 drivers/power/regulator/Kconfig
>>>>    create mode 100644 drivers/power/regulator/Makefile
>>>>    create mode 100644 drivers/power/regulator/fixed.c
>>>>    create mode 100644 drivers/power/regulator/max77686.c
>>>>    create mode 100644 drivers/power/regulator/regulator-uclass.c
>>>>    create mode 100644 include/power/regulator.h
>>>>
>>>> --
>>>> 1.9.1
>>>>
>>>
>>> I'm going to test this and apply to u-boot-dm/next while we wait for
>>> you to finish the sandbox test code. I've made a few comments on the
>>> series. They are minor so I don't want to do another whole spin of the
>>> series (it takes time to review!), but you could do a fix-up patch or
>>> two if you like. I could perhaps squash them in if you prefer that,
>>> but honestly they are nits.
>>>
>>> It will be great to get this into mainline soon!
>>>
>>> Regards,
>>> Simon
>>>
>>
>> Thank you again for the review. I know, that it takes a time, especially
>> when things are fully reworked.
>> I have some other things to do now. So I think, that the end of the next
>> week is reliable time to have the sandbox tests ready.
>>
>> I will try to resend the fixes today or tomorrow.
>>
>> One more question about the sandbox.
>>
>> What do you think about adding: "drivers/power/pmic/max77686-sandbox.c"?
>> The read/write could be done for the file as in the sandbox SPI driver.
>> Then it's no problem to enable the max77686 regulator for the sandbox. This
>> could probably work without dump the real device, when keep the set -> get
>> I/O order.
>
> I'd be more comfortable with a new pmic, a really simple 'fake' one
> with just a few LDOs, maybe one BUCK. Enough to support some
> reasonable tests.
>
> The problem with max77686 is that is it quite complicated, and when
> writing and expanding tests, we really want things to be simple.
>
> Also for the tests, we really don't need anything elaborate. Try to
> test the main functions.
>
> Regards,
> Simon
>

Ok, I see. This should be easy.

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command
  2015-04-24 12:18               ` Przemyslaw Marczak
@ 2015-04-24 12:34                 ` Simon Glass
  2015-04-24 12:53                   ` Przemyslaw Marczak
  0 siblings, 1 reply; 218+ messages in thread
From: Simon Glass @ 2015-04-24 12:34 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 April 2015 at 06:18, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 04/24/2015 06:51 AM, Simon Glass wrote:
>>
>> Hi Przemyslaw,
>>
>> On 23 April 2015 at 05:33, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>>> Hello Simon,
>>>
>>>
>>> On 04/22/2015 06:30 PM, Simon Glass wrote:
>>>>
>>>>
>>>> Hi Przemyslaw,
>>>>
>>>> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com>
>>>> wrote:
>>>>>
>>>>>
>>>>> This command is based on driver model regulator's API.
>>>>> The user interface provides:
>>>>> - list UCLASS regulator devices
>>>>> - show or [set] operating regulator device
>>>>> - print constraints info
>>>>> - print operating status
>>>>> - print/[set] voltage value [uV] (force)
>>>>> - print/[set] current value [uA]
>>>>> - print/[set] operating mode id
>>>>> - enable the regulator output
>>>>> - disable the regulator output
>>>>>
>>>>> The 'force' option can be used for setting the value which exceeds
>>>>> the constraints min/max limits.
>>>>>
>>>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>>>> ---
>>>>> Changes v3:
>>>>> - new file
>>>>> - Kconfig entry
>>>>>
>>>>> Changes V4:
>>>>> - cmd regulator: move platdata to uc pdata
>>>>> - cmd_regulator: includes cleanup
>>>>> - cmd_regulator: add get_curr_dev_and_pl() check type
>>>>> - move config name: CONFIG_DM_REGULATOR_CMD to CONFIG_CMD_REGULATOR
>>>>> - common/Kconfig - cleanup
>>>>> ---
>>>>>    common/Kconfig         |  22 +++
>>>>>    common/Makefile        |   1 +
>>>>>    common/cmd_regulator.c | 403
>>>>> +++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>    3 files changed, 426 insertions(+)
>>>>>    create mode 100644 common/cmd_regulator.c
>>>>
>>>>
>>>>
>>>> Acked-by: Simon Glass <sjg@chromium.org>
>>>>
>>>> I have a few nits that could be dealt with by a follow-on patch.
>>>>
>>>
>>> Ok.
>>>
>>>
>>>>>
>>>>> diff --git a/common/Kconfig b/common/Kconfig
>>>>> index 4666f8e..52f8bb1 100644
>>>>> --- a/common/Kconfig
>>>>> +++ b/common/Kconfig
>>>>> @@ -470,5 +470,27 @@ config CMD_PMIC
>>>>>             - pmic read address  - read byte of register at address
>>>>>             - pmic write address - write byte to register at address
>>>>>             The only one change for this command is 'dev' subcommand.
>>>>> +
>>>>> +config CMD_REGULATOR
>>>>> +       bool "Enable Driver Model REGULATOR command"
>>>>> +       depends on DM_REGULATOR
>>>>> +       help
>>>>> +         This command is based on driver model regulator's API.
>>>>> +         User interface features:
>>>>> +         - list               - list regulator devices
>>>>> +         - regulator dev <id> - show or [set] operating regulator
>>>>> device
>>>>> +         - regulator info     - print constraints info
>>>>> +         - regulator status   - print operating status
>>>>> +         - regulator value <val] <-f> - print/[set] voltage value [uV]
>>>>> +         - regulator current <val>    - print/[set] current value [uA]
>>>>> +         - regulator mode <id>        - print/[set] operating mode id
>>>>> +         - regulator enable           - enable the regulator output
>>>>> +         - regulator disable          - disable the regulator output
>>>>> +
>>>>> +         The '-f' (force) option can be used for set the value which
>>>>> exceeds
>>>>> +         the limits, which are found in device-tree and are kept in
>>>>> regulator's
>>>>> +         uclass platdata structure.
>>>>> +
>>>>>    endmenu
>>>>> +
>>>>>    endmenu
>>>>> diff --git a/common/Makefile b/common/Makefile
>>>>> index 87a3efe..93bded3 100644
>>>>> --- a/common/Makefile
>>>>> +++ b/common/Makefile
>>>>> @@ -212,6 +212,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
>>>>>
>>>>>    # Power
>>>>>    obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o
>>>>> +obj-$(CONFIG_CMD_REGULATOR) += cmd_regulator.o
>>>>>    endif
>>>>>
>>>>>    ifdef CONFIG_SPL_BUILD
>>>>> diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
>>>>> new file mode 100644
>>>>> index 0000000..b1b9e87
>>>>> --- /dev/null
>>>>> +++ b/common/cmd_regulator.c
>>>>> @@ -0,0 +1,403 @@
>>>>> +/*
>>>>> + * Copyright (C) 2014-2015 Samsung Electronics
>>>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>>>> + *
>>>>> + * SPDX-License-Identifier:    GPL-2.0+
>>>>> + */
>>>>> +#include <common.h>
>>>>> +#include <errno.h>
>>>>> +#include <dm.h>
>>>>> +#include <dm/uclass-internal.h>
>>>>> +#include <power/regulator.h>
>>>>> +
>>>>> +#define LIMIT_SEQ      3
>>>>> +#define LIMIT_DEVNAME  20
>>>>> +#define LIMIT_OFNAME   20
>>>>> +#define LIMIT_INFO     16
>>>>> +
>>>>> +static struct udevice *currdev;
>>>>> +
>>>>> +static int failed(const char *getset, const char *thing,
>>>>> +                 const char *for_dev, int ret)
>>>>> +{
>>>>> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing,
>>>>> for_dev,
>>>>> +                                                   ret,
>>>>> errno_str(ret));
>>>>
>>>>
>>>>
>>>> blank line here.
>>>
>>>
>>>
>>> I don't see the blank line here in the patch, which I send.
>>
>>
>> Odd, there seem to be two blank lines there, and we only need one.
>>
>
> Ah, sorry. You mean, that there should be added a blank line.
> Ok, will add one.
>
>>>
>>>>
>>>> I worry that if someone gets one of these messages they will not be
>>>> able to find it in the source code. How about passing in the full
>>>> printf() string in each case, or just using printf() in situ? I don't
>>>> think the code space saving is significant.
>>>>
>>>
>>> It's not a debug message. And each one is different, and easy to grep
>>> "failed". The code is a little cleaner with this. Also the command code
>>> is
>>> not complicated.
>>
>>
>> git grep -i  failed |wc -l
>> 2089
>>
>> Is there some way to know it is a PMIC error message, and find it that
>> way?
>>
>
> Ok, I assumed that you know which command you called, and where to find it,
> so you could use:
> grep -i "failed" common/cmd_regulator.c | wc -l
> 15
>
> But this was only the function name, not a useful text for grep.
> Now I see that this should use the printf instead of the helper funcion.
>
>>>
>>>>> +       return CMD_RET_FAILURE;
>>>>> +}
>>>>> +
>>>>> +static int regulator_get(bool list_only, int get_seq, struct udevice
>>>>> **devp)
>>>>
>>>>
>>>>
>>>> This function seems to do multiple things (find and list). Should we
>>>> split it into two?
>>>>
>>>>> +{
>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>> +       struct udevice *dev;
>>>>> +       int ret;
>>>>> +
>>>>> +       if (devp)
>>>>> +               *devp = NULL;
>>>>> +
>>>>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
>>>>> +            ret = uclass_next_device(&dev)) {
>>>>
>>>>
>>>>
>>>> This will probe all regulators that it checks. I think it should avoid
>>>> that. Do you mean to use
>>>>
>>>
>>> Regarding the two above comments, we have two problems:
>>>
>>> 1. Getting the regulator by sequencial number (dev->seq).
>>> I think it's required, because only this method returns the right device.
>>> Disadvantage: need to probe all devices.
>>
>>
>> But you can use req_seq, or if you have platform data, check that.
>>
>
> Ok, we could use the req_seq for the PMIC uclass, it's natural that
> interface, has its address and <reg> property - but this can repeat,
> if we have two PMICs on a different busses. This is probably possible.
>
> We also shouldn't set the req_seq as the number found in node name, because
> those numbers can repeat: ldo1 {}; buck1 {}; regulator1 { }.
>
> I think that, using the req_seq is bad idea, since we can't be sure that
> those values are unique.
>
> I understand that, the probe is not ideal here? But from the other side,
> if we call "pmic list", then we are sure, that listed devices are ready to
> use. Shouldn't we expect this?

I was hoping that we would not probe devices until they are actually
used, and that listing them would not constitute 'use'.

In the case of listing, you should not need to worry about ->seq or
->req_seq. If you avoid 'getting' the device you will not probe it.

In the case of getting a device ready for use, yes, it must be probed.
But I am only commenting on your 'list' function.

>
>>>
>>> 2. Getting the regulator by "regulator-name"
>>> (regulator_uclass_platdata->name).
>>> This would be clean, but unreliable if we have few regulators with the
>>> same
>>> name - I think we should keep this in mind. Advantage: can use for
>>> non-probed devices.
>>
>>
>> We could probably refuse to bind regulators with the same name. That's
>> just going to lead to confusion.
>>
>
> This could be good, because at present we can get only the first matching
> device, for the given "regulator-name" constraint.
> Ok, I need add the checking routine to regulator's post_bind() method.
> And then refuse the bind of the repeated one.
>
>>>
>>> And about the doing multiple things by the regulator_get().
>>> Following your comments about avoiding the code duplication, I put those
>>> things into one function, since both actually do the same - loops over
>>> the
>>> uclass's devices.
>>>
>>> So we can threat it as a subcommands:
>>> - regulator_get list
>>> - regulator_get dev
>>> Maybe the enum { GET_LIST, GET_DEV } would be better, than the bool.
>>>
>>> Is that really bad?
>>
>>
>> I suspect it would be better as two completely separate functions, and
>> probably not much more code size.
>>
>
> Ok.
>
>
>>>
>>>
>>>>> +               if (list_only) {
>>>>> +                       uc_pdata = dev_get_uclass_platdata(dev);
>>>>> +                       printf("|%*d | %*.*s @ %-*.*s| %s @ %s\n",
>>>>> +                              LIMIT_SEQ, dev->seq,
>>>>> +                              LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name,
>>>>> +                              LIMIT_OFNAME, LIMIT_OFNAME,
>>>>> uc_pdata->name,
>>>>> +                              dev->parent->name,
>>>>> +                              dev_get_uclass_name(dev->parent));
>>>>> +                       continue;
>>>>> +               }
>>>>> +
>>>>> +               if (dev->seq == get_seq) {
>>>>> +                       if (devp)
>>>>> +                               *devp = dev;
>>>>> +                       else
>>>>> +                               return -EINVAL;
>>>>> +
>>>>> +                       return 0;
>>>>> +               }
>>>>> +       }
>>>>> +
>>>>> +       if (list_only)
>>>>> +               return ret;
>>>>> +
>>>>> +       return -ENODEV;
>>>>> +}
>>>>> +
>>>>> +static int do_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>>> argv[])
>>>>> +{
>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>> +       int seq, ret = -ENXIO;
>>>>> +
>>>>> +       switch (argc) {
>>>>> +       case 2:
>>>>> +               seq = simple_strtoul(argv[1], NULL, 0);
>>>>> +               ret = uclass_get_device_by_seq(UCLASS_REGULATOR, seq,
>>>>> &currdev);
>>>>> +               if (ret && (ret = regulator_get(false, seq, &currdev)))
>>>>> +                       goto failed;
>>>>> +       case 1:
>>>>> +               uc_pdata = dev_get_uclass_platdata(currdev);
>>>>> +               if (!uc_pdata)
>>>>> +                       goto failed;
>>>>> +
>>>>> +               printf("dev: %d @ %s\n", currdev->seq, uc_pdata->name);
>>>>> +       }
>>>>> +
>>>>> +       return CMD_RET_SUCCESS;
>>>>> +failed:
>>>>> +       return failed("get", "the", "device", ret);
>>>>> +}
>>>>> +
>>>>> +static int get_curr_dev_and_pl(struct udevice **devp,
>>>>
>>>>
>>>>
>>>> What is pl? The name does not seem very meaningful to me.
>>>>
>>>
>>> The platdata, ok I will tune it.
>>>
>>>
>>>>> +                              struct dm_regulator_uclass_platdata
>>>>> **uc_pdata,
>>>>> +                              bool allow_type_fixed)
>>>>> +{
>>>>> +       *devp = NULL;
>>>>> +       *uc_pdata = NULL;
>>>>> +
>>>>> +       if (!currdev)
>>>>> +               return failed("get", "current", "device", -ENODEV);
>>>>> +
>>>>> +       *devp = currdev;
>>>>> +
>>>>> +       *uc_pdata = dev_get_uclass_platdata(*devp);
>>>>> +       if (!*uc_pdata)
>>>>> +               return failed("get", "regulator", "platdata", -ENXIO);
>>>>> +
>>>>> +       if (!allow_type_fixed && (*uc_pdata)->type ==
>>>>> REGULATOR_TYPE_FIXED) {
>>>>> +               printf("Operation not allowed for fixed regulator!\n");
>>>>> +               return CMD_RET_FAILURE;
>>>>> +       }
>>>>> +
>>>>> +       return CMD_RET_SUCCESS;
>>>>> +}
>>>>> +
>>>>> +static int do_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>>> argv[])
>>>>> +{
>>>>> +       int ret;
>>>>> +
>>>>> +       printf("|%*s | %*.*s @ %-*.*s| %s @ %s\n",
>>>>> +              LIMIT_SEQ, "Seq",
>>>>> +              LIMIT_DEVNAME, LIMIT_DEVNAME, "Name",
>>>>> +              LIMIT_OFNAME, LIMIT_OFNAME, "fdtname",
>>>>> +              "Parent", "uclass");
>>>>> +
>>>>> +       ret = regulator_get(true, 0, NULL);
>>>>> +       if (ret)
>>>>> +               return CMD_RET_FAILURE;
>>>>> +
>>>>> +       return CMD_RET_SUCCESS;
>>>>> +}
>>>>> +
>>>>> +static int constraint(const char *name, int val, const char *val_name)
>>>>> +{
>>>>> +       printf("%-*s", LIMIT_INFO, name);
>>>>> +       if (val < 0) {
>>>>> +               printf(" %s (err: %d)\n", errno_str(val), val);
>>>>> +               return val;
>>>>> +       }
>>>>> +
>>>>> +       if (val_name)
>>>>> +               printf(" %d (%s)\n", val, val_name);
>>>>> +       else
>>>>> +               printf(" %d\n", val);
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +static const char *get_mode_name(struct dm_regulator_mode *mode,
>>>>> +                                int mode_count,
>>>>> +                                int mode_id)
>>>>> +{
>>>>> +       while (mode_count--) {
>>>>> +               if (mode->id == mode_id)
>>>>> +                       return mode->name;
>>>>> +               mode++;
>>>>> +       }
>>>>> +
>>>>> +       return NULL;
>>>>> +}
>>>>> +
>>>>> +static int do_info(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>>> argv[])
>>>>> +{
>>>>> +       struct udevice *dev;
>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>> +       struct dm_regulator_mode *modes;
>>>>> +       const char *parent_uc;
>>>>> +       int mode_count;
>>>>> +       int ret;
>>>>> +       int i;
>>>>> +
>>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       parent_uc = dev_get_uclass_name(dev->parent);
>>>>> +
>>>>> +       printf("Uclass regulator dev %d info:\n", dev->seq);
>>>>> +       printf("%-*s %s @ %s\n%-*s %s\n%-*s %s\n%-*s\n",
>>>>> +              LIMIT_INFO, "* parent:", dev->parent->name, parent_uc,
>>>>> +              LIMIT_INFO, "* dev name:", dev->name,
>>>>> +              LIMIT_INFO, "* fdt name:", uc_pdata->name,
>>>>> +              LIMIT_INFO, "* constraints:");
>>>>> +
>>>>> +       constraint("  - min uV:", uc_pdata->min_uV, NULL);
>>>>> +       constraint("  - max uV:", uc_pdata->max_uV, NULL);
>>>>> +       constraint("  - min uA:", uc_pdata->min_uA, NULL);
>>>>> +       constraint("  - max uA:", uc_pdata->max_uA, NULL);
>>>>> +       constraint("  - always on:", uc_pdata->always_on,
>>>>> +                  uc_pdata->always_on ? "true" : "false");
>>>>> +       constraint("  - boot on:", uc_pdata->boot_on,
>>>>> +                  uc_pdata->boot_on ? "true" : "false");
>>>>> +
>>>>> +       mode_count = regulator_mode(dev, &modes);
>>>>> +       constraint("* op modes:", mode_count, NULL);
>>>>> +
>>>>> +       for (i = 0; i < mode_count; i++, modes++)
>>>>> +               constraint("  - mode id:", modes->id, modes->name);
>>>>> +
>>>>> +       return CMD_RET_SUCCESS;
>>>>> +}
>>>>> +
>>>>> +static int do_status(cmd_tbl_t *cmdtp, int flag, int argc, char *
>>>>> const
>>>>> argv[])
>>>>> +{
>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>> +       int current, value, mode, ret;
>>>>> +       const char *mode_name = NULL;
>>>>> +       struct udevice *dev;
>>>>> +       bool enabled;
>>>>> +
>>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       enabled = regulator_get_enable(dev);
>>>>> +       constraint(" * enable:", enabled, enabled ? "true" : "false");
>>>>> +
>>>>> +       value = regulator_get_value(dev);
>>>>> +       constraint(" * value uV:", value, NULL);
>>>>> +
>>>>> +       current = regulator_get_current(dev);
>>>>> +       constraint(" * current uA:", current, NULL);
>>>>> +
>>>>> +       mode = regulator_get_mode(dev);
>>>>> +       mode_name = get_mode_name(uc_pdata->mode, uc_pdata->mode_count,
>>>>> mode);
>>>>> +       constraint(" * mode id:", mode, mode_name);
>>>>> +
>>>>> +       return CMD_RET_SUCCESS;
>>>>> +}
>>>>> +
>>>>> +static int do_value(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>>> argv[])
>>>>> +{
>>>>> +       struct udevice *dev;
>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>> +       int value;
>>>>> +       int force;
>>>>> +       int ret;
>>>>> +
>>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       if (argc == 1) {
>>>>> +               value = regulator_get_value(dev);
>>>>> +               if (value < 0)
>>>>> +                       return failed("get", uc_pdata->name, "voltage",
>>>>> value);
>>>>> +
>>>>> +               printf("%d uV\n", value);
>>>>> +               return CMD_RET_SUCCESS;
>>>>> +       }
>>>>> +
>>>>> +       if (argc == 3)
>>>>> +               force = !strcmp("-f", argv[2]);
>>>>> +       else
>>>>> +               force = 0;
>>>>> +
>>>>> +       value = simple_strtoul(argv[1], NULL, 0);
>>>>> +       if ((value < uc_pdata->min_uV || value > uc_pdata->max_uV) &&
>>>>> !force) {
>>>>> +               printf("Value exceeds regulator constraint limits\n");
>>>>> +               return CMD_RET_FAILURE;
>>>>> +       }
>>>>> +
>>>>> +       ret = regulator_set_value(dev, value);
>>>>> +       if (ret)
>>>>> +               return failed("set", uc_pdata->name, "voltage value",
>>>>> ret);
>>>>> +
>>>>> +       return CMD_RET_SUCCESS;
>>>>> +}
>>>>> +
>>>>> +static int do_current(cmd_tbl_t *cmdtp, int flag, int argc, char *
>>>>> const
>>>>> argv[])
>>>>> +{
>>>>> +       struct udevice *dev;
>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>> +       int current;
>>>>> +       int ret;
>>>>> +
>>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, argc == 1);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       if (argc == 1) {
>>>>> +               current = regulator_get_current(dev);
>>>>> +               if (current < 0)
>>>>> +                       return failed("get", uc_pdata->name, "current",
>>>>> current);
>>>>> +
>>>>> +               printf("%d uA\n", current);
>>>>> +               return CMD_RET_SUCCESS;
>>>>> +       }
>>>>> +
>>>>> +       current = simple_strtoul(argv[1], NULL, 0);
>>>>> +       if (current < uc_pdata->min_uA || current > uc_pdata->max_uA) {
>>>>> +               printf("Current exceeds regulator constraint
>>>>> limits\n");
>>>>> +               return CMD_RET_FAILURE;
>>>>> +       }
>>>>> +
>>>>> +       ret = regulator_set_current(dev, current);
>>>>> +       if (ret)
>>>>> +               return failed("set", uc_pdata->name, "current value",
>>>>> ret);
>>>>> +
>>>>> +       return CMD_RET_SUCCESS;
>>>>> +}
>>>>> +
>>>>> +static int do_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>>>>> argv[])
>>>>> +{
>>>>> +       struct udevice *dev;
>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>> +       int new_mode;
>>>>> +       int mode;
>>>>> +       int ret;
>>>>> +
>>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, false);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       if (argc == 1) {
>>>>> +               mode = regulator_get_mode(dev);
>>>>> +               if (mode < 0)
>>>>> +                       return failed("get", uc_pdata->name, "mode",
>>>>> mode);
>>>>> +
>>>>> +               printf("mode id: %d\n", mode);
>>>>> +               return CMD_RET_SUCCESS;
>>>>> +       }
>>>>> +
>>>>> +       new_mode = simple_strtoul(argv[1], NULL, 0);
>>>>> +
>>>>> +       ret = regulator_set_mode(dev, new_mode);
>>>>> +       if (ret)
>>>>> +               return failed("set", uc_pdata->name, "mode", ret);
>>>>> +
>>>>> +       return CMD_RET_SUCCESS;
>>>>> +}
>>>>> +
>>>>> +static int do_enable(cmd_tbl_t *cmdtp, int flag, int argc, char *
>>>>> const
>>>>> argv[])
>>>>> +{
>>>>> +       struct udevice *dev;
>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>> +       int ret;
>>>>> +
>>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       ret = regulator_set_enable(dev, true);
>>>>> +       if (ret)
>>>>> +               return failed("enable", "regulator", uc_pdata->name,
>>>>> ret);
>>>>> +
>>>>> +       return CMD_RET_SUCCESS;
>>>>> +}
>>>>> +
>>>>> +static int do_disable(cmd_tbl_t *cmdtp, int flag, int argc, char *
>>>>> const
>>>>> argv[])
>>>>> +{
>>>>> +       struct udevice *dev;
>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>> +       int ret;
>>>>> +
>>>>> +       ret = get_curr_dev_and_pl(&dev, &uc_pdata, true);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       ret = regulator_set_enable(dev, false);
>>>>> +       if (ret)
>>>>> +               return failed("disable", "regulator", uc_pdata->name,
>>>>> ret);
>>>>> +
>>>>> +       return CMD_RET_SUCCESS;
>>>>> +}
>>>>> +
>>>>> +static cmd_tbl_t subcmd[] = {
>>>>> +       U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""),
>>>>> +       U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""),
>>>>> +       U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""),
>>>>> +       U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""),
>>>>> +       U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""),
>>>>> +       U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""),
>>>>> +       U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""),
>>>>> +       U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""),
>>>>> +       U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""),
>>>>> +};
>>>>> +
>>>>> +static int do_regulator(cmd_tbl_t *cmdtp, int flag, int argc,
>>>>> +                       char * const argv[])
>>>>> +{
>>>>> +       cmd_tbl_t *cmd;
>>>>> +
>>>>> +       argc--;
>>>>> +       argv++;
>>>>> +
>>>>> +       cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd));
>>>>> +       if (cmd == NULL || argc > cmd->maxargs)
>>>>> +               return CMD_RET_USAGE;
>>>>> +
>>>>> +       return cmd->cmd(cmdtp, flag, argc, argv);
>>>>> +}
>>>>> +
>>>>> +U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator,
>>>>> +       "uclass operations",
>>>>> +       "list         - list UCLASS regulator devices\n"
>>>>> +       "regulator dev [id]     - show or [set] operating regulator
>>>>> device\n"
>>>>> +       "regulator [info]       - print constraints info\n"
>>>>> +       "regulator [status]     - print operating status\n"
>>>>> +       "regulator [value] [-f] - print/[set] voltage value [uV]
>>>>> (force)\n"
>>>>> +       "regulator [current]    - print/[set] current value [uA]\n"
>>>>> +       "regulator [mode_id]    - print/[set] operating mode id\n"
>>>>> +       "regulator [enable]     - enable the regulator output\n"
>>>>> +       "regulator [disable]    - disable the regulator output\n"
>>>>> +);
>>>>> --
>>>>> 1.9.1
>>>>>
>>
>> Regards,
>> Simon


Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command
  2015-04-24 12:34                 ` Simon Glass
@ 2015-04-24 12:53                   ` Przemyslaw Marczak
  2015-04-24 13:00                     ` Simon Glass
  0 siblings, 1 reply; 218+ messages in thread
From: Przemyslaw Marczak @ 2015-04-24 12:53 UTC (permalink / raw)
  To: u-boot

Hello Simon,

On 04/24/2015 02:34 PM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 24 April 2015 at 06:18, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> Hello Simon,
>>
>>
>> On 04/24/2015 06:51 AM, Simon Glass wrote:
>>>
>>> Hi Przemyslaw,
>>>
>>> On 23 April 2015 at 05:33, Przemyslaw Marczak <p.marczak@samsung.com>
>>> wrote:
>>>>
>>>> Hello Simon,
>>>>
>>>>
>>>> On 04/22/2015 06:30 PM, Simon Glass wrote:
>>>>>
>>>>>
>>>>> Hi Przemyslaw,
>>>>>
>>>>> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com>
>>>>> wrote:
>>>>>>
>>>>>>
>>>>>> This command is based on driver model regulator's API.
>>>>>> The user interface provides:
>>>>>> - list UCLASS regulator devices
>>>>>> - show or [set] operating regulator device
>>>>>> - print constraints info
>>>>>> - print operating status
>>>>>> - print/[set] voltage value [uV] (force)
>>>>>> - print/[set] current value [uA]
>>>>>> - print/[set] operating mode id
>>>>>> - enable the regulator output
>>>>>> - disable the regulator output
>>>>>>
>>>>>> The 'force' option can be used for setting the value which exceeds
>>>>>> the constraints min/max limits.
>>>>>>
>>>>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>>>>> ---
>>>>>> Changes v3:
>>>>>> - new file
>>>>>> - Kconfig entry
>>>>>>
>>>>>> Changes V4:
>>>>>> - cmd regulator: move platdata to uc pdata
>>>>>> - cmd_regulator: includes cleanup
>>>>>> - cmd_regulator: add get_curr_dev_and_pl() check type
>>>>>> - move config name: CONFIG_DM_REGULATOR_CMD to CONFIG_CMD_REGULATOR
>>>>>> - common/Kconfig - cleanup
>>>>>> ---
>>>>>>     common/Kconfig         |  22 +++
>>>>>>     common/Makefile        |   1 +
>>>>>>     common/cmd_regulator.c | 403
>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>     3 files changed, 426 insertions(+)
>>>>>>     create mode 100644 common/cmd_regulator.c
>>>>>
>>>>>
>>>>>
>>>>> Acked-by: Simon Glass <sjg@chromium.org>
>>>>>
>>>>> I have a few nits that could be dealt with by a follow-on patch.
>>>>>
>>>>
>>>> Ok.
>>>>
>>>>
>>>>>>
>>>>>> diff --git a/common/Kconfig b/common/Kconfig
>>>>>> index 4666f8e..52f8bb1 100644
>>>>>> --- a/common/Kconfig
>>>>>> +++ b/common/Kconfig
>>>>>> @@ -470,5 +470,27 @@ config CMD_PMIC
>>>>>>              - pmic read address  - read byte of register at address
>>>>>>              - pmic write address - write byte to register at address
>>>>>>              The only one change for this command is 'dev' subcommand.
>>>>>> +
>>>>>> +config CMD_REGULATOR
>>>>>> +       bool "Enable Driver Model REGULATOR command"
>>>>>> +       depends on DM_REGULATOR
>>>>>> +       help
>>>>>> +         This command is based on driver model regulator's API.
>>>>>> +         User interface features:
>>>>>> +         - list               - list regulator devices
>>>>>> +         - regulator dev <id> - show or [set] operating regulator
>>>>>> device
>>>>>> +         - regulator info     - print constraints info
>>>>>> +         - regulator status   - print operating status
>>>>>> +         - regulator value <val] <-f> - print/[set] voltage value [uV]
>>>>>> +         - regulator current <val>    - print/[set] current value [uA]
>>>>>> +         - regulator mode <id>        - print/[set] operating mode id
>>>>>> +         - regulator enable           - enable the regulator output
>>>>>> +         - regulator disable          - disable the regulator output
>>>>>> +
>>>>>> +         The '-f' (force) option can be used for set the value which
>>>>>> exceeds
>>>>>> +         the limits, which are found in device-tree and are kept in
>>>>>> regulator's
>>>>>> +         uclass platdata structure.
>>>>>> +
>>>>>>     endmenu
>>>>>> +
>>>>>>     endmenu
>>>>>> diff --git a/common/Makefile b/common/Makefile
>>>>>> index 87a3efe..93bded3 100644
>>>>>> --- a/common/Makefile
>>>>>> +++ b/common/Makefile
>>>>>> @@ -212,6 +212,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
>>>>>>
>>>>>>     # Power
>>>>>>     obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o
>>>>>> +obj-$(CONFIG_CMD_REGULATOR) += cmd_regulator.o
>>>>>>     endif
>>>>>>
>>>>>>     ifdef CONFIG_SPL_BUILD
>>>>>> diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
>>>>>> new file mode 100644
>>>>>> index 0000000..b1b9e87
>>>>>> --- /dev/null
>>>>>> +++ b/common/cmd_regulator.c
>>>>>> @@ -0,0 +1,403 @@
>>>>>> +/*
>>>>>> + * Copyright (C) 2014-2015 Samsung Electronics
>>>>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>>>>> + *
>>>>>> + * SPDX-License-Identifier:    GPL-2.0+
>>>>>> + */
>>>>>> +#include <common.h>
>>>>>> +#include <errno.h>
>>>>>> +#include <dm.h>
>>>>>> +#include <dm/uclass-internal.h>
>>>>>> +#include <power/regulator.h>
>>>>>> +
>>>>>> +#define LIMIT_SEQ      3
>>>>>> +#define LIMIT_DEVNAME  20
>>>>>> +#define LIMIT_OFNAME   20
>>>>>> +#define LIMIT_INFO     16
>>>>>> +
>>>>>> +static struct udevice *currdev;
>>>>>> +
>>>>>> +static int failed(const char *getset, const char *thing,
>>>>>> +                 const char *for_dev, int ret)
>>>>>> +{
>>>>>> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing,
>>>>>> for_dev,
>>>>>> +                                                   ret,
>>>>>> errno_str(ret));
>>>>>
>>>>>
>>>>>
>>>>> blank line here.
>>>>
>>>>
>>>>
>>>> I don't see the blank line here in the patch, which I send.
>>>
>>>
>>> Odd, there seem to be two blank lines there, and we only need one.
>>>
>>
>> Ah, sorry. You mean, that there should be added a blank line.
>> Ok, will add one.
>>
>>>>
>>>>>
>>>>> I worry that if someone gets one of these messages they will not be
>>>>> able to find it in the source code. How about passing in the full
>>>>> printf() string in each case, or just using printf() in situ? I don't
>>>>> think the code space saving is significant.
>>>>>
>>>>
>>>> It's not a debug message. And each one is different, and easy to grep
>>>> "failed". The code is a little cleaner with this. Also the command code
>>>> is
>>>> not complicated.
>>>
>>>
>>> git grep -i  failed |wc -l
>>> 2089
>>>
>>> Is there some way to know it is a PMIC error message, and find it that
>>> way?
>>>
>>
>> Ok, I assumed that you know which command you called, and where to find it,
>> so you could use:
>> grep -i "failed" common/cmd_regulator.c | wc -l
>> 15
>>
>> But this was only the function name, not a useful text for grep.
>> Now I see that this should use the printf instead of the helper funcion.
>>
>>>>
>>>>>> +       return CMD_RET_FAILURE;
>>>>>> +}
>>>>>> +
>>>>>> +static int regulator_get(bool list_only, int get_seq, struct udevice
>>>>>> **devp)
>>>>>
>>>>>
>>>>>
>>>>> This function seems to do multiple things (find and list). Should we
>>>>> split it into two?
>>>>>
>>>>>> +{
>>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>>> +       struct udevice *dev;
>>>>>> +       int ret;
>>>>>> +
>>>>>> +       if (devp)
>>>>>> +               *devp = NULL;
>>>>>> +
>>>>>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
>>>>>> +            ret = uclass_next_device(&dev)) {
>>>>>
>>>>>
>>>>>
>>>>> This will probe all regulators that it checks. I think it should avoid
>>>>> that. Do you mean to use
>>>>>
>>>>
>>>> Regarding the two above comments, we have two problems:
>>>>
>>>> 1. Getting the regulator by sequencial number (dev->seq).
>>>> I think it's required, because only this method returns the right device.
>>>> Disadvantage: need to probe all devices.
>>>
>>>
>>> But you can use req_seq, or if you have platform data, check that.
>>>
>>
>> Ok, we could use the req_seq for the PMIC uclass, it's natural that
>> interface, has its address and <reg> property - but this can repeat,
>> if we have two PMICs on a different busses. This is probably possible.
>>
>> We also shouldn't set the req_seq as the number found in node name, because
>> those numbers can repeat: ldo1 {}; buck1 {}; regulator1 { }.
>>
>> I think that, using the req_seq is bad idea, since we can't be sure that
>> those values are unique.
>>
>> I understand that, the probe is not ideal here? But from the other side,
>> if we call "pmic list", then we are sure, that listed devices are ready to
>> use. Shouldn't we expect this?
>
> I was hoping that we would not probe devices until they are actually
> used, and that listing them would not constitute 'use'.
>
> In the case of listing, you should not need to worry about ->seq or
> ->req_seq. If you avoid 'getting' the device you will not probe it.

Yes I know, that I can use the uclass_find_first/next_device() functions 
here. But only after moving the "regulator dev" command to getting the 
regulator by it's "name" constraint as will do in the fixup patches.

>
> In the case of getting a device ready for use, yes, it must be probed.
> But I am only commenting on your 'list' function.
>

Yes this is clean for me.

I'm only wonder now, what to do with the "pmic list/dev" commands.

Actually, for the multi interface PMIC IC, we can be sure, that for each 
interface device will have a different name (dev->name).

But even if the nodes are inside a different parent bus nodes, and have 
the same names, we probably could assume, that each PMIC's interface has 
a different address.
To be sure we could put some note into the documentation, that for the 
PMICs, each node name should be unique.

Then I can use:
- uclass_find_first/next_device() for listing PMIC devices
- uclass_get_device_by_name() for getting the required PMIC

Is that correct, for you?

[snip]

Best regards,
-- 
Przemyslaw Marczak
Samsung R&D Institute Poland
Samsung Electronics
p.marczak at samsung.com

^ permalink raw reply	[flat|nested] 218+ messages in thread

* [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command
  2015-04-24 12:53                   ` Przemyslaw Marczak
@ 2015-04-24 13:00                     ` Simon Glass
  0 siblings, 0 replies; 218+ messages in thread
From: Simon Glass @ 2015-04-24 13:00 UTC (permalink / raw)
  To: u-boot

Hi Przemyslaw,

On 24 April 2015 at 06:53, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> Hello Simon,
>
>
> On 04/24/2015 02:34 PM, Simon Glass wrote:
>>
>> Hi Przemyslaw,
>>
>> On 24 April 2015 at 06:18, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>>
>>> Hello Simon,
>>>
>>>
>>> On 04/24/2015 06:51 AM, Simon Glass wrote:
>>>>
>>>>
>>>> Hi Przemyslaw,
>>>>
>>>> On 23 April 2015 at 05:33, Przemyslaw Marczak <p.marczak@samsung.com>
>>>> wrote:
>>>>>
>>>>>
>>>>> Hello Simon,
>>>>>
>>>>>
>>>>> On 04/22/2015 06:30 PM, Simon Glass wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> Hi Przemyslaw,
>>>>>>
>>>>>> On 20 April 2015 at 12:07, Przemyslaw Marczak <p.marczak@samsung.com>
>>>>>> wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> This command is based on driver model regulator's API.
>>>>>>> The user interface provides:
>>>>>>> - list UCLASS regulator devices
>>>>>>> - show or [set] operating regulator device
>>>>>>> - print constraints info
>>>>>>> - print operating status
>>>>>>> - print/[set] voltage value [uV] (force)
>>>>>>> - print/[set] current value [uA]
>>>>>>> - print/[set] operating mode id
>>>>>>> - enable the regulator output
>>>>>>> - disable the regulator output
>>>>>>>
>>>>>>> The 'force' option can be used for setting the value which exceeds
>>>>>>> the constraints min/max limits.
>>>>>>>
>>>>>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>>>>>> ---
>>>>>>> Changes v3:
>>>>>>> - new file
>>>>>>> - Kconfig entry
>>>>>>>
>>>>>>> Changes V4:
>>>>>>> - cmd regulator: move platdata to uc pdata
>>>>>>> - cmd_regulator: includes cleanup
>>>>>>> - cmd_regulator: add get_curr_dev_and_pl() check type
>>>>>>> - move config name: CONFIG_DM_REGULATOR_CMD to CONFIG_CMD_REGULATOR
>>>>>>> - common/Kconfig - cleanup
>>>>>>> ---
>>>>>>>     common/Kconfig         |  22 +++
>>>>>>>     common/Makefile        |   1 +
>>>>>>>     common/cmd_regulator.c | 403
>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>     3 files changed, 426 insertions(+)
>>>>>>>     create mode 100644 common/cmd_regulator.c
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> Acked-by: Simon Glass <sjg@chromium.org>
>>>>>>
>>>>>> I have a few nits that could be dealt with by a follow-on patch.
>>>>>>
>>>>>
>>>>> Ok.
>>>>>
>>>>>
>>>>>>>
>>>>>>> diff --git a/common/Kconfig b/common/Kconfig
>>>>>>> index 4666f8e..52f8bb1 100644
>>>>>>> --- a/common/Kconfig
>>>>>>> +++ b/common/Kconfig
>>>>>>> @@ -470,5 +470,27 @@ config CMD_PMIC
>>>>>>>              - pmic read address  - read byte of register at address
>>>>>>>              - pmic write address - write byte to register at address
>>>>>>>              The only one change for this command is 'dev'
>>>>>>> subcommand.
>>>>>>> +
>>>>>>> +config CMD_REGULATOR
>>>>>>> +       bool "Enable Driver Model REGULATOR command"
>>>>>>> +       depends on DM_REGULATOR
>>>>>>> +       help
>>>>>>> +         This command is based on driver model regulator's API.
>>>>>>> +         User interface features:
>>>>>>> +         - list               - list regulator devices
>>>>>>> +         - regulator dev <id> - show or [set] operating regulator
>>>>>>> device
>>>>>>> +         - regulator info     - print constraints info
>>>>>>> +         - regulator status   - print operating status
>>>>>>> +         - regulator value <val] <-f> - print/[set] voltage value
>>>>>>> [uV]
>>>>>>> +         - regulator current <val>    - print/[set] current value
>>>>>>> [uA]
>>>>>>> +         - regulator mode <id>        - print/[set] operating mode
>>>>>>> id
>>>>>>> +         - regulator enable           - enable the regulator output
>>>>>>> +         - regulator disable          - disable the regulator output
>>>>>>> +
>>>>>>> +         The '-f' (force) option can be used for set the value which
>>>>>>> exceeds
>>>>>>> +         the limits, which are found in device-tree and are kept in
>>>>>>> regulator's
>>>>>>> +         uclass platdata structure.
>>>>>>> +
>>>>>>>     endmenu
>>>>>>> +
>>>>>>>     endmenu
>>>>>>> diff --git a/common/Makefile b/common/Makefile
>>>>>>> index 87a3efe..93bded3 100644
>>>>>>> --- a/common/Makefile
>>>>>>> +++ b/common/Makefile
>>>>>>> @@ -212,6 +212,7 @@ obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
>>>>>>>
>>>>>>>     # Power
>>>>>>>     obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o
>>>>>>> +obj-$(CONFIG_CMD_REGULATOR) += cmd_regulator.o
>>>>>>>     endif
>>>>>>>
>>>>>>>     ifdef CONFIG_SPL_BUILD
>>>>>>> diff --git a/common/cmd_regulator.c b/common/cmd_regulator.c
>>>>>>> new file mode 100644
>>>>>>> index 0000000..b1b9e87
>>>>>>> --- /dev/null
>>>>>>> +++ b/common/cmd_regulator.c
>>>>>>> @@ -0,0 +1,403 @@
>>>>>>> +/*
>>>>>>> + * Copyright (C) 2014-2015 Samsung Electronics
>>>>>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>>>>>> + *
>>>>>>> + * SPDX-License-Identifier:    GPL-2.0+
>>>>>>> + */
>>>>>>> +#include <common.h>
>>>>>>> +#include <errno.h>
>>>>>>> +#include <dm.h>
>>>>>>> +#include <dm/uclass-internal.h>
>>>>>>> +#include <power/regulator.h>
>>>>>>> +
>>>>>>> +#define LIMIT_SEQ      3
>>>>>>> +#define LIMIT_DEVNAME  20
>>>>>>> +#define LIMIT_OFNAME   20
>>>>>>> +#define LIMIT_INFO     16
>>>>>>> +
>>>>>>> +static struct udevice *currdev;
>>>>>>> +
>>>>>>> +static int failed(const char *getset, const char *thing,
>>>>>>> +                 const char *for_dev, int ret)
>>>>>>> +{
>>>>>>> +       printf("Can't %s %s %s.\nError: %d (%s)\n", getset, thing,
>>>>>>> for_dev,
>>>>>>> +                                                   ret,
>>>>>>> errno_str(ret));
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> blank line here.
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> I don't see the blank line here in the patch, which I send.
>>>>
>>>>
>>>>
>>>> Odd, there seem to be two blank lines there, and we only need one.
>>>>
>>>
>>> Ah, sorry. You mean, that there should be added a blank line.
>>> Ok, will add one.
>>>
>>>>>
>>>>>>
>>>>>> I worry that if someone gets one of these messages they will not be
>>>>>> able to find it in the source code. How about passing in the full
>>>>>> printf() string in each case, or just using printf() in situ? I don't
>>>>>> think the code space saving is significant.
>>>>>>
>>>>>
>>>>> It's not a debug message. And each one is different, and easy to grep
>>>>> "failed". The code is a little cleaner with this. Also the command code
>>>>> is
>>>>> not complicated.
>>>>
>>>>
>>>>
>>>> git grep -i  failed |wc -l
>>>> 2089
>>>>
>>>> Is there some way to know it is a PMIC error message, and find it that
>>>> way?
>>>>
>>>
>>> Ok, I assumed that you know which command you called, and where to find
>>> it,
>>> so you could use:
>>> grep -i "failed" common/cmd_regulator.c | wc -l
>>> 15
>>>
>>> But this was only the function name, not a useful text for grep.
>>> Now I see that this should use the printf instead of the helper funcion.
>>>
>>>>>
>>>>>>> +       return CMD_RET_FAILURE;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int regulator_get(bool list_only, int get_seq, struct udevice
>>>>>>> **devp)
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> This function seems to do multiple things (find and list). Should we
>>>>>> split it into two?
>>>>>>
>>>>>>> +{
>>>>>>> +       struct dm_regulator_uclass_platdata *uc_pdata;
>>>>>>> +       struct udevice *dev;
>>>>>>> +       int ret;
>>>>>>> +
>>>>>>> +       if (devp)
>>>>>>> +               *devp = NULL;
>>>>>>> +
>>>>>>> +       for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev;
>>>>>>> +            ret = uclass_next_device(&dev)) {
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> This will probe all regulators that it checks. I think it should avoid
>>>>>> that. Do you mean to use
>>>>>>
>>>>>
>>>>> Regarding the two above comments, we have two problems:
>>>>>
>>>>> 1. Getting the regulator by sequencial number (dev->seq).
>>>>> I think it's required, because only this method returns the right
>>>>> device.
>>>>> Disadvantage: need to probe all devices.
>>>>
>>>>
>>>>
>>>> But you can use req_seq, or if you have platform data, check that.
>>>>
>>>
>>> Ok, we could use the req_seq for the PMIC uclass, it's natural that
>>> interface, has its address and <reg> property - but this can repeat,
>>> if we have two PMICs on a different busses. This is probably possible.
>>>
>>> We also shouldn't set the req_seq as the number found in node name,
>>> because
>>> those numbers can repeat: ldo1 {}; buck1 {}; regulator1 { }.
>>>
>>> I think that, using the req_seq is bad idea, since we can't be sure that
>>> those values are unique.
>>>
>>> I understand that, the probe is not ideal here? But from the other side,
>>> if we call "pmic list", then we are sure, that listed devices are ready
>>> to
>>> use. Shouldn't we expect this?
>>
>>
>> I was hoping that we would not probe devices until they are actually
>> used, and that listing them would not constitute 'use'.
>>
>> In the case of listing, you should not need to worry about ->seq or
>> ->req_seq. If you avoid 'getting' the device you will not probe it.
>
>
> Yes I know, that I can use the uclass_find_first/next_device() functions
> here. But only after moving the "regulator dev" command to getting the
> regulator by it's "name" constraint as will do in the fixup patches.
>
>>
>> In the case of getting a device ready for use, yes, it must be probed.
>> But I am only commenting on your 'list' function.
>>
>
> Yes this is clean for me.
>
> I'm only wonder now, what to do with the "pmic list/dev" commands.
>
> Actually, for the multi interface PMIC IC, we can be sure, that for each
> interface device will have a different name (dev->name).
>
> But even if the nodes are inside a different parent bus nodes, and have the
> same names, we probably could assume, that each PMIC's interface has a
> different address.
> To be sure we could put some note into the documentation, that for the
> PMICs, each node name should be unique.
>
> Then I can use:
> - uclass_find_first/next_device() for listing PMIC devices
> - uclass_get_device_by_name() for getting the required PMIC
>
> Is that correct, for you?

Yes, I think that is good. It will just confuse everyone if we try to
handle two PMICs with the same name (or two regulators for that
matter). Adding a note to the doc sounds good.

Regards,
Simon

^ permalink raw reply	[flat|nested] 218+ messages in thread

end of thread, other threads:[~2015-04-24 13:00 UTC | newest]

Thread overview: 218+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-08 20:48 [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 01/19] lib: errno: introduce errno_str(): returns errno related message Przemyslaw Marczak
2014-10-09  6:46   ` Joakim Tjernlund
2014-10-09 16:23     ` Przemyslaw Marczak
2014-10-09 22:53       ` Simon Glass
2014-10-10  5:03       ` Joakim Tjernlund
2014-10-10 11:49         ` Przemyslaw Marczak
2014-10-22 15:31   ` Tom Rini
2014-12-11  3:25     ` Simon Glass
2014-12-11 10:11       ` Przemyslaw Marczak
2014-12-11 13:24         ` Simon Glass
2014-10-08 20:48 ` [U-Boot] [PATCH 02/19] exynos: config-common: enable errno_str() function Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 03/19] exynos: config-common: enable generic fs command Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 04/19] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
2014-10-10  3:17   ` Simon Glass
2014-10-10 13:32     ` Przemyslaw Marczak
2014-10-10 23:18       ` Simon Glass
2014-10-20 15:44         ` Przemyslaw Marczak
2014-10-20 15:46           ` Simon Glass
2014-10-20 15:51             ` Przemyslaw Marczak
2014-11-06 22:34               ` Simon Glass
2014-11-12 10:29                 ` Przemyslaw Marczak
2014-11-12 15:26                   ` Simon Glass
2014-10-08 20:48 ` [U-Boot] [PATCH 05/19] dm: pmic: add implementation of driver model regulator uclass Przemyslaw Marczak
2014-10-10  3:10   ` Simon Glass
2014-10-10 13:41     ` Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 06/19] dm: common: board_r: add call and weak of power_init_dm() Przemyslaw Marczak
2014-10-10  3:32   ` Simon Glass
2014-10-20 15:45     ` Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 07/19] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
2014-10-22 15:31   ` Tom Rini
2014-10-08 20:48 ` [U-Boot] [PATCH 08/19] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
2014-10-22 15:32   ` Tom Rini
2014-10-08 20:48 ` [U-Boot] [PATCH 09/19] dm: pmic: new commands: pmic and regulator Przemyslaw Marczak
2014-10-22 15:32   ` Tom Rini
2014-10-08 20:48 ` [U-Boot] [PATCH 10/19] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 11/19] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
2014-10-10  3:36   ` Simon Glass
2014-10-10 13:45     ` Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 12/19] samsung: board: lcd menu: check if any power framework is enabled Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 13/19] samsung: misc: power_key_pressed: add support to dm pmic framework Przemyslaw Marczak
2014-10-10  3:37   ` Simon Glass
2014-10-08 20:48 ` [U-Boot] [PATCH 14/19] trats2: board: add support to dm pmic api Przemyslaw Marczak
2014-10-10  3:39   ` Simon Glass
2014-10-10 13:46     ` Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 15/19] trats2: dts: max77686: add pmic alias and names cleanup Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 16/19] trats2: config: enable dm pmic, dm regulator api, dm max77686 Przemyslaw Marczak
2014-10-10  3:40   ` Simon Glass
2014-10-10 13:50     ` Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 17/19] odroid: board: add support to dm pmic api Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 18/19] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
2014-10-08 20:48 ` [U-Boot] [PATCH 19/19] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
2014-10-08 20:55 ` [U-Boot] [PATCH 00/19] [RFC] Power(full) framework based on Driver Model Przemyslaw Marczak
2014-10-09  6:05   ` Simon Glass
2014-10-09 15:04     ` Przemyslaw Marczak
2014-10-22 15:31 ` Tom Rini
2014-10-24 15:50   ` Przemyslaw Marczak
2014-10-27 12:41   ` Przemyslaw Marczak
2015-03-03 16:24 ` [U-Boot] [PATCH v2 00/12] " Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 01/12] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
2015-03-04 12:19     ` Minkyu Kang
2015-03-03 16:24   ` [U-Boot] [PATCH v2 02/12] dm: device: add function device_get_first_child_by_uclass_id() Przemyslaw Marczak
2015-03-06 14:11     ` Simon Glass
2015-03-25 16:08       ` Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 03/12] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
2015-03-06 14:11     ` Simon Glass
2015-03-25 16:08       ` Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 04/12] dm: pmic: add implementation of driver model regulator uclass Przemyslaw Marczak
2015-03-06 14:12     ` Simon Glass
2015-03-25 16:08       ` Przemyslaw Marczak
2015-03-10 11:41     ` Robert Baldyga
2015-03-25 16:08       ` Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 05/12] dm: pmic: new commands: pmic and regulator Przemyslaw Marczak
2015-03-06 14:13     ` Simon Glass
2015-03-25 16:08       ` Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 06/12] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 07/12] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
2015-03-06 14:14     ` Simon Glass
2015-03-25 16:08       ` Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 08/12] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
2015-03-06 14:14     ` Simon Glass
2015-03-25 16:08       ` Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 09/12] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 10/12] odroid: board: add support to dm pmic api Przemyslaw Marczak
2015-03-06 14:14     ` Simon Glass
2015-03-25 16:09       ` Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 11/12] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
2015-03-03 16:24   ` [U-Boot] [PATCH v2 12/12] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
2015-03-06 14:15     ` Simon Glass
2015-03-03 16:30   ` [U-Boot] [PATCH v2 00/12] Power(full) framework based on Driver Model Przemyslaw Marczak
2015-03-03 16:40   ` Przemyslaw Marczak
2015-03-06 14:10   ` Simon Glass
2015-03-06 15:08     ` Przemyslaw Marczak
2015-03-06 19:58       ` Simon Glass
2015-03-10  2:12     ` Simon Glass
2015-03-25 16:09       ` Przemyslaw Marczak
2015-03-24 20:30   ` [U-Boot] [PATCH v3 00/17] " Przemyslaw Marczak
2015-03-24 20:30     ` [U-Boot] [PATCH v3 01/17] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
2015-03-29 13:05       ` Simon Glass
2015-03-24 20:30     ` [U-Boot] [PATCH v3 02/17] fdt_ro.c: add new function: fdt_node_check_prop_compatible() Przemyslaw Marczak
2015-03-29 13:07       ` Simon Glass
2015-03-24 20:30     ` [U-Boot] [PATCH v3 03/17] dm: core: lists.c: add new function lists_bind_fdt_by_prop() Przemyslaw Marczak
2015-03-29 13:07       ` Simon Glass
2015-03-24 20:30     ` [U-Boot] [PATCH v3 04/17] lib: Kconfig: add entry for errno_str() function Przemyslaw Marczak
2015-03-29 13:07       ` Simon Glass
2015-03-24 20:30     ` [U-Boot] [PATCH v3 05/17] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
2015-03-29 13:07       ` Simon Glass
2015-04-03 16:08         ` Przemyslaw Marczak
2015-04-05 18:30           ` Simon Glass
2015-03-24 20:30     ` [U-Boot] [PATCH v3 06/17] dm: regulator: add implementation of driver model regulator uclass Przemyslaw Marczak
2015-03-29 13:07       ` Simon Glass
2015-04-03 16:09         ` Przemyslaw Marczak
2015-04-05 18:30           ` Simon Glass
2015-04-07 15:31             ` Przemyslaw Marczak
2015-04-08  1:47               ` Simon Glass
2015-04-08  7:37                 ` Przemyslaw Marczak
2015-03-24 20:30     ` [U-Boot] [PATCH v3 07/17] dm: pmic: add pmic command Przemyslaw Marczak
2015-03-29 13:07       ` Simon Glass
2015-04-03 16:08         ` Przemyslaw Marczak
2015-03-24 20:30     ` [U-Boot] [PATCH v3 08/17] dm: regulator: add regulator command Przemyslaw Marczak
2015-03-29 13:07       ` Simon Glass
2015-04-03 16:08         ` Przemyslaw Marczak
2015-03-24 20:30     ` [U-Boot] [PATCH v3 09/17] pmic: max77686 set the same compatible as in the kernel Przemyslaw Marczak
2015-03-29 13:08       ` Simon Glass
2015-03-24 20:30     ` [U-Boot] [PATCH v3 10/17] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
2015-03-29 13:08       ` Simon Glass
2015-03-24 20:30     ` [U-Boot] [PATCH v3 11/17] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
2015-03-29 13:08       ` Simon Glass
2015-04-03 16:08         ` Przemyslaw Marczak
2015-03-24 20:30     ` [U-Boot] [PATCH v3 12/17] dm: regulator: add fixed voltage " Przemyslaw Marczak
2015-03-29 13:08       ` Simon Glass
2015-04-03 16:09         ` Przemyslaw Marczak
2015-03-24 20:30     ` [U-Boot] [PATCH v3 13/17] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
2015-03-29 13:08       ` Simon Glass
2015-04-03 16:09         ` Przemyslaw Marczak
2015-03-24 20:30     ` [U-Boot] [PATCH v3 14/17] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
2015-03-29 13:09       ` Simon Glass
2015-03-24 20:30     ` [U-Boot] [PATCH v3 15/17] odroid: board: add support to dm pmic api Przemyslaw Marczak
2015-03-29 13:08       ` Simon Glass
2015-04-03 16:09         ` Przemyslaw Marczak
2015-03-24 20:30     ` [U-Boot] [PATCH v3 16/17] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
2015-03-29 13:10       ` Simon Glass
2015-03-24 20:30     ` [U-Boot] [PATCH v3 17/17] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
2015-03-29 13:10       ` Simon Glass
2015-04-03 16:10         ` Przemyslaw Marczak
2015-03-25  7:47     ` [U-Boot] [PATCH v3 00/17] Power(full) framework based on Driver Model Przemyslaw Marczak
2015-03-29 13:05     ` Simon Glass
2015-04-03 16:11       ` Przemyslaw Marczak
2015-04-05 18:30         ` Simon Glass
2015-04-20 18:07     ` [U-Boot] [PATCH v4 00/16] " Przemyslaw Marczak
2015-04-20 18:07       ` [U-Boot] [PATCH v4 01/16] exynos5: fix build break by adding CONFIG_POWER Przemyslaw Marczak
2015-04-22 16:29         ` Simon Glass
2015-04-22 17:08           ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 02/16] exynos4-common: remove the unsued CONFIG_CMD_PMIC Przemyslaw Marczak
2015-04-22 16:29         ` Simon Glass
2015-04-22 17:09           ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 03/16] lib: Kconfig: add entry for errno_str() function Przemyslaw Marczak
2015-04-22 16:29         ` Simon Glass
2015-04-22 17:09           ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 04/16] dm: pmic: add implementation of driver model pmic uclass Przemyslaw Marczak
2015-04-22 16:30         ` Simon Glass
2015-04-22 17:09           ` Simon Glass
2015-04-23 11:33           ` Przemyslaw Marczak
2015-04-24  4:51             ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 05/16] dm: regulator: add implementation of driver model regulator uclass Przemyslaw Marczak
2015-04-22 16:30         ` Simon Glass
2015-04-22 16:54           ` Simon Glass
2015-04-22 17:09             ` Simon Glass
2015-04-23 11:33           ` Przemyslaw Marczak
2015-04-20 18:07       ` [U-Boot] [PATCH v4 06/16] dm: pmic: add pmic command Przemyslaw Marczak
2015-04-22 16:30         ` Simon Glass
2015-04-22 17:09           ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 07/16] dm: regulator: add regulator command Przemyslaw Marczak
2015-04-22 16:30         ` Simon Glass
2015-04-22 17:09           ` Simon Glass
2015-04-23 11:33           ` Przemyslaw Marczak
2015-04-24  4:51             ` Simon Glass
2015-04-24 12:18               ` Przemyslaw Marczak
2015-04-24 12:34                 ` Simon Glass
2015-04-24 12:53                   ` Przemyslaw Marczak
2015-04-24 13:00                     ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 08/16] pmic: max77686 set the same compatible as in the kernel Przemyslaw Marczak
2015-04-22 16:30         ` Simon Glass
2015-04-22 17:09           ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 09/16] dm: pmic: add max77686 pmic driver Przemyslaw Marczak
2015-04-22 16:30         ` Simon Glass
2015-04-22 17:10           ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 10/16] dm: regulator: add max77686 regulator driver Przemyslaw Marczak
2015-04-22 16:31         ` Simon Glass
2015-04-22 17:10           ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 11/16] dm: regulator: add fixed voltage " Przemyslaw Marczak
2015-04-22 16:31         ` Simon Glass
2015-04-22 17:10           ` Simon Glass
2015-04-23 12:31         ` Przemyslaw Marczak
2015-04-23 12:36           ` Przemyslaw Marczak
2015-04-24  4:50           ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 12/16] doc: driver-model: pmic and regulator uclass documentation Przemyslaw Marczak
2015-04-22 16:31         ` Simon Glass
2015-04-22 17:10           ` Simon Glass
2015-04-23 11:33           ` Przemyslaw Marczak
2015-04-20 18:07       ` [U-Boot] [PATCH v4 13/16] dm: board:samsung: power_init_board: add requirement of CONFIG_DM_PMIC Przemyslaw Marczak
2015-04-22 16:31         ` Simon Glass
2015-04-22 17:10           ` Simon Glass
2015-04-23 11:33           ` Przemyslaw Marczak
2015-04-20 18:07       ` [U-Boot] [PATCH v4 14/16] odroid: board: add support to dm pmic api Przemyslaw Marczak
2015-04-22 16:31         ` Simon Glass
2015-04-22 17:11           ` Simon Glass
2015-04-23 11:33           ` Przemyslaw Marczak
2015-04-20 18:07       ` [U-Boot] [PATCH v4 15/16] odroid: dts: add 'voltage-regulators' description to max77686 node Przemyslaw Marczak
2015-04-22 16:31         ` Simon Glass
2015-04-22 17:11           ` Simon Glass
2015-04-20 18:07       ` [U-Boot] [PATCH v4 16/16] odroid: config: enable dm pmic, dm regulator and max77686 driver Przemyslaw Marczak
2015-04-22 16:31         ` Simon Glass
2015-04-22 17:11           ` Simon Glass
2015-04-22 16:29       ` [U-Boot] [PATCH v4 00/16] Power(full) framework based on Driver Model Simon Glass
2015-04-23 11:33         ` Przemyslaw Marczak
2015-04-24  4:48           ` Simon Glass
2015-04-24 12:18             ` Przemyslaw Marczak

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.