Linux-RTC Archive on lore.kernel.org
 help / color / Atom feed
* [RFC PATCH 00/13] Support ROHM BD71828 PMIC
@ 2019-10-17  9:40 Matti Vaittinen
  2019-10-17  9:41 ` [RFC PATCH 01/13] mfd: bd71828: Support ROHM BD71828 PMIC - core Matti Vaittinen
                   ` (12 more replies)
  0 siblings, 13 replies; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:40 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

Patch series introducing support for ROHM BD71828 PMIC

ROHM BD71828 is a power management IC containing 7 bucks and 7 LDOs. All
regulators can either be controlled individually via I2C. Bucks 1,2,6 and
7 can also be assigned to a "regulator group" controlled by run-levels.
Eg. Run level specific voltages and enable/disable statuses for each of
these bucks can be set via register interface. The buck run-level group
assignment (selection if buck is to be controlled individually or via
run-levels) can be changed at run-time via I2C.

Run level changes can then be initiated wither via I2C writes or GPIO.
and when run-level is changed, state of all bucks which are set to be
controlled via run-levels are changed accrdingly.

This control mechanism selection (I2C or GPIO) is selected by data in
one time programmable PMIC memory area (during production) and can't be
changed later.

In addition to the bucks and LDOs there are:

- The usual clk gate
- 4 IO pins (mostly usable as GPO or tied to specific purpose)
- power button support
- RTC
- two LEDs
- battery charger
- HALL sensor input

This patch series adds support to regulators, clk, RTC, GPIOs and LEDs.
Power-supply driver for charger is "under construction" and not included
in this RFC series.

Reason for RFC status is the regulator grouping to run-levels. I don't
know what would be the best way to do what patches 6,7 and 8 intend to
provide. (Sure some of this is visible also patches 11 and 12 which
provide dt binding documents.) All suggestions are welcome. Rest of the
patches should be business as usual.

Patch 1:
	BD71828 MFD core.
Patch 2:
	Power button support using GPIO keys.
Patch 3:
	CLK gate support using existing clk-bd718x7
Patch 4:
	Split existing bd718x7 regulator driver to generic ROHM dt
	parsing portion (used by more than one ROHM drivers) and
	bd718x8 specific parts
Patch 5:
	Basic regulator support (individual control via I2C). This
	should be pretty standard stuff.
Patch 6:
	Add support for getting regulator voltages when GPIO controlled
	run-levels are used. Allow specifying voltages for run-levels
	via DT. Allow controlling run-levels via sysfs entries (I am not
	happy about this. Probably should only provide in-kernel API for
	this or is there better ideas? Showing can be done vis sysfs?
	Debugfs?)
Patch 7:
	Support setting/getting run-levels when they are controlled via
	I2C instead of GPIO. Add in-kernel API for settin run-level
	voltages for regulators at run-time.
Patch 8:
	Add in-kernel APIs for changing the RUN-level. Safer than sysfs
	I guess. But is there some better method for controlling this
	kind of dynamic group of regulators?
Patch 9:
	Support BD71828 RTC block using BD70528 RTC driver
Patch 10:
	Allow control of GP(I)O pins on BD71828 via GPIO subsystem
Patch 11:
	Support toggling the LEDs
Patch 12:
	dt-bindings for BD71828 PMIC
Patch 13:
	dt-bindings for regulators on BD71828 PMIC

This patch series is based on v5.3-rc4

---

Matti Vaittinen (13):
  mfd: bd71828: Support ROHM BD71828 PMIC - core
  mfd: input: bd71828: Add power-key support
  clk: bd718x7: Support ROHM BD71828 clk block
  regulator: bd718x7: Split driver to common and bd718x7 specific parts
  regulator: bd71828: Basic support for ROHM bd71828 PMIC regulators
  regulator: bd71828: Add GPIO based run-level control for regulators
  regulator: bd71828: enhanced run-level support
  regulator: bd71828: Support in-kernel APIs to change run-level
  mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver
  gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs
  led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  dt-bindings: mfd: Document ROHM BD71282 bindings
  dt-bindings: regulator: Document ROHM BD71282 regulator bindings

 .../bindings/mfd/rohm,bd71828-pmic.txt        |  180 ++
 .../regulator/rohm,bd71828-regulator.txt      |  164 ++
 drivers/clk/Kconfig                           |    6 +-
 drivers/clk/clk-bd718x7.c                     |   15 +-
 drivers/gpio/Kconfig                          |   12 +
 drivers/gpio/Makefile                         |    1 +
 drivers/gpio/gpio-bd71828.c                   |  161 ++
 drivers/leds/Kconfig                          |   10 +
 drivers/leds/Makefile                         |    1 +
 drivers/leds/leds-bd71828.c                   |   97 ++
 drivers/mfd/Kconfig                           |   15 +
 drivers/mfd/Makefile                          |    2 +-
 drivers/mfd/rohm-bd71828.c                    |  350 ++++
 drivers/regulator/Kconfig                     |   16 +
 drivers/regulator/Makefile                    |    2 +
 drivers/regulator/bd71828-regulator.c         | 1442 +++++++++++++++++
 drivers/regulator/bd718x7-regulator.c         |  183 +--
 drivers/regulator/rohm-regulator.c            |   95 ++
 drivers/rtc/Kconfig                           |    5 +-
 drivers/rtc/rtc-bd70528.c                     |  369 +++--
 include/linux/mfd/rohm-bd70528.h              |   12 +-
 include/linux/mfd/rohm-bd71828.h              |  432 +++++
 include/linux/mfd/rohm-generic.h              |   45 +
 include/linux/mfd/rohm-shared.h               |   22 +
 24 files changed, 3388 insertions(+), 249 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.txt
 create mode 100644 Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.txt
 create mode 100644 drivers/gpio/gpio-bd71828.c
 create mode 100644 drivers/leds/leds-bd71828.c
 create mode 100644 drivers/mfd/rohm-bd71828.c
 create mode 100644 drivers/regulator/bd71828-regulator.c
 create mode 100644 drivers/regulator/rohm-regulator.c
 create mode 100644 include/linux/mfd/rohm-bd71828.h
 create mode 100644 include/linux/mfd/rohm-shared.h

-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 01/13] mfd: bd71828: Support ROHM BD71828 PMIC - core
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
@ 2019-10-17  9:41 ` Matti Vaittinen
  2019-10-17  9:42 ` [RFC PATCH 02/13] mfd: input: bd71828: Add power-key support Matti Vaittinen
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:41 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

BD71828GW is a single-chip power management IC for battery-powered portable
devices. The IC integrates 7 buck converters, 7 LDOs, and a 1500 mA
single-cell linear charger. Also included is a Coulomb counter, a real-time
clock (RTC), 3 GPO/regulator control pins, HALL input and a 32.768 kHz
clock gate.

Add MFD core driver providing interrupt controller facilities and i2c
access to sub device drivers.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/mfd/Kconfig              |  15 ++
 drivers/mfd/Makefile             |   2 +-
 drivers/mfd/rohm-bd71828.c       | 322 +++++++++++++++++++++++
 include/linux/mfd/rohm-bd71828.h | 425 +++++++++++++++++++++++++++++++
 include/linux/mfd/rohm-generic.h |   1 +
 5 files changed, 764 insertions(+), 1 deletion(-)
 create mode 100644 drivers/mfd/rohm-bd71828.c
 create mode 100644 include/linux/mfd/rohm-bd71828.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f129f9678940..c0d6d3921f18 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1921,6 +1921,21 @@ config MFD_ROHM_BD70528
 	  10 bits SAR ADC for battery temperature monitor and 1S battery
 	  charger.
 
+config MFD_ROHM_BD71828
+	tristate "ROHM BD71828 Power Management IC"
+	depends on I2C=y
+	depends on OF
+	select REGMAP_I2C
+	select REGMAP_IRQ
+	select MFD_CORE
+	help
+	  Select this option to get support for the ROHM BD71828 Power
+	  Management IC. BD71828GW is a single-chip power management IC for
+	  battery-powered portable devices. The IC integrates 7 buck
+	  converters, 7 LDOs, and a 1500 mA single-cell linear charger.
+	  Also included is a Coulomb counter, a real-time clock (RTC), and
+	  a 32.768 kHz clock gate.
+
 config MFD_STM32_LPTIMER
 	tristate "Support for STM32 Low-Power Timer"
 	depends on (ARCH_STM32 && OF) || COMPILE_TEST
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f026ada68f6a..d154dfcaf665 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -254,6 +254,6 @@ obj-$(CONFIG_MFD_MXS_LRADC)     += mxs-lradc.o
 obj-$(CONFIG_MFD_SC27XX_PMIC)	+= sprd-sc27xx-spi.o
 obj-$(CONFIG_RAVE_SP_CORE)	+= rave-sp.o
 obj-$(CONFIG_MFD_ROHM_BD70528)	+= rohm-bd70528.o
+obj-$(CONFIG_MFD_ROHM_BD71828)	+= rohm-bd71828.o
 obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
 obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
-
diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
new file mode 100644
index 000000000000..b7de79e1fcdb
--- /dev/null
+++ b/drivers/mfd/rohm-bd71828.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Copyright (C) 2019 ROHM Semiconductors
+//
+// ROHM BD71828 PMIC driver
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rohm-bd71828.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+static const struct resource rtc_irqs[] = {
+	DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC0, "bd71828-rtc-alm-0"),
+	DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC1, "bd71828-rtc-alm-1"),
+	DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC2, "bd71828-rtc-alm-2"),
+};
+
+static struct mfd_cell bd71828_mfd_cells[] = {
+	{ .name = "bd71828-pmic", },
+	{ .name = "bd71828-gpio", },
+	{ .name = "bd71828-led", },
+	/*
+	 * We use BD71837 driver to drive the clock block. Only differences to
+	 * BD70528 clock gate are the register address and mask.
+	 */
+	{ .name = "bd718xx-clk", },
+	{
+		.name = "bd71827-power",
+	}, {
+		.name = "bd70528-rtc",
+		.resources = rtc_irqs,
+		.num_resources = ARRAY_SIZE(rtc_irqs),
+	},
+};
+
+static const struct regmap_range volatile_ranges[] = {
+	{
+		.range_min = BD71828_REG_PS_CTRL_1,
+		.range_max = BD71828_REG_PS_CTRL_1,
+	}, {
+		.range_min = BD71828_REG_PS_CTRL_3,
+		.range_max = BD71828_REG_PS_CTRL_3,
+	}, {
+		.range_min = BD71828_REG_RTC_SEC,
+		.range_max = BD71828_REG_RTC_YEAR,
+	}, {
+		/*
+		 * For now make all charger registers volatile because many
+		 * needs to be and because the charger block is not that
+		 * performance critical. TBD: Check which charger registers
+		 * could be cached
+		 */
+		.range_min = BD71828_REG_CHG_STATE,
+		.range_max = BD71828_REG_CHG_FULL,
+	}, {
+		.range_min = BD71828_REG_INT_MAIN,
+		.range_max = BD71828_REG_IO_STAT,
+	},
+};
+
+static const struct regmap_access_table volatile_regs = {
+	.yes_ranges = &volatile_ranges[0],
+	.n_yes_ranges = ARRAY_SIZE(volatile_ranges),
+};
+
+static struct regmap_config bd71828_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.volatile_table = &volatile_regs,
+	.max_register = BD71828_MAX_REGISTER,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+/*
+ * Mapping of main IRQ register bits to sub-IRQ register offsets so that we can
+ * access corect sub-IRQ registers based on bits that are set in main IRQ
+ * register.
+ */
+
+unsigned int bit0_offsets[] = {11};		/* RTC IRQ register */
+unsigned int bit1_offsets[] = {10};		/* TEMP IRQ register */
+unsigned int bit2_offsets[] = {6, 7, 8, 9};	/* BAT MON IRQ registers */
+unsigned int bit3_offsets[] = {5};		/* BAT IRQ register */
+unsigned int bit4_offsets[] = {4};		/* CHG IRQ register */
+unsigned int bit5_offsets[] = {3};		/* VSYS IRQ register */
+unsigned int bit6_offsets[] = {1, 2};		/* DCIN IRQ registers */
+unsigned int bit7_offsets[] = {0};		/* BUCK IRQ register */
+
+static struct regmap_irq_sub_irq_map bd71828_sub_irq_offsets[] = {
+	REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
+	REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
+	REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
+	REGMAP_IRQ_MAIN_REG_OFFSET(bit3_offsets),
+	REGMAP_IRQ_MAIN_REG_OFFSET(bit4_offsets),
+	REGMAP_IRQ_MAIN_REG_OFFSET(bit5_offsets),
+	REGMAP_IRQ_MAIN_REG_OFFSET(bit6_offsets),
+	REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
+};
+
+static struct regmap_irq bd71828_irqs[] = {
+	REGMAP_IRQ_REG(BD71828_INT_BUCK1_OCP, 0, BD71828_INT_BUCK1_OCP_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BUCK2_OCP, 0, BD71828_INT_BUCK2_OCP_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BUCK3_OCP, 0, BD71828_INT_BUCK3_OCP_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BUCK4_OCP, 0, BD71828_INT_BUCK4_OCP_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BUCK5_OCP, 0, BD71828_INT_BUCK5_OCP_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BUCK6_OCP, 0, BD71828_INT_BUCK6_OCP_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BUCK7_OCP, 0, BD71828_INT_BUCK7_OCP_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_PGFAULT, 0, BD71828_INT_PGFAULT_MASK),
+	/* DCIN1 interrupts */
+	REGMAP_IRQ_REG(BD71828_INT_DCIN_DET, 1, BD71828_INT_DCIN_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_DCIN_RMV, 1, BD71828_INT_DCIN_RMV_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_CLPS_OUT, 1, BD71828_INT_CLPS_OUT_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_CLPS_IN, 1, BD71828_INT_CLPS_IN_MASK),
+	/* DCIN2 interrupts */
+	REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_RES, 2,
+		       BD71828_INT_DCIN_MON_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_DET, 2,
+		       BD71828_INT_DCIN_MON_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_LONGPUSH, 2, BD71828_INT_LONGPUSH_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_MIDPUSH, 2, BD71828_INT_MIDPUSH_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_SHORTPUSH, 2, BD71828_INT_SHORTPUSH_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_PUSH, 2, BD71828_INT_PUSH_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_WDOG, 2, BD71828_INT_WDOG_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_SWRESET, 2, BD71828_INT_SWRESET_MASK),
+	/* Vsys */
+	REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_RES, 3,
+		       BD71828_INT_VSYS_UV_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_DET, 3,
+		       BD71828_INT_VSYS_UV_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_RES, 3,
+		       BD71828_INT_VSYS_LOW_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_DET, 3,
+		       BD71828_INT_VSYS_LOW_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_IN, 3,
+		       BD71828_INT_VSYS_HALL_IN_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_TOGGLE, 3,
+		       BD71828_INT_VSYS_HALL_TOGGLE_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_RES, 3,
+		       BD71828_INT_VSYS_MON_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_DET, 3,
+		       BD71828_INT_VSYS_MON_DET_MASK),
+	/* Charger */
+	REGMAP_IRQ_REG(BD71828_INT_CHG_DCIN_ILIM, 4,
+		       BD71828_INT_CHG_DCIN_ILIM_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_CHG_TOPOFF_TO_DONE, 4,
+		       BD71828_INT_CHG_TOPOFF_TO_DONE_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TEMP, 4,
+		       BD71828_INT_CHG_WDG_TEMP_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TIME, 4,
+		       BD71828_INT_CHG_WDG_TIME_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_RES, 4,
+		       BD71828_INT_CHG_RECHARGE_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_DET, 4,
+		       BD71828_INT_CHG_RECHARGE_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_CHG_RANGED_TEMP_TRANSITION, 4,
+		       BD71828_INT_CHG_RANGED_TEMP_TRANSITION_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_CHG_STATE_TRANSITION, 4,
+		       BD71828_INT_CHG_STATE_TRANSITION_MASK),
+	/* Battery */
+	REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_NORMAL, 5,
+		       BD71828_INT_BAT_TEMP_NORMAL_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_ERANGE, 5,
+		       BD71828_INT_BAT_TEMP_ERANGE_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_WARN, 5,
+		       BD71828_INT_BAT_TEMP_WARN_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_REMOVED, 5,
+		       BD71828_INT_BAT_REMOVED_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_DETECTED, 5,
+		       BD71828_INT_BAT_DETECTED_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_THERM_REMOVED, 5,
+		       BD71828_INT_THERM_REMOVED_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_THERM_DETECTED, 5,
+		       BD71828_INT_THERM_DETECTED_MASK),
+	/* Battery Mon 1 */
+	REGMAP_IRQ_REG(BD71828_INT_BAT_DEAD, 6, BD71828_INT_BAT_DEAD_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_RES, 6,
+		       BD71828_INT_BAT_SHORTC_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_DET, 6,
+		       BD71828_INT_BAT_SHORTC_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_RES, 6,
+		       BD71828_INT_BAT_LOW_VOLT_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_DET, 6,
+		       BD71828_INT_BAT_LOW_VOLT_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_RES, 6,
+		       BD71828_INT_BAT_OVER_VOLT_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_DET, 6,
+		       BD71828_INT_BAT_OVER_VOLT_DET_MASK),
+	/* Battery Mon 2 */
+	REGMAP_IRQ_REG(BD71828_INT_BAT_MON_RES, 7,
+		       BD71828_INT_BAT_MON_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_MON_DET, 7,
+		       BD71828_INT_BAT_MON_DET_MASK),
+	/* Battery Mon 3 (Coulomb counter) */
+	REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON1, 8,
+		       BD71828_INT_BAT_CC_MON1_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON2, 8,
+		       BD71828_INT_BAT_CC_MON2_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON3, 8,
+		       BD71828_INT_BAT_CC_MON3_MASK),
+	/* Battery Mon 4 */
+	REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_RES, 9,
+		       BD71828_INT_BAT_OVER_CURR_1_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_DET, 9,
+		       BD71828_INT_BAT_OVER_CURR_1_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_RES, 9,
+		       BD71828_INT_BAT_OVER_CURR_2_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_DET, 9,
+		       BD71828_INT_BAT_OVER_CURR_2_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_RES, 9,
+		       BD71828_INT_BAT_OVER_CURR_3_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_DET, 9,
+		       BD71828_INT_BAT_OVER_CURR_3_DET_MASK),
+	/* Temperature */
+	REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_RES, 10,
+		       BD71828_INT_TEMP_BAT_LOW_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_DET, 10,
+		       BD71828_INT_TEMP_BAT_LOW_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_RES, 10,
+		       BD71828_INT_TEMP_BAT_HI_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_DET, 10,
+		       BD71828_INT_TEMP_BAT_HI_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_125_RES, 10,
+		       BD71828_INT_TEMP_CHIP_OVER_125_RES_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_125_DET, 10,
+		       BD71828_INT_TEMP_CHIP_OVER_125_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_VF_DET, 10,
+		       BD71828_INT_TEMP_CHIP_OVER_VF_DET_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_VF_RES, 10,
+		       BD71828_INT_TEMP_CHIP_OVER_VF_RES_MASK),
+	/* RTC Alarm */
+	REGMAP_IRQ_REG(BD71828_INT_RTC0, 11, BD71828_INT_RTC0_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_RTC1, 11, BD71828_INT_RTC1_MASK),
+	REGMAP_IRQ_REG(BD71828_INT_RTC2, 11, BD71828_INT_RTC2_MASK),
+};
+
+static struct regmap_irq_chip bd71828_irq_chip = {
+	.name = "bd71828_irq",
+	.main_status = BD71828_REG_INT_MAIN,
+	.irqs = &bd71828_irqs[0],
+	.num_irqs = ARRAY_SIZE(bd71828_irqs),
+	.status_base = BD71828_REG_INT_BUCK,
+	.mask_base = BD71828_REG_INT_MASK_BUCK,
+	.ack_base = BD71828_REG_INT_BUCK,
+	.mask_invert = true,
+	.init_ack_masked = true,
+	.num_regs = 12,
+	.num_main_regs = 1,
+	.sub_reg_offsets = &bd71828_sub_irq_offsets[0],
+	.num_main_status_bits = 8,
+	.irq_reg_stride = 1,
+};
+
+static int bd71828_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct rohm_regmap_dev *chip;
+	struct regmap_irq_chip_data *irq_data;
+	int ret;
+
+	if (!i2c->irq) {
+		dev_err(&i2c->dev, "No IRQ configured\n");
+		return -EINVAL;
+	}
+
+	chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	dev_set_drvdata(&i2c->dev, chip);
+
+	chip->chip_type = ROHM_CHIP_TYPE_BD71828;
+	chip->regmap = devm_regmap_init_i2c(i2c, &bd71828_regmap);
+	if (IS_ERR(chip->regmap)) {
+		dev_err(&i2c->dev, "Failed to initialize Regmap\n");
+		return PTR_ERR(chip->regmap);
+	}
+
+	ret = devm_regmap_add_irq_chip(&i2c->dev, chip->regmap,
+				       i2c->irq, IRQF_ONESHOT, 0,
+				       &bd71828_irq_chip, &irq_data);
+	if (ret) {
+		dev_err(&i2c->dev, "Failed to add IRQ chip\n");
+		return ret;
+	}
+	dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n",
+		bd71828_irq_chip.num_irqs);
+
+	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
+				   bd71828_mfd_cells,
+				   ARRAY_SIZE(bd71828_mfd_cells), NULL, 0,
+				   regmap_irq_get_domain(irq_data));
+	if (ret)
+		dev_err(&i2c->dev, "Failed to create subdevices\n");
+
+	return ret;
+}
+
+static const struct of_device_id bd71828_of_match[] = {
+	{ .compatible = "rohm,bd71828", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bd71828_of_match);
+
+static struct i2c_driver bd71828_drv = {
+	.driver = {
+		.name = "rohm-bd71828",
+		.of_match_table = bd71828_of_match,
+	},
+	.probe = &bd71828_i2c_probe,
+};
+
+module_i2c_driver(bd71828_drv);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("ROHM BD71828 Power Management IC driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h
new file mode 100644
index 000000000000..eb0557eb5314
--- /dev/null
+++ b/include/linux/mfd/rohm-bd71828.h
@@ -0,0 +1,425 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2019 ROHM Semiconductors */
+
+#ifndef __LINUX_MFD_BD71828_H__
+#define __LINUX_MFD_BD71828_H__
+
+#include <linux/mfd/rohm-generic.h>
+
+/* Regulator IDs */
+enum {
+	BD71828_BUCK1,
+	BD71828_BUCK2,
+	BD71828_BUCK3,
+	BD71828_BUCK4,
+	BD71828_BUCK5,
+	BD71828_BUCK6,
+	BD71828_BUCK7,
+	BD71828_LDO1,
+	BD71828_LDO2,
+	BD71828_LDO3,
+	BD71828_LDO4,
+	BD71828_LDO5,
+	BD71828_LDO6,
+	BD71828_LDO_SNVS,
+	BD71828_REGULATOR_AMOUNT,
+};
+
+#define BD71828_BUCK1267_VOLTS		0xEF
+#define BD71828_BUCK3_VOLTS		0x10
+#define BD71828_BUCK4_VOLTS		0x20
+#define BD71828_BUCK5_VOLTS		0x10
+#define BD71828_LDO_VOLTS		0x32
+/* LDO6 is fixed 1.8V voltage */
+#define BD71828_LDO_6_VOLTAGE		1800000
+
+/* Registers and masks*/
+
+/* MODE control */
+#define BD71828_REG_PS_CTRL_1		0x04
+#define BD71828_REG_PS_CTRL_2		0x05
+#define BD71828_REG_PS_CTRL_3		0x06
+
+//#define BD71828_REG_SWRESET		0x06
+#define BD71828_MASK_RUN_LVL_CTRL	0x30
+
+/* Regulator control masks */
+
+#define BD71828_MASK_RAMP_DELAY		0x6
+
+#define BD71828_MASK_RUN_EN		0x08
+#define BD71828_MASK_SUSP_EN		0x04
+#define BD71828_MASK_IDLE_EN		0x02
+#define BD71828_MASK_LPSR_EN		0x01
+
+#define BD71828_MASK_RUN0_EN		0x01
+#define BD71828_MASK_RUN1_EN		0x02
+#define BD71828_MASK_RUN2_EN		0x04
+#define BD71828_MASK_RUN3_EN		0x08
+
+#define BD71828_MASK_DVS_BUCK1_CTRL	0x10
+#define BD71828_DVS_BUCK1_CTRL_I2C	0
+#define BD71828_DVS_BUCK1_USE_RUNLVL	0x10
+
+#define BD71828_MASK_DVS_BUCK2_CTRL	0x20
+#define BD71828_DVS_BUCK2_CTRL_I2C	0
+#define BD71828_DVS_BUCK2_USE_RUNLVL	0x20
+
+#define BD71828_MASK_DVS_BUCK6_CTRL	0x40
+#define BD71828_DVS_BUCK6_CTRL_I2C	0
+#define BD71828_DVS_BUCK6_USE_RUNLVL	0x40
+
+#define BD71828_MASK_DVS_BUCK7_CTRL	0x80
+#define BD71828_DVS_BUCK7_CTRL_I2C	0
+#define BD71828_DVS_BUCK7_USE_RUNLVL	0x80
+
+#define BD71828_MASK_BUCK1267_VOLT	0xff
+#define BD71828_MASK_BUCK3_VOLT		0x1f
+#define BD71828_MASK_BUCK4_VOLT		0x3f
+#define BD71828_MASK_BUCK5_VOLT		0x1f
+#define BD71828_MASK_LDO_VOLT		0x3f
+
+/* Regulator control regs */
+#define BD71828_REG_BUCK1_EN		0x08
+#define BD71828_REG_BUCK1_CTRL		0x09
+#define BD71828_REG_BUCK1_MODE		0x0a
+#define BD71828_REG_BUCK1_IDLE_VOLT	0x0b
+#define BD71828_REG_BUCK1_SUSP_VOLT	0x0c
+#define BD71828_REG_BUCK1_VOLT		0x0d
+
+#define BD71828_REG_BUCK2_EN		0x12
+#define BD71828_REG_BUCK2_CTRL		0x13
+#define BD71828_REG_BUCK2_MODE		0x14
+#define BD71828_REG_BUCK2_IDLE_VOLT	0x15
+#define BD71828_REG_BUCK2_SUSP_VOLT	0x16
+#define BD71828_REG_BUCK2_VOLT		0x17
+
+#define BD71828_REG_BUCK3_EN		0x1c
+#define BD71828_REG_BUCK3_MODE		0x1d
+#define BD71828_REG_BUCK3_VOLT		0x1e
+
+#define BD71828_REG_BUCK4_EN		0x1f
+#define BD71828_REG_BUCK4_MODE		0x20
+#define BD71828_REG_BUCK4_VOLT		0x21
+
+#define BD71828_REG_BUCK5_EN		0x22
+#define BD71828_REG_BUCK5_MODE		0x23
+#define BD71828_REG_BUCK5_VOLT		0x24
+
+#define BD71828_REG_BUCK6_EN		0x25
+#define BD71828_REG_BUCK6_CTRL		0x26
+#define BD71828_REG_BUCK6_MODE		0x27
+#define BD71828_REG_BUCK6_IDLE_VOLT	0x28
+#define BD71828_REG_BUCK6_SUSP_VOLT	0x29
+#define BD71828_REG_BUCK6_VOLT		0x2a
+
+#define BD71828_REG_BUCK7_EN		0x2f
+#define BD71828_REG_BUCK7_CTRL		0x30
+#define BD71828_REG_BUCK7_MODE		0x31
+#define BD71828_REG_BUCK7_IDLE_VOLT	0x32
+#define BD71828_REG_BUCK7_SUSP_VOLT	0x33
+#define BD71828_REG_BUCK7_VOLT		0x34
+
+#define BD71828_REG_LDO1_EN		0x39
+#define BD71828_REG_LDO1_VOLT		0x3a
+#define BD71828_REG_LDO2_EN		0x3b
+#define BD71828_REG_LDO2_VOLT		0x3c
+#define BD71828_REG_LDO3_EN		0x3d
+#define BD71828_REG_LDO3_VOLT		0x3e
+#define BD71828_REG_LDO4_EN		0x3f
+#define BD71828_REG_LDO4_VOLT		0x40
+#define BD71828_REG_LDO5_EN		0x41
+#define BD71828_REG_LDO5_VOLT		0x43
+#define BD71828_REG_LDO5_VOLT_OPT	0x42
+#define BD71828_REG_LDO6_EN		0x44
+//#define BD71828_REG_LDO6_VOLT		0x4
+#define BD71828_REG_LDO7_EN		0x45
+#define BD71828_REG_LDO7_VOLT		0x46
+
+/* GPIO */
+
+#define BD71828_GPIO_DRIVE_MASK		0x2
+#define BD71828_GPIO_OPEN_DRAIN		0x0
+#define BD71828_GPIO_PUSH_PULL		0x2
+#define BD71828_GPIO_OUT_HI		0x1
+#define BD71828_GPIO_OUT_LO		0x0
+#define BD71828_GPIO_OUT_MASK		0x1
+
+#define BD71828_REG_GPIO_CTRL1		0x47
+#define BD71828_REG_GPIO_CTRL2		0x48
+#define BD71828_REG_GPIO_CTRL3		0x49
+#define BD71828_REG_IO_STAT		0xed
+
+/* RTC */
+#define BD71828_REG_RTC_SEC		0x4c
+#define BD71828_REG_RTC_MINUTE		0x4d
+#define BD71828_REG_RTC_HOUR		0x4e
+#define BD71828_REG_RTC_WEEK		0x4f
+#define BD71828_REG_RTC_DAY		0x50
+#define BD71828_REG_RTC_MONTH		0x51
+#define BD71828_REG_RTC_YEAR		0x52
+
+#define BD71828_REG_RTC_ALM0_SEC	0x53
+#define BD71828_REG_RTC_ALM0_MINUTE	0x54
+#define BD71828_REG_RTC_ALM0_HOUR	0x55
+#define BD71828_REG_RTC_ALM0_WEEK	0x56
+#define BD71828_REG_RTC_ALM0_DAY	0x57
+#define BD71828_REG_RTC_ALM0_MONTH	0x58
+#define BD71828_REG_RTC_ALM0_YEAR	0x59
+#define BD71828_REG_RTC_ALM0_MASK	0x61
+
+#define BD71828_REG_RTC_ALM1_SEC	0x5a
+#define BD71828_REG_RTC_ALM1_MINUTE	0x5b
+#define BD71828_REG_RTC_ALM1_HOUR	0x5c
+#define BD71828_REG_RTC_ALM1_WEEK	0x5d
+#define BD71828_REG_RTC_ALM1_DAY	0x5e
+#define BD71828_REG_RTC_ALM1_MONTH	0x5f
+#define BD71828_REG_RTC_ALM1_YEAR	0x60
+#define BD71828_REG_RTC_ALM1_MASK	0x62
+
+#define BD71828_REG_RTC_ALM2		0x63
+
+/* Charger/Battey */
+#define BD71828_REG_CHG_STATE		0x65
+#define BD71828_REG_CHG_FULL		0xd2
+
+/* CLK */
+#define BD71828_REG_OUT32K		0x4B
+
+/* LEDs */
+#define BD71828_REG_LED_CTRL		0x4A
+#define BD71828_MASK_LED_AMBER		0x80
+#define BD71828_MASK_LED_GREEN		0x40
+#define BD71828_LED_ON			0xff
+#define BD71828_LED_OFF			0x0
+
+/* IRQ registers */
+#define BD71828_REG_INT_MASK_BUCK	0xd3
+#define BD71828_REG_INT_MASK_DCIN1	0xd4
+#define BD71828_REG_INT_MASK_DCIN2	0xd5
+#define BD71828_REG_INT_MASK_VSYS	0xd6
+#define BD71828_REG_INT_MASK_CHG	0xd7
+#define BD71828_REG_INT_MASK_BAT	0xd8
+#define BD71828_REG_INT_MASK_BAT_MON1	0xd9
+#define BD71828_REG_INT_MASK_BAT_MON2	0xda
+#define BD71828_REG_INT_MASK_BAT_MON3	0xdb
+#define BD71828_REG_INT_MASK_BAT_MON4	0xdc
+#define BD71828_REG_INT_MASK_TEMP	0xdd
+#define BD71828_REG_INT_MASK_RTC	0xde
+
+
+#define BD71828_REG_INT_MAIN		0xdf
+#define BD71828_REG_INT_BUCK		0xe0
+#define BD71828_REG_INT_DCIN1		0xe1
+#define BD71828_REG_INT_DCIN2		0xe2
+#define BD71828_REG_INT_VSYS		0xe3
+#define BD71828_REG_INT_CHG		0xe4
+#define BD71828_REG_INT_BAT		0xe5
+#define BD71828_REG_INT_BAT_MON1	0xe6
+#define BD71828_REG_INT_BAT_MON2	0xe7
+#define BD71828_REG_INT_BAT_MON3	0xe8
+#define BD71828_REG_INT_BAT_MON4	0xe9
+#define BD71828_REG_INT_TEMP		0xea
+#define BD71828_REG_INT_RTC		0xeb
+#define BD71828_REG_INT_UPDATE		0xec
+
+#define BD71828_MAX_REGISTER BD71828_REG_IO_STAT
+
+/* Masks for main IRQ register bits */
+enum {
+	BD71828_INT_BUCK,
+#define BD71828_INT_BUCK_MASK BIT(BD71828_INT_BUCK)
+	BD71828_INT_DCIN,
+#define BD71828_INT_DCIN_MASK BIT(BD71828_INT_DCIN)
+	BD71828_INT_VSYS,
+#define BD71828_INT_VSYS_MASK BIT(BD71828_INT_VSYS)
+	BD71828_INT_CHG,
+#define BD71828_INT_CHG_MASK BIT(BD71828_INT_CHG)
+	BD71828_INT_BAT,
+#define BD71828_INT_BAT_MASK BIT(BD71828_INT_BAT)
+	BD71828_INT_BAT_MON,
+#define BD71828_INT_BAT_MON_MASK BIT(BD71828_INT_BAT_MON)
+	BD71828_INT_TEMP,
+#define BD71828_INT_TEMP_MASK BIT(BD71828_INT_TEMP)
+	BD71828_INT_RTC,
+#define BD71828_INT_RTC_MASK BIT(BD71828_INT_RTC)
+};
+
+/* Interrupts */
+enum {
+	/* BUCK reg interrupts */
+	BD71828_INT_BUCK1_OCP,
+	BD71828_INT_BUCK2_OCP,
+	BD71828_INT_BUCK3_OCP,
+	BD71828_INT_BUCK4_OCP,
+	BD71828_INT_BUCK5_OCP,
+	BD71828_INT_BUCK6_OCP,
+	BD71828_INT_BUCK7_OCP,
+	BD71828_INT_PGFAULT,
+	/* DCIN1 interrupts */
+	BD71828_INT_DCIN_DET,
+	BD71828_INT_DCIN_RMV,
+	BD71828_INT_CLPS_OUT,
+	BD71828_INT_CLPS_IN,
+	/* DCIN2 interrupts */
+	BD71828_INT_DCIN_MON_RES,
+	BD71828_INT_DCIN_MON_DET,
+	BD71828_INT_LONGPUSH,
+	BD71828_INT_MIDPUSH,
+	BD71828_INT_SHORTPUSH,
+	BD71828_INT_PUSH,
+	BD71828_INT_WDOG,
+	BD71828_INT_SWRESET,
+	/* Vsys */
+	BD71828_INT_VSYS_UV_RES,
+	BD71828_INT_VSYS_UV_DET,
+	BD71828_INT_VSYS_LOW_RES,
+	BD71828_INT_VSYS_LOW_DET,
+	BD71828_INT_VSYS_HALL_IN,
+	BD71828_INT_VSYS_HALL_TOGGLE,
+	BD71828_INT_VSYS_MON_RES,
+	BD71828_INT_VSYS_MON_DET,
+	/* Charger */
+	BD71828_INT_CHG_DCIN_ILIM,
+	BD71828_INT_CHG_TOPOFF_TO_DONE,
+	BD71828_INT_CHG_WDG_TEMP,
+	BD71828_INT_CHG_WDG_TIME,
+	BD71828_INT_CHG_RECHARGE_RES,
+	BD71828_INT_CHG_RECHARGE_DET,
+	BD71828_INT_CHG_RANGED_TEMP_TRANSITION,
+	BD71828_INT_CHG_STATE_TRANSITION,
+	/* Battery */
+	BD71828_INT_BAT_TEMP_NORMAL,
+	BD71828_INT_BAT_TEMP_ERANGE,
+	BD71828_INT_BAT_TEMP_WARN,
+	BD71828_INT_BAT_REMOVED,
+	BD71828_INT_BAT_DETECTED,
+	BD71828_INT_THERM_REMOVED,
+	BD71828_INT_THERM_DETECTED,
+	/* Battery Mon 1 */
+	BD71828_INT_BAT_DEAD,
+	BD71828_INT_BAT_SHORTC_RES,
+	BD71828_INT_BAT_SHORTC_DET,
+	BD71828_INT_BAT_LOW_VOLT_RES,
+	BD71828_INT_BAT_LOW_VOLT_DET,
+	BD71828_INT_BAT_OVER_VOLT_RES,
+	BD71828_INT_BAT_OVER_VOLT_DET,
+	/* Battery Mon 2 */
+	BD71828_INT_BAT_MON_RES,
+	BD71828_INT_BAT_MON_DET,
+	/* Battery Mon 3 (Coulomb counter) */
+	BD71828_INT_BAT_CC_MON1,
+	BD71828_INT_BAT_CC_MON2,
+	BD71828_INT_BAT_CC_MON3,
+	/* Battery Mon 4 */
+	BD71828_INT_BAT_OVER_CURR_1_RES,
+	BD71828_INT_BAT_OVER_CURR_1_DET,
+	BD71828_INT_BAT_OVER_CURR_2_RES,
+	BD71828_INT_BAT_OVER_CURR_2_DET,
+	BD71828_INT_BAT_OVER_CURR_3_RES,
+	BD71828_INT_BAT_OVER_CURR_3_DET,
+	/* Temperature */
+	BD71828_INT_TEMP_BAT_LOW_RES,
+	BD71828_INT_TEMP_BAT_LOW_DET,
+	BD71828_INT_TEMP_BAT_HI_RES,
+	BD71828_INT_TEMP_BAT_HI_DET,
+	BD71828_INT_TEMP_CHIP_OVER_125_RES,
+	BD71828_INT_TEMP_CHIP_OVER_125_DET,
+	BD71828_INT_TEMP_CHIP_OVER_VF_DET,
+	BD71828_INT_TEMP_CHIP_OVER_VF_RES,
+	/* RTC Alarm */
+	BD71828_INT_RTC0,
+	BD71828_INT_RTC1,
+	BD71828_INT_RTC2,
+};
+
+#define BD71828_INT_BUCK1_OCP_MASK			0x1
+#define BD71828_INT_BUCK2_OCP_MASK			0x2
+#define BD71828_INT_BUCK3_OCP_MASK			0x4
+#define BD71828_INT_BUCK4_OCP_MASK			0x8
+#define BD71828_INT_BUCK5_OCP_MASK			0x10
+#define BD71828_INT_BUCK6_OCP_MASK			0x20
+#define BD71828_INT_BUCK7_OCP_MASK			0x40
+#define BD71828_INT_PGFAULT_MASK			0x80
+
+#define BD71828_INT_DCIN_DET_MASK			0x1
+#define BD71828_INT_DCIN_RMV_MASK			0x2
+#define BD71828_INT_CLPS_OUT_MASK			0x4
+#define BD71828_INT_CLPS_IN_MASK			0x8
+	/* DCIN2 interrupts */
+#define BD71828_INT_DCIN_MON_RES_MASK			0x1
+#define BD71828_INT_DCIN_MON_DET_MASK			0x2
+#define BD71828_INT_LONGPUSH_MASK			0x4
+#define BD71828_INT_MIDPUSH_MASK			0x8
+#define BD71828_INT_SHORTPUSH_MASK			0x10
+#define BD71828_INT_PUSH_MASK				0x20
+#define BD71828_INT_WDOG_MASK				0x40
+#define BD71828_INT_SWRESET_MASK			0x80
+	/* Vsys */
+#define BD71828_INT_VSYS_UV_RES_MASK			0x1
+#define BD71828_INT_VSYS_UV_DET_MASK			0x2
+#define BD71828_INT_VSYS_LOW_RES_MASK			0x4
+#define BD71828_INT_VSYS_LOW_DET_MASK			0x8
+#define BD71828_INT_VSYS_HALL_IN_MASK			0x10
+#define BD71828_INT_VSYS_HALL_TOGGLE_MASK		0x20
+#define BD71828_INT_VSYS_MON_RES_MASK			0x40
+#define BD71828_INT_VSYS_MON_DET_MASK			0x80
+	/* Charger */
+#define BD71828_INT_CHG_DCIN_ILIM_MASK			0x1
+#define BD71828_INT_CHG_TOPOFF_TO_DONE_MASK		0x2
+#define BD71828_INT_CHG_WDG_TEMP_MASK			0x4
+#define BD71828_INT_CHG_WDG_TIME_MASK			0x8
+#define BD71828_INT_CHG_RECHARGE_RES_MASK		0x10
+#define BD71828_INT_CHG_RECHARGE_DET_MASK		0x20
+#define BD71828_INT_CHG_RANGED_TEMP_TRANSITION_MASK	0x40
+#define BD71828_INT_CHG_STATE_TRANSITION_MASK		0x80
+	/* Battery */
+#define BD71828_INT_BAT_TEMP_NORMAL_MASK		0x1
+#define BD71828_INT_BAT_TEMP_ERANGE_MASK		0x2
+#define BD71828_INT_BAT_TEMP_WARN_MASK			0x4
+#define BD71828_INT_BAT_REMOVED_MASK			0x10
+#define BD71828_INT_BAT_DETECTED_MASK			0x20
+#define BD71828_INT_THERM_REMOVED_MASK			0x40
+#define BD71828_INT_THERM_DETECTED_MASK			0x80
+	/* Battery Mon 1 */
+#define BD71828_INT_BAT_DEAD_MASK			0x2
+#define BD71828_INT_BAT_SHORTC_RES_MASK			0x4
+#define BD71828_INT_BAT_SHORTC_DET_MASK			0x8
+#define BD71828_INT_BAT_LOW_VOLT_RES_MASK		0x10
+#define BD71828_INT_BAT_LOW_VOLT_DET_MASK		0x20
+#define BD71828_INT_BAT_OVER_VOLT_RES_MASK		0x40
+#define BD71828_INT_BAT_OVER_VOLT_DET_MASK		0x80
+	/* Battery Mon 2 */
+#define BD71828_INT_BAT_MON_RES_MASK			0x1
+#define BD71828_INT_BAT_MON_DET_MASK			0x2
+	/* Battery Mon 3 (Coulomb counter) */
+#define BD71828_INT_BAT_CC_MON1_MASK			0x1
+#define BD71828_INT_BAT_CC_MON2_MASK			0x2
+#define BD71828_INT_BAT_CC_MON3_MASK			0x4
+	/* Battery Mon 4 */
+#define BD71828_INT_BAT_OVER_CURR_1_RES_MASK		0x1
+#define BD71828_INT_BAT_OVER_CURR_1_DET_MASK		0x2
+#define BD71828_INT_BAT_OVER_CURR_2_RES_MASK		0x4
+#define BD71828_INT_BAT_OVER_CURR_2_DET_MASK		0x8
+#define BD71828_INT_BAT_OVER_CURR_3_RES_MASK		0x10
+#define BD71828_INT_BAT_OVER_CURR_3_DET_MASK		0x20
+	/* Temperature */
+#define BD71828_INT_TEMP_BAT_LOW_RES_MASK		0x1
+#define BD71828_INT_TEMP_BAT_LOW_DET_MASK		0x2
+#define BD71828_INT_TEMP_BAT_HI_RES_MASK		0x4
+#define BD71828_INT_TEMP_BAT_HI_DET_MASK		0x8
+#define BD71828_INT_TEMP_CHIP_OVER_125_RES_MASK		0x10
+#define BD71828_INT_TEMP_CHIP_OVER_125_DET_MASK		0x20
+#define BD71828_INT_TEMP_CHIP_OVER_VF_RES_MASK		0x40
+#define BD71828_INT_TEMP_CHIP_OVER_VF_DET_MASK		0x80
+	/* RTC Alarm */
+#define BD71828_INT_RTC0_MASK				0x1
+#define BD71828_INT_RTC1_MASK				0x2
+#define BD71828_INT_RTC2_MASK				0x4
+
+#define BD71828_OUT32K_EN				0x1
+#define BD71828_OUT_TYPE_MASK				0x2
+#define BD71828_OUT_TYPE_OPEN_DRAIN			0x0
+#define BD71828_OUT_TYPE_CMOS				0x2
+
+#endif /* __LINUX_MFD_BD71828_H__ */
diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h
index bff15ac26f2c..b280f3100d6c 100644
--- a/include/linux/mfd/rohm-generic.h
+++ b/include/linux/mfd/rohm-generic.h
@@ -8,6 +8,7 @@ enum {
 	ROHM_CHIP_TYPE_BD71837 = 0,
 	ROHM_CHIP_TYPE_BD71847,
 	ROHM_CHIP_TYPE_BD70528,
+	ROHM_CHIP_TYPE_BD71828,
 	ROHM_CHIP_TYPE_AMOUNT
 };
 
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 02/13] mfd: input: bd71828: Add power-key support
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
  2019-10-17  9:41 ` [RFC PATCH 01/13] mfd: bd71828: Support ROHM BD71828 PMIC - core Matti Vaittinen
@ 2019-10-17  9:42 ` Matti Vaittinen
  2019-10-17  9:43 ` [RFC PATCH 03/13] clk: bd718x7: Support ROHM BD71828 clk block Matti Vaittinen
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:42 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Dmitry Torokhov, Lee Jones, Rob Herring, Mark Rutland,
	Liam Girdwood, Mark Brown, Michael Turquette, Stephen Boyd,
	Linus Walleij, Bartosz Golaszewski, Jacek Anaszewski,
	Pavel Machek, Dan Murphy, Alessandro Zummo, Alexandre Belloni,
	devicetree, linux-kernel, linux-clk, linux-gpio, linux-leds,
	linux-rtc

Use gpio_keys to send power input-event to user-space when power
button (short) press is detected.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/mfd/rohm-bd71828.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
index b7de79e1fcdb..f77ba1ec3e99 100644
--- a/drivers/mfd/rohm-bd71828.c
+++ b/drivers/mfd/rohm-bd71828.c
@@ -4,7 +4,9 @@
 //
 // ROHM BD71828 PMIC driver
 
+#include <linux/gpio_keys.h>
 #include <linux/i2c.h>
+#include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/irq.h>
@@ -15,6 +17,18 @@
 #include <linux/regmap.h>
 #include <linux/types.h>
 
+static struct gpio_keys_button button = {
+	.code = KEY_POWER,
+	.gpio = -1,
+	.type = EV_KEY,
+};
+
+static struct gpio_keys_platform_data bd71828_powerkey_data = {
+	.buttons = &button,
+	.nbuttons = 1,
+	.name = "bd71828-pwrkey",
+};
+
 static const struct resource rtc_irqs[] = {
 	DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC0, "bd71828-rtc-alm-0"),
 	DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC1, "bd71828-rtc-alm-1"),
@@ -36,6 +50,10 @@ static struct mfd_cell bd71828_mfd_cells[] = {
 		.name = "bd70528-rtc",
 		.resources = rtc_irqs,
 		.num_resources = ARRAY_SIZE(rtc_irqs),
+	}, {
+		.name = "gpio-keys",
+		.platform_data = &bd71828_powerkey_data,
+		.pdata_size = sizeof(bd71828_powerkey_data),
 	},
 };
 
@@ -288,9 +306,19 @@ static int bd71828_i2c_probe(struct i2c_client *i2c,
 		dev_err(&i2c->dev, "Failed to add IRQ chip\n");
 		return ret;
 	}
+
 	dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n",
 		bd71828_irq_chip.num_irqs);
 
+	ret = regmap_irq_get_virq(irq_data, BD71828_INT_SHORTPUSH);
+
+	if (ret < 0) {
+		dev_err(&i2c->dev, "Failed to get the power-key IRQ\n");
+		return ret;
+	}
+
+	button.irq = ret;
+
 	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
 				   bd71828_mfd_cells,
 				   ARRAY_SIZE(bd71828_mfd_cells), NULL, 0,
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 03/13] clk: bd718x7: Support ROHM BD71828 clk block
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
  2019-10-17  9:41 ` [RFC PATCH 01/13] mfd: bd71828: Support ROHM BD71828 PMIC - core Matti Vaittinen
  2019-10-17  9:42 ` [RFC PATCH 02/13] mfd: input: bd71828: Add power-key support Matti Vaittinen
@ 2019-10-17  9:43 ` Matti Vaittinen
  2019-10-17  9:44 ` [RFC PATCH 04/13] regulator: bd718x7: Split driver to common and bd718x7 specific parts Matti Vaittinen
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:43 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

BD71828GW is a single-chip power management IC for battery-powered portable
devices. Add support for controlling BD71828 clk using bd718x7 driver.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/clk/Kconfig       |  6 +++---
 drivers/clk/clk-bd718x7.c | 15 ++++++++++-----
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 801fa1cd0321..1d61d94cdb29 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -302,10 +302,10 @@ config COMMON_CLK_STM32H7
 	  Support for stm32h7 SoC family clocks
 
 config COMMON_CLK_BD718XX
-	tristate "Clock driver for ROHM BD718x7 PMIC"
-	depends on MFD_ROHM_BD718XX || MFD_ROHM_BD70528
+	tristate "Clock driver for 32K clk gates on ROHM PMICs"
+	depends on MFD_ROHM_BD718XX || MFD_ROHM_BD70528 || MFD_ROHM_BD71828
 	help
-	  This driver supports ROHM BD71837, ROHM BD71847 and
+	  This driver supports ROHM BD71837, ROHM BD71847, ROHM BD71828 and
 	  ROHM BD70528 PMICs clock gates.
 
 config COMMON_CLK_FIXED_MMIO
diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c
index ae6e5baee330..d17a19e04592 100644
--- a/drivers/clk/clk-bd718x7.c
+++ b/drivers/clk/clk-bd718x7.c
@@ -8,6 +8,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/mfd/rohm-bd718x7.h>
+#include <linux/mfd/rohm-bd71828.h>
 #include <linux/mfd/rohm-bd70528.h>
 #include <linux/clk-provider.h>
 #include <linux/clkdev.h>
@@ -21,10 +22,8 @@ struct bd718xx_clk {
 	struct rohm_regmap_dev *mfd;
 };
 
-static int bd71837_clk_set(struct clk_hw *hw, int status)
+static int bd71837_clk_set(struct bd718xx_clk *c, int status)
 {
-	struct bd718xx_clk *c = container_of(hw, struct bd718xx_clk, hw);
-
 	return regmap_update_bits(c->mfd->regmap, c->reg, c->mask, status);
 }
 
@@ -33,14 +32,16 @@ static void bd71837_clk_disable(struct clk_hw *hw)
 	int rv;
 	struct bd718xx_clk *c = container_of(hw, struct bd718xx_clk, hw);
 
-	rv = bd71837_clk_set(hw, 0);
+	rv = bd71837_clk_set(c, 0);
 	if (rv)
 		dev_dbg(&c->pdev->dev, "Failed to disable 32K clk (%d)\n", rv);
 }
 
 static int bd71837_clk_enable(struct clk_hw *hw)
 {
-	return bd71837_clk_set(hw, 1);
+	struct bd718xx_clk *c = container_of(hw, struct bd718xx_clk, hw);
+
+	return bd71837_clk_set(c, 0xffffffff);
 }
 
 static int bd71837_clk_is_enabled(struct clk_hw *hw)
@@ -93,6 +94,10 @@ static int bd71837_clk_probe(struct platform_device *pdev)
 		c->reg = BD718XX_REG_OUT32K;
 		c->mask = BD718XX_OUT32K_EN;
 		break;
+	case ROHM_CHIP_TYPE_BD71828:
+		c->reg = BD71828_REG_OUT32K;
+		c->mask = BD71828_OUT32K_EN;
+		break;
 	case ROHM_CHIP_TYPE_BD70528:
 		c->reg = BD70528_REG_CLK_OUT;
 		c->mask = BD70528_CLK_OUT_EN_MASK;
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 04/13] regulator: bd718x7: Split driver to common and bd718x7 specific parts
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
                   ` (2 preceding siblings ...)
  2019-10-17  9:43 ` [RFC PATCH 03/13] clk: bd718x7: Support ROHM BD71828 clk block Matti Vaittinen
@ 2019-10-17  9:44 ` Matti Vaittinen
  2019-10-17  9:44 ` [RFC PATCH 05/13] regulator: bd71828: Basic support for ROHM bd71828 PMIC regulators Matti Vaittinen
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:44 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

Few ROHM PMICs allow setting the voltage states for different system states
like RUN, IDLE, SUSPEND and LPSR. States are then changed via SoC specific
mechanisms. bd718x7 driver implemented device-tree parsing functions for
these state specific voltages. The parsing functions can be re-used by
other ROHM chip drivers like bd71828. Split the generic functions from
bd718x7-regulator.c to rohm-regulator.c and export them for other modules
to use.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/regulator/Kconfig             |   4 +
 drivers/regulator/Makefile            |   1 +
 drivers/regulator/bd718x7-regulator.c | 183 ++++++++------------------
 drivers/regulator/rohm-regulator.c    |  95 +++++++++++++
 include/linux/mfd/rohm-generic.h      |  44 +++++++
 5 files changed, 199 insertions(+), 128 deletions(-)
 create mode 100644 drivers/regulator/rohm-regulator.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index b57093d7c01f..74de76bf98d7 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -196,6 +196,7 @@ config REGULATOR_BD70528
 config REGULATOR_BD718XX
 	tristate "ROHM BD71837 Power Regulator"
 	depends on MFD_ROHM_BD718XX
+	select REGULATOR_ROHM
 	help
 	  This driver supports voltage regulators on ROHM BD71837 PMIC.
 	  This will enable support for the software controllable buck
@@ -780,6 +781,9 @@ config REGULATOR_RN5T618
 	  Say y here to support the regulators found on Ricoh RN5T567,
 	  RN5T618 or RC5T619 PMIC.
 
+config REGULATOR_ROHM
+	tristate
+
 config REGULATOR_RT5033
 	tristate "Richtek RT5033 Regulators"
 	depends on MFD_RT5033
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index eef73b5a35a4..41e4055d2d66 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -98,6 +98,7 @@ obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
 obj-$(CONFIG_REGULATOR_RC5T583)  += rc5t583-regulator.o
 obj-$(CONFIG_REGULATOR_RK808)   += rk808-regulator.o
 obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o
+obj-$(CONFIG_REGULATOR_ROHM)	+= rohm-regulator.o
 obj-$(CONFIG_REGULATOR_RT5033)	+= rt5033-regulator.o
 obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
 obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c
index bdab46a5c461..2dcb010a626c 100644
--- a/drivers/regulator/bd718x7-regulator.c
+++ b/drivers/regulator/bd718x7-regulator.c
@@ -318,6 +318,7 @@ struct reg_init {
 };
 struct bd718xx_regulator_data {
 	struct regulator_desc desc;
+	const struct rohm_dvs_config dvs;
 	const struct reg_init init;
 	const struct reg_init *additional_inits;
 	int additional_init_amnt;
@@ -349,133 +350,15 @@ static const struct reg_init bd71837_ldo6_inits[] = {
 	},
 };
 
-#define NUM_DVS_BUCKS 4
-
-struct of_dvs_setting {
-	const char *prop;
-	unsigned int reg;
-};
-
-static int set_dvs_levels(const struct of_dvs_setting *dvs,
-			  struct device_node *np,
-			  const struct regulator_desc *desc,
-			  struct regmap *regmap)
-{
-	int ret, i;
-	unsigned int uv;
-
-	ret = of_property_read_u32(np, dvs->prop, &uv);
-	if (ret) {
-		if (ret != -EINVAL)
-			return ret;
-		return 0;
-	}
-
-	for (i = 0; i < desc->n_voltages; i++) {
-		ret = regulator_desc_list_voltage_linear_range(desc, i);
-		if (ret < 0)
-			continue;
-		if (ret == uv) {
-			i <<= ffs(desc->vsel_mask) - 1;
-			ret = regmap_update_bits(regmap, dvs->reg,
-						 DVS_BUCK_RUN_MASK, i);
-			break;
-		}
-	}
-	return ret;
-}
-
-static int buck4_set_hw_dvs_levels(struct device_node *np,
+static int buck_set_hw_dvs_levels(struct device_node *np,
 			    const struct regulator_desc *desc,
 			    struct regulator_config *cfg)
 {
-	int ret, i;
-	const struct of_dvs_setting dvs[] = {
-		{
-			.prop = "rohm,dvs-run-voltage",
-			.reg = BD71837_REG_BUCK4_VOLT_RUN,
-		},
-	};
+	struct bd718xx_regulator_data *data;
 
-	for (i = 0; i < ARRAY_SIZE(dvs); i++) {
-		ret = set_dvs_levels(&dvs[i], np, desc, cfg->regmap);
-		if (ret)
-			break;
-	}
-	return ret;
-}
-static int buck3_set_hw_dvs_levels(struct device_node *np,
-			    const struct regulator_desc *desc,
-			    struct regulator_config *cfg)
-{
-	int ret, i;
-	const struct of_dvs_setting dvs[] = {
-		{
-			.prop = "rohm,dvs-run-voltage",
-			.reg = BD71837_REG_BUCK3_VOLT_RUN,
-		},
-	};
+	data = container_of(desc, struct bd718xx_regulator_data, desc);
 
-	for (i = 0; i < ARRAY_SIZE(dvs); i++) {
-		ret = set_dvs_levels(&dvs[i], np, desc, cfg->regmap);
-		if (ret)
-			break;
-	}
-	return ret;
-}
-
-static int buck2_set_hw_dvs_levels(struct device_node *np,
-			    const struct regulator_desc *desc,
-			    struct regulator_config *cfg)
-{
-	int ret, i;
-	const struct of_dvs_setting dvs[] = {
-		{
-			.prop = "rohm,dvs-run-voltage",
-			.reg = BD718XX_REG_BUCK2_VOLT_RUN,
-		},
-		{
-			.prop = "rohm,dvs-idle-voltage",
-			.reg = BD718XX_REG_BUCK2_VOLT_IDLE,
-		},
-	};
-
-
-
-	for (i = 0; i < ARRAY_SIZE(dvs); i++) {
-		ret = set_dvs_levels(&dvs[i], np, desc, cfg->regmap);
-		if (ret)
-			break;
-	}
-	return ret;
-}
-
-static int buck1_set_hw_dvs_levels(struct device_node *np,
-			    const struct regulator_desc *desc,
-			    struct regulator_config *cfg)
-{
-	int ret, i;
-	const struct of_dvs_setting dvs[] = {
-		{
-			.prop = "rohm,dvs-run-voltage",
-			.reg = BD718XX_REG_BUCK1_VOLT_RUN,
-		},
-		{
-			.prop = "rohm,dvs-idle-voltage",
-			.reg = BD718XX_REG_BUCK1_VOLT_IDLE,
-		},
-		{
-			.prop = "rohm,dvs-suspend-voltage",
-			.reg = BD718XX_REG_BUCK1_VOLT_SUSP,
-		},
-	};
-
-	for (i = 0; i < ARRAY_SIZE(dvs); i++) {
-		ret = set_dvs_levels(&dvs[i], np, desc, cfg->regmap);
-		if (ret)
-			break;
-	}
-	return ret;
+	return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
 }
 
 static const struct bd718xx_regulator_data bd71847_regulators[] = {
@@ -496,7 +379,17 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = {
 			.enable_reg = BD718XX_REG_BUCK1_CTRL,
 			.enable_mask = BD718XX_BUCK_EN,
 			.owner = THIS_MODULE,
-			.of_parse_cb = buck1_set_hw_dvs_levels,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND,
+			.run_reg = BD718XX_REG_BUCK1_VOLT_RUN,
+			.run_mask = DVS_BUCK_RUN_MASK,
+			.idle_reg = BD718XX_REG_BUCK1_VOLT_IDLE,
+			.idle_mask = DVS_BUCK_RUN_MASK,
+			.suspend_reg = BD718XX_REG_BUCK1_VOLT_SUSP,
+			.suspend_mask = DVS_BUCK_RUN_MASK,
 		},
 		.init = {
 			.reg = BD718XX_REG_BUCK1_CTRL,
@@ -520,7 +413,14 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = {
 			.enable_reg = BD718XX_REG_BUCK2_CTRL,
 			.enable_mask = BD718XX_BUCK_EN,
 			.owner = THIS_MODULE,
-			.of_parse_cb = buck2_set_hw_dvs_levels,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE,
+			.run_reg = BD718XX_REG_BUCK2_VOLT_RUN,
+			.run_mask = DVS_BUCK_RUN_MASK,
+			.idle_reg = BD718XX_REG_BUCK2_VOLT_IDLE,
+			.idle_mask = DVS_BUCK_RUN_MASK,
 		},
 		.init = {
 			.reg = BD718XX_REG_BUCK2_CTRL,
@@ -792,7 +692,17 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
 			.enable_reg = BD718XX_REG_BUCK1_CTRL,
 			.enable_mask = BD718XX_BUCK_EN,
 			.owner = THIS_MODULE,
-			.of_parse_cb = buck1_set_hw_dvs_levels,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND,
+			.run_reg = BD718XX_REG_BUCK1_VOLT_RUN,
+			.run_mask = DVS_BUCK_RUN_MASK,
+			.idle_reg = BD718XX_REG_BUCK1_VOLT_IDLE,
+			.idle_mask = DVS_BUCK_RUN_MASK,
+			.suspend_reg = BD718XX_REG_BUCK1_VOLT_SUSP,
+			.suspend_mask = DVS_BUCK_RUN_MASK,
 		},
 		.init = {
 			.reg = BD718XX_REG_BUCK1_CTRL,
@@ -816,7 +726,14 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
 			.enable_reg = BD718XX_REG_BUCK2_CTRL,
 			.enable_mask = BD718XX_BUCK_EN,
 			.owner = THIS_MODULE,
-			.of_parse_cb = buck2_set_hw_dvs_levels,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE,
+			.run_reg = BD718XX_REG_BUCK2_VOLT_RUN,
+			.run_mask = DVS_BUCK_RUN_MASK,
+			.idle_reg = BD718XX_REG_BUCK2_VOLT_IDLE,
+			.idle_mask = DVS_BUCK_RUN_MASK,
 		},
 		.init = {
 			.reg = BD718XX_REG_BUCK2_CTRL,
@@ -840,7 +757,12 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
 			.enable_reg = BD71837_REG_BUCK3_CTRL,
 			.enable_mask = BD718XX_BUCK_EN,
 			.owner = THIS_MODULE,
-			.of_parse_cb = buck3_set_hw_dvs_levels,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN,
+			.run_reg = BD71837_REG_BUCK3_VOLT_RUN,
+			.run_mask = DVS_BUCK_RUN_MASK,
 		},
 		.init = {
 			.reg = BD71837_REG_BUCK3_CTRL,
@@ -864,7 +786,12 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
 			.enable_reg = BD71837_REG_BUCK4_CTRL,
 			.enable_mask = BD718XX_BUCK_EN,
 			.owner = THIS_MODULE,
-			.of_parse_cb = buck4_set_hw_dvs_levels,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN,
+			.run_reg = BD71837_REG_BUCK4_VOLT_RUN,
+			.run_mask = DVS_BUCK_RUN_MASK,
 		},
 		.init = {
 			.reg = BD71837_REG_BUCK4_CTRL,
diff --git a/drivers/regulator/rohm-regulator.c b/drivers/regulator/rohm-regulator.c
new file mode 100644
index 000000000000..ca368ada53c6
--- /dev/null
+++ b/drivers/regulator/rohm-regulator.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 ROHM Semiconductors
+
+#include <linux/errno.h>
+#include <linux/mfd/rohm-generic.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+
+static int set_dvs_level(const struct regulator_desc *desc,
+			 struct device_node *np, struct regmap *regmap,
+			 char *prop, unsigned int reg, unsigned int mask,
+			 unsigned int omask, unsigned int oreg)
+{
+	int ret, i;
+	uint32_t uv;
+
+	ret = of_property_read_u32(np, prop, &uv);
+	if (ret) {
+		if (ret != -EINVAL)
+			return ret;
+		return 0;
+	}
+
+	if (uv == 0) {
+		if (omask)
+			return regmap_update_bits(regmap, oreg, omask, 0);
+	}
+	for (i = 0; i < desc->n_voltages; i++) {
+		ret = regulator_desc_list_voltage_linear_range(desc, i);
+		if (ret < 0)
+			continue;
+		if (ret == uv) {
+			i <<= ffs(desc->vsel_mask) - 1;
+			ret = regmap_update_bits(regmap, reg, mask, i);
+			if (omask && !ret)
+				ret = regmap_update_bits(regmap, oreg, omask,
+							 omask);
+			break;
+		}
+	}
+	return ret;
+}
+
+int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
+			  struct device_node *np,
+			  const struct regulator_desc *desc,
+			  struct regmap *regmap)
+{
+	int i, ret = 0;
+	char *prop;
+	unsigned int reg, mask, omask, oreg = desc->enable_reg;
+
+	for (i = 0; i < ROHM_DVS_LEVEL_MAX && !ret; i++) {
+		if (dvs->level_map & (1 << i)) {
+			switch (i + 1) {
+			case ROHM_DVS_LEVEL_RUN:
+				prop = "rohm,dvs-run-voltage";
+				reg = dvs->run_reg;
+				mask = dvs->run_mask;
+				omask = dvs->run_on_mask;
+				break;
+			case ROHM_DVS_LEVEL_IDLE:
+				prop = "rohm,dvs-idle-voltage";
+				reg = dvs->idle_reg;
+				mask = dvs->idle_mask;
+				omask = dvs->idle_on_mask;
+				break;
+			case ROHM_DVS_LEVEL_SUSPEND:
+				prop = "rohm,dvs-suspend-voltage";
+				reg = dvs->suspend_reg;
+				mask = dvs->suspend_mask;
+				omask = dvs->suspend_on_mask;
+				break;
+			case ROHM_DVS_LEVEL_LPSR:
+				prop = "rohm,dvs-lpsr-voltage";
+				reg = dvs->lpsr_reg;
+				mask = dvs->lpsr_mask;
+				omask = dvs->lpsr_on_mask;
+				break;
+			default:
+				return -EINVAL;
+			}
+			ret = set_dvs_level(desc, np, regmap, prop, reg, mask,
+					    omask, oreg);
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL(rohm_regulator_set_dvs_levels);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers");
diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h
index b280f3100d6c..86397b42e9bf 100644
--- a/include/linux/mfd/rohm-generic.h
+++ b/include/linux/mfd/rohm-generic.h
@@ -4,6 +4,9 @@
 #ifndef __LINUX_MFD_ROHM_H__
 #define __LINUX_MFD_ROHM_H__
 
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+
 enum {
 	ROHM_CHIP_TYPE_BD71837 = 0,
 	ROHM_CHIP_TYPE_BD71847,
@@ -18,4 +21,45 @@ struct rohm_regmap_dev {
 	struct regmap *regmap;
 };
 
+enum {
+	ROHM_DVS_LEVEL_UNKNOWN,
+	ROHM_DVS_LEVEL_RUN,
+	ROHM_DVS_LEVEL_IDLE,
+	ROHM_DVS_LEVEL_SUSPEND,
+	ROHM_DVS_LEVEL_LPSR,
+#define ROHM_DVS_LEVEL_MAX ROHM_DVS_LEVEL_LPSR
+};
+
+struct rohm_dvs_config {
+	uint64_t level_map;
+	unsigned int run_reg;
+	unsigned int run_mask;
+	unsigned int run_on_mask;
+	unsigned int idle_reg;
+	unsigned int idle_mask;
+	unsigned int idle_on_mask;
+	unsigned int suspend_reg;
+	unsigned int suspend_mask;
+	unsigned int suspend_on_mask;
+	unsigned int lpsr_reg;
+	unsigned int lpsr_mask;
+	unsigned int lpsr_on_mask;
+};
+
+#if IS_ENABLED(CONFIG_REGULATOR_ROHM)
+int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
+				  struct device_node *np,
+				  const struct regulator_desc *desc,
+				  struct regmap *regmap);
+
+#else
+static inline int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
+						struct device_node *np,
+						const struct regulator_desc *desc,
+						struct regmap *regmap)
+{
+	return 0;
+}
+#endif //IS_ENABLED(CONFIG_REGULATOR_ROHM)
+
 #endif
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 05/13] regulator: bd71828: Basic support for ROHM bd71828 PMIC regulators
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
                   ` (3 preceding siblings ...)
  2019-10-17  9:44 ` [RFC PATCH 04/13] regulator: bd718x7: Split driver to common and bd718x7 specific parts Matti Vaittinen
@ 2019-10-17  9:44 ` Matti Vaittinen
  2019-10-17  9:48 ` [RFC PATCH 06/13] regulator: bd71828: Add GPIO based run-level control for regulators Matti Vaittinen
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:44 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

ROHM BD71828 is a power management IC containing 7 bucks and 7 LDOs. Bucks
1,2,6 and 7 can be assigned to a regulator group controlled by run-levels.
Eg. Voltages and enable/disable statuses for specific run-levels (run0 to
run3) can be set via register interface and run level changes can then be
done either via I2C or GPIO.

This initial commit does not support assigning bucks to be controlled via
run-levels but only allows them to be individually controlled.

LDO5 voltage can also be controlled by GPIO2 pin and register interfaces
but this driver only supports the control via register.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/regulator/Kconfig             |  12 +
 drivers/regulator/Makefile            |   1 +
 drivers/regulator/bd71828-regulator.c | 832 ++++++++++++++++++++++++++
 3 files changed, 845 insertions(+)
 create mode 100644 drivers/regulator/bd71828-regulator.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 74de76bf98d7..8c94a35d059a 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -193,6 +193,18 @@ config REGULATOR_BD70528
 	  This driver can also be built as a module. If so, the module
 	  will be called bd70528-regulator.
 
+config REGULATOR_BD71828
+	tristate "ROHM BD71828 Power Regulator"
+	depends on MFD_ROHM_BD71828
+	select REGULATOR_ROHM
+	help
+	  This driver supports voltage regulators on ROHM BD71828 PMIC.
+	  This will enable support for the software controllable buck
+	  and LDO regulators.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called bd71828-regulator.
+
 config REGULATOR_BD718XX
 	tristate "ROHM BD71837 Power Regulator"
 	depends on MFD_ROHM_BD718XX
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 41e4055d2d66..b363b592a429 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
 obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
 obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
 obj-$(CONFIG_REGULATOR_BD70528) += bd70528-regulator.o
+obj-$(CONFIG_REGULATOR_BD71828) += bd71828-regulator.o
 obj-$(CONFIG_REGULATOR_BD718XX) += bd718x7-regulator.o
 obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
 obj-$(CONFIG_REGULATOR_DA903X)	+= da903x.o
diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c
new file mode 100644
index 000000000000..44e4e7235346
--- /dev/null
+++ b/drivers/regulator/bd71828-regulator.c
@@ -0,0 +1,832 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2019 ROHM Semiconductors
+// bd71828-regulator.c ROHM BD71828GW-DS1 regulator driver
+//
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/rohm-bd71828.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+struct reg_init {
+	unsigned int reg;
+	unsigned int mask;
+	unsigned int val;
+};
+struct bd71828_regulator_data {
+	struct regulator_desc desc;
+	const struct rohm_dvs_config dvs;
+	const struct reg_init *reg_inits;
+	int reg_init_amnt;
+};
+
+static const struct reg_init buck1_inits[] = {
+	/*
+	 * DVS Buck voltages can be changed by register values or via GPIO.
+	 * Use register accesses by default.
+	 */
+	{
+		.reg = BD71828_REG_PS_CTRL_1,
+		.mask = BD71828_MASK_DVS_BUCK1_CTRL,
+		.val = BD71828_DVS_BUCK1_CTRL_I2C,
+	},
+};
+
+static const struct reg_init buck2_inits[] = {
+	{
+		.reg = BD71828_REG_PS_CTRL_1,
+		.mask = BD71828_MASK_DVS_BUCK2_CTRL,
+		.val = BD71828_DVS_BUCK2_CTRL_I2C,
+	},
+};
+
+static const struct reg_init buck6_inits[] = {
+	{
+		.reg = BD71828_REG_PS_CTRL_1,
+		.mask = BD71828_MASK_DVS_BUCK6_CTRL,
+		.val = BD71828_DVS_BUCK6_CTRL_I2C,
+	},
+};
+
+static const struct reg_init buck7_inits[] = {
+	{
+		.reg = BD71828_REG_PS_CTRL_1,
+		.mask = BD71828_MASK_DVS_BUCK7_CTRL,
+		.val = BD71828_DVS_BUCK7_CTRL_I2C,
+	},
+};
+
+static const struct regulator_linear_range bd71828_buck1267_volts[] = {
+	REGULATOR_LINEAR_RANGE(500000, 0x00, 0xef, 6250),
+	REGULATOR_LINEAR_RANGE(2000000, 0xf0, 0xff, 0),
+};
+
+static const struct regulator_linear_range bd71828_buck3_volts[] = {
+	REGULATOR_LINEAR_RANGE(1200000, 0x00, 0x0f, 50000),
+	REGULATOR_LINEAR_RANGE(2000000, 0x10, 0x1f, 0),
+};
+
+static const struct regulator_linear_range bd71828_buck4_volts[] = {
+	REGULATOR_LINEAR_RANGE(1000000, 0x00, 0x1f, 25000),
+	REGULATOR_LINEAR_RANGE(1800000, 0x20, 0x3f, 0),
+};
+
+static const struct regulator_linear_range bd71828_buck5_volts[] = {
+	REGULATOR_LINEAR_RANGE(2500000, 0x00, 0x0f, 50000),
+	REGULATOR_LINEAR_RANGE(3300000, 0x10, 0x1f, 0),
+};
+
+static const struct regulator_linear_range bd71828_ldo_volts[] = {
+	REGULATOR_LINEAR_RANGE(800000, 0x00, 0x31, 50000),
+	REGULATOR_LINEAR_RANGE(3300000, 0x32, 0x3f, 0),
+};
+
+static int ramp_delay_supported(struct regulator_dev *rdev)
+{
+	switch (rdev->desc->id) {
+	case BD71828_BUCK1:
+	case BD71828_BUCK2:
+	case BD71828_BUCK6:
+	case BD71828_BUCK7:
+		return 1;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int bd71828_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+	unsigned int val;
+
+	if (!ramp_delay_supported(rdev)) {
+		dev_err(&rdev->dev, "%s: can't set ramp-delay\n",
+			rdev->desc->name);
+		return -EINVAL;
+	}
+
+	switch (ramp_delay) {
+	case 1 ... 2500:
+		val = 0;
+		break;
+	case 2501 ... 5000:
+		val = 1;
+		break;
+	case 5001 ... 10000:
+		val = 2;
+		break;
+	case 10001 ... 20000:
+		val = 3;
+		break;
+	default:
+		val = 3;
+		dev_err(&rdev->dev,
+			"ramp_delay: %d not supported, setting 20mV/uS",
+			 ramp_delay);
+	}
+
+	/*
+	 * On BD71828 the ramp delay level control reg is at offset +2 to
+	 * enable reg
+	 */
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg + 2,
+				  BD71828_MASK_RAMP_DELAY,
+				  val << (ffs(BD71828_MASK_RAMP_DELAY) - 1));
+
+}
+
+static int buck_set_hw_dvs_levels(struct device_node *np,
+				  const struct regulator_desc *desc,
+				  struct regulator_config *cfg)
+{
+	struct bd71828_regulator_data *data;
+
+	data = container_of(desc, struct bd71828_regulator_data, desc);
+
+	return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
+}
+
+static int ldo6_parse_dt(struct device_node *np,
+			 const struct regulator_desc *desc,
+			 struct regulator_config *cfg)
+{
+	int ret, i;
+	uint32_t uv = 0;
+	unsigned int en;
+	struct regmap *regmap = cfg->regmap;
+	static const char * const props[] = { "rohm,dvs-run-voltage",
+					      "rohm,dvs-idle-voltage",
+					      "rohm,dvs-suspend-voltage",
+					      "rohm,dvs-lpsr-voltage" };
+	unsigned int mask[] = { BD71828_MASK_RUN_EN, BD71828_MASK_IDLE_EN,
+			       BD71828_MASK_SUSP_EN, BD71828_MASK_LPSR_EN };
+
+	for (i = 0; i < ARRAY_SIZE(props); i++) {
+		ret = of_property_read_u32(np, props[i], &uv);
+		if (ret) {
+			if (ret != -EINVAL)
+				return ret;
+			continue;
+		}
+		if (uv)
+			en = 0xffffffff;
+		else
+			en = 0;
+
+		ret = regmap_update_bits(regmap, desc->enable_reg, mask[i], en);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static const struct regulator_ops bd71828_buck_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.list_voltage = regulator_list_voltage_linear_range,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static const struct regulator_ops bd71828_dvs_buck_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.list_voltage = regulator_list_voltage_linear_range,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_time_sel = regulator_set_voltage_time_sel,
+	.set_ramp_delay = bd71828_set_ramp_delay,
+};
+
+static const struct regulator_ops bd71828_ldo_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.list_voltage = regulator_list_voltage_linear_range,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static int bd71828_ldo6_get_voltage(struct regulator_dev *rdev)
+{
+	return BD71828_LDO_6_VOLTAGE;
+}
+
+static const struct regulator_ops bd71828_ldo6_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.get_voltage = bd71828_ldo6_get_voltage,
+	.is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct bd71828_regulator_data bd71828_rdata[] = {
+	{
+		.desc = {
+			.name = "buck1",
+			.of_match = of_match_ptr("BUCK1"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_BUCK1,
+			.ops = &bd71828_dvs_buck_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_buck1267_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_buck1267_volts),
+			.n_voltages = BD71828_BUCK1267_VOLTS,
+			.enable_reg = BD71828_REG_BUCK1_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_BUCK1_VOLT,
+			.vsel_mask = BD71828_MASK_BUCK1267_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_BUCK1_VOLT,
+			.run_mask = BD71828_MASK_BUCK1267_VOLT,
+			.idle_reg = BD71828_REG_BUCK1_IDLE_VOLT,
+			.idle_mask = BD71828_MASK_BUCK1267_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_reg = BD71828_REG_BUCK1_SUSP_VOLT,
+			.suspend_mask = BD71828_MASK_BUCK1267_VOLT,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+			/*
+			 * LPSR voltage is same as SUSPEND voltage. Allow
+			 * setting it so that regulator can be set enabled at
+			 * LPSR state
+			 */
+			.lpsr_reg = BD71828_REG_BUCK1_SUSP_VOLT,
+			.lpsr_mask = BD71828_MASK_BUCK1267_VOLT,
+		},
+		.reg_inits = buck1_inits,
+		.reg_init_amnt = ARRAY_SIZE(buck1_inits),
+	},
+	{
+		.desc = {
+			.name = "buck2",
+			.of_match = of_match_ptr("BUCK2"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_BUCK2,
+			.ops = &bd71828_dvs_buck_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_buck1267_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_buck1267_volts),
+			.n_voltages = BD71828_BUCK1267_VOLTS,
+			.enable_reg = BD71828_REG_BUCK2_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_BUCK2_VOLT,
+			.vsel_mask = BD71828_MASK_BUCK1267_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_BUCK2_VOLT,
+			.run_mask = BD71828_MASK_BUCK1267_VOLT,
+			.idle_reg = BD71828_REG_BUCK2_IDLE_VOLT,
+			.idle_mask = BD71828_MASK_BUCK1267_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_reg = BD71828_REG_BUCK2_SUSP_VOLT,
+			.suspend_mask = BD71828_MASK_BUCK1267_VOLT,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+			.lpsr_reg = BD71828_REG_BUCK2_SUSP_VOLT,
+			.lpsr_mask = BD71828_MASK_BUCK1267_VOLT,
+		},
+		.reg_inits = buck2_inits,
+		.reg_init_amnt = ARRAY_SIZE(buck2_inits),
+	},
+	{
+		.desc = {
+			.name = "buck3",
+			.of_match = of_match_ptr("BUCK3"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_BUCK3,
+			.ops = &bd71828_buck_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_buck3_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_buck3_volts),
+			.n_voltages = BD71828_BUCK3_VOLTS,
+			.enable_reg = BD71828_REG_BUCK3_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_BUCK3_VOLT,
+			.vsel_mask = BD71828_MASK_BUCK3_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			/*
+			 * BUCK3 only supports single voltage for all states.
+			 * voltage can be individually enabled for each state
+			 * though => allow setting all states to support
+			 * enabling power rail on different states.
+			 */
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_BUCK3_VOLT,
+			.idle_reg = BD71828_REG_BUCK3_VOLT,
+			.suspend_reg = BD71828_REG_BUCK3_VOLT,
+			.lpsr_reg = BD71828_REG_BUCK3_VOLT,
+			.run_mask = BD71828_MASK_BUCK3_VOLT,
+			.idle_mask = BD71828_MASK_BUCK3_VOLT,
+			.suspend_mask = BD71828_MASK_BUCK3_VOLT,
+			.lpsr_mask = BD71828_MASK_BUCK3_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+		},
+	},
+	{
+		.desc = {
+			.name = "buck4",
+			.of_match = of_match_ptr("BUCK4"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_BUCK4,
+			.ops = &bd71828_buck_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_buck4_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_buck4_volts),
+			.n_voltages = BD71828_BUCK4_VOLTS,
+			.enable_reg = BD71828_REG_BUCK4_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_BUCK4_VOLT,
+			.vsel_mask = BD71828_MASK_BUCK4_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			/*
+			 * BUCK4 only supports single voltage for all states.
+			 * voltage can be individually enabled for each state
+			 * though => allow setting all states to support
+			 * enabling power rail on different states.
+			 */
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_BUCK4_VOLT,
+			.idle_reg = BD71828_REG_BUCK4_VOLT,
+			.suspend_reg = BD71828_REG_BUCK4_VOLT,
+			.lpsr_reg = BD71828_REG_BUCK4_VOLT,
+			.run_mask = BD71828_MASK_BUCK4_VOLT,
+			.idle_mask = BD71828_MASK_BUCK4_VOLT,
+			.suspend_mask = BD71828_MASK_BUCK4_VOLT,
+			.lpsr_mask = BD71828_MASK_BUCK4_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+		},
+	},
+	{
+		.desc = {
+			.name = "buck5",
+			.of_match = of_match_ptr("BUCK5"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_BUCK5,
+			.ops = &bd71828_buck_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_buck5_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_buck5_volts),
+			.n_voltages = BD71828_BUCK5_VOLTS,
+			.enable_reg = BD71828_REG_BUCK5_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_BUCK5_VOLT,
+			.vsel_mask = BD71828_MASK_BUCK5_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			/*
+			 * BUCK5 only supports single voltage for all states.
+			 * voltage can be individually enabled for each state
+			 * though => allow setting all states to support
+			 * enabling power rail on different states.
+			 */
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_BUCK5_VOLT,
+			.idle_reg = BD71828_REG_BUCK5_VOLT,
+			.suspend_reg = BD71828_REG_BUCK5_VOLT,
+			.lpsr_reg = BD71828_REG_BUCK5_VOLT,
+			.run_mask = BD71828_MASK_BUCK5_VOLT,
+			.idle_mask = BD71828_MASK_BUCK5_VOLT,
+			.suspend_mask = BD71828_MASK_BUCK5_VOLT,
+			.lpsr_mask = BD71828_MASK_BUCK5_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+		},
+	},
+	{
+		.desc = {
+			.name = "buck6",
+			.of_match = of_match_ptr("BUCK6"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_BUCK6,
+			.ops = &bd71828_dvs_buck_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_buck1267_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_buck1267_volts),
+			.n_voltages = BD71828_BUCK1267_VOLTS,
+			.enable_reg = BD71828_REG_BUCK6_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_BUCK6_VOLT,
+			.vsel_mask = BD71828_MASK_BUCK1267_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_BUCK6_VOLT,
+			.run_mask = BD71828_MASK_BUCK1267_VOLT,
+			.idle_reg = BD71828_REG_BUCK6_IDLE_VOLT,
+			.idle_mask = BD71828_MASK_BUCK1267_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_reg = BD71828_REG_BUCK6_SUSP_VOLT,
+			.suspend_mask = BD71828_MASK_BUCK1267_VOLT,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+			.lpsr_reg = BD71828_REG_BUCK6_SUSP_VOLT,
+			.lpsr_mask = BD71828_MASK_BUCK1267_VOLT,
+		},
+		.reg_inits = buck6_inits,
+		.reg_init_amnt = ARRAY_SIZE(buck6_inits),
+	},
+	{
+		.desc = {
+			.name = "buck7",
+			.of_match = of_match_ptr("BUCK7"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_BUCK7,
+			.ops = &bd71828_dvs_buck_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_buck1267_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_buck1267_volts),
+			.n_voltages = BD71828_BUCK1267_VOLTS,
+			.enable_reg = BD71828_REG_BUCK7_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_BUCK7_VOLT,
+			.vsel_mask = BD71828_MASK_BUCK1267_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_BUCK7_VOLT,
+			.run_mask = BD71828_MASK_BUCK1267_VOLT,
+			.idle_reg = BD71828_REG_BUCK7_IDLE_VOLT,
+			.idle_mask = BD71828_MASK_BUCK1267_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_reg = BD71828_REG_BUCK7_SUSP_VOLT,
+			.suspend_mask = BD71828_MASK_BUCK1267_VOLT,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+			.lpsr_reg = BD71828_REG_BUCK7_SUSP_VOLT,
+			.lpsr_mask = BD71828_MASK_BUCK1267_VOLT,
+		},
+		.reg_inits = buck7_inits,
+		.reg_init_amnt = ARRAY_SIZE(buck7_inits),
+	},
+	{
+		.desc = {
+			.name = "ldo1",
+			.of_match = of_match_ptr("LDO1"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_LDO1,
+			.ops = &bd71828_ldo_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_ldo_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_ldo_volts),
+			.n_voltages = BD71828_LDO_VOLTS,
+			.enable_reg = BD71828_REG_LDO1_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_LDO1_VOLT,
+			.vsel_mask = BD71828_MASK_LDO_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			/*
+			 * LDO1 only supports single voltage for all states.
+			 * voltage can be individually enabled for each state
+			 * though => allow setting all states to support
+			 * enabling power rail on different states.
+			 */
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_LDO1_VOLT,
+			.idle_reg = BD71828_REG_LDO1_VOLT,
+			.suspend_reg = BD71828_REG_LDO1_VOLT,
+			.lpsr_reg = BD71828_REG_LDO1_VOLT,
+			.run_mask = BD71828_MASK_LDO_VOLT,
+			.idle_mask = BD71828_MASK_LDO_VOLT,
+			.suspend_mask = BD71828_MASK_LDO_VOLT,
+			.lpsr_mask = BD71828_MASK_LDO_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo2",
+			.of_match = of_match_ptr("LDO2"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_LDO2,
+			.ops = &bd71828_ldo_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_ldo_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_ldo_volts),
+			.n_voltages = BD71828_LDO_VOLTS,
+			.enable_reg = BD71828_REG_LDO2_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_LDO2_VOLT,
+			.vsel_mask = BD71828_MASK_LDO_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			/*
+			 * LDO2 only supports single voltage for all states.
+			 * voltage can be individually enabled for each state
+			 * though => allow setting all states to support
+			 * enabling power rail on different states.
+			 */
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_LDO2_VOLT,
+			.idle_reg = BD71828_REG_LDO2_VOLT,
+			.suspend_reg = BD71828_REG_LDO2_VOLT,
+			.lpsr_reg = BD71828_REG_LDO2_VOLT,
+			.run_mask = BD71828_MASK_LDO_VOLT,
+			.idle_mask = BD71828_MASK_LDO_VOLT,
+			.suspend_mask = BD71828_MASK_LDO_VOLT,
+			.lpsr_mask = BD71828_MASK_LDO_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo3",
+			.of_match = of_match_ptr("LDO3"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_LDO3,
+			.ops = &bd71828_ldo_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_ldo_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_ldo_volts),
+			.n_voltages = BD71828_LDO_VOLTS,
+			.enable_reg = BD71828_REG_LDO3_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_LDO3_VOLT,
+			.vsel_mask = BD71828_MASK_LDO_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			/*
+			 * LDO3 only supports single voltage for all states.
+			 * voltage can be individually enabled for each state
+			 * though => allow setting all states to support
+			 * enabling power rail on different states.
+			 */
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_LDO3_VOLT,
+			.idle_reg = BD71828_REG_LDO3_VOLT,
+			.suspend_reg = BD71828_REG_LDO3_VOLT,
+			.lpsr_reg = BD71828_REG_LDO3_VOLT,
+			.run_mask = BD71828_MASK_LDO_VOLT,
+			.idle_mask = BD71828_MASK_LDO_VOLT,
+			.suspend_mask = BD71828_MASK_LDO_VOLT,
+			.lpsr_mask = BD71828_MASK_LDO_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+		},
+
+	}, {
+		.desc = {
+			.name = "ldo4",
+			.of_match = of_match_ptr("LDO4"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_LDO4,
+			.ops = &bd71828_ldo_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_ldo_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_ldo_volts),
+			.n_voltages = BD71828_LDO_VOLTS,
+			.enable_reg = BD71828_REG_LDO4_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_LDO4_VOLT,
+			.vsel_mask = BD71828_MASK_LDO_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			/*
+			 * LDO1 only supports single voltage for all states.
+			 * voltage can be individually enabled for each state
+			 * though => allow setting all states to support
+			 * enabling power rail on different states.
+			 */
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_LDO4_VOLT,
+			.idle_reg = BD71828_REG_LDO4_VOLT,
+			.suspend_reg = BD71828_REG_LDO4_VOLT,
+			.lpsr_reg = BD71828_REG_LDO4_VOLT,
+			.run_mask = BD71828_MASK_LDO_VOLT,
+			.idle_mask = BD71828_MASK_LDO_VOLT,
+			.suspend_mask = BD71828_MASK_LDO_VOLT,
+			.lpsr_mask = BD71828_MASK_LDO_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo5",
+			.of_match = of_match_ptr("LDO5"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_LDO5,
+			.ops = &bd71828_ldo_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_ldo_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_ldo_volts),
+			.n_voltages = BD71828_LDO_VOLTS,
+			.enable_reg = BD71828_REG_LDO5_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_LDO5_VOLT,
+			.vsel_mask = BD71828_MASK_LDO_VOLT,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+			.owner = THIS_MODULE,
+		},
+		/*
+		 * LDO5 is special. It can choose vsel settings to be configured
+		 * from 2 different registers (by GPIO).
+		 *
+		 * This driver supports only configuration where
+		 * BD71828_REG_LDO5_VOLT_L is used.
+		 */
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_LDO5_VOLT,
+			.idle_reg = BD71828_REG_LDO5_VOLT,
+			.suspend_reg = BD71828_REG_LDO5_VOLT,
+			.lpsr_reg = BD71828_REG_LDO5_VOLT,
+			.run_mask = BD71828_MASK_LDO_VOLT,
+			.idle_mask = BD71828_MASK_LDO_VOLT,
+			.suspend_mask = BD71828_MASK_LDO_VOLT,
+			.lpsr_mask = BD71828_MASK_LDO_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+		},
+
+	}, {
+		.desc = {
+			.name = "ldo6",
+			.of_match = of_match_ptr("LDO6"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_LDO6,
+			.ops = &bd71828_ldo6_ops,
+			.type = REGULATOR_VOLTAGE,
+			.n_voltages = 1,
+			.enable_reg = BD71828_REG_LDO6_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.owner = THIS_MODULE,
+			/*
+			 * LDO6 only supports enable/disable for all states.
+			 * Voltage for LDO6 is fixed.
+			 */
+			.of_parse_cb = ldo6_parse_dt,
+		},
+	}, {
+		.desc = {
+			/* SNVS LDO in data-sheet */
+			.name = "ldo7",
+			.of_match = of_match_ptr("LDO7"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD71828_LDO_SNVS,
+			.ops = &bd71828_ldo_ops,
+			.type = REGULATOR_VOLTAGE,
+			.linear_ranges = bd71828_ldo_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd71828_ldo_volts),
+			.n_voltages = BD71828_LDO_VOLTS,
+			.enable_reg = BD71828_REG_LDO7_EN,
+			.enable_mask = BD71828_MASK_RUN_EN,
+			.vsel_reg = BD71828_REG_LDO7_VOLT,
+			.vsel_mask = BD71828_MASK_LDO_VOLT,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			/*
+			 * LDO7 only supports single voltage for all states.
+			 * voltage can be individually enabled for each state
+			 * though => allow setting all states to support
+			 * enabling power rail on different states.
+			 */
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD71828_REG_LDO7_VOLT,
+			.idle_reg = BD71828_REG_LDO7_VOLT,
+			.suspend_reg = BD71828_REG_LDO7_VOLT,
+			.lpsr_reg = BD71828_REG_LDO7_VOLT,
+			.run_mask = BD71828_MASK_LDO_VOLT,
+			.idle_mask = BD71828_MASK_LDO_VOLT,
+			.suspend_mask = BD71828_MASK_LDO_VOLT,
+			.lpsr_mask = BD71828_MASK_LDO_VOLT,
+			.idle_on_mask = BD71828_MASK_IDLE_EN,
+			.suspend_on_mask = BD71828_MASK_SUSP_EN,
+			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
+		},
+
+	},
+};
+
+static int bd71828_probe(struct platform_device *pdev)
+{
+	struct rohm_regmap_dev *bd71828;
+	int i, j, ret;
+	struct regulator_config config = {
+		.dev = pdev->dev.parent,
+	};
+
+	bd71828 = dev_get_drvdata(pdev->dev.parent);
+	if (!bd71828) {
+		dev_err(&pdev->dev, "No MFD driver data\n");
+		return -EINVAL;
+	}
+
+	config.regmap = bd71828->regmap;
+
+	for (i = 0; i < ARRAY_SIZE(bd71828_rdata); i++) {
+		struct regulator_dev *rdev;
+		const struct bd71828_regulator_data *rd;
+
+		rd = &bd71828_rdata[i];
+		rdev = devm_regulator_register(&pdev->dev,
+					       &rd->desc, &config);
+		if (IS_ERR(rdev)) {
+			dev_err(&pdev->dev,
+				"failed to register %s regulator\n",
+				rd->desc.name);
+			return PTR_ERR(rdev);
+		}
+		for (j = 0; j < rd->reg_init_amnt; j++) {
+			ret = regmap_update_bits(bd71828->regmap,
+						 rd->reg_inits[j].reg,
+						 rd->reg_inits[j].mask,
+						 rd->reg_inits[j].val);
+			if (ret) {
+				dev_err(&pdev->dev,
+					"regulator %s init failed\n",
+					rd->desc.name);
+				return ret;
+			}
+		}
+	}
+	return 0;
+}
+
+static struct platform_driver bd71828_regulator = {
+	.driver = {
+		.name = "bd71828-pmic"
+	},
+	.probe = bd71828_probe,
+};
+
+module_platform_driver(bd71828_regulator);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD71828 voltage regulator driver");
+MODULE_LICENSE("GPL");
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 06/13] regulator: bd71828: Add GPIO based run-level control for regulators
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
                   ` (4 preceding siblings ...)
  2019-10-17  9:44 ` [RFC PATCH 05/13] regulator: bd71828: Basic support for ROHM bd71828 PMIC regulators Matti Vaittinen
@ 2019-10-17  9:48 ` Matti Vaittinen
  2019-10-17  9:50 ` [RFC PATCH 07/13] regulator: bd71828: enhanced run-level support Matti Vaittinen
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:48 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

Bucks 1,2,6 and 7 on RPHM BD71828 can be either controlled as
individual regulartors - or they can be grouped to a group of
regulators that are controlled by 'run levels'. This can be
done via I2C. Each regulator can be assigned a voltage and
enable/disable status for each run-level. These statuses are
also changeable via I2C.

Run-levels can then be changed either by I2C or GPIO. This
control mechanism is selected by data in one time programmable
area (during production) and can't be changed later.

Allow regulators to be controlled via run-levels and allow
getting/setting the current run-level also via GPIO.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---

This patch is intended as a opener for discussion - not as a final
version to be merged in.

 drivers/regulator/bd71828-regulator.c | 373 +++++++++++++++++++++++++-
 1 file changed, 362 insertions(+), 11 deletions(-)

diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c
index 44e4e7235346..36f5aef941e5 100644
--- a/drivers/regulator/bd71828-regulator.c
+++ b/drivers/regulator/bd71828-regulator.c
@@ -4,8 +4,10 @@
 //
 
 #include <linux/delay.h>
+#include <linux/device.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/mfd/rohm-bd71828.h>
@@ -17,16 +19,27 @@
 #include <linux/regulator/machine.h>
 #include <linux/regulator/of_regulator.h>
 
+#define MAX_GPIO_DVS_BUCKS 4
+#define DVS_RUN_LEVELS 4
+
 struct reg_init {
 	unsigned int reg;
 	unsigned int mask;
 	unsigned int val;
 };
+
+struct run_lvl_ctrl {
+	unsigned int voltage;
+	bool enabled;
+};
+
 struct bd71828_regulator_data {
 	struct regulator_desc desc;
-	const struct rohm_dvs_config dvs;
+	struct rohm_dvs_config dvs;
 	const struct reg_init *reg_inits;
 	int reg_init_amnt;
+	struct run_lvl_ctrl run_lvl[DVS_RUN_LEVELS];
+	struct gpio_descs *gps;
 };
 
 static const struct reg_init buck1_inits[] = {
@@ -41,6 +54,14 @@ static const struct reg_init buck1_inits[] = {
 	},
 };
 
+static const struct reg_init buck1_gpio_inits[] = {
+	{
+		.reg = BD71828_REG_PS_CTRL_1,
+		.mask = BD71828_MASK_DVS_BUCK1_CTRL,
+		.val = BD71828_DVS_BUCK1_USE_RUNLVL,
+	},
+};
+
 static const struct reg_init buck2_inits[] = {
 	{
 		.reg = BD71828_REG_PS_CTRL_1,
@@ -49,6 +70,14 @@ static const struct reg_init buck2_inits[] = {
 	},
 };
 
+static const struct reg_init buck2_gpio_inits[] = {
+	{
+		.reg = BD71828_REG_PS_CTRL_1,
+		.mask = BD71828_MASK_DVS_BUCK2_CTRL,
+		.val = BD71828_DVS_BUCK2_USE_RUNLVL,
+	},
+};
+
 static const struct reg_init buck6_inits[] = {
 	{
 		.reg = BD71828_REG_PS_CTRL_1,
@@ -57,6 +86,14 @@ static const struct reg_init buck6_inits[] = {
 	},
 };
 
+static const struct reg_init buck6_gpio_inits[] = {
+	{
+		.reg = BD71828_REG_PS_CTRL_1,
+		.mask = BD71828_MASK_DVS_BUCK6_CTRL,
+		.val = BD71828_DVS_BUCK6_USE_RUNLVL,
+	},
+};
+
 static const struct reg_init buck7_inits[] = {
 	{
 		.reg = BD71828_REG_PS_CTRL_1,
@@ -65,6 +102,14 @@ static const struct reg_init buck7_inits[] = {
 	},
 };
 
+static const struct reg_init buck7_gpio_inits[] = {
+	{
+		.reg = BD71828_REG_PS_CTRL_1,
+		.mask = BD71828_MASK_DVS_BUCK7_CTRL,
+		.val = BD71828_DVS_BUCK7_USE_RUNLVL,
+	},
+};
+
 static const struct regulator_linear_range bd71828_buck1267_volts[] = {
 	REGULATOR_LINEAR_RANGE(500000, 0x00, 0xef, 6250),
 	REGULATOR_LINEAR_RANGE(2000000, 0xf0, 0xff, 0),
@@ -155,6 +200,80 @@ static int buck_set_hw_dvs_levels(struct device_node *np,
 	return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
 }
 
+static int set_runlevel_voltage(struct regmap *regmap,
+				const struct regulator_desc *desc,
+				unsigned int uv, unsigned int level)
+{
+	int i, ret = -EINVAL;
+	/*
+	 * RUN level registers are next to vsel_reg. RUN0 reg is next, then
+	 * is the RUN 1 reg and so on...
+	 */
+	u8 reg = desc->vsel_reg + level + 1;
+	u8 mask = BD71828_MASK_BUCK1267_VOLT;
+
+	for (i = 0; i < desc->n_voltages; i++) {
+		ret = regulator_desc_list_voltage_linear_range(desc, i);
+		if (ret < 0)
+			continue;
+		if (ret == uv) {
+			i <<= ffs(desc->vsel_mask) - 1;
+			ret = regmap_update_bits(regmap, reg, mask, i);
+			break;
+		}
+	}
+	return ret;
+}
+
+static int buck_set_gpio_hw_dvs_levels(struct device_node *np,
+				       const struct regulator_desc *desc,
+				       struct regulator_config *cfg)
+{
+	struct bd71828_regulator_data *data;
+	uint32_t uv;
+	int i, ret;
+	/* On BD71828 the RUN level control reg is next to enable reg */
+	u8 en_reg = desc->enable_reg + 1;
+	const char *props[DVS_RUN_LEVELS] = { "rohm,dvs-runlevel0-voltage",
+					      "rohm,dvs-runlevel1-voltage",
+					      "rohm,dvs-runlevel2-voltage",
+					      "rohm,dvs-runlevel3-voltage" };
+	u8 en_masks[DVS_RUN_LEVELS] = { BD71828_MASK_RUN0_EN,
+					BD71828_MASK_RUN1_EN,
+					BD71828_MASK_RUN2_EN,
+					BD71828_MASK_RUN3_EN };
+
+	data = container_of(desc, struct bd71828_regulator_data, desc);
+
+	for (i = 0; i < DVS_RUN_LEVELS; i++) {
+		ret = of_property_read_u32(np, props[i], &uv);
+		if (ret) {
+			if (ret != -EINVAL)
+				return ret;
+			uv = 0;
+		}
+		if (uv) {
+			data->run_lvl[i].voltage = uv;
+			data->run_lvl[i].enabled = true;
+
+			ret = set_runlevel_voltage(cfg->regmap, desc, uv, i);
+
+			if (ret)
+				return ret;
+
+			ret = regmap_update_bits(cfg->regmap, en_reg,
+						 en_masks[i], en_masks[i]);
+		} else {
+			ret = regmap_update_bits(cfg->regmap, en_reg,
+						 en_masks[i], 0);
+		}
+		if (ret)
+			return ret;
+	}
+
+	return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
+}
+
 static int ldo6_parse_dt(struct device_node *np,
 			 const struct regulator_desc *desc,
 			 struct regulator_config *cfg)
@@ -189,6 +308,85 @@ static int ldo6_parse_dt(struct device_node *np,
 	return 0;
 }
 
+static int bd71828_dvs_gpio_set_run_level(struct bd71828_regulator_data *rd,
+					  int val)
+{
+	DECLARE_BITMAP(values, 2);
+
+	if (rd->gps->ndescs != 2)
+		return -EINVAL;
+
+	if (val < 0 || val > 3)
+		return -EINVAL;
+
+	values[0] = val;
+
+	return gpiod_set_array_value_cansleep(rd->gps->ndescs, rd->gps->desc,
+				     rd->gps->info, values);
+}
+static int bd71828_dvs_gpio_get_run_level(struct bd71828_regulator_data *rd)
+{
+	int run_level;
+	int ret;
+
+	DECLARE_BITMAP(values, 2);
+
+	values[0] = 0;
+
+	if (rd->gps->ndescs != 2)
+		return -EINVAL;
+
+	ret = gpiod_get_array_value_cansleep(rd->gps->ndescs, rd->gps->desc,
+				     rd->gps->info, values);
+	if (ret)
+		return ret;
+
+	run_level = values[0];
+
+	return run_level;
+}
+
+static int bd71828_dvs_gpio_is_enabled(struct regulator_dev *rdev)
+{
+	struct bd71828_regulator_data *data = rdev_get_drvdata(rdev);
+	int ret;
+
+	// TODO: lock GPIO state (Is this needed)
+	ret = bd71828_dvs_gpio_get_run_level(data);
+	if (ret < 0)
+		goto unlock_out;
+
+	ret = data->run_lvl[ret].enabled;
+
+unlock_out:
+	//TODO: unlock
+
+	return ret;
+}
+
+static int bd71828_dvs_gpio_get_voltage(struct regulator_dev *rdev)
+{
+	int ret;
+	struct bd71828_regulator_data *data = rdev_get_drvdata(rdev);
+
+	// TODO: lock GPIO state (Is this needed)
+	ret = bd71828_dvs_gpio_get_run_level(data);
+	if (ret < 0)
+		goto unlock_out;
+
+	ret = data->run_lvl[ret].voltage;
+
+unlock_out:
+	//TODO: unlock
+
+	return ret;
+}
+
+static const struct regulator_ops dvs_buck_gpio_ops = {
+	.is_enabled = bd71828_dvs_gpio_is_enabled,
+	.get_voltage = bd71828_dvs_gpio_get_voltage,
+};
+
 static const struct regulator_ops bd71828_buck_ops = {
 	.enable = regulator_enable_regmap,
 	.disable = regulator_disable_regmap,
@@ -773,6 +971,132 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
 	},
 };
 
+struct bd71828_gpio_cfg {
+	unsigned int gpiobucks;
+	struct gpio_descs *gps;
+};
+
+static int check_dt_for_gpio_controls(struct device *d,
+				      struct bd71828_gpio_cfg *g)
+{
+	int ret, i;
+	struct device_node *np = d->of_node;
+	const char *prop = "rohm,dvs_gpio_bucks";
+	uint32_t bucks[MAX_GPIO_DVS_BUCKS];
+
+	g->gps = devm_gpiod_get_array(d, "rohm,dvs-vsel", GPIOD_OUT_LOW);
+
+	if (IS_ERR(g->gps)) {
+		ret = PTR_ERR(g->gps);
+		if (ret == -ENOENT)
+			return 0;
+		return ret;
+	}
+
+	if (g->gps->ndescs != 2)
+		return -ENOENT;
+
+	ret = of_property_read_variable_u32_array(np, prop, bucks, 0,
+						  ARRAY_SIZE(bucks));
+
+	if (ret < 0) {
+		if (ret == -EOVERFLOW)
+			return -EINVAL;
+	}
+	for (i = 0; i < ret; i++)
+		g->gpiobucks |= 1 << bucks[i];
+
+	return 0;
+}
+
+static void set_buck_gpio_controlled(struct rohm_regmap_dev *bd71828,
+				     struct bd71828_regulator_data *rd,
+				     struct bd71828_gpio_cfg *g)
+{
+	switch (rd->desc.id) {
+	case BD71828_BUCK1:
+		rd->reg_inits = buck1_gpio_inits;
+		break;
+	case BD71828_BUCK2:
+		rd->reg_inits = buck2_gpio_inits;
+		break;
+	case BD71828_BUCK6:
+		rd->reg_inits = buck6_gpio_inits;
+		break;
+	case BD71828_BUCK7:
+		rd->reg_inits = buck7_gpio_inits;
+		break;
+	default:
+		return;
+	}
+	/*
+	 * Disallow setters. Get voltages/enable states based
+	 * on current RUN level
+	 */
+	rd->gps = g->gps;
+	rd->desc.ops = &dvs_buck_gpio_ops;
+	rd->desc.of_parse_cb = buck_set_gpio_hw_dvs_levels;
+}
+
+static ssize_t show_runlevel(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	int runlevel;
+	struct bd71828_regulator_data *rd = dev_get_drvdata(dev);
+
+	if (!rd || !rd->gps)
+		return -ENOENT;
+
+	runlevel = bd71828_dvs_gpio_get_run_level(rd);
+	if (runlevel < 0)
+		return runlevel;
+
+	return sprintf(buf, "0x%x\n", runlevel);
+}
+
+static ssize_t set_runlevel(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct bd71828_regulator_data *rd = dev_get_drvdata(dev);
+	long val;
+
+	if (kstrtol(buf, 0, &val) != 0)
+		return -EINVAL;
+
+	val = bd71828_dvs_gpio_set_run_level(rd, val);
+	if (val)
+		return val;
+
+	return count;
+}
+
+static DEVICE_ATTR(runlevel, 0664, show_runlevel, set_runlevel);
+
+static struct attribute *runlevel_attributes[] = {
+	&dev_attr_runlevel.attr,
+	NULL
+};
+
+static const struct attribute_group bd71828_attr_group = {
+	.attrs	= runlevel_attributes,
+};
+
+static int bd71828_create_sysfs(struct platform_device *pdev)
+{
+	return sysfs_create_group(&pdev->dev.kobj, &bd71828_attr_group);
+}
+
+static int bd71828_remove_sysfs(struct platform_device *pdev)
+{
+	sysfs_remove_group(&pdev->dev.kobj, &bd71828_attr_group);
+	return 0;
+}
+
+static int bd71828_remove(struct platform_device *pdev)
+{
+	return bd71828_remove_sysfs(pdev);
+}
+
 static int bd71828_probe(struct platform_device *pdev)
 {
 	struct rohm_regmap_dev *bd71828;
@@ -780,6 +1104,8 @@ static int bd71828_probe(struct platform_device *pdev)
 	struct regulator_config config = {
 		.dev = pdev->dev.parent,
 	};
+	struct bd71828_gpio_cfg gcfg = {0};
+	struct bd71828_regulator_data *rd;
 
 	bd71828 = dev_get_drvdata(pdev->dev.parent);
 	if (!bd71828) {
@@ -787,35 +1113,59 @@ static int bd71828_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
+	ret = check_dt_for_gpio_controls(pdev->dev.parent, &gcfg);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to get DVS gpio resources\n");
+		return ret;
+	}
+
+	/*
+	 * Allocate device data to allow controlling more than one PMICs
+	 */
+	rd = devm_kmalloc_array(&pdev->dev, ARRAY_SIZE(bd71828_rdata),
+				sizeof(*rd), GFP_KERNEL);
+	if (!rd)
+		return -ENOMEM;
+
+	dev_set_drvdata(&pdev->dev, rd);
+
+	for (i = 0; i < ARRAY_SIZE(bd71828_rdata); i++) {
+		/* Use bd71828_rdata as template */
+		rd[i] = bd71828_rdata[i];
+
+		if (gcfg.gpiobucks & (1 << i))
+			set_buck_gpio_controlled(bd71828, &rd[i], &gcfg);
+	}
+
 	config.regmap = bd71828->regmap;
 
 	for (i = 0; i < ARRAY_SIZE(bd71828_rdata); i++) {
 		struct regulator_dev *rdev;
-		const struct bd71828_regulator_data *rd;
 
-		rd = &bd71828_rdata[i];
+		config.driver_data = &rd[i];
+
 		rdev = devm_regulator_register(&pdev->dev,
-					       &rd->desc, &config);
+					       &rd[i].desc, &config);
 		if (IS_ERR(rdev)) {
 			dev_err(&pdev->dev,
 				"failed to register %s regulator\n",
-				rd->desc.name);
+				rd[i].desc.name);
 			return PTR_ERR(rdev);
 		}
-		for (j = 0; j < rd->reg_init_amnt; j++) {
+		for (j = 0; j < rd[i].reg_init_amnt; j++) {
 			ret = regmap_update_bits(bd71828->regmap,
-						 rd->reg_inits[j].reg,
-						 rd->reg_inits[j].mask,
-						 rd->reg_inits[j].val);
+						 rd[i].reg_inits[j].reg,
+						 rd[i].reg_inits[j].mask,
+						 rd[i].reg_inits[j].val);
 			if (ret) {
 				dev_err(&pdev->dev,
 					"regulator %s init failed\n",
-					rd->desc.name);
+					rd[i].desc.name);
 				return ret;
 			}
 		}
 	}
-	return 0;
+	return bd71828_create_sysfs(pdev);
 }
 
 static struct platform_driver bd71828_regulator = {
@@ -823,6 +1173,7 @@ static struct platform_driver bd71828_regulator = {
 		.name = "bd71828-pmic"
 	},
 	.probe = bd71828_probe,
+	.remove = bd71828_remove,
 };
 
 module_platform_driver(bd71828_regulator);
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 07/13] regulator: bd71828: enhanced run-level support
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
                   ` (5 preceding siblings ...)
  2019-10-17  9:48 ` [RFC PATCH 06/13] regulator: bd71828: Add GPIO based run-level control for regulators Matti Vaittinen
@ 2019-10-17  9:50 ` Matti Vaittinen
  2019-10-17  9:51 ` [RFC PATCH 08/13] regulator: bd71828: Support in-kernel APIs to change run-level Matti Vaittinen
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:50 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

Support changing run-level via I2C and add in-kernel API for allowing
run time changes of run-level voltages.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/regulator/bd71828-regulator.c | 266 ++++++++++++++++++++++----
 include/linux/mfd/rohm-bd71828.h      |   3 +
 2 files changed, 232 insertions(+), 37 deletions(-)

diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c
index 36f5aef941e5..d5d0dcee90cf 100644
--- a/drivers/regulator/bd71828-regulator.c
+++ b/drivers/regulator/bd71828-regulator.c
@@ -19,6 +19,12 @@
 #include <linux/regulator/machine.h>
 #include <linux/regulator/of_regulator.h>
 
+/* Drivers should not do this. But we provide this custom kernel interface
+ * for users to switch the run-level. Hence we need to get the rdev from
+ * struct regulator
+ */
+#include "internal.h"
+
 #define MAX_GPIO_DVS_BUCKS 4
 #define DVS_RUN_LEVELS 4
 
@@ -39,7 +45,10 @@ struct bd71828_regulator_data {
 	const struct reg_init *reg_inits;
 	int reg_init_amnt;
 	struct run_lvl_ctrl run_lvl[DVS_RUN_LEVELS];
+	struct mutex dvs_lock;
 	struct gpio_descs *gps;
+	struct regmap *regmap;
+	bool allow_runlvl;
 };
 
 static const struct reg_init buck1_inits[] = {
@@ -225,7 +234,7 @@ static int set_runlevel_voltage(struct regmap *regmap,
 	return ret;
 }
 
-static int buck_set_gpio_hw_dvs_levels(struct device_node *np,
+static int buck_set_runlvl_hw_dvs_levels(struct device_node *np,
 				       const struct regulator_desc *desc,
 				       struct regulator_config *cfg)
 {
@@ -245,11 +254,12 @@ static int buck_set_gpio_hw_dvs_levels(struct device_node *np,
 
 	data = container_of(desc, struct bd71828_regulator_data, desc);
 
+	mutex_lock(&data->dvs_lock);
 	for (i = 0; i < DVS_RUN_LEVELS; i++) {
 		ret = of_property_read_u32(np, props[i], &uv);
 		if (ret) {
 			if (ret != -EINVAL)
-				return ret;
+				goto unlock_out;
 			uv = 0;
 		}
 		if (uv) {
@@ -259,7 +269,7 @@ static int buck_set_gpio_hw_dvs_levels(struct device_node *np,
 			ret = set_runlevel_voltage(cfg->regmap, desc, uv, i);
 
 			if (ret)
-				return ret;
+				goto unlock_out;
 
 			ret = regmap_update_bits(cfg->regmap, en_reg,
 						 en_masks[i], en_masks[i]);
@@ -268,10 +278,15 @@ static int buck_set_gpio_hw_dvs_levels(struct device_node *np,
 						 en_masks[i], 0);
 		}
 		if (ret)
-			return ret;
+			goto unlock_out;
 	}
 
-	return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
+	ret = rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
+
+unlock_out:
+	mutex_unlock(&data->dvs_lock);
+
+	return ret;
 }
 
 static int ldo6_parse_dt(struct device_node *np,
@@ -324,11 +339,40 @@ static int bd71828_dvs_gpio_set_run_level(struct bd71828_regulator_data *rd,
 	return gpiod_set_array_value_cansleep(rd->gps->ndescs, rd->gps->desc,
 				     rd->gps->info, values);
 }
+
+/* Get current run level when RUN levels are controlled using I2C */
+static int bd71828_dvs_i2c_set_run_level(struct regmap *regmap,
+					 int lvl)
+{
+	unsigned int reg;
+
+	reg = lvl << (ffs(BD71828_MASK_RUN_LVL_CTRL) - 1);
+
+	return regmap_update_bits(regmap, BD71828_REG_PS_CTRL_3,
+				  BD71828_MASK_RUN_LVL_CTRL, reg);
+}
+/* Get current run level when RUN levels are controlled using I2C */
+static int bd71828_dvs_i2c_get_run_level(struct regmap *regmap,
+					 struct bd71828_regulator_data *rd)
+{
+	int ret;
+	unsigned int val;
+
+	ret = regmap_read(regmap, BD71828_REG_PS_CTRL_3, &val);
+	if (ret)
+		return ret;
+
+	ret = (val & BD71828_MASK_RUN_LVL_CTRL);
+	ret >>= ffs(BD71828_MASK_RUN_LVL_CTRL) - 1;
+
+	return ret;
+}
+
+/* Get current RUN level when run levels are controlled by GPIO */
 static int bd71828_dvs_gpio_get_run_level(struct bd71828_regulator_data *rd)
 {
 	int run_level;
 	int ret;
-
 	DECLARE_BITMAP(values, 2);
 
 	values[0] = 0;
@@ -346,47 +390,131 @@ static int bd71828_dvs_gpio_get_run_level(struct bd71828_regulator_data *rd)
 	return run_level;
 }
 
+/*
+ * To be used when BD71828 regulator is controlled by RUN levels
+ * via I2C instead of GPIO
+ */
+static int bd71828_dvs_i2c_is_enabled(struct regulator_dev *rdev)
+{
+	struct bd71828_regulator_data *data = rdev_get_drvdata(rdev);
+	int ret;
+
+	mutex_lock(&data->dvs_lock);
+	ret = bd71828_dvs_i2c_get_run_level(rdev->regmap, data);
+	if (ret < 0)
+		goto unlock_out;
+
+	ret = data->run_lvl[ret].enabled;
+
+unlock_out:
+	mutex_unlock(&data->dvs_lock);
+
+	return ret;
+}
+
+/*
+ * To be used when BD71828 regulator is controlled by RUN levels
+ * via GPIO
+ */
 static int bd71828_dvs_gpio_is_enabled(struct regulator_dev *rdev)
 {
 	struct bd71828_regulator_data *data = rdev_get_drvdata(rdev);
 	int ret;
 
-	// TODO: lock GPIO state (Is this needed)
+	mutex_lock(&data->dvs_lock);
 	ret = bd71828_dvs_gpio_get_run_level(data);
-	if (ret < 0)
+	if (ret < 0 || ret >= DVS_RUN_LEVELS)
 		goto unlock_out;
 
 	ret = data->run_lvl[ret].enabled;
 
 unlock_out:
-	//TODO: unlock
+	mutex_unlock(&data->dvs_lock);
 
 	return ret;
 }
 
+/*
+ * To be used when BD71828 regulator is controlled by RUN levels
+ * via I2C instead of GPIO
+ */
+static int bd71828_dvs_i2c_get_voltage(struct regulator_dev *rdev)
+{
+	int ret;
+	struct bd71828_regulator_data *data = rdev_get_drvdata(rdev);
+
+	mutex_lock(&data->dvs_lock);
+	ret = bd71828_dvs_i2c_get_run_level(rdev->regmap, data);
+	if (ret < 0)
+		goto unlock_out;
+
+	ret = data->run_lvl[ret].voltage;
+
+unlock_out:
+	mutex_unlock(&data->dvs_lock);
+
+	return ret;
+}
+
+/*
+ * To be used when BD71828 regulator is controlled by RUN levels
+ * via GPIO
+ */
 static int bd71828_dvs_gpio_get_voltage(struct regulator_dev *rdev)
 {
 	int ret;
 	struct bd71828_regulator_data *data = rdev_get_drvdata(rdev);
 
-	// TODO: lock GPIO state (Is this needed)
+	mutex_lock(&data->dvs_lock);
 	ret = bd71828_dvs_gpio_get_run_level(data);
-	if (ret < 0)
+	if (ret < 0 || DVS_RUN_LEVELS <= ret)
 		goto unlock_out;
 
 	ret = data->run_lvl[ret].voltage;
 
 unlock_out:
-	//TODO: unlock
+	mutex_unlock(&data->dvs_lock);
+
+	return ret;
+}
+
+/**
+ * bd71828_set_runlevel_voltage - change run-level voltage
+ *
+ * @regulator:  pointer to regulator for which the run-level voltage is changed
+ * @uv:		New voltage for run-level in micro volts
+ * @level:	run-level for which the voltage is to be changed
+ *
+ * Changes the run-level voltage for given regulator
+ */
+int bd71828_set_runlevel_voltage(struct regulator *regulator, unsigned int uv,
+				 unsigned int level)
+{
+	struct regulator_dev *rdev = regulator->rdev;
+	struct bd71828_regulator_data *data = rdev_get_drvdata(rdev);
+	int ret;
+
+	if (!data || !data->allow_runlvl)
+		return -EINVAL;
+
+	mutex_lock(&data->dvs_lock);
+	ret = set_runlevel_voltage(rdev->regmap, rdev->desc, uv, level);
+	mutex_unlock(&data->dvs_lock);
 
 	return ret;
 }
+EXPORT_SYMBOL(bd71828_set_runlevel_voltage);
 
 static const struct regulator_ops dvs_buck_gpio_ops = {
 	.is_enabled = bd71828_dvs_gpio_is_enabled,
 	.get_voltage = bd71828_dvs_gpio_get_voltage,
 };
 
+static const struct regulator_ops dvs_buck_i2c_ops = {
+	.is_enabled = bd71828_dvs_i2c_is_enabled,
+	.get_voltage = bd71828_dvs_i2c_get_voltage,
+};
+
 static const struct regulator_ops bd71828_buck_ops = {
 	.enable = regulator_enable_regmap,
 	.disable = regulator_disable_regmap,
@@ -972,17 +1100,72 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
 };
 
 struct bd71828_gpio_cfg {
-	unsigned int gpiobucks;
+	bool use_gpio;
+	unsigned int runlvl;
 	struct gpio_descs *gps;
 };
 
+static void mark_regulator_runlvl_controlled(struct device *dev,
+					     struct device_node *np,
+					     struct bd71828_gpio_cfg *g)
+{
+	int i;
+
+	for (i = 1; i <= ARRAY_SIZE(bd71828_rdata); i++) {
+		if (!of_node_name_eq(np, bd71828_rdata[i-1].desc.of_match))
+			continue;
+		switch (i) {
+		case 1:
+		case 2:
+		case 6:
+		case 7:
+			g->runlvl |= 1 << (i - 1);
+			dev_dbg(dev, "buck %d runlevel controlled\n", i);
+			break;
+		default:
+			dev_err(dev,
+				"Only bucks 1,2,6,7 support run-level dvs\n");
+			break;
+		}
+	}
+}
+
+static int get_runcontrolled_bucks_dt(struct device *dev,
+				      struct bd71828_gpio_cfg *g)
+{
+	struct device_node *np;
+	struct device_node *nproot = dev->of_node;
+	const char *prop = "rohm,dvs-runlvl-ctrl";
+
+	g->runlvl = 0;
+
+	nproot = of_get_child_by_name(nproot, "regulators");
+	if (!nproot) {
+		dev_err(dev, "failed to find regulators node\n");
+		return -ENODEV;
+	}
+	for_each_child_of_node(nproot, np)
+		if (of_property_read_bool(np, prop))
+			mark_regulator_runlvl_controlled(dev, np, g);
+
+	of_node_put(nproot);
+	return 0;
+}
+
 static int check_dt_for_gpio_controls(struct device *d,
 				      struct bd71828_gpio_cfg *g)
 {
-	int ret, i;
-	struct device_node *np = d->of_node;
-	const char *prop = "rohm,dvs_gpio_bucks";
-	uint32_t bucks[MAX_GPIO_DVS_BUCKS];
+	int ret;
+
+	ret = get_runcontrolled_bucks_dt(d, g);
+	if (ret)
+		return ret;
+
+	g->use_gpio = false;
+
+	/* If the run level control is not requested by any bucks we're done */
+	if (!g->runlvl)
+		return 0;
 
 	g->gps = devm_gpiod_get_array(d, "rohm,dvs-vsel", GPIOD_OUT_LOW);
 
@@ -996,22 +1179,14 @@ static int check_dt_for_gpio_controls(struct device *d,
 	if (g->gps->ndescs != 2)
 		return -ENOENT;
 
-	ret = of_property_read_variable_u32_array(np, prop, bucks, 0,
-						  ARRAY_SIZE(bucks));
-
-	if (ret < 0) {
-		if (ret == -EOVERFLOW)
-			return -EINVAL;
-	}
-	for (i = 0; i < ret; i++)
-		g->gpiobucks |= 1 << bucks[i];
+	g->use_gpio = true;
 
 	return 0;
 }
 
-static void set_buck_gpio_controlled(struct rohm_regmap_dev *bd71828,
-				     struct bd71828_regulator_data *rd,
-				     struct bd71828_gpio_cfg *g)
+static void set_buck_runlvl_controlled(struct rohm_regmap_dev *bd71828,
+				      struct bd71828_regulator_data *rd,
+				      struct bd71828_gpio_cfg *g)
 {
 	switch (rd->desc.id) {
 	case BD71828_BUCK1:
@@ -1033,9 +1208,16 @@ static void set_buck_gpio_controlled(struct rohm_regmap_dev *bd71828,
 	 * Disallow setters. Get voltages/enable states based
 	 * on current RUN level
 	 */
-	rd->gps = g->gps;
-	rd->desc.ops = &dvs_buck_gpio_ops;
-	rd->desc.of_parse_cb = buck_set_gpio_hw_dvs_levels;
+
+	rd->allow_runlvl = true;
+
+	if (g->use_gpio) {
+		rd->gps = g->gps;
+		rd->desc.ops = &dvs_buck_gpio_ops;
+	} else {
+		rd->desc.ops = &dvs_buck_i2c_ops;
+	}
+	rd->desc.of_parse_cb = buck_set_runlvl_hw_dvs_levels;
 }
 
 static ssize_t show_runlevel(struct device *dev,
@@ -1044,10 +1226,14 @@ static ssize_t show_runlevel(struct device *dev,
 	int runlevel;
 	struct bd71828_regulator_data *rd = dev_get_drvdata(dev);
 
-	if (!rd || !rd->gps)
+	if (!rd)
 		return -ENOENT;
 
-	runlevel = bd71828_dvs_gpio_get_run_level(rd);
+	if (!rd->gps)
+		runlevel = bd71828_dvs_i2c_get_run_level(rd->regmap, rd);
+	else
+		runlevel = bd71828_dvs_gpio_get_run_level(rd);
+
 	if (runlevel < 0)
 		return runlevel;
 
@@ -1063,7 +1249,10 @@ static ssize_t set_runlevel(struct device *dev, struct device_attribute *attr,
 	if (kstrtol(buf, 0, &val) != 0)
 		return -EINVAL;
 
-	val = bd71828_dvs_gpio_set_run_level(rd, val);
+	if (rd->gps)
+		val = bd71828_dvs_gpio_set_run_level(rd, val);
+	else
+		val = bd71828_dvs_i2c_set_run_level(rd->regmap, val);
 	if (val)
 		return val;
 
@@ -1133,8 +1322,11 @@ static int bd71828_probe(struct platform_device *pdev)
 		/* Use bd71828_rdata as template */
 		rd[i] = bd71828_rdata[i];
 
-		if (gcfg.gpiobucks & (1 << i))
-			set_buck_gpio_controlled(bd71828, &rd[i], &gcfg);
+		mutex_init(&rd[i].dvs_lock);
+		if (gcfg.runlvl & (1 << i))
+			set_buck_runlvl_controlled(bd71828, &rd[i], &gcfg);
+
+		rd[i].regmap = bd71828->regmap;
 	}
 
 	config.regmap = bd71828->regmap;
diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h
index eb0557eb5314..5f15c6a309e6 100644
--- a/include/linux/mfd/rohm-bd71828.h
+++ b/include/linux/mfd/rohm-bd71828.h
@@ -422,4 +422,7 @@ enum {
 #define BD71828_OUT_TYPE_OPEN_DRAIN			0x0
 #define BD71828_OUT_TYPE_CMOS				0x2
 
+int bd71828_set_runlevel_voltage(struct regulator *regulator, unsigned int uv,
+				 unsigned int level);
+
 #endif /* __LINUX_MFD_BD71828_H__ */
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 08/13] regulator: bd71828: Support in-kernel APIs to change run-level
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
                   ` (6 preceding siblings ...)
  2019-10-17  9:50 ` [RFC PATCH 07/13] regulator: bd71828: enhanced run-level support Matti Vaittinen
@ 2019-10-17  9:51 ` Matti Vaittinen
  2019-10-17  9:52 ` [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver Matti Vaittinen
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:51 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

Add exported functions bd71828_set_runlevel and
bd71828_get_runlevel for setting and getting the system run
level.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/regulator/bd71828-regulator.c | 67 +++++++++++++++++++++++++++
 include/linux/mfd/rohm-bd71828.h      |  2 +
 2 files changed, 69 insertions(+)

diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c
index d5d0dcee90cf..e242d829cbf5 100644
--- a/drivers/regulator/bd71828-regulator.c
+++ b/drivers/regulator/bd71828-regulator.c
@@ -505,6 +505,73 @@ int bd71828_set_runlevel_voltage(struct regulator *regulator, unsigned int uv,
 }
 EXPORT_SYMBOL(bd71828_set_runlevel_voltage);
 
+/**
+ * bd71828_set_runlevel - change system run-level.
+ *
+ * @regulator:	pointer to one of the BD71828 regulators obtained by
+ *		call to regulator_get
+ * @level:	New run-level the system should enter
+ *
+ * Changes the system to run-level which was given as argument. This
+ * operation will change state of all regulators which are set to be
+ * controlled by run-levels. Note that 'regulator' must point to a
+ * regulator which is controlled by run-levels.
+ */
+int bd71828_set_runlevel(struct regulator *regulator, unsigned int level)
+{
+	struct regulator_dev *rdev = regulator->rdev;
+	struct bd71828_regulator_data *rd = rdev_get_drvdata(rdev);
+
+	if (!rd)
+		return -ENOENT;
+
+	if (!rd || !rd->allow_runlvl)
+		return -EINVAL;
+
+	if (rd->gps)
+		return bd71828_dvs_gpio_set_run_level(rd, level);
+
+	return bd71828_dvs_i2c_set_run_level(rd->regmap, level);
+}
+EXPORT_SYMBOL(bd71828_set_runlevel);
+
+/**
+ * bd71828_get_runlevel - get the current system run-level.
+ *
+ * @regulator:	pointer to one of the BD71828 regulators obtained by
+ *		call to regulator_get
+ * @level:	Pointer to value where current run-level is stored
+ *
+ * Returns the current system run-level. Note that 'regulator' must
+ * point to a regulator which is controlled by run-levels.
+ */
+int bd71828_get_runlevel(struct regulator *regulator, unsigned int *level)
+{
+	struct regulator_dev *rdev = regulator->rdev;
+	struct bd71828_regulator_data *rd = rdev_get_drvdata(rdev);
+	int ret;
+
+	if (!rd)
+		return -ENOENT;
+
+	if (!rd || !rd->allow_runlvl)
+		return -EINVAL;
+
+	if (!rd->gps)
+		ret = bd71828_dvs_i2c_get_run_level(rd->regmap, rd);
+	else
+		ret = bd71828_dvs_gpio_get_run_level(rd);
+
+	if (ret < 0)
+		return ret;
+
+	*level = (unsigned int) ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(bd71828_get_runlevel);
+
+
 static const struct regulator_ops dvs_buck_gpio_ops = {
 	.is_enabled = bd71828_dvs_gpio_is_enabled,
 	.get_voltage = bd71828_dvs_gpio_get_voltage,
diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h
index 5f15c6a309e6..bbbd4f118550 100644
--- a/include/linux/mfd/rohm-bd71828.h
+++ b/include/linux/mfd/rohm-bd71828.h
@@ -424,5 +424,7 @@ enum {
 
 int bd71828_set_runlevel_voltage(struct regulator *regulator, unsigned int uv,
 				 unsigned int level);
+int bd71828_set_runlevel(struct regulator *regulator, unsigned int level);
+int bd71828_get_runlevel(struct regulator *regulator, unsigned int *level);
 
 #endif /* __LINUX_MFD_BD71828_H__ */
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
                   ` (7 preceding siblings ...)
  2019-10-17  9:51 ` [RFC PATCH 08/13] regulator: bd71828: Support in-kernel APIs to change run-level Matti Vaittinen
@ 2019-10-17  9:52 ` Matti Vaittinen
  2019-10-17 10:12   ` Alexandre Belloni
  2019-10-17  9:53 ` [RFC PATCH 10/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs Matti Vaittinen
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:52 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

ROHM BD71828 PMIC RTC block is from many parts similar to one
on BD70528. Support BD71828 RTC using BD70528 RTC driver and
avoid re-inventing the wheel.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/rtc/Kconfig              |   5 +-
 drivers/rtc/rtc-bd70528.c        | 369 ++++++++++++++++++++++---------
 include/linux/mfd/rohm-bd70528.h |  12 +-
 include/linux/mfd/rohm-bd71828.h |   4 +-
 include/linux/mfd/rohm-shared.h  |  22 ++
 5 files changed, 299 insertions(+), 113 deletions(-)
 create mode 100644 include/linux/mfd/rohm-shared.h

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e72f65b61176..5c5b18a99bf9 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -498,11 +498,12 @@ config RTC_DRV_M41T80_WDT
 	help
 	  If you say Y here you will get support for the
 	  watchdog timer in the ST M41T60 and M41T80 RTC chips series.
+
 config RTC_DRV_BD70528
-	tristate "ROHM BD70528 PMIC RTC"
+	tristate "ROHM BD70528 and BD71828 PMIC RTC"
 	help
 	  If you say Y here you will get support for the RTC
-	  on ROHM BD70528 Power Management IC.
+	  block on ROHM BD70528 and BD71828 Power Management IC.
 
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-bd70528.
diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c
index f9bdd555e1a2..2940b57002ac 100644
--- a/drivers/rtc/rtc-bd70528.c
+++ b/drivers/rtc/rtc-bd70528.c
@@ -2,10 +2,11 @@
 //
 // Copyright (C) 2018 ROHM Semiconductors
 //
-// RTC driver for ROHM BD70528 PMIC
+// RTC driver for ROHM BD70528 and BD71828 PMICs
 
 #include <linux/bcd.h>
 #include <linux/mfd/rohm-bd70528.h>
+#include <linux/mfd/rohm-bd71828.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -15,16 +16,16 @@
 /*
  * We read regs RTC_SEC => RTC_YEAR
  * this struct is ordered according to chip registers.
- * Keep it u8 only to avoid padding issues.
+ * Keep it u8 only (or packed) to avoid padding issues.
  */
-struct bd70528_rtc_day {
+struct bd7xx28_rtc_day {
 	u8 sec;
 	u8 min;
 	u8 hour;
 } __packed;
 
-struct bd70528_rtc_data {
-	struct bd70528_rtc_day time;
+struct bd7xx28_rtc_data {
+	struct bd7xx28_rtc_day time;
 	u8 week;
 	u8 day;
 	u8 month;
@@ -32,19 +33,28 @@ struct bd70528_rtc_data {
 } __packed;
 
 struct bd70528_rtc_wake {
-	struct bd70528_rtc_day time;
+	struct bd7xx28_rtc_day time;
 	u8 ctrl;
 } __packed;
 
+struct bd71828_rtc_alm {
+	struct bd7xx28_rtc_data alm0;
+	struct bd7xx28_rtc_data alm1;
+	u8 alm_mask;
+	u8 alm1_mask;
+} __packed;
+
 struct bd70528_rtc_alm {
-	struct bd70528_rtc_data data;
+	struct bd7xx28_rtc_data data;
 	u8 alm_mask;
 	u8 alm_repeat;
 } __packed;
 
-struct bd70528_rtc {
+struct bd7xx28_rtc {
 	struct rohm_regmap_dev *mfd;
 	struct device *dev;
+	u8 reg_time_start;
+	bool has_rtc_timers;
 };
 
 static int bd70528_set_wake(struct rohm_regmap_dev *bd70528,
@@ -118,7 +128,7 @@ static int bd70528_set_elapsed_tmr(struct rohm_regmap_dev *bd70528,
 			    ctrl_reg);
 }
 
-static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state,
+static int bd70528_set_rtc_based_timers(struct bd7xx28_rtc *r, int new_state,
 					int *old_state)
 {
 	int ret;
@@ -127,122 +137,162 @@ static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state,
 			      old_state);
 	if (ret) {
 		dev_err(r->dev,
-			"Failed to disable WDG for RTC setting (%d)\n", ret);
+			"Failed to disable WDG for RTC setting (%d)\n",
+			ret);
 		return ret;
 	}
-	ret = bd70528_set_elapsed_tmr(r->mfd,
-				      new_state & BD70528_ELAPSED_STATE_BIT,
+	ret = bd70528_set_elapsed_tmr(r->mfd, new_state &
+				      BD70528_ELAPSED_STATE_BIT,
 				      old_state);
 	if (ret) {
 		dev_err(r->dev,
 			"Failed to disable 'elapsed timer' for RTC setting\n");
 		return ret;
 	}
-	ret = bd70528_set_wake(r->mfd, new_state & BD70528_WAKE_STATE_BIT,
-			       old_state);
+
+	ret = bd70528_set_wake(r->mfd, new_state &
+			       BD70528_WAKE_STATE_BIT, old_state);
 	if (ret) {
 		dev_err(r->dev,
-			"Failed to disable 'wake timer' for RTC setting\n");
+			"Failed to disable 'wake timer'\n");
 		return ret;
 	}
 
 	return ret;
 }
 
-static int bd70528_re_enable_rtc_based_timers(struct bd70528_rtc *r,
+static int bd7xx28_re_enable_rtc_based_timers(struct bd7xx28_rtc *r,
 					      int old_state)
 {
+	if (!r->has_rtc_timers)
+		return 0;
+
 	return bd70528_set_rtc_based_timers(r, old_state, NULL);
 }
 
-static int bd70528_disable_rtc_based_timers(struct bd70528_rtc *r,
+static int bd7xx28_disable_rtc_based_timers(struct bd7xx28_rtc *r,
 					    int *old_state)
 {
+	if (!r->has_rtc_timers)
+		return 0;
+
 	return bd70528_set_rtc_based_timers(r, 0, old_state);
 }
 
-static inline void tmday2rtc(struct rtc_time *t, struct bd70528_rtc_day *d)
+static inline void tmday2rtc(struct rtc_time *t, struct bd7xx28_rtc_day *d)
 {
-	d->sec &= ~BD70528_MASK_RTC_SEC;
-	d->min &= ~BD70528_MASK_RTC_MINUTE;
-	d->hour &= ~BD70528_MASK_RTC_HOUR;
+	d->sec &= ~ROHM_BD1_MASK_RTC_SEC;
+	d->min &= ~ROHM_BD1_MASK_RTC_MINUTE;
+	d->hour &= ~ROHM_BD1_MASK_RTC_HOUR;
 	d->sec |= bin2bcd(t->tm_sec);
 	d->min |= bin2bcd(t->tm_min);
 	d->hour |= bin2bcd(t->tm_hour);
 }
 
-static inline void tm2rtc(struct rtc_time *t, struct bd70528_rtc_data *r)
+static inline void tm2rtc(struct rtc_time *t, struct bd7xx28_rtc_data *r)
 {
-	r->day &= ~BD70528_MASK_RTC_DAY;
-	r->week &= ~BD70528_MASK_RTC_WEEK;
-	r->month &= ~BD70528_MASK_RTC_MONTH;
+	r->day &= ~ROHM_BD1_MASK_RTC_DAY;
+	r->week &= ~ROHM_BD1_MASK_RTC_WEEK;
+	r->month &= ~ROHM_BD1_MASK_RTC_MONTH;
 	/*
 	 * PM and 24H bits are not used by Wake - thus we clear them
 	 * here and not in tmday2rtc() which is also used by wake.
 	 */
-	r->time.hour &= ~(BD70528_MASK_RTC_HOUR_PM | BD70528_MASK_RTC_HOUR_24H);
+	r->time.hour &= ~(ROHM_BD1_MASK_RTC_HOUR_PM |
+			  ROHM_BD1_MASK_RTC_HOUR_24H);
 
 	tmday2rtc(t, &r->time);
 	/*
 	 * We do always set time in 24H mode.
 	 */
-	r->time.hour |= BD70528_MASK_RTC_HOUR_24H;
+	r->time.hour |= ROHM_BD1_MASK_RTC_HOUR_24H;
 	r->day |= bin2bcd(t->tm_mday);
 	r->week |= bin2bcd(t->tm_wday);
 	r->month |= bin2bcd(t->tm_mon + 1);
 	r->year = bin2bcd(t->tm_year - 100);
+
 }
 
-static inline void rtc2tm(struct bd70528_rtc_data *r, struct rtc_time *t)
+static inline void rtc2tm(struct bd7xx28_rtc_data *r, struct rtc_time *t)
 {
-	t->tm_sec = bcd2bin(r->time.sec & BD70528_MASK_RTC_SEC);
-	t->tm_min = bcd2bin(r->time.min & BD70528_MASK_RTC_MINUTE);
-	t->tm_hour = bcd2bin(r->time.hour & BD70528_MASK_RTC_HOUR);
+	t->tm_sec = bcd2bin(r->time.sec & ROHM_BD1_MASK_RTC_SEC);
+	t->tm_min = bcd2bin(r->time.min & ROHM_BD1_MASK_RTC_MINUTE);
+	t->tm_hour = bcd2bin(r->time.hour & ROHM_BD1_MASK_RTC_HOUR);
 	/*
-	 * If RTC is in 12H mode, then bit BD70528_MASK_RTC_HOUR_PM
+	 * If RTC is in 12H mode, then bit ROHM_BD1_MASK_RTC_HOUR_PM
 	 * is not BCD value but tells whether it is AM or PM
 	 */
-	if (!(r->time.hour & BD70528_MASK_RTC_HOUR_24H)) {
+	if (!(r->time.hour & ROHM_BD1_MASK_RTC_HOUR_24H)) {
 		t->tm_hour %= 12;
-		if (r->time.hour & BD70528_MASK_RTC_HOUR_PM)
+		if (r->time.hour & ROHM_BD1_MASK_RTC_HOUR_PM)
 			t->tm_hour += 12;
 	}
-	t->tm_mday = bcd2bin(r->day & BD70528_MASK_RTC_DAY);
-	t->tm_mon = bcd2bin(r->month & BD70528_MASK_RTC_MONTH) - 1;
-	t->tm_year = 100 + bcd2bin(r->year & BD70528_MASK_RTC_YEAR);
-	t->tm_wday = bcd2bin(r->week & BD70528_MASK_RTC_WEEK);
+	t->tm_mday = bcd2bin(r->day & ROHM_BD1_MASK_RTC_DAY);
+	t->tm_mon = bcd2bin(r->month & ROHM_BD1_MASK_RTC_MONTH) - 1;
+	t->tm_year = 100 + bcd2bin(r->year & ROHM_BD1_MASK_RTC_YEAR);
+	t->tm_wday = bcd2bin(r->week & ROHM_BD1_MASK_RTC_WEEK);
 }
 
-static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
+static int bd71828_set_alarm(struct bd7xx28_rtc *r, struct rtc_wkalrm *a)
+{
+	int ret;
+	struct bd71828_rtc_alm alm;
+	struct rohm_regmap_dev *bd71828 = r->mfd;
+
+	ret = regmap_bulk_read(bd71828->regmap, BD71828_REG_RTC_ALM_START,
+			       &alm, sizeof(alm));
+	if (ret) {
+		dev_err(r->dev, "Failed to read alarm regs\n");
+		return ret;
+	}
+
+	tm2rtc(&a->time, &alm.alm0);
+
+	if (!a->enabled)
+		alm.alm_mask &= ~ROHM_BD1_MASK_ALM_EN;
+	else
+		alm.alm_mask |= ROHM_BD1_MASK_ALM_EN;
+
+	pr_debug("%s: new ALM0 EN bits are 0x%x\n", __func__, alm.alm_mask);
+
+	ret = regmap_bulk_write(bd71828->regmap, BD71828_REG_RTC_ALM_START,
+				&alm, sizeof(alm));
+	if (ret)
+		dev_err(r->dev, "Failed to set alarm time\n");
+
+	return ret;
+
+}
+static int bd70528_set_alarm(struct bd7xx28_rtc *r, struct rtc_wkalrm *a)
 {
 	struct bd70528_rtc_wake wake;
 	struct bd70528_rtc_alm alm;
 	int ret;
-	struct bd70528_rtc *r = dev_get_drvdata(dev);
 	struct rohm_regmap_dev *bd70528 = r->mfd;
 
-	ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_WAKE_START,
+	ret = regmap_bulk_read(bd70528->regmap,
+			       BD70528_REG_RTC_WAKE_START,
 			       &wake, sizeof(wake));
 	if (ret) {
-		dev_err(dev, "Failed to read wake regs\n");
+		dev_err(r->dev, "Failed to read wake regs\n");
 		return ret;
 	}
+	tmday2rtc(&a->time, &wake.time);
 
 	ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_ALM_START,
 			       &alm, sizeof(alm));
 	if (ret) {
-		dev_err(dev, "Failed to read alarm regs\n");
+		dev_err(r->dev, "Failed to read alarm regs\n");
 		return ret;
 	}
 
 	tm2rtc(&a->time, &alm.data);
-	tmday2rtc(&a->time, &wake.time);
 
 	if (a->enabled) {
-		alm.alm_mask &= ~BD70528_MASK_ALM_EN;
+		alm.alm_mask &= ~ROHM_BD1_MASK_ALM_EN;
 		wake.ctrl |= BD70528_MASK_WAKE_EN;
 	} else {
-		alm.alm_mask |= BD70528_MASK_ALM_EN;
+		alm.alm_mask |= ROHM_BD1_MASK_ALM_EN;
 		wake.ctrl &= ~BD70528_MASK_WAKE_EN;
 	}
 
@@ -250,28 +300,69 @@ static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
 				BD70528_REG_RTC_WAKE_START, &wake,
 				sizeof(wake));
 	if (ret) {
-		dev_err(dev, "Failed to set wake time\n");
+		dev_err(r->dev, "Failed to set wake time\n");
 		return ret;
 	}
 	ret = regmap_bulk_write(bd70528->regmap, BD70528_REG_RTC_ALM_START,
 				&alm, sizeof(alm));
 	if (ret)
-		dev_err(dev, "Failed to set alarm time\n");
+		dev_err(r->dev, "Failed to set alarm time\n");
 
 	return ret;
 }
+static int bd7xx28_set_alarm(struct device *dev, struct rtc_wkalrm *a)
+{
+	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
+	struct rohm_regmap_dev *bd_dev = r->mfd;
+
+	switch (bd_dev->chip_type) {
+	case ROHM_CHIP_TYPE_BD70528:
+		return bd70528_set_alarm(r, a);
+	case ROHM_CHIP_TYPE_BD71828:
+		return bd71828_set_alarm(r, a);
+	default:
+		dev_err(dev, "Unknown RTC chip\n");
+		break;
+	}
+
+	return -ENOENT;
+}
 
-static int bd70528_read_alarm(struct device *dev, struct rtc_wkalrm *a)
+static int bd71828_read_alarm(struct bd7xx28_rtc *r, struct rtc_wkalrm *a)
+{
+	int ret;
+	struct bd71828_rtc_alm alm;
+	struct rohm_regmap_dev *bd71828 = r->mfd;
+
+	ret = regmap_bulk_read(bd71828->regmap, BD71828_REG_RTC_ALM_START,
+			       &alm, sizeof(alm));
+	if (ret) {
+		dev_err(r->dev, "Failed to read alarm regs\n");
+		return ret;
+	}
+
+	rtc2tm(&alm.alm0, &a->time);
+	a->time.tm_mday = -1;
+	a->time.tm_mon = -1;
+	a->time.tm_year = -1;
+	a->enabled = !!(alm.alm_mask & ROHM_BD1_MASK_ALM_EN);
+	a->pending = 0;
+	pr_debug("%s: ALM0 EN bits are 0x%x, returniong enabled %d\n", __func__,
+		alm.alm_mask, a->enabled);
+
+	return 0;
+}
+
+static int bd70528_read_alarm(struct bd7xx28_rtc *r, struct rtc_wkalrm *a)
 {
-	struct bd70528_rtc_alm alm;
 	int ret;
-	struct bd70528_rtc *r = dev_get_drvdata(dev);
+	struct bd70528_rtc_alm alm;
 	struct rohm_regmap_dev *bd70528 = r->mfd;
 
 	ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_ALM_START,
 			       &alm, sizeof(alm));
 	if (ret) {
-		dev_err(dev, "Failed to read alarm regs\n");
+		dev_err(r->dev, "Failed to read alarm regs\n");
 		return ret;
 	}
 
@@ -279,25 +370,42 @@ static int bd70528_read_alarm(struct device *dev, struct rtc_wkalrm *a)
 	a->time.tm_mday = -1;
 	a->time.tm_mon = -1;
 	a->time.tm_year = -1;
-	a->enabled = !(alm.alm_mask & BD70528_MASK_ALM_EN);
+	a->enabled = !(alm.alm_mask & ROHM_BD1_MASK_ALM_EN);
 	a->pending = 0;
 
 	return 0;
 }
 
-static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
+static int bd7xx28_read_alarm(struct device *dev, struct rtc_wkalrm *a)
+{
+	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
+	struct rohm_regmap_dev *bd_dev = r->mfd;
+
+	switch (bd_dev->chip_type) {
+	case ROHM_CHIP_TYPE_BD70528:
+		return bd70528_read_alarm(r, a);
+	case ROHM_CHIP_TYPE_BD71828:
+		return bd71828_read_alarm(r, a);
+	default:
+		dev_err(dev, "Unknown RTC chip\n");
+		break;
+	}
+	return -ENOENT;
+}
+
+static int bd7xx28_set_time_locked(struct device *dev, struct rtc_time *t)
 {
 	int ret, tmpret, old_states;
-	struct bd70528_rtc_data rtc_data;
-	struct bd70528_rtc *r = dev_get_drvdata(dev);
-	struct rohm_regmap_dev *bd70528 = r->mfd;
+	struct bd7xx28_rtc_data rtc_data;
+	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
+	struct rohm_regmap_dev *bd7xx28 = r->mfd;
 
-	ret = bd70528_disable_rtc_based_timers(r, &old_states);
+	ret = bd7xx28_disable_rtc_based_timers(r, &old_states);
 	if (ret)
 		return ret;
 
-	tmpret = regmap_bulk_read(bd70528->regmap,
-				  BD70528_REG_RTC_START, &rtc_data,
+	tmpret = regmap_bulk_read(bd7xx28->regmap,
+				  r->reg_time_start, &rtc_data,
 				  sizeof(rtc_data));
 	if (tmpret) {
 		dev_err(dev, "Failed to read RTC time registers\n");
@@ -305,8 +413,8 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
 	}
 	tm2rtc(t, &rtc_data);
 
-	tmpret = regmap_bulk_write(bd70528->regmap,
-				   BD70528_REG_RTC_START, &rtc_data,
+	tmpret = regmap_bulk_write(bd7xx28->regmap,
+				   r->reg_time_start, &rtc_data,
 				   sizeof(rtc_data));
 	if (tmpret) {
 		dev_err(dev, "Failed to set RTC time\n");
@@ -314,34 +422,37 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
 	}
 
 renable_out:
-	ret = bd70528_re_enable_rtc_based_timers(r, old_states);
+	ret = bd7xx28_re_enable_rtc_based_timers(r, old_states);
 	if (tmpret)
 		ret = tmpret;
 
 	return ret;
 }
 
-static int bd70528_set_time(struct device *dev, struct rtc_time *t)
+static int bd7xx28_set_time(struct device *dev, struct rtc_time *t)
 {
 	int ret;
-	struct bd70528_rtc *r = dev_get_drvdata(dev);
+	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
 
+	pr_debug("%s called\n", __func__);
 	bd70528_wdt_lock(r->mfd);
-	ret = bd70528_set_time_locked(dev, t);
+	ret = bd7xx28_set_time_locked(dev, t);
 	bd70528_wdt_unlock(r->mfd);
 	return ret;
 }
 
-static int bd70528_get_time(struct device *dev, struct rtc_time *t)
+static int bd7xx28_get_time(struct device *dev, struct rtc_time *t)
 {
-	struct bd70528_rtc *r = dev_get_drvdata(dev);
-	struct rohm_regmap_dev *bd70528 = r->mfd;
-	struct bd70528_rtc_data rtc_data;
+	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
+	struct rohm_regmap_dev *bd7xx28 = r->mfd;
+	struct bd7xx28_rtc_data rtc_data;
 	int ret;
 
+	pr_debug("%s called\n", __func__);
+
 	/* read the RTC date and time registers all at once */
-	ret = regmap_bulk_read(bd70528->regmap,
-			       BD70528_REG_RTC_START, &rtc_data,
+	ret = regmap_bulk_read(bd7xx28->regmap,
+			       r->reg_time_start, &rtc_data,
 			       sizeof(rtc_data));
 	if (ret) {
 		dev_err(dev, "Failed to read RTC time (err %d)\n", ret);
@@ -353,55 +464,94 @@ static int bd70528_get_time(struct device *dev, struct rtc_time *t)
 	return 0;
 }
 
-static int bd70528_alm_enable(struct device *dev, unsigned int enabled)
+static int bd70528_alm_enable(struct bd7xx28_rtc *r, unsigned int enabled)
 {
 	int ret;
-	unsigned int enableval = BD70528_MASK_ALM_EN;
-	struct bd70528_rtc *r = dev_get_drvdata(dev);
+	unsigned int enableval = ROHM_BD1_MASK_ALM_EN;
 
 	if (enabled)
 		enableval = 0;
 
 	bd70528_wdt_lock(r->mfd);
-	ret = bd70528_set_wake(r->mfd, enabled, NULL);
+	ret = bd70528_set_wake(r->mfd, !enableval, NULL);
 	if (ret) {
-		dev_err(dev, "Failed to change wake state\n");
+		dev_err(r->dev, "Failed to change wake state\n");
 		goto out_unlock;
 	}
 	ret = regmap_update_bits(r->mfd->regmap, BD70528_REG_RTC_ALM_MASK,
-				 BD70528_MASK_ALM_EN, enableval);
+				 ROHM_BD1_MASK_ALM_EN, enableval);
 	if (ret)
-		dev_err(dev, "Failed to change alarm state\n");
+		dev_err(r->dev, "Failed to change alarm state\n");
 
 out_unlock:
 	bd70528_wdt_unlock(r->mfd);
 	return ret;
 }
 
-static const struct rtc_class_ops bd70528_rtc_ops = {
-	.read_time		= bd70528_get_time,
-	.set_time		= bd70528_set_time,
-	.read_alarm		= bd70528_read_alarm,
-	.set_alarm		= bd70528_set_alarm,
-	.alarm_irq_enable	= bd70528_alm_enable,
+static int bd71828_alm_enable(struct bd7xx28_rtc *r, unsigned int enabled)
+{
+	int ret;
+	unsigned int enableval = ROHM_BD1_MASK_ALM_EN;
+
+	if (!enabled)
+		enableval = 0;
+
+	pr_debug("%s called (enabled=0x%x)\n", __func__, enabled);
+	ret = regmap_update_bits(r->mfd->regmap, BD71828_REG_RTC_ALM0_MASK,
+				 ROHM_BD1_MASK_ALM_EN, enableval);
+	if (ret)
+		dev_err(r->dev, "Failed to change alarm state\n");
+
+	pr_debug("%s: Wrote alm mask reg addr 0x%x val 0x%x\n",
+		__func__, BD71828_REG_RTC_ALM0_MASK, enableval);
+
+	return ret;
+}
+
+static int bd7xx28_alm_enable(struct device *dev, unsigned int enabled)
+{
+	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
+
+	switch (r->mfd->chip_type) {
+	case ROHM_CHIP_TYPE_BD70528:
+		return bd70528_alm_enable(r, enabled);
+	case ROHM_CHIP_TYPE_BD71828:
+		return bd71828_alm_enable(r, enabled);
+	default:
+		dev_err(dev, "Unknown RTC chip\n");
+	}
+
+	return -ENOENT;
+}
+
+static const struct rtc_class_ops bd7xx28_rtc_ops = {
+	.read_time		= bd7xx28_get_time,
+	.set_time		= bd7xx28_set_time,
+	.read_alarm		= bd7xx28_read_alarm,
+	.set_alarm		= bd7xx28_set_alarm,
+	.alarm_irq_enable	= bd7xx28_alm_enable,
 };
 
 static irqreturn_t alm_hndlr(int irq, void *data)
 {
 	struct rtc_device *rtc = data;
 
+	pr_debug("bd71828 RTC IRQ fired\n");
 	rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF | RTC_PF);
 	return IRQ_HANDLED;
 }
 
-static int bd70528_probe(struct platform_device *pdev)
+static int bd7xx28_probe(struct platform_device *pdev)
 {
-	struct bd70528_rtc *bd_rtc;
+	struct bd7xx28_rtc *bd_rtc;
 	struct rohm_regmap_dev *mfd;
+	const char *irq_name;
 	int ret;
 	struct rtc_device *rtc;
 	int irq;
 	unsigned int hr;
+	bool enable_main_irq = false;
+	u8 hour_reg;
 
 	mfd = dev_get_drvdata(pdev->dev.parent);
 	if (!mfd) {
@@ -415,7 +565,25 @@ static int bd70528_probe(struct platform_device *pdev)
 	bd_rtc->mfd = mfd;
 	bd_rtc->dev = &pdev->dev;
 
-	irq = platform_get_irq_byname(pdev, "bd70528-rtc-alm");
+	switch (mfd->chip_type) {
+	case ROHM_CHIP_TYPE_BD70528:
+		irq_name = "bd70528-rtc-alm";
+		bd_rtc->has_rtc_timers = true;
+		bd_rtc->reg_time_start = BD70528_REG_RTC_START;
+		hour_reg = BD70528_REG_RTC_HOUR;
+		enable_main_irq = true;
+		break;
+	case ROHM_CHIP_TYPE_BD71828:
+		irq_name = "bd71828-rtc-alm-0";
+		bd_rtc->reg_time_start = BD71828_REG_RTC_START;
+		hour_reg = BD71828_REG_RTC_HOUR;
+		break;
+	default:
+		dev_err(&pdev->dev, "Unknown chip\n");
+		return -ENOENT;
+	}
+
+	irq = platform_get_irq_byname(pdev, irq_name);
 
 	if (irq < 0) {
 		dev_err(&pdev->dev, "Failed to get irq\n");
@@ -424,20 +592,20 @@ static int bd70528_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, bd_rtc);
 
-	ret = regmap_read(mfd->regmap, BD70528_REG_RTC_HOUR, &hr);
+	ret = regmap_read(mfd->regmap, hour_reg, &hr);
 
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to reag RTC clock\n");
 		return ret;
 	}
 
-	if (!(hr & BD70528_MASK_RTC_HOUR_24H)) {
+	if (!(hr & ROHM_BD1_MASK_RTC_HOUR_24H)) {
 		struct rtc_time t;
 
-		ret = bd70528_get_time(&pdev->dev, &t);
+		ret = bd7xx28_get_time(&pdev->dev, &t);
 
 		if (!ret)
-			ret = bd70528_set_time(&pdev->dev, &t);
+			ret = bd7xx28_set_time(&pdev->dev, &t);
 
 		if (ret) {
 			dev_err(&pdev->dev,
@@ -457,7 +625,7 @@ static int bd70528_probe(struct platform_device *pdev)
 
 	rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
 	rtc->range_max = RTC_TIMESTAMP_END_2099;
-	rtc->ops = &bd70528_rtc_ops;
+	rtc->ops = &bd7xx28_rtc_ops;
 
 	/* Request alarm IRQ prior to registerig the RTC */
 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, &alm_hndlr,
@@ -471,12 +639,15 @@ static int bd70528_probe(struct platform_device *pdev)
 	 *  leave them enabled as irq-controller should disable irqs
 	 *  from sub-registers when IRQ is disabled or freed.
 	 */
-	ret = regmap_update_bits(mfd->regmap,
+	if (enable_main_irq) {
+		ret = regmap_update_bits(mfd->regmap,
 				 BD70528_REG_INT_MAIN_MASK,
 				 BD70528_INT_RTC_MASK, 0);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to enable RTC interrupts\n");
-		return ret;
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Failed to enable RTC interrupts\n");
+			return ret;
+		}
 	}
 
 	ret = rtc_register_device(rtc);
@@ -490,11 +661,11 @@ static struct platform_driver bd70528_rtc = {
 	.driver = {
 		.name = "bd70528-rtc"
 	},
-	.probe = bd70528_probe,
+	.probe = bd7xx28_probe,
 };
 
 module_platform_driver(bd70528_rtc);
 
 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
-MODULE_DESCRIPTION("BD70528 RTC driver");
+MODULE_DESCRIPTION("ROHM BD70528 and BD71828 PMIC RTC driver");
 MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/rohm-bd70528.h b/include/linux/mfd/rohm-bd70528.h
index 1013e60c5b25..8d8c0e0b2df6 100644
--- a/include/linux/mfd/rohm-bd70528.h
+++ b/include/linux/mfd/rohm-bd70528.h
@@ -8,6 +8,7 @@
 #include <linux/device.h>
 #include <linux/mfd/rohm-generic.h>
 #include <linux/regmap.h>
+#include <linux/mfd/rohm-shared.h>
 
 enum {
 	BD70528_BUCK1,
@@ -313,16 +314,6 @@ enum {
 
 /* RTC masks to mask out reserved bits */
 
-#define BD70528_MASK_RTC_SEC		0x7f
-#define BD70528_MASK_RTC_MINUTE		0x7f
-#define BD70528_MASK_RTC_HOUR_24H	0x80
-#define BD70528_MASK_RTC_HOUR_PM	0x20
-#define BD70528_MASK_RTC_HOUR		0x1f
-#define BD70528_MASK_RTC_DAY		0x3f
-#define BD70528_MASK_RTC_WEEK		0x07
-#define BD70528_MASK_RTC_MONTH		0x1f
-#define BD70528_MASK_RTC_YEAR		0xff
-#define BD70528_MASK_RTC_COUNT_L	0x7f
 
 #define BD70528_MASK_ELAPSED_TIMER_EN	0x1
 /* Mask second, min and hour fields
@@ -332,7 +323,6 @@ enum {
  * wake-up we limit ALM to 24H and only
  * unmask sec, min and hour
  */
-#define BD70528_MASK_ALM_EN		0x7
 #define BD70528_MASK_WAKE_EN		0x1
 
 /* WDT masks */
diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h
index bbbd4f118550..bd9dfd53759d 100644
--- a/include/linux/mfd/rohm-bd71828.h
+++ b/include/linux/mfd/rohm-bd71828.h
@@ -5,6 +5,7 @@
 #define __LINUX_MFD_BD71828_H__
 
 #include <linux/mfd/rohm-generic.h>
+#include <linux/mfd/rohm-shared.h>
 
 /* Regulator IDs */
 enum {
@@ -160,6 +161,7 @@ enum {
 #define BD71828_REG_RTC_YEAR		0x52
 
 #define BD71828_REG_RTC_ALM0_SEC	0x53
+#define BD71828_REG_RTC_ALM_START	BD71828_REG_RTC_ALM0_SEC
 #define BD71828_REG_RTC_ALM0_MINUTE	0x54
 #define BD71828_REG_RTC_ALM0_HOUR	0x55
 #define BD71828_REG_RTC_ALM0_WEEK	0x56
@@ -178,6 +180,7 @@ enum {
 #define BD71828_REG_RTC_ALM1_MASK	0x62
 
 #define BD71828_REG_RTC_ALM2		0x63
+#define BD71828_REG_RTC_START		BD71828_REG_RTC_SEC
 
 /* Charger/Battey */
 #define BD71828_REG_CHG_STATE		0x65
@@ -207,7 +210,6 @@ enum {
 #define BD71828_REG_INT_MASK_TEMP	0xdd
 #define BD71828_REG_INT_MASK_RTC	0xde
 
-
 #define BD71828_REG_INT_MAIN		0xdf
 #define BD71828_REG_INT_BUCK		0xe0
 #define BD71828_REG_INT_DCIN1		0xe1
diff --git a/include/linux/mfd/rohm-shared.h b/include/linux/mfd/rohm-shared.h
new file mode 100644
index 000000000000..5f61fa81d6a2
--- /dev/null
+++ b/include/linux/mfd/rohm-shared.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2018 ROHM Semiconductors */
+
+
+/*
+ * RTC definitions shared between
+ *
+ * BD70528
+ * and BD71828
+ */
+
+#define ROHM_BD1_MASK_RTC_SEC		0x7f
+#define ROHM_BD1_MASK_RTC_MINUTE	0x7f
+#define ROHM_BD1_MASK_RTC_HOUR_24H	0x80
+#define ROHM_BD1_MASK_RTC_HOUR_PM	0x20
+#define ROHM_BD1_MASK_RTC_HOUR		0x3f
+#define ROHM_BD1_MASK_RTC_DAY		0x3f
+#define ROHM_BD1_MASK_RTC_WEEK		0x07
+#define ROHM_BD1_MASK_RTC_MONTH		0x1f
+#define ROHM_BD1_MASK_RTC_YEAR		0xff
+#define ROHM_BD1_MASK_ALM_EN		0x7
+
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 10/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
                   ` (8 preceding siblings ...)
  2019-10-17  9:52 ` [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver Matti Vaittinen
@ 2019-10-17  9:53 ` Matti Vaittinen
  2019-10-17 12:45   ` Bartosz Golaszewski
  2019-10-17  9:53 ` [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC Matti Vaittinen
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:53 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

ROHM BD71828 PMIC contains 4 pins which can be configured by OTP
to be used for general purposes. First 3 can be used as outputs
and 4.th pin can be used as input. Allow them to be controlled
via GPIO framework.

The driver assumes all of the pins are configured as GPIOs and
rusts that the reserved pins in other OTP configurations are
excluded from control using "gpio-reserved-ranges" device tree
property (or left untouched by GPIO users).

Typical use for 4.th pin (input) is to use it as HALL sensor
input so that this pin state is toggled when HALL sensor detects
LID position change (from close to open or open to close). PMIC
HW implements some extra logic which allows PMIC to power-up the
system when this pin is toggled. Please see the data sheet for
details of GPIO options which can be selcted by OTP settings.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/gpio/Kconfig        |  12 +++
 drivers/gpio/Makefile       |   1 +
 drivers/gpio/gpio-bd71828.c | 161 ++++++++++++++++++++++++++++++++++++
 3 files changed, 174 insertions(+)
 create mode 100644 drivers/gpio/gpio-bd71828.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index bb13c266c329..fb0a099de961 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -986,6 +986,18 @@ config GPIO_BD70528
 	  This driver can also be built as a module. If so, the module
 	  will be called gpio-bd70528.
 
+config GPIO_BD71828
+	tristate "ROHM BD71828 GPIO support"
+	depends on MFD_ROHM_BD71828
+	help
+	  Support for GPIOs on ROHM BD71828 PMIC. There are three GPIOs
+	  available on the ROHM PMIC in total. The GPIOs are limited to
+	  outputs only and pins must be configured to GPIO outputs by
+	  OTP. Enable this only if you want to use these pins as outputs.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called gpio-bd71828.
+
 config GPIO_BD9571MWV
 	tristate "ROHM BD9571 GPIO support"
 	depends on MFD_BD9571MWV
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index a4e91175c708..b11932844768 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_GPIO_ASPEED)		+= gpio-aspeed.o
 obj-$(CONFIG_GPIO_ATH79)		+= gpio-ath79.o
 obj-$(CONFIG_GPIO_BCM_KONA)		+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BD70528)		+= gpio-bd70528.o
+obj-$(CONFIG_GPIO_BD71828)		+= gpio-bd71828.o
 obj-$(CONFIG_GPIO_BD9571MWV)		+= gpio-bd9571mwv.o
 obj-$(CONFIG_GPIO_BRCMSTB)		+= gpio-brcmstb.o
 obj-$(CONFIG_GPIO_BT8XX)		+= gpio-bt8xx.o
diff --git a/drivers/gpio/gpio-bd71828.c b/drivers/gpio/gpio-bd71828.c
new file mode 100644
index 000000000000..3cf3890a24c4
--- /dev/null
+++ b/drivers/gpio/gpio-bd71828.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 ROHM Semiconductors
+// gpio-bd71828.c ROHM BD71828 gpio driver
+
+#include <linux/gpio/driver.h>
+#include <linux/mfd/rohm-bd71828.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define OUT 0
+#define IN 1
+#define GPIO_OUT_REG(off) (BD71828_REG_GPIO_CTRL1 + (off))
+#define HALL_GPIO_OFFSET 3
+
+struct bd71828_gpio {
+	struct rohm_regmap_dev chip;
+	struct gpio_chip gpio;
+};
+
+static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int offset,
+			     int value)
+{
+	int ret;
+	struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
+	u8 val = (value) ? BD71828_GPIO_OUT_HI : BD71828_GPIO_OUT_LO;
+
+	if (offset == HALL_GPIO_OFFSET)
+		return;
+
+	ret = regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
+				 BD71828_GPIO_OUT_MASK, val);
+	if (ret)
+		dev_err(bdgpio->chip.dev, "Could not set gpio to %d\n", value);
+}
+
+static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	int ret;
+	unsigned int val;
+	struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
+
+	if (offset == HALL_GPIO_OFFSET)
+		ret = regmap_read(bdgpio->chip.regmap, BD71828_REG_IO_STAT,
+				  &val);
+	else
+		ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
+				  &val);
+	if (!ret)
+		ret = (val & BD71828_GPIO_OUT_MASK);
+
+	return ret;
+}
+
+static int bd71828_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+				   unsigned long config)
+{
+	struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
+
+	if (offset == HALL_GPIO_OFFSET)
+		return -ENOTSUPP;
+
+	switch (pinconf_to_config_param(config)) {
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		return regmap_update_bits(bdgpio->chip.regmap,
+					  GPIO_OUT_REG(offset),
+					  BD71828_GPIO_DRIVE_MASK,
+					  BD71828_GPIO_OPEN_DRAIN);
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+		return regmap_update_bits(bdgpio->chip.regmap,
+					  GPIO_OUT_REG(offset),
+					  BD71828_GPIO_DRIVE_MASK,
+					  BD71828_GPIO_PUSH_PULL);
+	default:
+		break;
+	}
+	return -ENOTSUPP;
+}
+
+static int bd71828_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+	/*
+	 * Pin usage is selected by OTP data. We can't read it runtime. Hence
+	 * we trust that if the pin is not excluded by "gpio-reserved-ranges"
+	 * the OTP configuration is set to OUT. (Other pins but HALL input pin
+	 * on BD71828 can't really be used for general purpose input - input
+	 * states are used for specific cases like regulator control or
+	 * PMIC_ON_REQ.
+	 */
+	if (offset == HALL_GPIO_OFFSET)
+		return IN;
+
+	return OUT;
+}
+
+static int bd71828_gpio_parse_dt(struct device *dev,
+				 struct bd71828_gpio *bdgpio)
+{
+	/*
+	 * TBD: See if we need some implementation to mark some PINs as
+	 * not controllable based on DT info or if core can handle
+	 * "gpio-reserved-ranges" and exclude them from control
+	 */
+	return 0;
+}
+
+static int bd71828_probe(struct platform_device *pdev)
+{
+	struct bd71828_gpio *bdgpio;
+	struct rohm_regmap_dev *bd71828;
+	int ret;
+
+	bd71828 = dev_get_drvdata(pdev->dev.parent);
+	if (!bd71828) {
+		dev_err(&pdev->dev, "No MFD driver data\n");
+		return -EINVAL;
+	}
+
+	bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio),
+			      GFP_KERNEL);
+	if (!bdgpio)
+		return -ENOMEM;
+
+	ret = bd71828_gpio_parse_dt(pdev->dev.parent, bdgpio);
+
+	bdgpio->chip.dev = &pdev->dev;
+	bdgpio->gpio.parent = pdev->dev.parent;
+	bdgpio->gpio.label = "bd71828-gpio";
+	bdgpio->gpio.owner = THIS_MODULE;
+	bdgpio->gpio.get_direction = bd71828_get_direction;
+	bdgpio->gpio.set_config = bd71828_gpio_set_config;
+	bdgpio->gpio.can_sleep = true;
+	bdgpio->gpio.get = bd71828_gpio_get;
+	bdgpio->gpio.set = bd71828_gpio_set;
+	bdgpio->gpio.base = -1;
+	bdgpio->gpio.ngpio = 4;
+#ifdef CONFIG_OF_GPIO
+	bdgpio->gpio.of_node = pdev->dev.parent->of_node;
+#endif
+	bdgpio->chip.regmap = bd71828->regmap;
+
+	ret = devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio,
+				     bdgpio);
+	if (ret)
+		dev_err(&pdev->dev, "gpio_init: Failed to add bd71828-gpio\n");
+
+	return ret;
+}
+
+static struct platform_driver bd71828_gpio = {
+	.driver = {
+		.name = "bd71828-gpio"
+	},
+	.probe = bd71828_probe,
+};
+
+module_platform_driver(bd71828_gpio);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD71828 voltage regulator driver");
+MODULE_LICENSE("GPL");
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
                   ` (9 preceding siblings ...)
  2019-10-17  9:53 ` [RFC PATCH 10/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs Matti Vaittinen
@ 2019-10-17  9:53 ` Matti Vaittinen
  2019-10-17 14:04   ` Dan Murphy
  2019-10-17  9:55 ` [RFC PATCH 12/13] dt-bindings: mfd: Document ROHM BD71282 bindings Matti Vaittinen
  2019-10-17  9:57 ` [RFC PATCH 13/13] dt-bindings: regulator: Document ROHM BD71282 regulator bindings Matti Vaittinen
  12 siblings, 1 reply; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:53 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

ROHM BD71828 power management IC has two LED outputs for charge status
and button pressing indications. The LED outputs can also be forced
bs SW so add driver allowing to use these LEDs for other indications
as well.

Leds are controlled by SW using 'Force ON' bits. Please note the
constrains mentioned in data-sheet:
1. If one LED is forced ON - then also the other LED is forced.
	=> You can't use SW control to force ON one LED and allow HW
	   to control the other.
2. You can't force both LEDs OFF. If the FORCE bit for both LED's is
   zero, then LEDs are controlled by HW and indicate button/charger
   states as explained in data-sheet.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/leds/Kconfig        | 10 ++++
 drivers/leds/Makefile       |  1 +
 drivers/leds/leds-bd71828.c | 97 +++++++++++++++++++++++++++++++++++++
 3 files changed, 108 insertions(+)
 create mode 100644 drivers/leds/leds-bd71828.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index b0fdeef10bd9..ec59f28bcb39 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -529,6 +529,16 @@ config LEDS_BD2802
 	  This option enables support for BD2802GU RGB LED driver chips
 	  accessed via the I2C bus.
 
+config LEDS_BD71828
+	tristate "LED driver for LED pins on ROHM BD71828 PMIC"
+	depends on LEDS_CLASS
+	depends on I2C
+	help
+	  This option enables support for LED outputs located on ROHM
+	   BD71828 power management IC. ROHM BD71828 has two led output pins
+	   which can be left to indicate HW states or controlled by SW. Say
+	   yes here if you want to enable SW control for these LEDs.
+
 config LEDS_INTEL_SS4200
 	tristate "LED driver for Intel NAS SS4200 series"
 	depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 41fb073a39c1..2a8f6a8e4c7c 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_LEDS_AN30259A)		+= leds-an30259a.o
 obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
 obj-$(CONFIG_LEDS_BCM6358)		+= leds-bcm6358.o
 obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
+obj-$(CONFIG_LEDS_BD71828)		+= leds-bd71828.o
 obj-$(CONFIG_LEDS_CPCAP)		+= leds-cpcap.o
 obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
 obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
diff --git a/drivers/leds/leds-bd71828.c b/drivers/leds/leds-bd71828.c
new file mode 100644
index 000000000000..2427619444f5
--- /dev/null
+++ b/drivers/leds/leds-bd71828.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2019 ROHM Semiconductors
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/mfd/rohm-bd71828.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ? \
+	container_of((l), struct bd71828_leds, green) : \
+	container_of((l), struct bd71828_leds, amber))
+
+enum {
+	ID_GREEN_LED,
+	ID_AMBER_LED,
+	ID_NMBR_OF,
+};
+
+struct bd71828_led {
+	int id;
+	struct led_classdev l;
+	u8 force_mask;
+};
+
+struct bd71828_leds {
+	struct rohm_regmap_dev *bd71828;
+	struct bd71828_led green;
+	struct bd71828_led amber;
+};
+
+static int bd71828_led_brightness_set(struct led_classdev *led_cdev,
+				      enum led_brightness value)
+{
+	struct bd71828_led *l = container_of(led_cdev, struct bd71828_led, l);
+	struct bd71828_leds *data;
+	unsigned int val = BD71828_LED_OFF;
+
+	data = BD71828_LED_TO_DATA(l);
+	if (value != LED_OFF)
+		val = BD71828_LED_ON;
+
+	return regmap_update_bits(data->bd71828->regmap, BD71828_REG_LED_CTRL,
+			    l->force_mask, val);
+}
+
+static int bd71828_led_probe(struct platform_device *pdev)
+{
+	struct rohm_regmap_dev *bd71828;
+	struct bd71828_leds *l;
+	struct bd71828_led *g, *a;
+	static const char *GNAME = "bd71828-green-led";
+	static const char *ANAME = "bd71828-amber-led";
+	int ret;
+
+	pr_info("bd71828 LED driver probed\n");
+
+	bd71828 = dev_get_drvdata(pdev->dev.parent);
+	l = devm_kzalloc(&pdev->dev, sizeof(*l), GFP_KERNEL);
+	if (!l)
+		return -ENOMEM;
+	l->bd71828 = bd71828;
+	a = &l->amber;
+	g = &l->green;
+	a->id = ID_AMBER_LED;
+	g->id = ID_GREEN_LED;
+	a->force_mask = BD71828_MASK_LED_AMBER;
+	g->force_mask = BD71828_MASK_LED_GREEN;
+
+	a->l.name = ANAME;
+	g->l.name = GNAME;
+	a->l.brightness_set_blocking = bd71828_led_brightness_set;
+	g->l.brightness_set_blocking = bd71828_led_brightness_set;
+
+	ret = devm_led_classdev_register(&pdev->dev, &g->l);
+	if (ret)
+		return ret;
+
+	return devm_led_classdev_register(&pdev->dev, &a->l);
+}
+
+static struct platform_driver bd71828_led_driver = {
+	.driver = {
+		.name  = "bd71828-led",
+	},
+	.probe  = bd71828_led_probe,
+};
+
+module_platform_driver(bd71828_led_driver);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("ROHM BD71828 LED driver");
+MODULE_LICENSE("GPL");
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 12/13] dt-bindings: mfd: Document ROHM BD71282 bindings
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
                   ` (10 preceding siblings ...)
  2019-10-17  9:53 ` [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC Matti Vaittinen
@ 2019-10-17  9:55 ` Matti Vaittinen
  2019-10-17 14:18   ` Dan Murphy
  2019-10-17  9:57 ` [RFC PATCH 13/13] dt-bindings: regulator: Document ROHM BD71282 regulator bindings Matti Vaittinen
  12 siblings, 1 reply; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:55 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

ROHM BD71828 Power management IC integrates 7 buck converters, 7 LDOs,
a real-time clock (RTC), 3 GPO/regulator control pins, HALL input pin
and a 32.768 kHz clock gate.

Document the dt bindings drivers are using.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 .../bindings/mfd/rohm,bd71828-pmic.txt        | 180 ++++++++++++++++++
 1 file changed, 180 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.txt

diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.txt b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.txt
new file mode 100644
index 000000000000..125efa9f3de0
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.txt
@@ -0,0 +1,180 @@
+* ROHM BD71828 Power Management Integrated Circuit bindings
+
+BD71828GW is a single-chip power management IC for battery-powered portable
+devices. The IC integrates 7 buck converters, 7 LDOs, and a 1500 mA single-cell
+linear charger. Also included is a Coulomb counter, a real-time clock (RTC),
+and a 32.768 kHz clock gate.
+
+Required properties:
+ - compatible			: Should be "rohm,bd71828".
+ - reg				: I2C slave address.
+ - interrupt-parent		: Phandle to the parent interrupt controller.
+ - interrupts			: The interrupt line the device is connected to.
+ - clocks			: The parent clock connected to PMIC.
+ - #clock-cells			: Should be 0.
+ - regulators			: List of child nodes that specify the
+				  regulators. Please see
+				  ../regulator/rohm,bd71828-regulator.txt
+ - gpio-controller		: To indicate BD71828 acts as a GPIO controller.
+ - #gpio-cells			: Should be 2. The first cell is the pin number
+				  and the second cell is used to specify flags.
+				  See ../gpio/gpio.txt for more information.
+
+The BD71828 RUN state is divided into 4 configurable run-levels named RUN0,
+RUN1, RUN2 and RUN3. Bucks 1, 2, 6 and 7 can be either controlled individually
+via I2C, or some/all of them can be bound to run-levels and controlled as a
+group. If bucks are controlled individually these run-levels are ignored. See
+../regulator/rohm,bd71828-regulator.txt for how to define regulator voltages
+for run-levels. Run-levels can be changed by I2C or GPIO depending on PMIC's OTP
+configuration.
+
+Optional properties:
+- clock-output-names		: Should contain name for output clock.
+- rohm,dvs-vsel-gpios		: GPIOs used to control PMIC run-levels. Should
+				  describe two GPIOs. (See run-level control in
+				  data-sheet). If this property is omitted but
+				  some bucks are marked to be controlled by
+				  run-levels - then OTP option allowing
+				  run-level control via I2C is assumed.
+- gpio-reserved-ranges		: Usage of GPIO pins can be changed via OTP.
+				  This property can be used to mark the pins
+				  which should not be configured for GPIO.
+				  Please see the ../gpio/gpio.txt for more
+				  information.
+
+Example:
+
+        /* external oscillator node */
+        osc: oscillator {
+                compatible = "fixed-clock";
+                #clock-cells = <1>;
+                clock-frequency  = <32768>;
+                clock-output-names = "osc";
+        };
+
+	pmic: pmic@4b {
+		compatible = "rohm,bd71828";
+		reg = <0x4b>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <29 GPIO_ACTIVE_LOW>;
+		clocks = <&osc 0>;
+		#clock-cells = <0>;
+		clock-output-names = "bd71828-32k-out";
+		gpio-controller;
+		#gpio-cells = <2>;
+		ngpios = <4>;
+		gpio-reserved-ranges = <0 1 2 1>;
+		gpio-line-names = "EPDEN";
+		rohm,dvs-vsel-gpios = <&gpio1 12 0>,
+				      <&gpio1 13 0>;
+		regulators {
+			buck1: BUCK1 {
+				regulator-name = "buck1";
+				regulator-min-microvolt = <500000>;
+				regulator-max-microvolt = <2000000>;
+				regulator-ramp-delay = <2500>;
+				rohm,dvs-runlvl-ctrl;
+				rohm,dvs-runlevel0-voltage = <500000>;
+				rohm,dvs-runlevel1-voltage = <506250>;
+				rohm,dvs-runlevel2-voltage = <512500>;
+				rohm,dvs-runlevel3-voltage = <518750>;
+				regulator-boot-on;
+			};
+			buck2: BUCK2 {
+				regulator-name = "buck2";
+				regulator-min-microvolt = <500000>;
+				regulator-max-microvolt = <2000000>;
+				regulator-ramp-delay = <2500>;
+				rohm,dvs-runlvl-ctrl;
+				rohm,dvs-runlevel0-voltage = <500000>;
+				rohm,dvs-runlevel1-voltage = <506250>;
+				rohm,dvs-runlevel2-voltage = <512500>;
+				rohm,dvs-runlevel3-voltage = <518750>;
+				regulator-boot-on;
+			};
+			buck3: BUCK3 {
+				regulator-name = "buck3";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <2000000>;
+				regulator-boot-on;
+			};
+			buck4: BUCK4 {
+				regulator-name = "buck4";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-boot-on;
+			};
+			buck5: BUCK5 {
+				regulator-name = "buck5";
+				regulator-min-microvolt = <2500000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+			};
+			buck6: BUCK6 {
+				regulator-name = "buck6";
+				regulator-min-microvolt = <500000>;
+				regulator-max-microvolt = <2000000>;
+				regulator-ramp-delay = <2500>;
+				rohm,dvs-runlvl-ctrl;
+				rohm,dvs-runlevel0-voltage = <500000>;
+				rohm,dvs-runlevel1-voltage = <506250>;
+				rohm,dvs-runlevel2-voltage = <512500>;
+				rohm,dvs-runlevel3-voltage = <518750>;
+				regulator-boot-on;
+			};
+			buck7: BUCK7 {
+				regulator-name = "buck7";
+				regulator-min-microvolt = <500000>;
+				regulator-max-microvolt = <2000000>;
+				regulator-ramp-delay = <2500>;
+				rohm,dvs-runlvl-ctrl;
+				rohm,dvs-runlevel0-voltage = <500000>;
+				rohm,dvs-runlevel1-voltage = <506250>;
+				rohm,dvs-runlevel2-voltage = <512500>;
+				rohm,dvs-runlevel3-voltage = <518750>;
+				regulator-boot-on;
+			};
+			ldo1: LDO1 {
+				regulator-name = "ldo1";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+			};
+			ldo2: LDO2 {
+				regulator-name = "ldo2";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+			};
+			ldo3: LDO3 {
+				regulator-name = "ldo3";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+			};
+			ldo4: LDO4 {
+				regulator-name = "ldo4";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+			};
+			ldo5: LDO5 {
+				regulator-name = "ldo5";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+			};
+			ldo6: LDO6 {
+				regulator-name = "ldo6";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-boot-on;
+			};
+			ldo7_reg: LDO7 {
+				regulator-name = "ldo7";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+			};
+		};
+	};
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* [RFC PATCH 13/13] dt-bindings: regulator: Document ROHM BD71282 regulator bindings
  2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
                   ` (11 preceding siblings ...)
  2019-10-17  9:55 ` [RFC PATCH 12/13] dt-bindings: mfd: Document ROHM BD71282 bindings Matti Vaittinen
@ 2019-10-17  9:57 ` Matti Vaittinen
  12 siblings, 0 replies; 43+ messages in thread
From: Matti Vaittinen @ 2019-10-17  9:57 UTC (permalink / raw)
  To: matti.vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

Document ROHM BD71828 PMIC regulator device tree bindings.

Add binding document for regulators on ROHM BD71828 PMIC

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 .../regulator/rohm,bd71828-regulator.txt      | 164 ++++++++++++++++++
 1 file changed, 164 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.txt

diff --git a/Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.txt b/Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.txt
new file mode 100644
index 000000000000..cf6a5123b98d
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.txt
@@ -0,0 +1,164 @@
+ROHM BD71828 Power Management Integrated Circuit regulator bindings
+
+Required properties:
+ - regulator-name: should be "buck1", ..., "buck7", and "ldo1", ..., "ldo7"
+
+List of regulators provided by this controller. BD71828 regulators node
+should be sub node of the BD71828 MFD node. See BD71828 MFD bindings at
+Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.txt
+Regulator nodes should be named to BUCK_<number> and LDO_<number>. The
+definition for each of these nodes is defined using the standard
+binding for regulators at
+Documentation/devicetree/bindings/regulator/regulator.txt.
+
+The valid names for BD71828 regulator nodes are:
+BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6, BUCK7
+LDO1, LDO2, LDO3, LDO4, LDO5, LDO6, LDO7
+
+Optional properties:
+- rohm,dvs-run-voltage		: PMIC default "RUN" state voltage in uV.
+				  See below table for bucks which support this.
+- rohm,dvs-idle-voltage		: PMIC default "IDLE" state voltage in uV.
+				  See below table for bucks which support this.
+- rohm,dvs-suspend-voltage	: PMIC default "SUSPEND" state voltage in uV.
+				  See below table for bucks which support this.
+- rohm,dvs-lpsr-voltage		: PMIC default "LPSR" state voltage in uV.
+- rohm,dvs-runlvl-ctrl		: buck control is done based on run-level.
+				  Regulator is not individually controllable.
+				  See ../mfd/rohm,bd71828-pmic.txt for how to
+				  specify run-level control mechanism. Only
+				  bucks 1, 2, 6 and 7 support this.
+- rohm,dvs-runlevel0-voltage    : voltage for run-level 0. Microvolts.
+- rohm,dvs-runlevel1-voltage	: voltage for run-level 1. Microvolts.
+- rohm,dvs-runlevel2-voltage	: voltage for run-level 2. Microvolts.
+- rohm,dvs-runlevel3-voltage	: voltage for run-level 3. Microvolts.
+
+- Any optional property defined in bindings/regulator/regulator.txt
+
+Supported default DVS states:
+
+buck		| run		| idle		| suspend	| lpsr
+-----------------------------------------------------------------------------
+1, 2, 6, and 7	| supported	| supported	| 	supported (*)
+----------------------------------------------------------------------------
+3, 4, and 5	| 			supported (**)
+----------------------------------------------------------------------------
+(*)  LPSR and SUSPEND states use same voltage but both states have own enable /
+     disable settings. Voltage 0 can be specified for a state to make regulator
+     disabled on that state.
+(**) All states use same voltage but have own enable / disable settings. Voltage
+     0 can be specified for a state to make regulator disabled on that state.
+
+Example:
+regulators {
+	buck1: BUCK1 {
+		regulator-name = "buck1";
+		regulator-min-microvolt = <500000>;
+		regulator-max-microvolt = <2000000>;
+		regulator-ramp-delay = <2500>;
+		rohm,dvs-runlvl-ctrl;
+		rohm,dvs-runlevel0-voltage = <500000>;
+		rohm,dvs-runlevel1-voltage = <506250>;
+		rohm,dvs-runlevel2-voltage = <512500>;
+		rohm,dvs-runlevel3-voltage = <518750>;
+		regulator-boot-on;
+	};
+	buck2: BUCK2 {
+		regulator-name = "buck2";
+		regulator-min-microvolt = <500000>;
+		regulator-max-microvolt = <2000000>;
+		regulator-ramp-delay = <2500>;
+		rohm,dvs-runlvl-ctrl;
+		rohm,dvs-runlevel0-voltage = <500000>;
+		rohm,dvs-runlevel1-voltage = <506250>;
+		rohm,dvs-runlevel2-voltage = <512500>;
+		rohm,dvs-runlevel3-voltage = <518750>;
+		regulator-boot-on;
+	};
+	buck3: BUCK3 {
+		regulator-name = "buck3";
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <2000000>;
+		regulator-boot-on;
+	};
+	buck4: BUCK4 {
+		regulator-name = "buck4";
+		regulator-min-microvolt = <1000000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-boot-on;
+	};
+	buck5: BUCK5 {
+		regulator-name = "buck5";
+		regulator-min-microvolt = <2500000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+	};
+	buck6: BUCK6 {
+		regulator-name = "buck6";
+		regulator-min-microvolt = <500000>;
+		regulator-max-microvolt = <2000000>;
+		regulator-ramp-delay = <2500>;
+		rohm,dvs-runlvl-ctrl;
+		rohm,dvs-runlevel0-voltage = <500000>;
+		rohm,dvs-runlevel1-voltage = <506250>;
+		rohm,dvs-runlevel2-voltage = <512500>;
+		rohm,dvs-runlevel3-voltage = <518750>;
+		regulator-boot-on;
+	};
+	buck7: BUCK7 {
+		regulator-name = "buck7";
+		regulator-min-microvolt = <500000>;
+		regulator-max-microvolt = <2000000>;
+		regulator-ramp-delay = <2500>;
+		rohm,dvs-runlvl-ctrl;
+		rohm,dvs-runlevel0-voltage = <500000>;
+		rohm,dvs-runlevel1-voltage = <506250>;
+		rohm,dvs-runlevel2-voltage = <512500>;
+		rohm,dvs-runlevel3-voltage = <518750>;
+		regulator-boot-on;
+	};
+	ldo1: LDO1 {
+		regulator-name = "ldo1";
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+	};
+	ldo2: LDO2 {
+		regulator-name = "ldo2";
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+	};
+	ldo3: LDO3 {
+		regulator-name = "ldo3";
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+	};
+	ldo4: LDO4 {
+		regulator-name = "ldo4";
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+	};
+	ldo5: LDO5 {
+		regulator-name = "ldo5";
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+	};
+	ldo6: LDO6 {
+		regulator-name = "ldo6";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-boot-on;
+	};
+	ldo7_reg: LDO7 {
+		regulator-name = "ldo7";
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+	};
+};
+
+
-- 
2.21.0


-- 
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =] 

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

* Re: [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver
  2019-10-17  9:52 ` [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver Matti Vaittinen
@ 2019-10-17 10:12   ` Alexandre Belloni
  2019-10-17 10:36     ` Vaittinen, Matti
  0 siblings, 1 reply; 43+ messages in thread
From: Alexandre Belloni @ 2019-10-17 10:12 UTC (permalink / raw)
  To: Matti Vaittinen
  Cc: mazziesaccount, Lee Jones, Rob Herring, Mark Rutland,
	Liam Girdwood, Mark Brown, Michael Turquette, Stephen Boyd,
	Linus Walleij, Bartosz Golaszewski, Jacek Anaszewski,
	Pavel Machek, Dan Murphy, Alessandro Zummo, devicetree,
	linux-kernel, linux-clk, linux-gpio, linux-leds, linux-rtc

Hi,

the subject should be rtc: bd70528 add BD71828 support

On 17/10/2019 12:52:21+0300, Matti Vaittinen wrote:
> ROHM BD71828 PMIC RTC block is from many parts similar to one
> on BD70528. Support BD71828 RTC using BD70528 RTC driver and
> avoid re-inventing the wheel.
> 
> Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> ---
>  drivers/rtc/Kconfig              |   5 +-
>  drivers/rtc/rtc-bd70528.c        | 369 ++++++++++++++++++++++---------
>  include/linux/mfd/rohm-bd70528.h |  12 +-
>  include/linux/mfd/rohm-bd71828.h |   4 +-
>  include/linux/mfd/rohm-shared.h  |  22 ++
>  5 files changed, 299 insertions(+), 113 deletions(-)
>  create mode 100644 include/linux/mfd/rohm-shared.h
> 
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index e72f65b61176..5c5b18a99bf9 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -498,11 +498,12 @@ config RTC_DRV_M41T80_WDT
>  	help
>  	  If you say Y here you will get support for the
>  	  watchdog timer in the ST M41T60 and M41T80 RTC chips series.
> +
>  config RTC_DRV_BD70528
> -	tristate "ROHM BD70528 PMIC RTC"
> +	tristate "ROHM BD70528 and BD71828 PMIC RTC"
>  	help
>  	  If you say Y here you will get support for the RTC
> -	  on ROHM BD70528 Power Management IC.
> +	  block on ROHM BD70528 and BD71828 Power Management IC.
>  
>  	  This driver can also be built as a module. If so, the module
>  	  will be called rtc-bd70528.
> diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c
> index f9bdd555e1a2..2940b57002ac 100644
> --- a/drivers/rtc/rtc-bd70528.c
> +++ b/drivers/rtc/rtc-bd70528.c
> @@ -2,10 +2,11 @@
>  //
>  // Copyright (C) 2018 ROHM Semiconductors
>  //
> -// RTC driver for ROHM BD70528 PMIC
> +// RTC driver for ROHM BD70528 and BD71828 PMICs
>  
>  #include <linux/bcd.h>
>  #include <linux/mfd/rohm-bd70528.h>
> +#include <linux/mfd/rohm-bd71828.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/platform_device.h>
> @@ -15,16 +16,16 @@
>  /*
>   * We read regs RTC_SEC => RTC_YEAR
>   * this struct is ordered according to chip registers.
> - * Keep it u8 only to avoid padding issues.
> + * Keep it u8 only (or packed) to avoid padding issues.
>   */
> -struct bd70528_rtc_day {
> +struct bd7xx28_rtc_day {

This is a useless and harmful rename.

>  	u8 sec;
>  	u8 min;
>  	u8 hour;
>  } __packed;
>  
> -struct bd70528_rtc_data {
> -	struct bd70528_rtc_day time;
> +struct bd7xx28_rtc_data {
> +	struct bd7xx28_rtc_day time;
>  	u8 week;
>  	u8 day;
>  	u8 month;
> @@ -32,19 +33,28 @@ struct bd70528_rtc_data {
>  } __packed;
>  
>  struct bd70528_rtc_wake {
> -	struct bd70528_rtc_day time;
> +	struct bd7xx28_rtc_day time;
>  	u8 ctrl;
>  } __packed;
>  
> +struct bd71828_rtc_alm {
> +	struct bd7xx28_rtc_data alm0;
> +	struct bd7xx28_rtc_data alm1;
> +	u8 alm_mask;
> +	u8 alm1_mask;
> +} __packed;
> +
>  struct bd70528_rtc_alm {
> -	struct bd70528_rtc_data data;
> +	struct bd7xx28_rtc_data data;
>  	u8 alm_mask;
>  	u8 alm_repeat;
>  } __packed;
>  
> -struct bd70528_rtc {
> +struct bd7xx28_rtc {
>  	struct rohm_regmap_dev *mfd;
>  	struct device *dev;
> +	u8 reg_time_start;
> +	bool has_rtc_timers;
>  };
>  
>  static int bd70528_set_wake(struct rohm_regmap_dev *bd70528,
> @@ -118,7 +128,7 @@ static int bd70528_set_elapsed_tmr(struct rohm_regmap_dev *bd70528,
>  			    ctrl_reg);
>  }
>  
> -static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state,
> +static int bd70528_set_rtc_based_timers(struct bd7xx28_rtc *r, int new_state,
>  					int *old_state)
>  {
>  	int ret;
> @@ -127,122 +137,162 @@ static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state,
>  			      old_state);
>  	if (ret) {
>  		dev_err(r->dev,
> -			"Failed to disable WDG for RTC setting (%d)\n", ret);
> +			"Failed to disable WDG for RTC setting (%d)\n",
> +			ret);

Unnecessary change

>  		return ret;
>  	}
> -	ret = bd70528_set_elapsed_tmr(r->mfd,
> -				      new_state & BD70528_ELAPSED_STATE_BIT,
> +	ret = bd70528_set_elapsed_tmr(r->mfd, new_state &
> +				      BD70528_ELAPSED_STATE_BIT,

Ditto

>  				      old_state);
>  	if (ret) {
>  		dev_err(r->dev,
>  			"Failed to disable 'elapsed timer' for RTC setting\n");
>  		return ret;
>  	}
> -	ret = bd70528_set_wake(r->mfd, new_state & BD70528_WAKE_STATE_BIT,
> -			       old_state);
> +
> +	ret = bd70528_set_wake(r->mfd, new_state &
> +			       BD70528_WAKE_STATE_BIT, old_state);

Ditto

>  	if (ret) {
>  		dev_err(r->dev,
> -			"Failed to disable 'wake timer' for RTC setting\n");
> +			"Failed to disable 'wake timer'\n");

Unrelated change

>  		return ret;
>  	}
>  
>  	return ret;
>  }
>  
> -static int bd70528_re_enable_rtc_based_timers(struct bd70528_rtc *r,
> +static int bd7xx28_re_enable_rtc_based_timers(struct bd7xx28_rtc *r,
>  					      int old_state)
>  {
> +	if (!r->has_rtc_timers)
> +		return 0;
> +
>  	return bd70528_set_rtc_based_timers(r, old_state, NULL);
>  }
>  
> -static int bd70528_disable_rtc_based_timers(struct bd70528_rtc *r,
> +static int bd7xx28_disable_rtc_based_timers(struct bd7xx28_rtc *r,
>  					    int *old_state)
>  {
> +	if (!r->has_rtc_timers)
> +		return 0;
> +
>  	return bd70528_set_rtc_based_timers(r, 0, old_state);
>  }
>  
> -static inline void tmday2rtc(struct rtc_time *t, struct bd70528_rtc_day *d)
> +static inline void tmday2rtc(struct rtc_time *t, struct bd7xx28_rtc_day *d)
>  {
> -	d->sec &= ~BD70528_MASK_RTC_SEC;
> -	d->min &= ~BD70528_MASK_RTC_MINUTE;
> -	d->hour &= ~BD70528_MASK_RTC_HOUR;
> +	d->sec &= ~ROHM_BD1_MASK_RTC_SEC;
> +	d->min &= ~ROHM_BD1_MASK_RTC_MINUTE;
> +	d->hour &= ~ROHM_BD1_MASK_RTC_HOUR;
>  	d->sec |= bin2bcd(t->tm_sec);
>  	d->min |= bin2bcd(t->tm_min);
>  	d->hour |= bin2bcd(t->tm_hour);
>  }
>  
> -static inline void tm2rtc(struct rtc_time *t, struct bd70528_rtc_data *r)
> +static inline void tm2rtc(struct rtc_time *t, struct bd7xx28_rtc_data *r)
>  {
> -	r->day &= ~BD70528_MASK_RTC_DAY;
> -	r->week &= ~BD70528_MASK_RTC_WEEK;
> -	r->month &= ~BD70528_MASK_RTC_MONTH;
> +	r->day &= ~ROHM_BD1_MASK_RTC_DAY;
> +	r->week &= ~ROHM_BD1_MASK_RTC_WEEK;
> +	r->month &= ~ROHM_BD1_MASK_RTC_MONTH;
>  	/*
>  	 * PM and 24H bits are not used by Wake - thus we clear them
>  	 * here and not in tmday2rtc() which is also used by wake.
>  	 */
> -	r->time.hour &= ~(BD70528_MASK_RTC_HOUR_PM | BD70528_MASK_RTC_HOUR_24H);
> +	r->time.hour &= ~(ROHM_BD1_MASK_RTC_HOUR_PM |
> +			  ROHM_BD1_MASK_RTC_HOUR_24H);
>  
>  	tmday2rtc(t, &r->time);
>  	/*
>  	 * We do always set time in 24H mode.
>  	 */
> -	r->time.hour |= BD70528_MASK_RTC_HOUR_24H;
> +	r->time.hour |= ROHM_BD1_MASK_RTC_HOUR_24H;
>  	r->day |= bin2bcd(t->tm_mday);
>  	r->week |= bin2bcd(t->tm_wday);
>  	r->month |= bin2bcd(t->tm_mon + 1);
>  	r->year = bin2bcd(t->tm_year - 100);
> +
>  }
>  
> -static inline void rtc2tm(struct bd70528_rtc_data *r, struct rtc_time *t)
> +static inline void rtc2tm(struct bd7xx28_rtc_data *r, struct rtc_time *t)
>  {
> -	t->tm_sec = bcd2bin(r->time.sec & BD70528_MASK_RTC_SEC);
> -	t->tm_min = bcd2bin(r->time.min & BD70528_MASK_RTC_MINUTE);
> -	t->tm_hour = bcd2bin(r->time.hour & BD70528_MASK_RTC_HOUR);
> +	t->tm_sec = bcd2bin(r->time.sec & ROHM_BD1_MASK_RTC_SEC);
> +	t->tm_min = bcd2bin(r->time.min & ROHM_BD1_MASK_RTC_MINUTE);
> +	t->tm_hour = bcd2bin(r->time.hour & ROHM_BD1_MASK_RTC_HOUR);
>  	/*
> -	 * If RTC is in 12H mode, then bit BD70528_MASK_RTC_HOUR_PM
> +	 * If RTC is in 12H mode, then bit ROHM_BD1_MASK_RTC_HOUR_PM
>  	 * is not BCD value but tells whether it is AM or PM
>  	 */
> -	if (!(r->time.hour & BD70528_MASK_RTC_HOUR_24H)) {
> +	if (!(r->time.hour & ROHM_BD1_MASK_RTC_HOUR_24H)) {
>  		t->tm_hour %= 12;
> -		if (r->time.hour & BD70528_MASK_RTC_HOUR_PM)
> +		if (r->time.hour & ROHM_BD1_MASK_RTC_HOUR_PM)
>  			t->tm_hour += 12;
>  	}
> -	t->tm_mday = bcd2bin(r->day & BD70528_MASK_RTC_DAY);
> -	t->tm_mon = bcd2bin(r->month & BD70528_MASK_RTC_MONTH) - 1;
> -	t->tm_year = 100 + bcd2bin(r->year & BD70528_MASK_RTC_YEAR);
> -	t->tm_wday = bcd2bin(r->week & BD70528_MASK_RTC_WEEK);
> +	t->tm_mday = bcd2bin(r->day & ROHM_BD1_MASK_RTC_DAY);
> +	t->tm_mon = bcd2bin(r->month & ROHM_BD1_MASK_RTC_MONTH) - 1;
> +	t->tm_year = 100 + bcd2bin(r->year & ROHM_BD1_MASK_RTC_YEAR);
> +	t->tm_wday = bcd2bin(r->week & ROHM_BD1_MASK_RTC_WEEK);
>  }
>  
> -static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
> +static int bd71828_set_alarm(struct bd7xx28_rtc *r, struct rtc_wkalrm *a)
> +{
> +	int ret;
> +	struct bd71828_rtc_alm alm;
> +	struct rohm_regmap_dev *bd71828 = r->mfd;
> +
> +	ret = regmap_bulk_read(bd71828->regmap, BD71828_REG_RTC_ALM_START,
> +			       &alm, sizeof(alm));
> +	if (ret) {
> +		dev_err(r->dev, "Failed to read alarm regs\n");
> +		return ret;
> +	}
> +
> +	tm2rtc(&a->time, &alm.alm0);
> +
> +	if (!a->enabled)
> +		alm.alm_mask &= ~ROHM_BD1_MASK_ALM_EN;
> +	else
> +		alm.alm_mask |= ROHM_BD1_MASK_ALM_EN;
> +
> +	pr_debug("%s: new ALM0 EN bits are 0x%x\n", __func__, alm.alm_mask);
> +
> +	ret = regmap_bulk_write(bd71828->regmap, BD71828_REG_RTC_ALM_START,
> +				&alm, sizeof(alm));
> +	if (ret)
> +		dev_err(r->dev, "Failed to set alarm time\n");
> +
> +	return ret;
> +
> +}
> +static int bd70528_set_alarm(struct bd7xx28_rtc *r, struct rtc_wkalrm *a)
>  {
>  	struct bd70528_rtc_wake wake;
>  	struct bd70528_rtc_alm alm;
>  	int ret;
> -	struct bd70528_rtc *r = dev_get_drvdata(dev);
>  	struct rohm_regmap_dev *bd70528 = r->mfd;
>  
> -	ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_WAKE_START,
> +	ret = regmap_bulk_read(bd70528->regmap,
> +			       BD70528_REG_RTC_WAKE_START,
>  			       &wake, sizeof(wake));
>  	if (ret) {
> -		dev_err(dev, "Failed to read wake regs\n");
> +		dev_err(r->dev, "Failed to read wake regs\n");
>  		return ret;
>  	}
> +	tmday2rtc(&a->time, &wake.time);
>  
>  	ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_ALM_START,
>  			       &alm, sizeof(alm));
>  	if (ret) {
> -		dev_err(dev, "Failed to read alarm regs\n");
> +		dev_err(r->dev, "Failed to read alarm regs\n");
>  		return ret;
>  	}
>  
>  	tm2rtc(&a->time, &alm.data);
> -	tmday2rtc(&a->time, &wake.time);
>  
>  	if (a->enabled) {
> -		alm.alm_mask &= ~BD70528_MASK_ALM_EN;
> +		alm.alm_mask &= ~ROHM_BD1_MASK_ALM_EN;
>  		wake.ctrl |= BD70528_MASK_WAKE_EN;
>  	} else {
> -		alm.alm_mask |= BD70528_MASK_ALM_EN;
> +		alm.alm_mask |= ROHM_BD1_MASK_ALM_EN;
>  		wake.ctrl &= ~BD70528_MASK_WAKE_EN;
>  	}
>  
> @@ -250,28 +300,69 @@ static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
>  				BD70528_REG_RTC_WAKE_START, &wake,
>  				sizeof(wake));
>  	if (ret) {
> -		dev_err(dev, "Failed to set wake time\n");
> +		dev_err(r->dev, "Failed to set wake time\n");
>  		return ret;
>  	}
>  	ret = regmap_bulk_write(bd70528->regmap, BD70528_REG_RTC_ALM_START,
>  				&alm, sizeof(alm));
>  	if (ret)
> -		dev_err(dev, "Failed to set alarm time\n");
> +		dev_err(r->dev, "Failed to set alarm time\n");
>  
>  	return ret;
>  }
> +static int bd7xx28_set_alarm(struct device *dev, struct rtc_wkalrm *a)
> +{
> +	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
> +	struct rohm_regmap_dev *bd_dev = r->mfd;
> +
> +	switch (bd_dev->chip_type) {
> +	case ROHM_CHIP_TYPE_BD70528:
> +		return bd70528_set_alarm(r, a);
> +	case ROHM_CHIP_TYPE_BD71828:
> +		return bd71828_set_alarm(r, a);
> +	default:
> +		dev_err(dev, "Unknown RTC chip\n");
> +		break;
> +	}
> +
> +	return -ENOENT;
> +}
>  
> -static int bd70528_read_alarm(struct device *dev, struct rtc_wkalrm *a)
> +static int bd71828_read_alarm(struct bd7xx28_rtc *r, struct rtc_wkalrm *a)
> +{
> +	int ret;
> +	struct bd71828_rtc_alm alm;
> +	struct rohm_regmap_dev *bd71828 = r->mfd;
> +
> +	ret = regmap_bulk_read(bd71828->regmap, BD71828_REG_RTC_ALM_START,
> +			       &alm, sizeof(alm));
> +	if (ret) {
> +		dev_err(r->dev, "Failed to read alarm regs\n");
> +		return ret;
> +	}
> +
> +	rtc2tm(&alm.alm0, &a->time);
> +	a->time.tm_mday = -1;
> +	a->time.tm_mon = -1;
> +	a->time.tm_year = -1;
> +	a->enabled = !!(alm.alm_mask & ROHM_BD1_MASK_ALM_EN);
> +	a->pending = 0;
> +	pr_debug("%s: ALM0 EN bits are 0x%x, returniong enabled %d\n", __func__,
> +		alm.alm_mask, a->enabled);
> +
> +	return 0;
> +}
> +
> +static int bd70528_read_alarm(struct bd7xx28_rtc *r, struct rtc_wkalrm *a)
>  {
> -	struct bd70528_rtc_alm alm;
>  	int ret;
> -	struct bd70528_rtc *r = dev_get_drvdata(dev);
> +	struct bd70528_rtc_alm alm;
>  	struct rohm_regmap_dev *bd70528 = r->mfd;
>  
>  	ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_ALM_START,
>  			       &alm, sizeof(alm));
>  	if (ret) {
> -		dev_err(dev, "Failed to read alarm regs\n");
> +		dev_err(r->dev, "Failed to read alarm regs\n");
>  		return ret;
>  	}
>  
> @@ -279,25 +370,42 @@ static int bd70528_read_alarm(struct device *dev, struct rtc_wkalrm *a)
>  	a->time.tm_mday = -1;
>  	a->time.tm_mon = -1;
>  	a->time.tm_year = -1;
> -	a->enabled = !(alm.alm_mask & BD70528_MASK_ALM_EN);
> +	a->enabled = !(alm.alm_mask & ROHM_BD1_MASK_ALM_EN);
>  	a->pending = 0;
>  
>  	return 0;
>  }
>  
> -static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
> +static int bd7xx28_read_alarm(struct device *dev, struct rtc_wkalrm *a)
> +{
> +	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
> +	struct rohm_regmap_dev *bd_dev = r->mfd;
> +
> +	switch (bd_dev->chip_type) {
> +	case ROHM_CHIP_TYPE_BD70528:
> +		return bd70528_read_alarm(r, a);
> +	case ROHM_CHIP_TYPE_BD71828:
> +		return bd71828_read_alarm(r, a);

You can avoid all of that by having multiple rtc_class_ops structs.

> +	default:
> +		dev_err(dev, "Unknown RTC chip\n");
> +		break;
> +	}
> +	return -ENOENT;
> +}
> +
> +static int bd7xx28_set_time_locked(struct device *dev, struct rtc_time *t)
>  {
>  	int ret, tmpret, old_states;
> -	struct bd70528_rtc_data rtc_data;
> -	struct bd70528_rtc *r = dev_get_drvdata(dev);
> -	struct rohm_regmap_dev *bd70528 = r->mfd;
> +	struct bd7xx28_rtc_data rtc_data;
> +	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
> +	struct rohm_regmap_dev *bd7xx28 = r->mfd;
>  
> -	ret = bd70528_disable_rtc_based_timers(r, &old_states);
> +	ret = bd7xx28_disable_rtc_based_timers(r, &old_states);
>  	if (ret)
>  		return ret;
>  
> -	tmpret = regmap_bulk_read(bd70528->regmap,
> -				  BD70528_REG_RTC_START, &rtc_data,
> +	tmpret = regmap_bulk_read(bd7xx28->regmap,
> +				  r->reg_time_start, &rtc_data,
>  				  sizeof(rtc_data));
>  	if (tmpret) {
>  		dev_err(dev, "Failed to read RTC time registers\n");
> @@ -305,8 +413,8 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
>  	}
>  	tm2rtc(t, &rtc_data);
>  
> -	tmpret = regmap_bulk_write(bd70528->regmap,
> -				   BD70528_REG_RTC_START, &rtc_data,
> +	tmpret = regmap_bulk_write(bd7xx28->regmap,
> +				   r->reg_time_start, &rtc_data,
>  				   sizeof(rtc_data));
>  	if (tmpret) {
>  		dev_err(dev, "Failed to set RTC time\n");
> @@ -314,34 +422,37 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
>  	}
>  
>  renable_out:
> -	ret = bd70528_re_enable_rtc_based_timers(r, old_states);
> +	ret = bd7xx28_re_enable_rtc_based_timers(r, old_states);
>  	if (tmpret)
>  		ret = tmpret;
>  
>  	return ret;
>  }
>  
> -static int bd70528_set_time(struct device *dev, struct rtc_time *t)
> +static int bd7xx28_set_time(struct device *dev, struct rtc_time *t)
>  {
>  	int ret;
> -	struct bd70528_rtc *r = dev_get_drvdata(dev);
> +	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
>  
> +	pr_debug("%s called\n", __func__);
>  	bd70528_wdt_lock(r->mfd);
> -	ret = bd70528_set_time_locked(dev, t);
> +	ret = bd7xx28_set_time_locked(dev, t);
>  	bd70528_wdt_unlock(r->mfd);
>  	return ret;
>  }
>  
> -static int bd70528_get_time(struct device *dev, struct rtc_time *t)
> +static int bd7xx28_get_time(struct device *dev, struct rtc_time *t)
>  {
> -	struct bd70528_rtc *r = dev_get_drvdata(dev);
> -	struct rohm_regmap_dev *bd70528 = r->mfd;
> -	struct bd70528_rtc_data rtc_data;
> +	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
> +	struct rohm_regmap_dev *bd7xx28 = r->mfd;
> +	struct bd7xx28_rtc_data rtc_data;
>  	int ret;
>  
> +	pr_debug("%s called\n", __func__);
> +
>  	/* read the RTC date and time registers all at once */
> -	ret = regmap_bulk_read(bd70528->regmap,
> -			       BD70528_REG_RTC_START, &rtc_data,
> +	ret = regmap_bulk_read(bd7xx28->regmap,
> +			       r->reg_time_start, &rtc_data,
>  			       sizeof(rtc_data));
>  	if (ret) {
>  		dev_err(dev, "Failed to read RTC time (err %d)\n", ret);
> @@ -353,55 +464,94 @@ static int bd70528_get_time(struct device *dev, struct rtc_time *t)
>  	return 0;
>  }
>  
> -static int bd70528_alm_enable(struct device *dev, unsigned int enabled)
> +static int bd70528_alm_enable(struct bd7xx28_rtc *r, unsigned int enabled)
>  {
>  	int ret;
> -	unsigned int enableval = BD70528_MASK_ALM_EN;
> -	struct bd70528_rtc *r = dev_get_drvdata(dev);
> +	unsigned int enableval = ROHM_BD1_MASK_ALM_EN;
>  
>  	if (enabled)
>  		enableval = 0;
>  
>  	bd70528_wdt_lock(r->mfd);
> -	ret = bd70528_set_wake(r->mfd, enabled, NULL);
> +	ret = bd70528_set_wake(r->mfd, !enableval, NULL);
>  	if (ret) {
> -		dev_err(dev, "Failed to change wake state\n");
> +		dev_err(r->dev, "Failed to change wake state\n");
>  		goto out_unlock;
>  	}
>  	ret = regmap_update_bits(r->mfd->regmap, BD70528_REG_RTC_ALM_MASK,
> -				 BD70528_MASK_ALM_EN, enableval);
> +				 ROHM_BD1_MASK_ALM_EN, enableval);
>  	if (ret)
> -		dev_err(dev, "Failed to change alarm state\n");
> +		dev_err(r->dev, "Failed to change alarm state\n");
>  
>  out_unlock:
>  	bd70528_wdt_unlock(r->mfd);
>  	return ret;
>  }
>  
> -static const struct rtc_class_ops bd70528_rtc_ops = {
> -	.read_time		= bd70528_get_time,
> -	.set_time		= bd70528_set_time,
> -	.read_alarm		= bd70528_read_alarm,
> -	.set_alarm		= bd70528_set_alarm,
> -	.alarm_irq_enable	= bd70528_alm_enable,
> +static int bd71828_alm_enable(struct bd7xx28_rtc *r, unsigned int enabled)
> +{
> +	int ret;
> +	unsigned int enableval = ROHM_BD1_MASK_ALM_EN;
> +
> +	if (!enabled)
> +		enableval = 0;
> +
> +	pr_debug("%s called (enabled=0x%x)\n", __func__, enabled);
> +	ret = regmap_update_bits(r->mfd->regmap, BD71828_REG_RTC_ALM0_MASK,
> +				 ROHM_BD1_MASK_ALM_EN, enableval);
> +	if (ret)
> +		dev_err(r->dev, "Failed to change alarm state\n");
> +
> +	pr_debug("%s: Wrote alm mask reg addr 0x%x val 0x%x\n",
> +		__func__, BD71828_REG_RTC_ALM0_MASK, enableval);
> +
> +	return ret;
> +}
> +
> +static int bd7xx28_alm_enable(struct device *dev, unsigned int enabled)
> +{
> +	struct bd7xx28_rtc *r = dev_get_drvdata(dev);
> +
> +	switch (r->mfd->chip_type) {
> +	case ROHM_CHIP_TYPE_BD70528:
> +		return bd70528_alm_enable(r, enabled);
> +	case ROHM_CHIP_TYPE_BD71828:
> +		return bd71828_alm_enable(r, enabled);
> +	default:
> +		dev_err(dev, "Unknown RTC chip\n");
> +	}
> +
> +	return -ENOENT;
> +}
> +
> +static const struct rtc_class_ops bd7xx28_rtc_ops = {
> +	.read_time		= bd7xx28_get_time,
> +	.set_time		= bd7xx28_set_time,
> +	.read_alarm		= bd7xx28_read_alarm,
> +	.set_alarm		= bd7xx28_set_alarm,
> +	.alarm_irq_enable	= bd7xx28_alm_enable,
>  };
>  
>  static irqreturn_t alm_hndlr(int irq, void *data)
>  {
>  	struct rtc_device *rtc = data;
>  
> +	pr_debug("bd71828 RTC IRQ fired\n");
>  	rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF | RTC_PF);
>  	return IRQ_HANDLED;
>  }
>  
> -static int bd70528_probe(struct platform_device *pdev)
> +static int bd7xx28_probe(struct platform_device *pdev)
>  {
> -	struct bd70528_rtc *bd_rtc;
> +	struct bd7xx28_rtc *bd_rtc;
>  	struct rohm_regmap_dev *mfd;
> +	const char *irq_name;
>  	int ret;
>  	struct rtc_device *rtc;
>  	int irq;
>  	unsigned int hr;
> +	bool enable_main_irq = false;
> +	u8 hour_reg;
>  
>  	mfd = dev_get_drvdata(pdev->dev.parent);
>  	if (!mfd) {
> @@ -415,7 +565,25 @@ static int bd70528_probe(struct platform_device *pdev)
>  	bd_rtc->mfd = mfd;
>  	bd_rtc->dev = &pdev->dev;
>  
> -	irq = platform_get_irq_byname(pdev, "bd70528-rtc-alm");
> +	switch (mfd->chip_type) {
> +	case ROHM_CHIP_TYPE_BD70528:
> +		irq_name = "bd70528-rtc-alm";
> +		bd_rtc->has_rtc_timers = true;
> +		bd_rtc->reg_time_start = BD70528_REG_RTC_START;
> +		hour_reg = BD70528_REG_RTC_HOUR;
> +		enable_main_irq = true;
> +		break;
> +	case ROHM_CHIP_TYPE_BD71828:
> +		irq_name = "bd71828-rtc-alm-0";
> +		bd_rtc->reg_time_start = BD71828_REG_RTC_START;
> +		hour_reg = BD71828_REG_RTC_HOUR;
> +		break;
> +	default:
> +		dev_err(&pdev->dev, "Unknown chip\n");
> +		return -ENOENT;
> +	}
> +
> +	irq = platform_get_irq_byname(pdev, irq_name);
>  
>  	if (irq < 0) {
>  		dev_err(&pdev->dev, "Failed to get irq\n");
> @@ -424,20 +592,20 @@ static int bd70528_probe(struct platform_device *pdev)
>  
>  	platform_set_drvdata(pdev, bd_rtc);
>  
> -	ret = regmap_read(mfd->regmap, BD70528_REG_RTC_HOUR, &hr);
> +	ret = regmap_read(mfd->regmap, hour_reg, &hr);
>  
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to reag RTC clock\n");
>  		return ret;
>  	}
>  
> -	if (!(hr & BD70528_MASK_RTC_HOUR_24H)) {
> +	if (!(hr & ROHM_BD1_MASK_RTC_HOUR_24H)) {
>  		struct rtc_time t;
>  
> -		ret = bd70528_get_time(&pdev->dev, &t);
> +		ret = bd7xx28_get_time(&pdev->dev, &t);
>  
>  		if (!ret)
> -			ret = bd70528_set_time(&pdev->dev, &t);
> +			ret = bd7xx28_set_time(&pdev->dev, &t);
>  
>  		if (ret) {
>  			dev_err(&pdev->dev,
> @@ -457,7 +625,7 @@ static int bd70528_probe(struct platform_device *pdev)
>  
>  	rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
>  	rtc->range_max = RTC_TIMESTAMP_END_2099;
> -	rtc->ops = &bd70528_rtc_ops;
> +	rtc->ops = &bd7xx28_rtc_ops;
>  
>  	/* Request alarm IRQ prior to registerig the RTC */
>  	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, &alm_hndlr,
> @@ -471,12 +639,15 @@ static int bd70528_probe(struct platform_device *pdev)
>  	 *  leave them enabled as irq-controller should disable irqs
>  	 *  from sub-registers when IRQ is disabled or freed.
>  	 */
> -	ret = regmap_update_bits(mfd->regmap,
> +	if (enable_main_irq) {
> +		ret = regmap_update_bits(mfd->regmap,
>  				 BD70528_REG_INT_MAIN_MASK,
>  				 BD70528_INT_RTC_MASK, 0);
> -	if (ret) {
> -		dev_err(&pdev->dev, "Failed to enable RTC interrupts\n");
> -		return ret;
> +		if (ret) {
> +			dev_err(&pdev->dev,
> +				"Failed to enable RTC interrupts\n");
> +			return ret;
> +		}
>  	}
>  
>  	ret = rtc_register_device(rtc);
> @@ -490,11 +661,11 @@ static struct platform_driver bd70528_rtc = {
>  	.driver = {
>  		.name = "bd70528-rtc"
>  	},
> -	.probe = bd70528_probe,
> +	.probe = bd7xx28_probe,
>  };
>  
>  module_platform_driver(bd70528_rtc);
>  
>  MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
> -MODULE_DESCRIPTION("BD70528 RTC driver");
> +MODULE_DESCRIPTION("ROHM BD70528 and BD71828 PMIC RTC driver");
>  MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/rohm-bd70528.h b/include/linux/mfd/rohm-bd70528.h
> index 1013e60c5b25..8d8c0e0b2df6 100644
> --- a/include/linux/mfd/rohm-bd70528.h
> +++ b/include/linux/mfd/rohm-bd70528.h
> @@ -8,6 +8,7 @@
>  #include <linux/device.h>
>  #include <linux/mfd/rohm-generic.h>
>  #include <linux/regmap.h>
> +#include <linux/mfd/rohm-shared.h>
>  
>  enum {
>  	BD70528_BUCK1,
> @@ -313,16 +314,6 @@ enum {
>  
>  /* RTC masks to mask out reserved bits */
>  
> -#define BD70528_MASK_RTC_SEC		0x7f
> -#define BD70528_MASK_RTC_MINUTE		0x7f
> -#define BD70528_MASK_RTC_HOUR_24H	0x80
> -#define BD70528_MASK_RTC_HOUR_PM	0x20
> -#define BD70528_MASK_RTC_HOUR		0x1f
> -#define BD70528_MASK_RTC_DAY		0x3f
> -#define BD70528_MASK_RTC_WEEK		0x07
> -#define BD70528_MASK_RTC_MONTH		0x1f
> -#define BD70528_MASK_RTC_YEAR		0xff
> -#define BD70528_MASK_RTC_COUNT_L	0x7f
>  
>  #define BD70528_MASK_ELAPSED_TIMER_EN	0x1
>  /* Mask second, min and hour fields
> @@ -332,7 +323,6 @@ enum {
>   * wake-up we limit ALM to 24H and only
>   * unmask sec, min and hour
>   */
> -#define BD70528_MASK_ALM_EN		0x7
>  #define BD70528_MASK_WAKE_EN		0x1
>  
>  /* WDT masks */
> diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h
> index bbbd4f118550..bd9dfd53759d 100644
> --- a/include/linux/mfd/rohm-bd71828.h
> +++ b/include/linux/mfd/rohm-bd71828.h
> @@ -5,6 +5,7 @@
>  #define __LINUX_MFD_BD71828_H__
>  
>  #include <linux/mfd/rohm-generic.h>
> +#include <linux/mfd/rohm-shared.h>
>  
>  /* Regulator IDs */
>  enum {
> @@ -160,6 +161,7 @@ enum {
>  #define BD71828_REG_RTC_YEAR		0x52
>  
>  #define BD71828_REG_RTC_ALM0_SEC	0x53
> +#define BD71828_REG_RTC_ALM_START	BD71828_REG_RTC_ALM0_SEC
>  #define BD71828_REG_RTC_ALM0_MINUTE	0x54
>  #define BD71828_REG_RTC_ALM0_HOUR	0x55
>  #define BD71828_REG_RTC_ALM0_WEEK	0x56
> @@ -178,6 +180,7 @@ enum {
>  #define BD71828_REG_RTC_ALM1_MASK	0x62
>  
>  #define BD71828_REG_RTC_ALM2		0x63
> +#define BD71828_REG_RTC_START		BD71828_REG_RTC_SEC
>  
>  /* Charger/Battey */
>  #define BD71828_REG_CHG_STATE		0x65
> @@ -207,7 +210,6 @@ enum {
>  #define BD71828_REG_INT_MASK_TEMP	0xdd
>  #define BD71828_REG_INT_MASK_RTC	0xde
>  
> -
>  #define BD71828_REG_INT_MAIN		0xdf
>  #define BD71828_REG_INT_BUCK		0xe0
>  #define BD71828_REG_INT_DCIN1		0xe1
> diff --git a/include/linux/mfd/rohm-shared.h b/include/linux/mfd/rohm-shared.h
> new file mode 100644
> index 000000000000..5f61fa81d6a2
> --- /dev/null
> +++ b/include/linux/mfd/rohm-shared.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/* Copyright (C) 2018 ROHM Semiconductors */
> +
> +
> +/*
> + * RTC definitions shared between
> + *
> + * BD70528
> + * and BD71828
> + */
> +
> +#define ROHM_BD1_MASK_RTC_SEC		0x7f
> +#define ROHM_BD1_MASK_RTC_MINUTE	0x7f
> +#define ROHM_BD1_MASK_RTC_HOUR_24H	0x80
> +#define ROHM_BD1_MASK_RTC_HOUR_PM	0x20
> +#define ROHM_BD1_MASK_RTC_HOUR		0x3f
> +#define ROHM_BD1_MASK_RTC_DAY		0x3f
> +#define ROHM_BD1_MASK_RTC_WEEK		0x07
> +#define ROHM_BD1_MASK_RTC_MONTH		0x1f
> +#define ROHM_BD1_MASK_RTC_YEAR		0xff
> +#define ROHM_BD1_MASK_ALM_EN		0x7
> +

All that renaming is distracting and useless. Please resubmit without
renaming defines, structs and functions to make it easier to review.


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver
  2019-10-17 10:12   ` Alexandre Belloni
@ 2019-10-17 10:36     ` Vaittinen, Matti
  2019-10-17 10:48       ` Alexandre Belloni
  0 siblings, 1 reply; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-17 10:36 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: dmurphy, linux-leds, linux-rtc, linux-gpio, linux-kernel,
	mturquette, mazziesaccount, devicetree, jacek.anaszewski,
	a.zummo, linus.walleij, lgirdwood, mark.rutland, robh+dt,
	bgolaszewski, linux-clk, pavel, sboyd, broonie, lee.jones

Hello Alexandre,

Thanks for quick check! I'll be off for the rest of the week but I will
re-work this patch at next week :) I agree with you regarding most of
the comments.

> > +
> > +
> > +/*
> > + * RTC definitions shared between
> > + *
> > + * BD70528
> > + * and BD71828
> > + */
> > +
> > +#define ROHM_BD1_MASK_RTC_SEC		0x7f
> > +#define ROHM_BD1_MASK_RTC_MINUTE	0x7f
> > +#define ROHM_BD1_MASK_RTC_HOUR_24H	0x80
> > +#define ROHM_BD1_MASK_RTC_HOUR_PM	0x20
> > +#define ROHM_BD1_MASK_RTC_HOUR		0x3f
> > +#define ROHM_BD1_MASK_RTC_DAY		0x3f
> > +#define ROHM_BD1_MASK_RTC_WEEK		0x07
> > +#define ROHM_BD1_MASK_RTC_MONTH		0x1f
> > +#define ROHM_BD1_MASK_RTC_YEAR		0xff
> > +#define ROHM_BD1_MASK_ALM_EN		0x7
> > +
> 
> All that renaming is distracting and useless. Please resubmit without
> renaming defines, structs and functions to make it easier to review.

I would prefer renaming because it makes it clearly visible which
defines/structs/functions are common for both PMICs and which are PMIC
specific. But I really understand the problem of spotting real changes.
Would it be Ok if I did renaming in separate patch which does not bring
in any other changes - and then the functional changes in separate
patch?

Best Regards
	Matti Vaittinen

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

* Re: [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver
  2019-10-17 10:36     ` Vaittinen, Matti
@ 2019-10-17 10:48       ` Alexandre Belloni
  2019-10-21  5:29         ` Vaittinen, Matti
  2019-10-23 10:27         ` Vaittinen, Matti
  0 siblings, 2 replies; 43+ messages in thread
From: Alexandre Belloni @ 2019-10-17 10:48 UTC (permalink / raw)
  To: Vaittinen, Matti
  Cc: dmurphy, linux-leds, linux-rtc, linux-gpio, linux-kernel,
	mturquette, mazziesaccount, devicetree, jacek.anaszewski,
	a.zummo, linus.walleij, lgirdwood, mark.rutland, robh+dt,
	bgolaszewski, linux-clk, pavel, sboyd, broonie, lee.jones

On 17/10/2019 10:36:44+0000, Vaittinen, Matti wrote:
> Hello Alexandre,
> 
> Thanks for quick check! I'll be off for the rest of the week but I will
> re-work this patch at next week :) I agree with you regarding most of
> the comments.
> 
> > > +
> > > +
> > > +/*
> > > + * RTC definitions shared between
> > > + *
> > > + * BD70528
> > > + * and BD71828
> > > + */
> > > +
> > > +#define ROHM_BD1_MASK_RTC_SEC		0x7f
> > > +#define ROHM_BD1_MASK_RTC_MINUTE	0x7f
> > > +#define ROHM_BD1_MASK_RTC_HOUR_24H	0x80
> > > +#define ROHM_BD1_MASK_RTC_HOUR_PM	0x20
> > > +#define ROHM_BD1_MASK_RTC_HOUR		0x3f
> > > +#define ROHM_BD1_MASK_RTC_DAY		0x3f
> > > +#define ROHM_BD1_MASK_RTC_WEEK		0x07
> > > +#define ROHM_BD1_MASK_RTC_MONTH		0x1f
> > > +#define ROHM_BD1_MASK_RTC_YEAR		0xff
> > > +#define ROHM_BD1_MASK_ALM_EN		0x7
> > > +
> > 
> > All that renaming is distracting and useless. Please resubmit without
> > renaming defines, structs and functions to make it easier to review.
> 
> I would prefer renaming because it makes it clearly visible which
> defines/structs/functions are common for both PMICs and which are PMIC
> specific. But I really understand the problem of spotting real changes.
> Would it be Ok if I did renaming in separate patch which does not bring
> in any other changes - and then the functional changes in separate
> patch?
> 

No, unless you can guarantee that all future PMICs from rohm matching
the wildcard will use this driver.

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [RFC PATCH 10/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs
  2019-10-17  9:53 ` [RFC PATCH 10/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs Matti Vaittinen
@ 2019-10-17 12:45   ` Bartosz Golaszewski
  2019-10-21  7:00     ` Vaittinen, Matti
  0 siblings, 1 reply; 43+ messages in thread
From: Bartosz Golaszewski @ 2019-10-17 12:45 UTC (permalink / raw)
  To: Matti Vaittinen
  Cc: Matti Vaittinen, Lee Jones, Rob Herring, Mark Rutland,
	Liam Girdwood, Mark Brown, Michael Turquette, Stephen Boyd,
	Linus Walleij, Jacek Anaszewski, Pavel Machek, Dan Murphy,
	Alessandro Zummo, Alexandre Belloni, linux-devicetree, LKML,
	linux-clk, linux-gpio, Linux LED Subsystem, linux-rtc

czw., 17 paź 2019 o 11:53 Matti Vaittinen
<matti.vaittinen@fi.rohmeurope.com> napisał(a):
>
> ROHM BD71828 PMIC contains 4 pins which can be configured by OTP
> to be used for general purposes. First 3 can be used as outputs
> and 4.th pin can be used as input. Allow them to be controlled
> via GPIO framework.
>
> The driver assumes all of the pins are configured as GPIOs and
> rusts that the reserved pins in other OTP configurations are
> excluded from control using "gpio-reserved-ranges" device tree
> property (or left untouched by GPIO users).
>
> Typical use for 4.th pin (input) is to use it as HALL sensor
> input so that this pin state is toggled when HALL sensor detects
> LID position change (from close to open or open to close). PMIC
> HW implements some extra logic which allows PMIC to power-up the
> system when this pin is toggled. Please see the data sheet for
> details of GPIO options which can be selcted by OTP settings.
>
> Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> ---
>  drivers/gpio/Kconfig        |  12 +++
>  drivers/gpio/Makefile       |   1 +
>  drivers/gpio/gpio-bd71828.c | 161 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 174 insertions(+)
>  create mode 100644 drivers/gpio/gpio-bd71828.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index bb13c266c329..fb0a099de961 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -986,6 +986,18 @@ config GPIO_BD70528
>           This driver can also be built as a module. If so, the module
>           will be called gpio-bd70528.
>
> +config GPIO_BD71828
> +       tristate "ROHM BD71828 GPIO support"
> +       depends on MFD_ROHM_BD71828
> +       help
> +         Support for GPIOs on ROHM BD71828 PMIC. There are three GPIOs
> +         available on the ROHM PMIC in total. The GPIOs are limited to
> +         outputs only and pins must be configured to GPIO outputs by
> +         OTP. Enable this only if you want to use these pins as outputs.
> +
> +         This driver can also be built as a module. If so, the module
> +         will be called gpio-bd71828.
> +
>  config GPIO_BD9571MWV
>         tristate "ROHM BD9571 GPIO support"
>         depends on MFD_BD9571MWV
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index a4e91175c708..b11932844768 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_GPIO_ASPEED)             += gpio-aspeed.o
>  obj-$(CONFIG_GPIO_ATH79)               += gpio-ath79.o
>  obj-$(CONFIG_GPIO_BCM_KONA)            += gpio-bcm-kona.o
>  obj-$(CONFIG_GPIO_BD70528)             += gpio-bd70528.o
> +obj-$(CONFIG_GPIO_BD71828)             += gpio-bd71828.o
>  obj-$(CONFIG_GPIO_BD9571MWV)           += gpio-bd9571mwv.o
>  obj-$(CONFIG_GPIO_BRCMSTB)             += gpio-brcmstb.o
>  obj-$(CONFIG_GPIO_BT8XX)               += gpio-bt8xx.o
> diff --git a/drivers/gpio/gpio-bd71828.c b/drivers/gpio/gpio-bd71828.c
> new file mode 100644
> index 000000000000..3cf3890a24c4
> --- /dev/null
> +++ b/drivers/gpio/gpio-bd71828.c
> @@ -0,0 +1,161 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (C) 2018 ROHM Semiconductors
> +// gpio-bd71828.c ROHM BD71828 gpio driver

I don't think the name of the source file is needed here.

> +
> +#include <linux/gpio/driver.h>
> +#include <linux/mfd/rohm-bd71828.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#define OUT 0
> +#define IN 1

If you really want to define those, please use a common prefix for all
symbols in the driver.

> +#define GPIO_OUT_REG(off) (BD71828_REG_GPIO_CTRL1 + (off))
> +#define HALL_GPIO_OFFSET 3
> +
> +struct bd71828_gpio {
> +       struct rohm_regmap_dev chip;
> +       struct gpio_chip gpio;
> +};
> +
> +static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int offset,
> +                            int value)
> +{
> +       int ret;
> +       struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
> +       u8 val = (value) ? BD71828_GPIO_OUT_HI : BD71828_GPIO_OUT_LO;
> +
> +       if (offset == HALL_GPIO_OFFSET)
> +               return;

Can you add a comment here saying that this pin can only be used as
input? Otherwise this information is only available in the commit
message.

> +
> +       ret = regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
> +                                BD71828_GPIO_OUT_MASK, val);
> +       if (ret)
> +               dev_err(bdgpio->chip.dev, "Could not set gpio to %d\n", value);
> +}
> +
> +static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> +       int ret;
> +       unsigned int val;
> +       struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
> +
> +       if (offset == HALL_GPIO_OFFSET)
> +               ret = regmap_read(bdgpio->chip.regmap, BD71828_REG_IO_STAT,
> +                                 &val);
> +       else
> +               ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
> +                                 &val);
> +       if (!ret)
> +               ret = (val & BD71828_GPIO_OUT_MASK);
> +
> +       return ret;
> +}
> +
> +static int bd71828_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
> +                                  unsigned long config)
> +{
> +       struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
> +
> +       if (offset == HALL_GPIO_OFFSET)
> +               return -ENOTSUPP;
> +
> +       switch (pinconf_to_config_param(config)) {
> +       case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +               return regmap_update_bits(bdgpio->chip.regmap,
> +                                         GPIO_OUT_REG(offset),
> +                                         BD71828_GPIO_DRIVE_MASK,
> +                                         BD71828_GPIO_OPEN_DRAIN);
> +       case PIN_CONFIG_DRIVE_PUSH_PULL:
> +               return regmap_update_bits(bdgpio->chip.regmap,
> +                                         GPIO_OUT_REG(offset),
> +                                         BD71828_GPIO_DRIVE_MASK,
> +                                         BD71828_GPIO_PUSH_PULL);
> +       default:
> +               break;
> +       }
> +       return -ENOTSUPP;
> +}
> +
> +static int bd71828_get_direction(struct gpio_chip *chip, unsigned int offset)
> +{
> +       /*
> +        * Pin usage is selected by OTP data. We can't read it runtime. Hence
> +        * we trust that if the pin is not excluded by "gpio-reserved-ranges"
> +        * the OTP configuration is set to OUT. (Other pins but HALL input pin
> +        * on BD71828 can't really be used for general purpose input - input
> +        * states are used for specific cases like regulator control or
> +        * PMIC_ON_REQ.
> +        */
> +       if (offset == HALL_GPIO_OFFSET)
> +               return IN;
> +
> +       return OUT;
> +}
> +
> +static int bd71828_gpio_parse_dt(struct device *dev,
> +                                struct bd71828_gpio *bdgpio)
> +{
> +       /*
> +        * TBD: See if we need some implementation to mark some PINs as
> +        * not controllable based on DT info or if core can handle
> +        * "gpio-reserved-ranges" and exclude them from control
> +        */
> +       return 0;
> +}

Please don't implement empty functions. Just add this comment next to
gpiochip's initialization...

> +
> +static int bd71828_probe(struct platform_device *pdev)
> +{
> +       struct bd71828_gpio *bdgpio;
> +       struct rohm_regmap_dev *bd71828;
> +       int ret;
> +
> +       bd71828 = dev_get_drvdata(pdev->dev.parent);
> +       if (!bd71828) {
> +               dev_err(&pdev->dev, "No MFD driver data\n");
> +               return -EINVAL;
> +       }
> +
> +       bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio),
> +                             GFP_KERNEL);
> +       if (!bdgpio)
> +               return -ENOMEM;
> +
> +       ret = bd71828_gpio_parse_dt(pdev->dev.parent, bdgpio);

... and remove this call.

> +
> +       bdgpio->chip.dev = &pdev->dev;
> +       bdgpio->gpio.parent = pdev->dev.parent;
> +       bdgpio->gpio.label = "bd71828-gpio";
> +       bdgpio->gpio.owner = THIS_MODULE;
> +       bdgpio->gpio.get_direction = bd71828_get_direction;
> +       bdgpio->gpio.set_config = bd71828_gpio_set_config;
> +       bdgpio->gpio.can_sleep = true;
> +       bdgpio->gpio.get = bd71828_gpio_get;
> +       bdgpio->gpio.set = bd71828_gpio_set;

Not implementing direction_output() and direction_input() here will
results in warnings from the GPIO framework: for instance you
implement set() but not direction_output(). I'd say: just add those
callbacks and return an error if they're called for invalid lines (for
instance: direction_output() being called for line 3).

> +       bdgpio->gpio.base = -1;
> +       bdgpio->gpio.ngpio = 4;
> +#ifdef CONFIG_OF_GPIO

This is not needed - for CONFIG_OF_GPIO disabled the parent of_node
will be NULL.

> +       bdgpio->gpio.of_node = pdev->dev.parent->of_node;
> +#endif
> +       bdgpio->chip.regmap = bd71828->regmap;
> +
> +       ret = devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio,
> +                                    bdgpio);
> +       if (ret)
> +               dev_err(&pdev->dev, "gpio_init: Failed to add bd71828-gpio\n");

Since there aren't many places where this function can fail, you can
directly return devm_gpiochip_add_data() here.

> +
> +       return ret;
> +}
> +
> +static struct platform_driver bd71828_gpio = {
> +       .driver = {
> +               .name = "bd71828-gpio"
> +       },
> +       .probe = bd71828_probe,
> +};
> +
> +module_platform_driver(bd71828_gpio);
> +
> +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
> +MODULE_DESCRIPTION("BD71828 voltage regulator driver");
> +MODULE_LICENSE("GPL");

Don't you need a MODULE_ALIAS() here since this is an MFD sub-module?

Bart

> --
> 2.21.0
>
>
> --
> Matti Vaittinen, Linux device drivers
> ROHM Semiconductors, Finland SWDC
> Kiviharjunlenkki 1E
> 90220 OULU
> FINLAND
>
> ~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
> Simon says - in Latin please.
> ~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
> Thanks to Simon Glass for the translation =]

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-17  9:53 ` [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC Matti Vaittinen
@ 2019-10-17 14:04   ` Dan Murphy
  2019-10-17 14:28     ` Alexandre Belloni
  2019-10-21  8:00     ` Vaittinen, Matti
  0 siblings, 2 replies; 43+ messages in thread
From: Dan Murphy @ 2019-10-17 14:04 UTC (permalink / raw)
  To: Matti Vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

Matt

On 10/17/19 4:53 AM, Matti Vaittinen wrote:
> ROHM BD71828 power management IC has two LED outputs for charge status
> and button pressing indications. The LED outputs can also be forced
> bs SW so add driver allowing to use these LEDs for other indications
s/bs/by
> as well.
>
> Leds are controlled by SW using 'Force ON' bits. Please note the
> constrains mentioned in data-sheet:
> 1. If one LED is forced ON - then also the other LED is forced.
> 	=> You can't use SW control to force ON one LED and allow HW
> 	   to control the other.
> 2. You can't force both LEDs OFF. If the FORCE bit for both LED's is
>     zero, then LEDs are controlled by HW and indicate button/charger
>     states as explained in data-sheet.
>
> Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> ---
>   drivers/leds/Kconfig        | 10 ++++
>   drivers/leds/Makefile       |  1 +
>   drivers/leds/leds-bd71828.c | 97 +++++++++++++++++++++++++++++++++++++
>   3 files changed, 108 insertions(+)
>   create mode 100644 drivers/leds/leds-bd71828.c
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index b0fdeef10bd9..ec59f28bcb39 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -529,6 +529,16 @@ config LEDS_BD2802
>   	  This option enables support for BD2802GU RGB LED driver chips
>   	  accessed via the I2C bus.
>   
> +config LEDS_BD71828
> +	tristate "LED driver for LED pins on ROHM BD71828 PMIC"
> +	depends on LEDS_CLASS
doesn't this have a dependency on MFD_ROHM_BD71828
> +	depends on I2C
> +	help
> +	  This option enables support for LED outputs located on ROHM
> +	   BD71828 power management IC. ROHM BD71828 has two led output pins
> +	   which can be left to indicate HW states or controlled by SW. Say
> +	   yes here if you want to enable SW control for these LEDs.
> +

Add module statement


>   config LEDS_INTEL_SS4200
>   	tristate "LED driver for Intel NAS SS4200 series"
>   	depends on LEDS_CLASS
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index 41fb073a39c1..2a8f6a8e4c7c 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -15,6 +15,7 @@ obj-$(CONFIG_LEDS_AN30259A)		+= leds-an30259a.o
>   obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
>   obj-$(CONFIG_LEDS_BCM6358)		+= leds-bcm6358.o
>   obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
> +obj-$(CONFIG_LEDS_BD71828)		+= leds-bd71828.o
>   obj-$(CONFIG_LEDS_CPCAP)		+= leds-cpcap.o
>   obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
>   obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
> diff --git a/drivers/leds/leds-bd71828.c b/drivers/leds/leds-bd71828.c
> new file mode 100644
> index 000000000000..2427619444f5
> --- /dev/null
> +++ b/drivers/leds/leds-bd71828.c
> @@ -0,0 +1,97 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (C) 2019 ROHM Semiconductors
> +
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/mfd/rohm-bd71828.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ? \
> +	container_of((l), struct bd71828_leds, green) : \
> +	container_of((l), struct bd71828_leds, amber))

I don't think we should be defining the color as the variable. The 
outputs can drive any color LED.


> +
> +enum {
> +	ID_GREEN_LED,
> +	ID_AMBER_LED,
> +	ID_NMBR_OF,
> +};
> +

Please use the color_id in linux/include/dt-bindings/leds/common.h


> +struct bd71828_led {
> +	int id;
> +	struct led_classdev l;
> +	u8 force_mask;
> +};
> +
> +struct bd71828_leds {
> +	struct rohm_regmap_dev *bd71828;
> +	struct bd71828_led green;
> +	struct bd71828_led amber;
> +};
> +
> +static int bd71828_led_brightness_set(struct led_classdev *led_cdev,
> +				      enum led_brightness value)
> +{
> +	struct bd71828_led *l = container_of(led_cdev, struct bd71828_led, l);
> +	struct bd71828_leds *data;
> +	unsigned int val = BD71828_LED_OFF;
> +
> +	data = BD71828_LED_TO_DATA(l);
> +	if (value != LED_OFF)
> +		val = BD71828_LED_ON;
> +
> +	return regmap_update_bits(data->bd71828->regmap, BD71828_REG_LED_CTRL,
> +			    l->force_mask, val);
> +}
> +
> +static int bd71828_led_probe(struct platform_device *pdev)
> +{
> +	struct rohm_regmap_dev *bd71828;
> +	struct bd71828_leds *l;
> +	struct bd71828_led *g, *a;
> +	static const char *GNAME = "bd71828-green-led";
> +	static const char *ANAME = "bd71828-amber-led";
The LED class creates the name it can get it from the DT.
> +	int ret;
> +
> +	pr_info("bd71828 LED driver probed\n");
> +
> +	bd71828 = dev_get_drvdata(pdev->dev.parent);
> +	l = devm_kzalloc(&pdev->dev, sizeof(*l), GFP_KERNEL);
> +	if (!l)
> +		return -ENOMEM;
> +	l->bd71828 = bd71828;
> +	a = &l->amber;
> +	g = &l->green;
> +	a->id = ID_AMBER_LED;
> +	g->id = ID_GREEN_LED;
> +	a->force_mask = BD71828_MASK_LED_AMBER;
> +	g->force_mask = BD71828_MASK_LED_GREEN;
> +
> +	a->l.name = ANAME;
> +	g->l.name = GNAME;
> +	a->l.brightness_set_blocking = bd71828_led_brightness_set;
> +	g->l.brightness_set_blocking = bd71828_led_brightness_set;
> +
> +	ret = devm_led_classdev_register(&pdev->dev, &g->l);
> +	if (ret)
> +		return ret;
> +
> +	return devm_led_classdev_register(&pdev->dev, &a->l);
> +}
> +

This looks different.  Not sure why you register both LEDs in this probe.

You can use the DT to define both LEDs and then each will be probed and 
registered separately.

This is how it is commonly done.

You can reference the LM36274 led driver as this is a MFD device to the 
ti-lmu.c in the MFD directory.


> +static struct platform_driver bd71828_led_driver = {
> +	.driver = {
> +		.name  = "bd71828-led",
> +	},
> +	.probe  = bd71828_led_probe,
> +};
> +
> +module_platform_driver(bd71828_led_driver);
> +
> +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
> +MODULE_DESCRIPTION("ROHM BD71828 LED driver");
> +MODULE_LICENSE("GPL");
GPL v2

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

* Re: [RFC PATCH 12/13] dt-bindings: mfd: Document ROHM BD71282 bindings
  2019-10-17  9:55 ` [RFC PATCH 12/13] dt-bindings: mfd: Document ROHM BD71282 bindings Matti Vaittinen
@ 2019-10-17 14:18   ` Dan Murphy
  2019-10-21  8:03     ` Vaittinen, Matti
  0 siblings, 1 reply; 43+ messages in thread
From: Dan Murphy @ 2019-10-17 14:18 UTC (permalink / raw)
  To: Matti Vaittinen, mazziesaccount
  Cc: Lee Jones, Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Linus Walleij,
	Bartosz Golaszewski, Jacek Anaszewski, Pavel Machek,
	Alessandro Zummo, Alexandre Belloni, devicetree, linux-kernel,
	linux-clk, linux-gpio, linux-leds, linux-rtc

Matt

On 10/17/19 4:55 AM, Matti Vaittinen wrote:
> ROHM BD71828 Power management IC integrates 7 buck converters, 7 LDOs,
> a real-time clock (RTC), 3 GPO/regulator control pins, HALL input pin
> and a 32.768 kHz clock gate.
>
> Document the dt bindings drivers are using.

This should be patch 1 of X


> Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> ---
>   .../bindings/mfd/rohm,bd71828-pmic.txt        | 180 ++++++++++++++++++
>   1 file changed, 180 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.txt b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.txt
> new file mode 100644
> index 000000000000..125efa9f3de0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.txt
> @@ -0,0 +1,180 @@
> +* ROHM BD71828 Power Management Integrated Circuit bindings
> +
> +BD71828GW is a single-chip power management IC for battery-powered portable
> +devices. The IC integrates 7 buck converters, 7 LDOs, and a 1500 mA single-cell
> +linear charger. Also included is a Coulomb counter, a real-time clock (RTC),
> +and a 32.768 kHz clock gate.
> +
> +Required properties:
> + - compatible			: Should be "rohm,bd71828".
> + - reg				: I2C slave address.
> + - interrupt-parent		: Phandle to the parent interrupt controller.
> + - interrupts			: The interrupt line the device is connected to.
> + - clocks			: The parent clock connected to PMIC.
> + - #clock-cells			: Should be 0.
> + - regulators			: List of child nodes that specify the
> +				  regulators. Please see
> +				  ../regulator/rohm,bd71828-regulator.txt
> + - gpio-controller		: To indicate BD71828 acts as a GPIO controller.
> + - #gpio-cells			: Should be 2. The first cell is the pin number
> +				  and the second cell is used to specify flags.
> +				  See ../gpio/gpio.txt for more information.
> +
> +The BD71828 RUN state is divided into 4 configurable run-levels named RUN0,
> +RUN1, RUN2 and RUN3. Bucks 1, 2, 6 and 7 can be either controlled individually
> +via I2C, or some/all of them can be bound to run-levels and controlled as a
> +group. If bucks are controlled individually these run-levels are ignored. See
> +../regulator/rohm,bd71828-regulator.txt for how to define regulator voltages
> +for run-levels. Run-levels can be changed by I2C or GPIO depending on PMIC's OTP
> +configuration.
> +
> +Optional properties:
> +- clock-output-names		: Should contain name for output clock.
> +- rohm,dvs-vsel-gpios		: GPIOs used to control PMIC run-levels. Should
> +				  describe two GPIOs. (See run-level control in
> +				  data-sheet). If this property is omitted but
> +				  some bucks are marked to be controlled by
> +				  run-levels - then OTP option allowing
> +				  run-level control via I2C is assumed.
> +- gpio-reserved-ranges		: Usage of GPIO pins can be changed via OTP.
> +				  This property can be used to mark the pins
> +				  which should not be configured for GPIO.
> +				  Please see the ../gpio/gpio.txt for more
> +				  information.
> +
> +Example:
> +
> +        /* external oscillator node */
> +        osc: oscillator {
> +                compatible = "fixed-clock";
> +                #clock-cells = <1>;
> +                clock-frequency  = <32768>;
> +                clock-output-names = "osc";
> +        };
> +
> +	pmic: pmic@4b {
> +		compatible = "rohm,bd71828";
> +		reg = <0x4b>;
> +		interrupt-parent = <&gpio1>;
> +		interrupts = <29 GPIO_ACTIVE_LOW>;
> +		clocks = <&osc 0>;
> +		#clock-cells = <0>;
> +		clock-output-names = "bd71828-32k-out";
> +		gpio-controller;
> +		#gpio-cells = <2>;
> +		ngpios = <4>;
> +		gpio-reserved-ranges = <0 1 2 1>;
> +		gpio-line-names = "EPDEN";
> +		rohm,dvs-vsel-gpios = <&gpio1 12 0>,
> +				      <&gpio1 13 0>;
> +		regulators {
> +			buck1: BUCK1 {
> +				regulator-name = "buck1";
> +				regulator-min-microvolt = <500000>;
> +				regulator-max-microvolt = <2000000>;
> +				regulator-ramp-delay = <2500>;
> +				rohm,dvs-runlvl-ctrl;
> +				rohm,dvs-runlevel0-voltage = <500000>;
> +				rohm,dvs-runlevel1-voltage = <506250>;
> +				rohm,dvs-runlevel2-voltage = <512500>;
> +				rohm,dvs-runlevel3-voltage = <518750>;
> +				regulator-boot-on;
> +			};
> +			buck2: BUCK2 {
> +				regulator-name = "buck2";
> +				regulator-min-microvolt = <500000>;
> +				regulator-max-microvolt = <2000000>;
> +				regulator-ramp-delay = <2500>;
> +				rohm,dvs-runlvl-ctrl;
> +				rohm,dvs-runlevel0-voltage = <500000>;
> +				rohm,dvs-runlevel1-voltage = <506250>;
> +				rohm,dvs-runlevel2-voltage = <512500>;
> +				rohm,dvs-runlevel3-voltage = <518750>;
> +				regulator-boot-on;
> +			};
> +			buck3: BUCK3 {
> +				regulator-name = "buck3";
> +				regulator-min-microvolt = <1200000>;
> +				regulator-max-microvolt = <2000000>;
> +				regulator-boot-on;
> +			};
> +			buck4: BUCK4 {
> +				regulator-name = "buck4";
> +				regulator-min-microvolt = <1000000>;
> +				regulator-max-microvolt = <1800000>;
> +				regulator-boot-on;
> +			};
> +			buck5: BUCK5 {
> +				regulator-name = "buck5";
> +				regulator-min-microvolt = <2500000>;
> +				regulator-max-microvolt = <3300000>;
> +				regulator-boot-on;
> +			};
> +			buck6: BUCK6 {
> +				regulator-name = "buck6";
> +				regulator-min-microvolt = <500000>;
> +				regulator-max-microvolt = <2000000>;
> +				regulator-ramp-delay = <2500>;
> +				rohm,dvs-runlvl-ctrl;
> +				rohm,dvs-runlevel0-voltage = <500000>;
> +				rohm,dvs-runlevel1-voltage = <506250>;
> +				rohm,dvs-runlevel2-voltage = <512500>;
> +				rohm,dvs-runlevel3-voltage = <518750>;
> +				regulator-boot-on;
> +			};
> +			buck7: BUCK7 {
> +				regulator-name = "buck7";
> +				regulator-min-microvolt = <500000>;
> +				regulator-max-microvolt = <2000000>;
> +				regulator-ramp-delay = <2500>;
> +				rohm,dvs-runlvl-ctrl;
> +				rohm,dvs-runlevel0-voltage = <500000>;
> +				rohm,dvs-runlevel1-voltage = <506250>;
> +				rohm,dvs-runlevel2-voltage = <512500>;
> +				rohm,dvs-runlevel3-voltage = <518750>;
> +				regulator-boot-on;
> +			};
> +			ldo1: LDO1 {
> +				regulator-name = "ldo1";
> +				regulator-min-microvolt = <800000>;
> +				regulator-max-microvolt = <3300000>;
> +				regulator-boot-on;
> +			};
> +			ldo2: LDO2 {
> +				regulator-name = "ldo2";
> +				regulator-min-microvolt = <800000>;
> +				regulator-max-microvolt = <3300000>;
> +				regulator-boot-on;
> +			};
> +			ldo3: LDO3 {
> +				regulator-name = "ldo3";
> +				regulator-min-microvolt = <800000>;
> +				regulator-max-microvolt = <3300000>;
> +				regulator-boot-on;
> +			};
> +			ldo4: LDO4 {
> +				regulator-name = "ldo4";
> +				regulator-min-microvolt = <800000>;
> +				regulator-max-microvolt = <3300000>;
> +				regulator-boot-on;
> +			};
> +			ldo5: LDO5 {
> +				regulator-name = "ldo5";
> +				regulator-min-microvolt = <800000>;
> +				regulator-max-microvolt = <3300000>;
> +				regulator-boot-on;
> +			};
> +			ldo6: LDO6 {
> +				regulator-name = "ldo6";
> +				regulator-min-microvolt = <1800000>;
> +				regulator-max-microvolt = <1800000>;
> +				regulator-boot-on;
> +			};
> +			ldo7_reg: LDO7 {
> +				regulator-name = "ldo7";
> +				regulator-min-microvolt = <800000>;
> +				regulator-max-microvolt = <3300000>;
> +				regulator-boot-on;
> +			};
> +		};
> +	};

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-17 14:04   ` Dan Murphy
@ 2019-10-17 14:28     ` Alexandre Belloni
  2019-10-21  8:00     ` Vaittinen, Matti
  1 sibling, 0 replies; 43+ messages in thread
From: Alexandre Belloni @ 2019-10-17 14:28 UTC (permalink / raw)
  To: Dan Murphy
  Cc: Matti Vaittinen, mazziesaccount, Lee Jones, Rob Herring,
	Mark Rutland, Liam Girdwood, Mark Brown, Michael Turquette,
	Stephen Boyd, Linus Walleij, Bartosz Golaszewski,
	Jacek Anaszewski, Pavel Machek, Alessandro Zummo, devicetree,
	linux-kernel, linux-clk, linux-gpio, linux-leds, linux-rtc

On 17/10/2019 09:04:48-0500, Dan Murphy wrote:
> Matt
> 
> On 10/17/19 4:53 AM, Matti Vaittinen wrote:
> > ROHM BD71828 power management IC has two LED outputs for charge status
> > and button pressing indications. The LED outputs can also be forced
> > bs SW so add driver allowing to use these LEDs for other indications
> s/bs/by
> > as well.
> > 
> > Leds are controlled by SW using 'Force ON' bits. Please note the
> > constrains mentioned in data-sheet:
> > 1. If one LED is forced ON - then also the other LED is forced.
> > 	=> You can't use SW control to force ON one LED and allow HW
> > 	   to control the other.
> > 2. You can't force both LEDs OFF. If the FORCE bit for both LED's is
> >     zero, then LEDs are controlled by HW and indicate button/charger
> >     states as explained in data-sheet.
> > 
> > Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> > ---
> >   drivers/leds/Kconfig        | 10 ++++
> >   drivers/leds/Makefile       |  1 +
> >   drivers/leds/leds-bd71828.c | 97 +++++++++++++++++++++++++++++++++++++
> >   3 files changed, 108 insertions(+)
> >   create mode 100644 drivers/leds/leds-bd71828.c
> > 
> > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> > index b0fdeef10bd9..ec59f28bcb39 100644
> > --- a/drivers/leds/Kconfig
> > +++ b/drivers/leds/Kconfig
> > @@ -529,6 +529,16 @@ config LEDS_BD2802
> >   	  This option enables support for BD2802GU RGB LED driver chips
> >   	  accessed via the I2C bus.
> > +config LEDS_BD71828
> > +	tristate "LED driver for LED pins on ROHM BD71828 PMIC"
> > +	depends on LEDS_CLASS
> doesn't this have a dependency on MFD_ROHM_BD71828
> > +	depends on I2C
> > +	help
> > +	  This option enables support for LED outputs located on ROHM
> > +	   BD71828 power management IC. ROHM BD71828 has two led output pins
> > +	   which can be left to indicate HW states or controlled by SW. Say
> > +	   yes here if you want to enable SW control for these LEDs.
> > +
> 
> Add module statement
> 
> 
> >   config LEDS_INTEL_SS4200
> >   	tristate "LED driver for Intel NAS SS4200 series"
> >   	depends on LEDS_CLASS
> > diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> > index 41fb073a39c1..2a8f6a8e4c7c 100644
> > --- a/drivers/leds/Makefile
> > +++ b/drivers/leds/Makefile
> > @@ -15,6 +15,7 @@ obj-$(CONFIG_LEDS_AN30259A)		+= leds-an30259a.o
> >   obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
> >   obj-$(CONFIG_LEDS_BCM6358)		+= leds-bcm6358.o
> >   obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
> > +obj-$(CONFIG_LEDS_BD71828)		+= leds-bd71828.o
> >   obj-$(CONFIG_LEDS_CPCAP)		+= leds-cpcap.o
> >   obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
> >   obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
> > diff --git a/drivers/leds/leds-bd71828.c b/drivers/leds/leds-bd71828.c
> > new file mode 100644
> > index 000000000000..2427619444f5
> > --- /dev/null
> > +++ b/drivers/leds/leds-bd71828.c
> > @@ -0,0 +1,97 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +// Copyright (C) 2019 ROHM Semiconductors
> > +
> > +#include <linux/device.h>
> > +#include <linux/err.h>
> > +#include <linux/kernel.h>
> > +#include <linux/leds.h>
> > +#include <linux/mfd/rohm-bd71828.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +#include <linux/slab.h>
> > +
> > +#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ? \
> > +	container_of((l), struct bd71828_leds, green) : \
> > +	container_of((l), struct bd71828_leds, amber))
> 
> I don't think we should be defining the color as the variable. The outputs
> can drive any color LED.
> 
> 
> > +
> > +enum {
> > +	ID_GREEN_LED,
> > +	ID_AMBER_LED,
> > +	ID_NMBR_OF,
> > +};
> > +
> 
> Please use the color_id in linux/include/dt-bindings/leds/common.h
> 
> 
> > +struct bd71828_led {
> > +	int id;
> > +	struct led_classdev l;
> > +	u8 force_mask;
> > +};
> > +
> > +struct bd71828_leds {
> > +	struct rohm_regmap_dev *bd71828;
> > +	struct bd71828_led green;
> > +	struct bd71828_led amber;
> > +};
> > +
> > +static int bd71828_led_brightness_set(struct led_classdev *led_cdev,
> > +				      enum led_brightness value)
> > +{
> > +	struct bd71828_led *l = container_of(led_cdev, struct bd71828_led, l);
> > +	struct bd71828_leds *data;
> > +	unsigned int val = BD71828_LED_OFF;
> > +
> > +	data = BD71828_LED_TO_DATA(l);
> > +	if (value != LED_OFF)
> > +		val = BD71828_LED_ON;
> > +
> > +	return regmap_update_bits(data->bd71828->regmap, BD71828_REG_LED_CTRL,
> > +			    l->force_mask, val);
> > +}
> > +
> > +static int bd71828_led_probe(struct platform_device *pdev)
> > +{
> > +	struct rohm_regmap_dev *bd71828;
> > +	struct bd71828_leds *l;
> > +	struct bd71828_led *g, *a;
> > +	static const char *GNAME = "bd71828-green-led";
> > +	static const char *ANAME = "bd71828-amber-led";
> The LED class creates the name it can get it from the DT.
> > +	int ret;
> > +
> > +	pr_info("bd71828 LED driver probed\n");
> > +
> > +	bd71828 = dev_get_drvdata(pdev->dev.parent);
> > +	l = devm_kzalloc(&pdev->dev, sizeof(*l), GFP_KERNEL);
> > +	if (!l)
> > +		return -ENOMEM;
> > +	l->bd71828 = bd71828;
> > +	a = &l->amber;
> > +	g = &l->green;
> > +	a->id = ID_AMBER_LED;
> > +	g->id = ID_GREEN_LED;
> > +	a->force_mask = BD71828_MASK_LED_AMBER;
> > +	g->force_mask = BD71828_MASK_LED_GREEN;
> > +
> > +	a->l.name = ANAME;
> > +	g->l.name = GNAME;
> > +	a->l.brightness_set_blocking = bd71828_led_brightness_set;
> > +	g->l.brightness_set_blocking = bd71828_led_brightness_set;
> > +
> > +	ret = devm_led_classdev_register(&pdev->dev, &g->l);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return devm_led_classdev_register(&pdev->dev, &a->l);
> > +}
> > +
> 
> This looks different.  Not sure why you register both LEDs in this probe.
> 
> You can use the DT to define both LEDs and then each will be probed and
> registered separately.
> 
> This is how it is commonly done.
> 
> You can reference the LM36274 led driver as this is a MFD device to the
> ti-lmu.c in the MFD directory.
> 
> 
> > +static struct platform_driver bd71828_led_driver = {
> > +	.driver = {
> > +		.name  = "bd71828-led",
> > +	},
> > +	.probe  = bd71828_led_probe,
> > +};
> > +
> > +module_platform_driver(bd71828_led_driver);
> > +
> > +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
> > +MODULE_DESCRIPTION("ROHM BD71828 LED driver");
> > +MODULE_LICENSE("GPL");
> GPL v2

GPL is fine, see bf7fbeeae6db ("module: Cure the MODULE_LICENSE "GPL"
vs. "GPL v2" bogosity")


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver
  2019-10-17 10:48       ` Alexandre Belloni
@ 2019-10-21  5:29         ` Vaittinen, Matti
  2019-10-23 10:27         ` Vaittinen, Matti
  1 sibling, 0 replies; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-21  5:29 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: dmurphy, linux-leds, linux-rtc, linux-gpio, linux-kernel,
	mazziesaccount, mturquette, devicetree, jacek.anaszewski,
	a.zummo, linus.walleij, lgirdwood, mark.rutland, robh+dt,
	bgolaszewski, linux-clk, pavel, sboyd, broonie, lee.jones

Hello Alexandre,

On Thu, 2019-10-17 at 12:48 +0200, Alexandre Belloni wrote:
> On 17/10/2019 10:36:44+0000, Vaittinen, Matti wrote:
> > Hello Alexandre,
> > 
> > Thanks for quick check! I'll be off for the rest of the week but I
> > will
> > re-work this patch at next week :) I agree with you regarding most
> > of
> > the comments.
> > 
> > > > +
> > > > +
> > > > +/*
> > > > + * RTC definitions shared between
> > > > + *
> > > > + * BD70528
> > > > + * and BD71828
> > > > + */
> > > > +
> > > > +#define ROHM_BD1_MASK_RTC_SEC		0x7f
> > > > +#define ROHM_BD1_MASK_RTC_MINUTE	0x7f
> > > > +#define ROHM_BD1_MASK_RTC_HOUR_24H	0x80
> > > > +#define ROHM_BD1_MASK_RTC_HOUR_PM	0x20
> > > > +#define ROHM_BD1_MASK_RTC_HOUR		0x3f
> > > > +#define ROHM_BD1_MASK_RTC_DAY		0x3f
> > > > +#define ROHM_BD1_MASK_RTC_WEEK		0x07
> > > > +#define ROHM_BD1_MASK_RTC_MONTH		0x1f
> > > > +#define ROHM_BD1_MASK_RTC_YEAR		0xff
> > > > +#define ROHM_BD1_MASK_ALM_EN		0x7
> > > > +
> > > 
> > > All that renaming is distracting and useless. Please resubmit
> > > without
> > > renaming defines, structs and functions to make it easier to
> > > review.
> > 
> > I would prefer renaming because it makes it clearly visible which
> > defines/structs/functions are common for both PMICs and which are
> > PMIC
> > specific. But I really understand the problem of spotting real
> > changes.
> > Would it be Ok if I did renaming in separate patch which does not
> > bring
> > in any other changes - and then the functional changes in separate
> > patch?
> > 
> 
> No, unless you can guarantee that all future PMICs from rohm matching
> the wildcard will use this driver.

Allright. I'll avoid renaming in next version.

Br,
	Matti Vaittinen

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

* Re: [RFC PATCH 10/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs
  2019-10-17 12:45   ` Bartosz Golaszewski
@ 2019-10-21  7:00     ` Vaittinen, Matti
  2019-10-21 14:36       ` Bartosz Golaszewski
  0 siblings, 1 reply; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-21  7:00 UTC (permalink / raw)
  To: bgolaszewski
  Cc: dmurphy, linux-leds, linux-rtc, linux-gpio, alexandre.belloni,
	linux-kernel, mturquette, lgirdwood, jacek.anaszewski,
	mazziesaccount, a.zummo, linus.walleij, mark.rutland, robh+dt,
	devicetree, linux-clk, pavel, sboyd, broonie, lee.jones

Hello Bartosz,

Thanks for reading this through! I'll rework this patch during this
week :)

On Thu, 2019-10-17 at 14:45 +0200, Bartosz Golaszewski wrote:
> czw., 17 paź 2019 o 11:53 Matti Vaittinen
> <matti.vaittinen@fi.rohmeurope.com> napisał(a):
> > ROHM BD71828 PMIC contains 4 pins which can be configured by OTP
> > to be used for general purposes. First 3 can be used as outputs
> > and 4.th pin can be used as input. Allow them to be controlled
> > via GPIO framework.
> > 
> > The driver assumes all of the pins are configured as GPIOs and
> > rusts that the reserved pins in other OTP configurations are
> > excluded from control using "gpio-reserved-ranges" device tree
> > property (or left untouched by GPIO users).
> > 
> > Typical use for 4.th pin (input) is to use it as HALL sensor
> > input so that this pin state is toggled when HALL sensor detects
> > LID position change (from close to open or open to close). PMIC
> > HW implements some extra logic which allows PMIC to power-up the
> > system when this pin is toggled. Please see the data sheet for
> > details of GPIO options which can be selcted by OTP settings.
> > 
> > Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> > 
> > +++ b/drivers/gpio/gpio-bd71828.c
> > @@ -0,0 +1,161 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +// Copyright (C) 2018 ROHM Semiconductors
> > +// gpio-bd71828.c ROHM BD71828 gpio driver
> 
> I don't think the name of the source file is needed here.

I Agree.

> 
> > +
> > +#include <linux/gpio/driver.h>
> > +#include <linux/mfd/rohm-bd71828.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +
> > +#define OUT 0
> > +#define IN 1
> 
> If you really want to define those, please use a common prefix for
> all
> symbols in the driver.

I prefer defining them because I always need to check the meaning of
these values. My brains just refuse from remembering which value is
used for in and which for out. I will add the prefix even though the
scope of these defines is limited to this file :)

> 
> > +#define GPIO_OUT_REG(off) (BD71828_REG_GPIO_CTRL1 + (off))
> > +#define HALL_GPIO_OFFSET 3
> > +
> > +struct bd71828_gpio {
> > +       struct rohm_regmap_dev chip;
> > +       struct gpio_chip gpio;
> > +};
> > +
> > +static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int
> > offset,
> > +                            int value)
> > +{
> > +       int ret;
> > +       struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
> > +       u8 val = (value) ? BD71828_GPIO_OUT_HI :
> > BD71828_GPIO_OUT_LO;
> > +
> > +       if (offset == HALL_GPIO_OFFSET)
> > +               return;
> 
> Can you add a comment here saying that this pin can only be used as
> input? Otherwise this information is only available in the commit
> message.

Sure thing. I thought the comment in get_direction was sufficient but
you are correct - it's nice to see this limitation also here.

> > +
> > +       ret = regmap_update_bits(bdgpio->chip.regmap,
> > GPIO_OUT_REG(offset),
> > +                                BD71828_GPIO_OUT_MASK, val);
> > +       if (ret)
> > +               dev_err(bdgpio->chip.dev, "Could not set gpio to
> > %d\n", value);
> > +}
> > +
> > +static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int
> > offset)
> > +{
> > +       int ret;
> > +       unsigned int val;
> > +       struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
> > +
> > +       if (offset == HALL_GPIO_OFFSET)
> > +               ret = regmap_read(bdgpio->chip.regmap,
> > BD71828_REG_IO_STAT,
> > +                                 &val);
> > +       else
> > +               ret = regmap_read(bdgpio->chip.regmap,
> > GPIO_OUT_REG(offset),
> > +                                 &val);
> > +       if (!ret)
> > +               ret = (val & BD71828_GPIO_OUT_MASK);
> > +
> > +       return ret;
> > +}
> > +
> > +static int bd71828_gpio_set_config(struct gpio_chip *chip,
> > unsigned int offset,
> > +                                  unsigned long config)
> > +{
> > +       struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
> > +
> > +       if (offset == HALL_GPIO_OFFSET)
> > +               return -ENOTSUPP;
> > +
> > +       switch (pinconf_to_config_param(config)) {
> > +       case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> > +               return regmap_update_bits(bdgpio->chip.regmap,
> > +                                         GPIO_OUT_REG(offset),
> > +                                         BD71828_GPIO_DRIVE_MASK,
> > +                                         BD71828_GPIO_OPEN_DRAIN);
> > +       case PIN_CONFIG_DRIVE_PUSH_PULL:
> > +               return regmap_update_bits(bdgpio->chip.regmap,
> > +                                         GPIO_OUT_REG(offset),
> > +                                         BD71828_GPIO_DRIVE_MASK,
> > +                                         BD71828_GPIO_PUSH_PULL);
> > +       default:
> > +               break;
> > +       }
> > +       return -ENOTSUPP;
> +}+static int bd71828_get_direction(struct gpio_chip *chip, unsigned
> int offset)
> > +{
> > +       /*
> > +        * Pin usage is selected by OTP data. We can't read it
> > runtime. Hence
> > +        * we trust that if the pin is not excluded by "gpio-
> > reserved-ranges"
> > +        * the OTP configuration is set to OUT. (Other pins but
> > HALL input pin
> > +        * on BD71828 can't really be used for general purpose
> > input - input
> > +        * states are used for specific cases like regulator
> > control or
> > +        * PMIC_ON_REQ.
> > +        */
> > +       if (offset == HALL_GPIO_OFFSET)
> > +               return IN;
> > +
> > +       return OUT;
> > +}
> > +
> > +static int bd71828_gpio_parse_dt(struct device *dev,
> > +                                struct bd71828_gpio *bdgpio)
> > +{
> > +       /*
> > +        * TBD: See if we need some implementation to mark some
> > PINs as
> > +        * not controllable based on DT info or if core can handle
> > +        * "gpio-reserved-ranges" and exclude them from control
> > +        */
> > +       return 0;
> > +}
> 
> Please don't implement empty functions. Just add this comment next to
> gpiochip's initialization...

Yep. I should have cleaned this before sending even this. Thanks for
pointing it out!

> 
> 
> > +
> > +       bdgpio->chip.dev = &pdev->dev;
> > +       bdgpio->gpio.parent = pdev->dev.parent;
> > +       bdgpio->gpio.label = "bd71828-gpio";
> > +       bdgpio->gpio.owner = THIS_MODULE;
> > +       bdgpio->gpio.get_direction = bd71828_get_direction;
> > +       bdgpio->gpio.set_config = bd71828_gpio_set_config;
> > +       bdgpio->gpio.can_sleep = true;
> > +       bdgpio->gpio.get = bd71828_gpio_get;
> > +       bdgpio->gpio.set = bd71828_gpio_set;
> 
> Not implementing direction_output() and direction_input() here will
> results in warnings from the GPIO framework: for instance you
> implement set() but not direction_output(). I'd say: just add those
> callbacks and return an error if they're called for invalid lines
> (for
> instance: direction_output() being called for line 3).

Ok. I will implement dummy functions.

But out of the curiosity - why the GPIO core emits the warnings if
these are not implemented? I think the core should not require "no-
operation" functions to be implemented for pins which don't support
both of the directions. GPIO core could only emit warning if it needs
to set direction to something the HW does not support. That would avoid
adding the dummy functions to all of the drivers, right?

> 
> > +       bdgpio->gpio.base = -1;
> > +       bdgpio->gpio.ngpio = 4;
> > +#ifdef CONFIG_OF_GPIO
> 
> This is not needed - for CONFIG_OF_GPIO disabled the parent of_node
> will be NULL.

Right. Thanks.

> > +       bdgpio->gpio.of_node = pdev->dev.parent->of_node;
> > +#endif
> > +       bdgpio->chip.regmap = bd71828->regmap;
> > +
> > +       ret = devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio,
> > +                                    bdgpio);
> > +       if (ret)
> > +               dev_err(&pdev->dev, "gpio_init: Failed to add
> > bd71828-gpio\n");
> 
> Since there aren't many places where this function can fail, you can
> directly return devm_gpiochip_add_data() here.

Ok.

> > +
> > +       return ret;
> > +}
> > +
> > +static struct platform_driver bd71828_gpio = {
> > +       .driver = {
> > +               .name = "bd71828-gpio"
> > +       },
> > +       .probe = bd71828_probe,
> > +};
> > +
> > +module_platform_driver(bd71828_gpio);
> > +
> > +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> > ");
> > +MODULE_DESCRIPTION("BD71828 voltage regulator driver");
> > +MODULE_LICENSE("GPL");
> 
> Don't you need a MODULE_ALIAS() here since this is an MFD sub-module?

I must admit I don't know the details of how module loading is done. I
used system where modules are load by scripts. (I guess the module
alias could be used to allow automatic module loading [by udev?])

Can you please educate me - If I add module aliases matching the sub-
device name given in in MFD cell - should the sub module loading be
automatic when MFD driver gets probed? For some reason I didn't get
that working on my test bed. Or maybe I misunderstood something.

Eg, this should be enough for GPIO sub-module to be also load:

MFD:
static struct mfd_cell bd71828_mfd_cells[] = {
        { .name = "bd71828-pmic", },
        { .name = "bd71828-gpio", },
...
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
                           bd71828_mfd_cells,
                           ARRAY_SIZE(bd71828_mfd_cells), NULL, 0,
                           regmap_irq_get_domain(irq_data)); 

GPIO driver:
MODULE_ALIAS("platform:bd71828-gpio");

I had the sub-devices probed even without the MODULE_ALIAS - but manual
loading is required. I will gladly add the alias if it should enable
the automatic module loading.

Br,
	Matti Vaittinen


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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-17 14:04   ` Dan Murphy
  2019-10-17 14:28     ` Alexandre Belloni
@ 2019-10-21  8:00     ` Vaittinen, Matti
  2019-10-21 19:09       ` Jacek Anaszewski
  1 sibling, 1 reply; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-21  8:00 UTC (permalink / raw)
  To: mazziesaccount, dmurphy
  Cc: linux-leds, linux-rtc, linux-gpio, alexandre.belloni,
	linux-kernel, mturquette, lgirdwood, devicetree,
	jacek.anaszewski, a.zummo, linus.walleij, mark.rutland, robh+dt,
	bgolaszewski, linux-clk, lee.jones, pavel, broonie, sboyd

Hello Dan,

Thanks for taking the time to check my driver :) I truly appreciate all
the help!

A "fundamental question" regarding these review comments is whether I
should add DT entries for these LEDs or not. I thought I shouldn't but
I would like to get a comment from Rob regarding it.

On Thu, 2019-10-17 at 09:04 -0500, Dan Murphy wrote:
> Matt
> 
> On 10/17/19 4:53 AM, Matti Vaittinen wrote:
> > ROHM BD71828 power management IC has two LED outputs for charge
> > status
> > and button pressing indications. The LED outputs can also be forced
> > bs SW so add driver allowing to use these LEDs for other
> > indications
> s/bs/by
> > as well.
> > 
> > Leds are controlled by SW using 'Force ON' bits. Please note the
> > constrains mentioned in data-sheet:
> > 1. If one LED is forced ON - then also the other LED is forced.
> > 	=> You can't use SW control to force ON one LED and allow HW
> > 	   to control the other.
> > 2. You can't force both LEDs OFF. If the FORCE bit for both LED's
> > is
> >     zero, then LEDs are controlled by HW and indicate
> > button/charger
> >     states as explained in data-sheet.
> > 
> > Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> > ---
> >   drivers/leds/Kconfig        | 10 ++++
> >   drivers/leds/Makefile       |  1 +
> >   drivers/leds/leds-bd71828.c | 97
> > +++++++++++++++++++++++++++++++++++++
> >   3 files changed, 108 insertions(+)
> >   create mode 100644 drivers/leds/leds-bd71828.c
> > 
> > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> > index b0fdeef10bd9..ec59f28bcb39 100644
> > --- a/drivers/leds/Kconfig
> > +++ b/drivers/leds/Kconfig
> > @@ -529,6 +529,16 @@ config LEDS_BD2802
> >   	  This option enables support for BD2802GU RGB LED driver chips
> >   	  accessed via the I2C bus.
> >   
> > +config LEDS_BD71828
> > +	tristate "LED driver for LED pins on ROHM BD71828 PMIC"
> > +	depends on LEDS_CLASS
> doesn't this have a dependency on MFD_ROHM_BD71828
> > +	depends on I2C
> > +	help
> > +	  This option enables support for LED outputs located on ROHM
> > +	   BD71828 power management IC. ROHM BD71828 has two led output
> > pins
> > +	   which can be left to indicate HW states or controlled by SW.
> > Say
> > +	   yes here if you want to enable SW control for these LEDs.
> > +
> 
> Add module statement

What is the module statement? Do you mean the 'if you compile this as a
module it will be called blahblah' or 'choose M to blahblah'?

I've never understood why some entries have those statements. 'Choose
M' stuff is help for config system - why should each module explain how
to use configs? This information should be in more generic
documentation. Furthermore, the 'tristate' there already says you can
compile this as a module. Module name on the other hand really is
module's property but it may well change if one changes the name of the
file. That should not require change in KConfig. Furthermore, where do
you need the module name? And if you really need the module name you
should check the config name from Makefile to be sure - module/file
names in comments or docs tend to get outdated.

After all this being said - I can add any boilerplate text in KConfig
if necessary - I just see zero benefit from this. And if you didn't
mean this - can you then please tell me what is the module statement?

> >   config LEDS_INTEL_SS4200
> >   	tristate "LED driver for Intel NAS SS4200 series"
> >   	depends on LEDS_CLASS
> > diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> > index 41fb073a39c1..2a8f6a8e4c7c 100644
> > --- a/drivers/leds/Makefile
> > +++ b/drivers/leds/Makefile
> > @@ -15,6 +15,7 @@ obj-$(CONFIG_LEDS_AN30259A)		+=
> > leds-an30259a.o
> >   obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
> >   obj-$(CONFIG_LEDS_BCM6358)		+= leds-bcm6358.o
> >   obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
> > +obj-$(CONFIG_LEDS_BD71828)		+= leds-bd71828.o
> >   obj-$(CONFIG_LEDS_CPCAP)		+= leds-cpcap.o
> >   obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
> >   obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
> > diff --git a/drivers/leds/leds-bd71828.c b/drivers/leds/leds-
> > bd71828.c
> > new file mode 100644
> > index 000000000000..2427619444f5
> > --- /dev/null
> > +++ b/drivers/leds/leds-bd71828.c
> > @@ -0,0 +1,97 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +// Copyright (C) 2019 ROHM Semiconductors
> > +
> > +#include <linux/device.h>
> > +#include <linux/err.h>
> > +#include <linux/kernel.h>
> > +#include <linux/leds.h>
> > +#include <linux/mfd/rohm-bd71828.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +#include <linux/slab.h>
> > +
> > +#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ? \
> > +	container_of((l), struct bd71828_leds, green) : \
> > +	container_of((l), struct bd71828_leds, amber))
> 
> I don't think we should be defining the color as the variable. The 
> outputs can drive any color LED.

I used the colors mentioned in BD71828 data-sheet. It is true someone
might use different LEDs on their board but at least this naming allows
one to match the output to one in data-sheet. I can add comment
explaining this if you thin it's worth mentioning.

> > +
> > +enum {
> > +	ID_GREEN_LED,
> > +	ID_AMBER_LED,
> > +	ID_NMBR_OF,
> > +};
> > +
> 
> Please use the color_id in linux/include/dt-bindings/leds/common.h

Maybe I should not include anything from dt-bindings if I don't use DT
for this sub-device? (Please see the comments below).

> > +struct bd71828_led {
> > +	int id;
> > +	struct led_classdev l;
> > +	u8 force_mask;
> > +};
> > +
> > +struct bd71828_leds {
> > +	struct rohm_regmap_dev *bd71828;
> > +	struct bd71828_led green;
> > +	struct bd71828_led amber;
> > +};
> > +
> > +static int bd71828_led_brightness_set(struct led_classdev
> > *led_cdev,
> > +				      enum led_brightness value)
> > +{
> > +	struct bd71828_led *l = container_of(led_cdev, struct
> > bd71828_led, l);
> > +	struct bd71828_leds *data;
> > +	unsigned int val = BD71828_LED_OFF;
> > +
> > +	data = BD71828_LED_TO_DATA(l);
> > +	if (value != LED_OFF)
> > +		val = BD71828_LED_ON;
> > +
> > +	return regmap_update_bits(data->bd71828->regmap,
> > BD71828_REG_LED_CTRL,
> > +			    l->force_mask, val);
> > +}
> > +
> > +static int bd71828_led_probe(struct platform_device *pdev)
> > +{
> > +	struct rohm_regmap_dev *bd71828;
> > +	struct bd71828_leds *l;
> > +	struct bd71828_led *g, *a;
> > +	static const char *GNAME = "bd71828-green-led";
> > +	static const char *ANAME = "bd71828-amber-led";
> The LED class creates the name it can get it from the DT.

I did not add DT node for LEDs as I thought this is preferred way when
there is not much HW information to bring from DT. I am not sure as
previous PMICs I did drivers for didn't have LEDs though. Currently
this is a MFD sub-device and gets no data from DT. Actually the driver
crashed when I first didn't explicitly give these names.



> > +	int ret;
> > +
> > +	pr_info("bd71828 LED driver probed\n");
as a comment from myself - this print should be eliminated or by
minimum converted to dev_dbg.

> > +
> > +	bd71828 = dev_get_drvdata(pdev->dev.parent);
> > +	l = devm_kzalloc(&pdev->dev, sizeof(*l), GFP_KERNEL);
> > +	if (!l)
> > +		return -ENOMEM;
> > +	l->bd71828 = bd71828;
> > +	a = &l->amber;
> > +	g = &l->green;
> > +	a->id = ID_AMBER_LED;
> > +	g->id = ID_GREEN_LED;
> > +	a->force_mask = BD71828_MASK_LED_AMBER;
> > +	g->force_mask = BD71828_MASK_LED_GREEN;
> > +
> > +	a->l.name = ANAME;
> > +	g->l.name = GNAME;
> > +	a->l.brightness_set_blocking = bd71828_led_brightness_set;
> > +	g->l.brightness_set_blocking = bd71828_led_brightness_set;
> > +
> > +	ret = devm_led_classdev_register(&pdev->dev, &g->l);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return devm_led_classdev_register(&pdev->dev, &a->l);
> > +}
> > +
> 
> This looks different.  Not sure why you register both LEDs in this
> probe.
> 
> You can use the DT to define both LEDs and then each will be probed
> and 
> registered separately.

As I mentioned above, this driver is currently not using DT at all.
Reason why it does not is that I didn't know all the LEDs are usually
having own DT entries/drivers.

But there is actually reason for bundling the LEDs to same driver. What
is not shown in driver is that LEDs can be controlled by PMIC state
machine so that they are indicating charger states. Other option is
driving LEDs using this driver - but forcing one of the LEDs will cause
also the other LED to be under SW control. Eg, one can't control just
single LED using SW, its both of them or none.

> 
> This is how it is commonly done.
> 
> You can reference the LM36274 led driver as this is a MFD device to
> the 
> ti-lmu.c in the MFD directory.

Thanks for pointing this out. I will gladly see how others have it
implemented! I just would like to know if the DT binding is really a
must? In this case I am unsure what LED related extra information we
could really give from DT (for this specific device).

I just checked the lm36274 you mentioned. I see that also lm36274 do
parse the dt and set the name itself (so maybe the led_class is not
doing this after all?) - although the name setting code in lm36274 is a
bit peculiar as it loops through all child nodes and overwrites the old
name if it finds more than one "label" properties from nodes (if I read
the code correctly).

In any case I am unsure what is the benefit from using DT and adding
the DT parsing code for this PMIC's LEDs. I could understand DT usage
if LED class handled dt parsing and if there was something to configure
in BD71828 LEDs - but I see no such benefits in this case.

Best Regards
	Matti Vaittinen




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

* Re: [RFC PATCH 12/13] dt-bindings: mfd: Document ROHM BD71282 bindings
  2019-10-17 14:18   ` Dan Murphy
@ 2019-10-21  8:03     ` Vaittinen, Matti
  0 siblings, 0 replies; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-21  8:03 UTC (permalink / raw)
  To: mazziesaccount, dmurphy
  Cc: linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mturquette, lgirdwood, devicetree,
	jacek.anaszewski, a.zummo, linus.walleij, mark.rutland, robh+dt,
	bgolaszewski, linux-clk, sboyd, pavel, broonie, lee.jones

Thanks Dan,

On Thu, 2019-10-17 at 09:18 -0500, Dan Murphy wrote:
> Matt

By the way, its Matti ;)

> On 10/17/19 4:55 AM, Matti Vaittinen wrote:
> > ROHM BD71828 Power management IC integrates 7 buck converters, 7
> > LDOs,
> > a real-time clock (RTC), 3 GPO/regulator control pins, HALL input
> > pin
> > and a 32.768 kHz clock gate.
> > 
> > Document the dt bindings drivers are using.
> 
> This should be patch 1 of X

I will rebase :)

Br,
	Matti Vaittinen

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

* Re: [RFC PATCH 10/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs
  2019-10-21  7:00     ` Vaittinen, Matti
@ 2019-10-21 14:36       ` Bartosz Golaszewski
  2019-10-21 14:56         ` Vaittinen, Matti
  2019-10-22 13:19         ` Vaittinen, Matti
  0 siblings, 2 replies; 43+ messages in thread
From: Bartosz Golaszewski @ 2019-10-21 14:36 UTC (permalink / raw)
  To: Vaittinen, Matti
  Cc: dmurphy, linux-leds, linux-rtc, linux-gpio, alexandre.belloni,
	linux-kernel, mturquette, lgirdwood, jacek.anaszewski,
	mazziesaccount, a.zummo, linus.walleij, mark.rutland, robh+dt,
	devicetree, linux-clk, pavel, sboyd, broonie, lee.jones

pon., 21 paź 2019 o 09:00 Vaittinen, Matti
<Matti.Vaittinen@fi.rohmeurope.com> napisał(a):
>
> Hello Bartosz,
>
> Thanks for reading this through! I'll rework this patch during this
> week :)
>
> On Thu, 2019-10-17 at 14:45 +0200, Bartosz Golaszewski wrote:
> > czw., 17 paź 2019 o 11:53 Matti Vaittinen
> > <matti.vaittinen@fi.rohmeurope.com> napisał(a):
> > > ROHM BD71828 PMIC contains 4 pins which can be configured by OTP
> > > to be used for general purposes. First 3 can be used as outputs
> > > and 4.th pin can be used as input. Allow them to be controlled
> > > via GPIO framework.
> > >
> > > The driver assumes all of the pins are configured as GPIOs and
> > > rusts that the reserved pins in other OTP configurations are
> > > excluded from control using "gpio-reserved-ranges" device tree
> > > property (or left untouched by GPIO users).
> > >
> > > Typical use for 4.th pin (input) is to use it as HALL sensor
> > > input so that this pin state is toggled when HALL sensor detects
> > > LID position change (from close to open or open to close). PMIC
> > > HW implements some extra logic which allows PMIC to power-up the
> > > system when this pin is toggled. Please see the data sheet for
> > > details of GPIO options which can be selcted by OTP settings.
> > >
> > > Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> > >
> > > +++ b/drivers/gpio/gpio-bd71828.c
> > > @@ -0,0 +1,161 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +// Copyright (C) 2018 ROHM Semiconductors
> > > +// gpio-bd71828.c ROHM BD71828 gpio driver
> >
> > I don't think the name of the source file is needed here.
>
> I Agree.
>
> >
> > > +
> > > +#include <linux/gpio/driver.h>
> > > +#include <linux/mfd/rohm-bd71828.h>
> > > +#include <linux/module.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/regmap.h>
> > > +
> > > +#define OUT 0
> > > +#define IN 1
> >
> > If you really want to define those, please use a common prefix for
> > all
> > symbols in the driver.
>
> I prefer defining them because I always need to check the meaning of
> these values. My brains just refuse from remembering which value is
> used for in and which for out. I will add the prefix even though the
> scope of these defines is limited to this file :)
>
> >
> > > +#define GPIO_OUT_REG(off) (BD71828_REG_GPIO_CTRL1 + (off))
> > > +#define HALL_GPIO_OFFSET 3
> > > +
> > > +struct bd71828_gpio {
> > > +       struct rohm_regmap_dev chip;
> > > +       struct gpio_chip gpio;
> > > +};
> > > +
> > > +static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int
> > > offset,
> > > +                            int value)
> > > +{
> > > +       int ret;
> > > +       struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
> > > +       u8 val = (value) ? BD71828_GPIO_OUT_HI :
> > > BD71828_GPIO_OUT_LO;
> > > +
> > > +       if (offset == HALL_GPIO_OFFSET)
> > > +               return;
> >
> > Can you add a comment here saying that this pin can only be used as
> > input? Otherwise this information is only available in the commit
> > message.
>
> Sure thing. I thought the comment in get_direction was sufficient but
> you are correct - it's nice to see this limitation also here.
>
> > > +
> > > +       ret = regmap_update_bits(bdgpio->chip.regmap,
> > > GPIO_OUT_REG(offset),
> > > +                                BD71828_GPIO_OUT_MASK, val);
> > > +       if (ret)
> > > +               dev_err(bdgpio->chip.dev, "Could not set gpio to
> > > %d\n", value);
> > > +}
> > > +
> > > +static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int
> > > offset)
> > > +{
> > > +       int ret;
> > > +       unsigned int val;
> > > +       struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
> > > +
> > > +       if (offset == HALL_GPIO_OFFSET)
> > > +               ret = regmap_read(bdgpio->chip.regmap,
> > > BD71828_REG_IO_STAT,
> > > +                                 &val);
> > > +       else
> > > +               ret = regmap_read(bdgpio->chip.regmap,
> > > GPIO_OUT_REG(offset),
> > > +                                 &val);
> > > +       if (!ret)
> > > +               ret = (val & BD71828_GPIO_OUT_MASK);
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +static int bd71828_gpio_set_config(struct gpio_chip *chip,
> > > unsigned int offset,
> > > +                                  unsigned long config)
> > > +{
> > > +       struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
> > > +
> > > +       if (offset == HALL_GPIO_OFFSET)
> > > +               return -ENOTSUPP;
> > > +
> > > +       switch (pinconf_to_config_param(config)) {
> > > +       case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> > > +               return regmap_update_bits(bdgpio->chip.regmap,
> > > +                                         GPIO_OUT_REG(offset),
> > > +                                         BD71828_GPIO_DRIVE_MASK,
> > > +                                         BD71828_GPIO_OPEN_DRAIN);
> > > +       case PIN_CONFIG_DRIVE_PUSH_PULL:
> > > +               return regmap_update_bits(bdgpio->chip.regmap,
> > > +                                         GPIO_OUT_REG(offset),
> > > +                                         BD71828_GPIO_DRIVE_MASK,
> > > +                                         BD71828_GPIO_PUSH_PULL);
> > > +       default:
> > > +               break;
> > > +       }
> > > +       return -ENOTSUPP;
> > +}+static int bd71828_get_direction(struct gpio_chip *chip, unsigned
> > int offset)
> > > +{
> > > +       /*
> > > +        * Pin usage is selected by OTP data. We can't read it
> > > runtime. Hence
> > > +        * we trust that if the pin is not excluded by "gpio-
> > > reserved-ranges"
> > > +        * the OTP configuration is set to OUT. (Other pins but
> > > HALL input pin
> > > +        * on BD71828 can't really be used for general purpose
> > > input - input
> > > +        * states are used for specific cases like regulator
> > > control or
> > > +        * PMIC_ON_REQ.
> > > +        */
> > > +       if (offset == HALL_GPIO_OFFSET)
> > > +               return IN;
> > > +
> > > +       return OUT;
> > > +}
> > > +
> > > +static int bd71828_gpio_parse_dt(struct device *dev,
> > > +                                struct bd71828_gpio *bdgpio)
> > > +{
> > > +       /*
> > > +        * TBD: See if we need some implementation to mark some
> > > PINs as
> > > +        * not controllable based on DT info or if core can handle
> > > +        * "gpio-reserved-ranges" and exclude them from control
> > > +        */
> > > +       return 0;
> > > +}
> >
> > Please don't implement empty functions. Just add this comment next to
> > gpiochip's initialization...
>
> Yep. I should have cleaned this before sending even this. Thanks for
> pointing it out!
>
> >
> >
> > > +
> > > +       bdgpio->chip.dev = &pdev->dev;
> > > +       bdgpio->gpio.parent = pdev->dev.parent;
> > > +       bdgpio->gpio.label = "bd71828-gpio";
> > > +       bdgpio->gpio.owner = THIS_MODULE;
> > > +       bdgpio->gpio.get_direction = bd71828_get_direction;
> > > +       bdgpio->gpio.set_config = bd71828_gpio_set_config;
> > > +       bdgpio->gpio.can_sleep = true;
> > > +       bdgpio->gpio.get = bd71828_gpio_get;
> > > +       bdgpio->gpio.set = bd71828_gpio_set;
> >
> > Not implementing direction_output() and direction_input() here will
> > results in warnings from the GPIO framework: for instance you
> > implement set() but not direction_output(). I'd say: just add those
> > callbacks and return an error if they're called for invalid lines
> > (for
> > instance: direction_output() being called for line 3).
>
> Ok. I will implement dummy functions.
>
> But out of the curiosity - why the GPIO core emits the warnings if
> these are not implemented? I think the core should not require "no-
> operation" functions to be implemented for pins which don't support
> both of the directions. GPIO core could only emit warning if it needs
> to set direction to something the HW does not support. That would avoid
> adding the dummy functions to all of the drivers, right?
>

I looked at the code again and it seems I was wrong. If we don't have
direction_input() or direction_output() we check the actual direction
with get_direction() before emitting any warnings and if there's no
direction_output(), but line is in input mode then all's fine. In
other words: false alarm, and you can keep it this way.

> >
> > > +       bdgpio->gpio.base = -1;
> > > +       bdgpio->gpio.ngpio = 4;
> > > +#ifdef CONFIG_OF_GPIO
> >
> > This is not needed - for CONFIG_OF_GPIO disabled the parent of_node
> > will be NULL.
>
> Right. Thanks.
>
> > > +       bdgpio->gpio.of_node = pdev->dev.parent->of_node;
> > > +#endif
> > > +       bdgpio->chip.regmap = bd71828->regmap;
> > > +
> > > +       ret = devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio,
> > > +                                    bdgpio);
> > > +       if (ret)
> > > +               dev_err(&pdev->dev, "gpio_init: Failed to add
> > > bd71828-gpio\n");
> >
> > Since there aren't many places where this function can fail, you can
> > directly return devm_gpiochip_add_data() here.
>
> Ok.
>
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +static struct platform_driver bd71828_gpio = {
> > > +       .driver = {
> > > +               .name = "bd71828-gpio"
> > > +       },
> > > +       .probe = bd71828_probe,
> > > +};
> > > +
> > > +module_platform_driver(bd71828_gpio);
> > > +
> > > +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> > > ");
> > > +MODULE_DESCRIPTION("BD71828 voltage regulator driver");
> > > +MODULE_LICENSE("GPL");
> >
> > Don't you need a MODULE_ALIAS() here since this is an MFD sub-module?
>
> I must admit I don't know the details of how module loading is done. I
> used system where modules are load by scripts. (I guess the module
> alias could be used to allow automatic module loading [by udev?])
>
> Can you please educate me - If I add module aliases matching the sub-
> device name given in in MFD cell - should the sub module loading be
> automatic when MFD driver gets probed? For some reason I didn't get
> that working on my test bed. Or maybe I misunderstood something.
>

If the gpio module is a sub-node on the device tree than you may need
to use a sub-compatible to get the module loaded by udev.

Bart

> Eg, this should be enough for GPIO sub-module to be also load:
>
> MFD:
> static struct mfd_cell bd71828_mfd_cells[] = {
>         { .name = "bd71828-pmic", },
>         { .name = "bd71828-gpio", },
> ...
> ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
>                            bd71828_mfd_cells,
>                            ARRAY_SIZE(bd71828_mfd_cells), NULL, 0,
>                            regmap_irq_get_domain(irq_data));
>
> GPIO driver:
> MODULE_ALIAS("platform:bd71828-gpio");
>
> I had the sub-devices probed even without the MODULE_ALIAS - but manual
> loading is required. I will gladly add the alias if it should enable
> the automatic module loading.
>
> Br,
>         Matti Vaittinen
>

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

* Re: [RFC PATCH 10/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs
  2019-10-21 14:36       ` Bartosz Golaszewski
@ 2019-10-21 14:56         ` Vaittinen, Matti
  2019-10-22 13:19         ` Vaittinen, Matti
  1 sibling, 0 replies; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-21 14:56 UTC (permalink / raw)
  To: bgolaszewski
  Cc: dmurphy, linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mazziesaccount, mturquette, devicetree,
	jacek.anaszewski, linus.walleij, a.zummo, lgirdwood,
	mark.rutland, robh+dt, linux-clk, pavel, sboyd, broonie,
	lee.jones

Thanks again Bart =)


On Mon, 2019-10-21 at 16:36 +0200, Bartosz Golaszewski wrote:
> pon., 21 paź 2019 o 09:00 Vaittinen, Matti
> <Matti.Vaittinen@fi.rohmeurope.com> napisał(a):
> > Hello Bartosz,
> > 
> > +
> > > > +       bdgpio->chip.dev = &pdev->dev;
> > > > +       bdgpio->gpio.parent = pdev->dev.parent;
> > > > +       bdgpio->gpio.label = "bd71828-gpio";
> > > > +       bdgpio->gpio.owner = THIS_MODULE;
> > > > +       bdgpio->gpio.get_direction = bd71828_get_direction;
> > > > +       bdgpio->gpio.set_config = bd71828_gpio_set_config;
> > > > +       bdgpio->gpio.can_sleep = true;
> > > > +       bdgpio->gpio.get = bd71828_gpio_get;
> > > > +       bdgpio->gpio.set = bd71828_gpio_set;
> > > 
> > > Not implementing direction_output() and direction_input() here
> > > will
> > > results in warnings from the GPIO framework: for instance you
> > > implement set() but not direction_output(). I'd say: just add
> > > those
> > > callbacks and return an error if they're called for invalid lines
> > > (for
> > > instance: direction_output() being called for line 3).
> > 
> > Ok. I will implement dummy functions.
> > 
> > But out of the curiosity - why the GPIO core emits the warnings if
> > these are not implemented? I think the core should not require "no-
> > operation" functions to be implemented for pins which don't support
> > both of the directions. GPIO core could only emit warning if it
> > needs
> > to set direction to something the HW does not support. That would
> > avoid
> > adding the dummy functions to all of the drivers, right?
> > 
> 
> I looked at the code again and it seems I was wrong. If we don't have
> direction_input() or direction_output() we check the actual direction
> with get_direction() before emitting any warnings and if there's no
> direction_output(), but line is in input mode then all's fine. In
> other words: false alarm, and you can keep it this way.

Thanks for clarifying this - it makes sense :) I wont change this then.

> > > Don't you need a MODULE_ALIAS() here since this is an MFD sub-
> > > module?
> > 
> > I must admit I don't know the details of how module loading is
> > done. I
> > used system where modules are load by scripts. (I guess the module
> > alias could be used to allow automatic module loading [by udev?])
> > 
> > Can you please educate me - If I add module aliases matching the
> > sub-
> > device name given in in MFD cell - should the sub module loading be
> > automatic when MFD driver gets probed? For some reason I didn't get
> > that working on my test bed. Or maybe I misunderstood something.
> > 
> 
> If the gpio module is a sub-node on the device tree than you may need
> to use a sub-compatible to get the module loaded by udev.

Just found out that the last update broke my test bed I2C completely.
I'll experiment with the MODULE_ALIAS when I get my board running...

I don't want to add own DT node for gpio (and all other sub devices).
That shouldn't be needed. It really should be enough to kick the MFD
part from DT using the compatible - subdevices should be load by MFD
without having own DT compatibles for them. But I'll see how this works
out - thanks!

Br,
	Matti Vaittinen

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-21  8:00     ` Vaittinen, Matti
@ 2019-10-21 19:09       ` Jacek Anaszewski
  2019-10-22 12:40         ` Vaittinen, Matti
  0 siblings, 1 reply; 43+ messages in thread
From: Jacek Anaszewski @ 2019-10-21 19:09 UTC (permalink / raw)
  To: Vaittinen, Matti, mazziesaccount, dmurphy
  Cc: linux-leds, linux-rtc, linux-gpio, alexandre.belloni,
	linux-kernel, mturquette, lgirdwood, devicetree, a.zummo,
	linus.walleij, mark.rutland, robh+dt, bgolaszewski, linux-clk,
	lee.jones, pavel, broonie, sboyd

Hi Matti,

On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
> Hello Dan,
> 
> Thanks for taking the time to check my driver :) I truly appreciate all
> the help!
> 
> A "fundamental question" regarding these review comments is whether I
> should add DT entries for these LEDs or not. I thought I shouldn't but
> I would like to get a comment from Rob regarding it.

If the LED controller is a part of MFD device probed from DT then
there is no doubt it should have corresponding DT sub-node.

We've recently added some standardization of LED naming so please
use the new 'function' and 'color' DT properties in the bindings.

Please refer to Documentation/devicetree/bindings/leds/common.txt.

You should register LED class devices using
devm_led_classdev_register_ext() API to benefit from generic support
for LED name composition. This support is available in Linus tree
starting from 5.4-rc1.

> On Thu, 2019-10-17 at 09:04 -0500, Dan Murphy wrote:
>> Matt
>>
>> On 10/17/19 4:53 AM, Matti Vaittinen wrote:
>>> ROHM BD71828 power management IC has two LED outputs for charge
>>> status
>>> and button pressing indications. The LED outputs can also be forced
>>> bs SW so add driver allowing to use these LEDs for other
>>> indications
>> s/bs/by
>>> as well.
>>>
>>> Leds are controlled by SW using 'Force ON' bits. Please note the
>>> constrains mentioned in data-sheet:
>>> 1. If one LED is forced ON - then also the other LED is forced.
>>> 	=> You can't use SW control to force ON one LED and allow HW
>>> 	   to control the other.
>>> 2. You can't force both LEDs OFF. If the FORCE bit for both LED's
>>> is
>>>     zero, then LEDs are controlled by HW and indicate
>>> button/charger
>>>     states as explained in data-sheet.
>>>
>>> Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
>>> ---
>>>   drivers/leds/Kconfig        | 10 ++++
>>>   drivers/leds/Makefile       |  1 +
>>>   drivers/leds/leds-bd71828.c | 97
>>> +++++++++++++++++++++++++++++++++++++
>>>   3 files changed, 108 insertions(+)
>>>   create mode 100644 drivers/leds/leds-bd71828.c
>>>
>>> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
>>> index b0fdeef10bd9..ec59f28bcb39 100644
>>> --- a/drivers/leds/Kconfig
>>> +++ b/drivers/leds/Kconfig
>>> @@ -529,6 +529,16 @@ config LEDS_BD2802
>>>   	  This option enables support for BD2802GU RGB LED driver chips
>>>   	  accessed via the I2C bus.
>>>   
>>> +config LEDS_BD71828
>>> +	tristate "LED driver for LED pins on ROHM BD71828 PMIC"
>>> +	depends on LEDS_CLASS
>> doesn't this have a dependency on MFD_ROHM_BD71828
>>> +	depends on I2C
>>> +	help
>>> +	  This option enables support for LED outputs located on ROHM
>>> +	   BD71828 power management IC. ROHM BD71828 has two led output
>>> pins
>>> +	   which can be left to indicate HW states or controlled by SW.
>>> Say
>>> +	   yes here if you want to enable SW control for these LEDs.
>>> +
>>
>> Add module statement
> 
> What is the module statement? Do you mean the 'if you compile this as a
> module it will be called blahblah' or 'choose M to blahblah'?
> 
> I've never understood why some entries have those statements. 'Choose
> M' stuff is help for config system - why should each module explain how
> to use configs? This information should be in more generic
> documentation. Furthermore, the 'tristate' there already says you can
> compile this as a module. Module name on the other hand really is
> module's property but it may well change if one changes the name of the
> file. That should not require change in KConfig. Furthermore, where do
> you need the module name? And if you really need the module name you
> should check the config name from Makefile to be sure - module/file
> names in comments or docs tend to get outdated.
> 
> After all this being said - I can add any boilerplate text in KConfig
> if necessary - I just see zero benefit from this. And if you didn't
> mean this - can you then please tell me what is the module statement?

Yes, like you noticed, this is boilerplate so please follow the
convention. If you'd like to discuss its relevance please submit
a message to kernel-janitors@vger.kernel.org.

>>>   config LEDS_INTEL_SS4200
>>>   	tristate "LED driver for Intel NAS SS4200 series"
>>>   	depends on LEDS_CLASS
>>> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
>>> index 41fb073a39c1..2a8f6a8e4c7c 100644
>>> --- a/drivers/leds/Makefile
>>> +++ b/drivers/leds/Makefile
>>> @@ -15,6 +15,7 @@ obj-$(CONFIG_LEDS_AN30259A)		+=
>>> leds-an30259a.o
>>>   obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
>>>   obj-$(CONFIG_LEDS_BCM6358)		+= leds-bcm6358.o
>>>   obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
>>> +obj-$(CONFIG_LEDS_BD71828)		+= leds-bd71828.o
>>>   obj-$(CONFIG_LEDS_CPCAP)		+= leds-cpcap.o
>>>   obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
>>>   obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
>>> diff --git a/drivers/leds/leds-bd71828.c b/drivers/leds/leds-
>>> bd71828.c
>>> new file mode 100644
>>> index 000000000000..2427619444f5
>>> --- /dev/null
>>> +++ b/drivers/leds/leds-bd71828.c
>>> @@ -0,0 +1,97 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +// Copyright (C) 2019 ROHM Semiconductors
>>> +
>>> +#include <linux/device.h>
>>> +#include <linux/err.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/leds.h>
>>> +#include <linux/mfd/rohm-bd71828.h>
>>> +#include <linux/module.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ? \
>>> +	container_of((l), struct bd71828_leds, green) : \
>>> +	container_of((l), struct bd71828_leds, amber))
>>
>> I don't think we should be defining the color as the variable. The 
>> outputs can drive any color LED.
> 
> I used the colors mentioned in BD71828 data-sheet. It is true someone
> might use different LEDs on their board but at least this naming allows
> one to match the output to one in data-sheet. I can add comment
> explaining this if you thin it's worth mentioning.

I see you've come up with below definitions in rohm-bd71828.h:

#define BD71828_MASK_LED_AMBER		0x80
#define BD71828_MASK_LED_GREEN		0x40

Is this how those bit fields are named in the data sheet?

>>> +
>>> +enum {
>>> +	ID_GREEN_LED,
>>> +	ID_AMBER_LED,
>>> +	ID_NMBR_OF,
>>> +};
>>> +
>>
>> Please use the color_id in linux/include/dt-bindings/leds/common.h
> 
> Maybe I should not include anything from dt-bindings if I don't use DT
> for this sub-device? (Please see the comments below).

But you should.

>>> +struct bd71828_led {
>>> +	int id;
>>> +	struct led_classdev l;
>>> +	u8 force_mask;
>>> +};
>>> +
>>> +struct bd71828_leds {
>>> +	struct rohm_regmap_dev *bd71828;
>>> +	struct bd71828_led green;
>>> +	struct bd71828_led amber;
>>> +};
>>> +
>>> +static int bd71828_led_brightness_set(struct led_classdev
>>> *led_cdev,
>>> +				      enum led_brightness value)
>>> +{
>>> +	struct bd71828_led *l = container_of(led_cdev, struct
>>> bd71828_led, l);
>>> +	struct bd71828_leds *data;
>>> +	unsigned int val = BD71828_LED_OFF;
>>> +
>>> +	data = BD71828_LED_TO_DATA(l);
>>> +	if (value != LED_OFF)
>>> +		val = BD71828_LED_ON;
>>> +
>>> +	return regmap_update_bits(data->bd71828->regmap,
>>> BD71828_REG_LED_CTRL,
>>> +			    l->force_mask, val);
>>> +}
>>> +
>>> +static int bd71828_led_probe(struct platform_device *pdev)
>>> +{
>>> +	struct rohm_regmap_dev *bd71828;
>>> +	struct bd71828_leds *l;
>>> +	struct bd71828_led *g, *a;
>>> +	static const char *GNAME = "bd71828-green-led";
>>> +	static const char *ANAME = "bd71828-amber-led";
>> The LED class creates the name it can get it from the DT.
> 
> I did not add DT node for LEDs as I thought this is preferred way when
> there is not much HW information to bring from DT. I am not sure as
> previous PMICs I did drivers for didn't have LEDs though. Currently
> this is a MFD sub-device and gets no data from DT. Actually the driver
> crashed when I first didn't explicitly give these names.

Please compare below bindings of MFD devices with LED support:

Documentation/devicetree/bindings/mfd/max77693.txt
Documentation/devicetree/bindings/mfd/ti-lmu.txt

Please follow them but use new 'function' and 'color' properties
instead of 'label'.

>>> +	int ret;
>>> +
>>> +	pr_info("bd71828 LED driver probed\n");
> as a comment from myself - this print should be eliminated or by
> minimum converted to dev_dbg.
> 
>>> +
>>> +	bd71828 = dev_get_drvdata(pdev->dev.parent);
>>> +	l = devm_kzalloc(&pdev->dev, sizeof(*l), GFP_KERNEL);
>>> +	if (!l)
>>> +		return -ENOMEM;
>>> +	l->bd71828 = bd71828;
>>> +	a = &l->amber;
>>> +	g = &l->green;
>>> +	a->id = ID_AMBER_LED;
>>> +	g->id = ID_GREEN_LED;
>>> +	a->force_mask = BD71828_MASK_LED_AMBER;
>>> +	g->force_mask = BD71828_MASK_LED_GREEN;
>>> +
>>> +	a->l.name = ANAME;
>>> +	g->l.name = GNAME;
>>> +	a->l.brightness_set_blocking = bd71828_led_brightness_set;
>>> +	g->l.brightness_set_blocking = bd71828_led_brightness_set;
>>> +
>>> +	ret = devm_led_classdev_register(&pdev->dev, &g->l);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	return devm_led_classdev_register(&pdev->dev, &a->l);

This way you force users to always register two LED class devices
whereas they might need only one. Please compare how other LED class
drivers handle DT parsing and LED class device registration.

>>> +}
>>> +
>>
>> This looks different.  Not sure why you register both LEDs in this
>> probe.
>>
>> You can use the DT to define both LEDs and then each will be probed
>> and 
>> registered separately.
> 
> As I mentioned above, this driver is currently not using DT at all.
> Reason why it does not is that I didn't know all the LEDs are usually
> having own DT entries/drivers.
> 
> But there is actually reason for bundling the LEDs to same driver. What
> is not shown in driver is that LEDs can be controlled by PMIC state
> machine so that they are indicating charger states. Other option is

This can be handled by the LED trigger that your driver should expose.
On activation the trigger would setup the hardware to control the
LEDs. But that can be covered later.

> driving LEDs using this driver - but forcing one of the LEDs will cause
> also the other LED to be under SW control. Eg, one can't control just
> single LED using SW, its both of them or none.

So this limitation will have to by documented in the trigger ABI.

>> This is how it is commonly done.
>>
>> You can reference the LM36274 led driver as this is a MFD device to
>> the 
>> ti-lmu.c in the MFD directory.
> 
> Thanks for pointing this out. I will gladly see how others have it
> implemented! I just would like to know if the DT binding is really a
> must? In this case I am unsure what LED related extra information we
> could really give from DT (for this specific device).
> 
> I just checked the lm36274 you mentioned. I see that also lm36274 do
> parse the dt and set the name itself (so maybe the led_class is not
> doing this after all?) - although the name setting code in lm36274 is a
> bit peculiar as it loops through all child nodes and overwrites the old
> name if it finds more than one "label" properties from nodes (if I read
> the code correctly).
> 
> In any case I am unsure what is the benefit from using DT and adding
> the DT parsing code for this PMIC's LEDs. I could understand DT usage
> if LED class handled dt parsing and if there was something to configure
> in BD71828 LEDs - but I see no such benefits in this case.

I hope I was able to clarify most of your doubts.

-- 
Best regards,
Jacek Anaszewski

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-21 19:09       ` Jacek Anaszewski
@ 2019-10-22 12:40         ` Vaittinen, Matti
  2019-10-22 17:40           ` Jacek Anaszewski
  0 siblings, 1 reply; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-22 12:40 UTC (permalink / raw)
  To: mazziesaccount, dmurphy, jacek.anaszewski
  Cc: linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mturquette, lgirdwood, devicetree,
	linus.walleij, a.zummo, mark.rutland, robh+dt, bgolaszewski,
	linux-clk, lee.jones, pavel, broonie, sboyd

Hello Jacek,

Thanks for the clarifications. I think I now understand the LED
subsystem a bit better :)

On Mon, 2019-10-21 at 21:09 +0200, Jacek Anaszewski wrote:
> Hi Matti,
> 
> On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
> > Hello Dan,
> > 
> > Thanks for taking the time to check my driver :) I truly appreciate
> > all
> > the help!
> > 
> > A "fundamental question" regarding these review comments is whether
> > I
> > should add DT entries for these LEDs or not. I thought I shouldn't
> > but
> > I would like to get a comment from Rob regarding it.
> 
> If the LED controller is a part of MFD device probed from DT then
> there is no doubt it should have corresponding DT sub-node.

Sorry but I still see no much benefit from adding this information in
DT. Why should it have corresponding DT-node if the LED properties are
fixed and if we only wish to allow user-space control and have no
dependencies to other devices in DT? 

In this specific case the information we can provide from DT is
supposed to be fixed. No board based variation. Furthermore, there is
not much generic driver/led core functionality which would be able to
parse and utilize relevant information from DT. I think we can only
give the name (function) and colour. And they are supposed to be fixed
and thus could be just hard-coded in driver. Hard-coding these would be
simpler and less error prone for users (no DT bindings to write) and
simpler to create and probably also to maintain (no separate binding
documents needed for LEDs).

But assuming this is Ok to DT-folks and if you insist - I will add LED
information to DT for the next patches. Hopefully this extra complexity
helps in some oddball use-case which I can't foresee =)

Then what comes to the DT format.

Do you think LED subsystem should try to follow the convention with
other sub-systems and not introduce multiple compatibles for single
device? MFD can handle instantiating the sub-devices just fine even
when sub-devices have no own compatible property or of_match. Maybe we
should also avoid unnecessary sub-nodes when they are not really
required.

If we look at clk and GPIOs it is nowadays preferred (as per my
understanding after discussions with Stephen, Rob and some others -
please correct me if I am wrong) to place the 'provider' information in
the MFD node and obtain the relevant properties from parent->of_node in
the sub-device driver (or for generic properties, in the core
framework) - at least for simple cases. I guess rationale was that it
reflects the real hardware better when no artificial sub-nodes are
required?

I see the example bindings (like max77693 below) you pointed to me
don't follow that convention but create own sub nodes with own
compatible properties in MFD for all the logical blocks.

I am asking this as I was "strongly advieced" (a.k.a told to change my
approach if I wish to get driver ion their trees ;]) by Rob and Stephen
to not do that with previous PMIC drivers I upstreamed.

As example - the relevant bindings for BD71837 clock output were
originally:

pmic: bd71837@4b {
	compatible = "rohm,bd71837";

	regulators {
		...
	};

	/* Clock node */
	clk: bd71837-32k-out {
		compatible = "rohm,bd71837-clk";
		#clock-cells = <0>;
		clock-frequency  = <32768>;
		clock-output-names = "bd71837-32k-out";
	};
};

but it was preferred to not have the the clk sub-node and place the
provider information directly in pmic node instead. Result was:

pmic: bd71837@4b {
	compatible = "rohm,bd71837";

	#clock-cells = <0>;
	clock-frequency  = <32768>;
	clock-output-names = "bd71837-32k-out";

	regulators {
		...
	};
};

clk consumers can then live anywhere in DT and just refer to clock
provider via reference.

The sub-device driver is instantiated by MFD and it does not need the
DT (or compatible) for this. After that the sub-driver can fetch the
parent DT node and looks for relevant information - or just passes that
node to core if it expects no driver specific properties.

The bd70528 is another example. It is a MFD with drivers for
regulators, clk-gate, few GPIOs, watchdog, charger and rtc. It still
has only one compatible in DT, and only MFD node with regulators sub-
node. There is no need for WDG, RTC or charger to be present in DT. (In
my books LEDs for BD71828 would fall in the same category but I guess I
already mentioned that xD ).

pmic: pmic@4b {
	compatible = "rohm,bd70528";
	reg = <0x4b>;
	interrupt-parent = <&gpio1>;
	interrupts = <29 GPIO_ACTIVE_LOW>;
	clocks = <&osc 0>;
	#clock-cells = <0>;
	clock-output-names = "bd70528-32k-out";
	#gpio-cells = <2>;
	gpio-controller;
	interrupt-controller;
	#interrupt-cells = <2>;

	regulators {
		...
	};
};

So, to make LED subsystem at least partially follow same principle I
suggest I'll do following:

Where max77693 does:
max77693@66 {
		compatible = "maxim,max77693";
		reg = <0x66>;
		interrupt-parent = <&gpx1>;
		interrupts = <5 2>;

		regulators {
			esafeout@1 {
				regulator-compatible = "ESAFEOUT1";
				...
			};
		};

		haptic {
			compatible = "maxim,max77693-haptic";
			...
		};

		charger {
			compatible = "maxim,max77693-charger";
			...
		};

		led {
			compatible = "maxim,max77693-led";
			...

			camera_flash: flash-led {
				label = "max77693-flash";
				...
			};
		};
	};

I would go with:

	pmic: pmic@4b {
		compatible = "rohm,bd71828";
		reg = <0x4b>;
		interrupt-parent = <&gpio1>;
		interrupts = <29 GPIO_ACTIVE_LOW>;
		clocks = <&osc 0>;
		#clock-cells = <0>;
		clock-output-names = "bd71828-32k-out";
		gpio-controller;
		#gpio-cells = <2>;
		ngpios = <4>;
		gpio-reserved-ranges = <0 1 2 1>;
		gpio-line-names = "EPDEN";
		rohm,dvs-vsel-gpios = <&gpio1 12 0>,
				      <&gpio1 13 0>;
		regulators {
			...
		};
		
		chg-led {
			function = LED_FUNCTION_CHARGING;
			color = LED_COLOR_ID_AMBER;
		};

		pwr-led {
			function = LED_FUNCTION_POWER;
			color = LED_COLOR_ID_GREEN;
		};
	};

How do you see this? Or do you really wish to have this one extra node:

	pmic: pmic@4b {
		compatible = "rohm,bd71828";
		
reg = <0x4b>;
		interrupt-parent = <&gpio1>;
		interru
pts = <29 GPIO_ACTIVE_LOW>;
		clocks = <&osc 0>;
		
#clock-cells = <0>;
		clock-output-names = "bd71828-32k-out";
		gpio-controller;
		#gpio-cells = <2>;
	
	ngpios = <4>;
		gpio-reserved-ranges = <0 1 2 1>;
	
	gpio-line-names = "EPDEN";
		rohm,dvs-vsel-gpios =
<&gpio1 12 0>,
				      <&gpio1 13 0>;
		
regulators {
			...
		};
		
		leds-dummy {
			chg-led {
				function = LED_FUNCTION_CHARGING;
				color = LED_COLOR_ID_AMBER;
			};

			pwr-led {
				function = LED_FUNCTION_POWER;
				color = LED_COLOR_ID_GREEN;
			};
		};
	};


> We've recently added some standardization of LED naming so please
> use the new 'function' and 'color' DT properties in the bindings.
> 
> Please refer to Documentation/devicetree/bindings/leds/common.txt.
> 
> You should register LED class devices using
> devm_led_classdev_register_ext() API to benefit from generic support
> for LED name composition. This support is available in Linus tree
> starting from 5.4-rc1.

Thanks for the hint. So this API would make the name out of function
and colour given via dt, right?

> > On Thu, 2019-10-17 at 09:04 -0500, Dan Murphy wrote:
> > > Matt
> > > 
> > > On 10/17/19 4:53 AM, Matti Vaittinen wrote:
> > > > ROHM BD71828 power management IC has two LED outputs for charge
> > > > status
> > > > and button pressing indications. The LED outputs can also be
> > > > forced
> > > > bs SW so add driver allowing to use these LEDs for other
> > > > indications
> > > s/bs/by
> > > > as well.
> > > > 
> > > > Leds are controlled by SW using 'Force ON' bits. Please note
> > > > the
> > > > constrains mentioned in data-sheet:
> > > > 1. If one LED is forced ON - then also the other LED is forced.
> > > > 	=> You can't use SW control to force ON one LED and
> > > > allow HW
> > > > 	   to control the other.
> > > > 2. You can't force both LEDs OFF. If the FORCE bit for both
> > > > LED's
> > > > is
> > > >     zero, then LEDs are controlled by HW and indicate
> > > > button/charger
> > > >     states as explained in data-sheet.
> > > > 
> > > > Signed-off-by: Matti Vaittinen <
> > > > matti.vaittinen@fi.rohmeurope.com>
> > > > ---
> > > >   drivers/leds/Kconfig        | 10 ++++
> > > >   drivers/leds/Makefile       |  1 +
> > > >   drivers/leds/leds-bd71828.c | 97
> > > > +++++++++++++++++++++++++++++++++++++
> > > >   3 files changed, 108 insertions(+)
> > > >   create mode 100644 drivers/leds/leds-bd71828.c
> > > > 
> > > > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> > > > index b0fdeef10bd9..ec59f28bcb39 100644
> > > > --- a/drivers/leds/Kconfig
> > > > +++ b/drivers/leds/Kconfig
> > > > @@ -529,6 +529,16 @@ config LEDS_BD2802
> > > >   	  This option enables support for BD2802GU RGB LED
> > > > driver chips
> > > >   	  accessed via the I2C bus.
> > > >   
> > > > +config LEDS_BD71828
> > > > +	tristate "LED driver for LED pins on ROHM BD71828 PMIC"
> > > > +	depends on LEDS_CLASS
> > > doesn't this have a dependency on MFD_ROHM_BD71828
> > > > +	depends on I2C
> > > > +	help
> > > > +	  This option enables support for LED outputs located
> > > > on ROHM
> > > > +	   BD71828 power management IC. ROHM BD71828 has two
> > > > led output
> > > > pins
> > > > +	   which can be left to indicate HW states or
> > > > controlled by SW.
> > > > Say
> > > > +	   yes here if you want to enable SW control for these
> > > > LEDs.
> > > > +
> > > 
> > > Add module statement
> > 
> > What is the module statement? Do you mean the 'if you compile this
> > as a
> > module it will be called blahblah' or 'choose M to blahblah'?
> > 
> > I've never understood why some entries have those statements.
> > 'Choose
> > M' stuff is help for config system - why should each module explain
> > how
> > to use configs? This information should be in more generic
> > documentation. Furthermore, the 'tristate' there already says you
> > can
> > compile this as a module. Module name on the other hand really is
> > module's property but it may well change if one changes the name of
> > the
> > file. That should not require change in KConfig. Furthermore, where
> > do
> > you need the module name? And if you really need the module name
> > you
> > should check the config name from Makefile to be sure - module/file
> > names in comments or docs tend to get outdated.
> > 
> > After all this being said - I can add any boilerplate text in
> > KConfig
> > if necessary - I just see zero benefit from this. And if you didn't
> > mean this - can you then please tell me what is the module
> > statement?
> 
> Yes, like you noticed, this is boilerplate so please follow the
> convention. If you'd like to discuss its relevance please submit
> a message to kernel-janitors@vger.kernel.org.

I did follow the convention. There is 67 tristated LED drivers which do
NOT add this module building babbling in description. Then there is 14
drivers which do. So common convention even in LED subsystem is to NOT
include meaningless mumbojumbo there.

So even regarding convention it is better to have short description to
the point. That actually makes the requiring boilerplate even more
useless. But as I said, I can put any meaningless letters there.
(again, if I can't convince you to reconsider how you like the LED
subsystem to appear like). Knowing how hard it is to help people
reducing waste - it's may be easier for me than discussing this further
:(

> 
> > > >   config LEDS_INTEL_SS4200
> > > >   	tristate "LED driver for Intel NAS SS4200 series"
> > > >   	depends on LEDS_CLASS
> > > > diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> > > > index 41fb073a39c1..2a8f6a8e4c7c 100644
> > > > --- a/drivers/leds/Makefile
> > > > +++ b/drivers/leds/Makefile
> > > > @@ -15,6 +15,7 @@ obj-$(CONFIG_LEDS_AN30259A)		+=
> > > > leds-an30259a.o
> > > >   obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
> > > >   obj-$(CONFIG_LEDS_BCM6358)		+= leds-bcm6358.o
> > > >   obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
> > > > +obj-$(CONFIG_LEDS_BD71828)		+= leds-bd71828.o
> > > >   obj-$(CONFIG_LEDS_CPCAP)		+= leds-cpcap.o
> > > >   obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
> > > >   obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
> > > > diff --git a/drivers/leds/leds-bd71828.c b/drivers/leds/leds-
> > > > bd71828.c
> > > > new file mode 100644
> > > > index 000000000000..2427619444f5
> > > > --- /dev/null
> > > > +++ b/drivers/leds/leds-bd71828.c
> > > > @@ -0,0 +1,97 @@
> > > > +// SPDX-License-Identifier: GPL-2.0
> > > > +// Copyright (C) 2019 ROHM Semiconductors
> > > > +
> > > > +#include <linux/device.h>
> > > > +#include <linux/err.h>
> > > > +#include <linux/kernel.h>
> > > > +#include <linux/leds.h>
> > > > +#include <linux/mfd/rohm-bd71828.h>
> > > > +#include <linux/module.h>
> > > > +#include <linux/platform_device.h>
> > > > +#include <linux/regmap.h>
> > > > +#include <linux/slab.h>
> > > > +
> > > > +#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ? \
> > > > +	container_of((l), struct bd71828_leds, green) : \
> > > > +	container_of((l), struct bd71828_leds, amber))
> > > 
> > > I don't think we should be defining the color as the variable.
> > > The 
> > > outputs can drive any color LED.
> > 
> > I used the colors mentioned in BD71828 data-sheet. It is true
> > someone
> > might use different LEDs on their board but at least this naming
> > allows
> > one to match the output to one in data-sheet. I can add comment
> > explaining this if you thin it's worth mentioning.
> 
> I see you've come up with below definitions in rohm-bd71828.h:
> 
> #define BD71828_MASK_LED_AMBER		0x80
> #define BD71828_MASK_LED_GREEN		0x40
> 
> Is this how those bit fields are named in the data sheet?

The leds are through the document referred as "GRNLED" and "AMBLED".
These specific bits are named "AMBLED_FORCE_ON" and "GRNLED_FORCE_ON".

> 
> > > > +
> > > > +enum {
> > > > +	ID_GREEN_LED,
> > > > +	ID_AMBER_LED,
> > > > +	ID_NMBR_OF,
> > > > +};
> > > > +
> > > 
> > > Please use the color_id in linux/include/dt-
> > > bindings/leds/common.h
> > 
> > Maybe I should not include anything from dt-bindings if I don't use
> > DT
> > for this sub-device? (Please see the comments below).
> 
> But you should.

Is this your final say? You think I can't convince you to reconsider?

Anyways, this is LED ID, not color property. Eg, this is not defining
the color, this is identifying the LED. Please see the macro:

#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ? \
        container_of((l), struct bd71828_leds, green) : \
        container_of((l), struct bd71828_leds, amber))

which is used to obtain the pointer to driver data based on pointer to
led structure. Eg, we get the correct offset by determining which LED
was contolled (based on the LED ID).

> 
> > > > +struct bd71828_led {
> > > > +	int id;
> > > > +	struct led_classdev l;
> > > > +	u8 force_mask;
> > > > +};
> > > > +
> > > > +struct bd71828_leds {
> > > > +	struct rohm_regmap_dev *bd71828;
> > > > +	struct bd71828_led green;
> > > > +	struct bd71828_led amber;
> > > > +};
> > > > +
> > > > +static int bd71828_led_brightness_set(struct led_classdev
> > > > *led_cdev,
> > > > +				      enum led_brightness
> > > > value)
> > > > +{
> > > > +	struct bd71828_led *l = container_of(led_cdev, struct
> > > > bd71828_led, l);
> > > > +	struct bd71828_leds *data;
> > > > +	unsigned int val = BD71828_LED_OFF;
> > > > +
> > > > +	data = BD71828_LED_TO_DATA(l);
> > > > +	if (value != LED_OFF)
> > > > +		val = BD71828_LED_ON;
> > > > +
> > > > +	return regmap_update_bits(data->bd71828->regmap,
> > > > BD71828_REG_LED_CTRL,
> > > > +			    l->force_mask, val);
> > > > +}
> > > > +
> > > > +static int bd71828_led_probe(struct platform_device *pdev)
> > > > +{
> > > > +	struct rohm_regmap_dev *bd71828;
> > > > +	struct bd71828_leds *l;
> > > > +	struct bd71828_led *g, *a;
> > > > +	static const char *GNAME = "bd71828-green-led";
> > > > +	static const char *ANAME = "bd71828-amber-led";
> > > The LED class creates the name it can get it from the DT.
> > 
> > I did not add DT node for LEDs as I thought this is preferred way
> > when
> > there is not much HW information to bring from DT. I am not sure as
> > previous PMICs I did drivers for didn't have LEDs though. Currently
> > this is a MFD sub-device and gets no data from DT. Actually the
> > driver
> > crashed when I first didn't explicitly give these names.
> 
> Please compare below bindings of MFD devices with LED support:
> 
> Documentation/devicetree/bindings/mfd/max77693.txt
> Documentation/devicetree/bindings/mfd/ti-lmu.txt
> 
> Please follow them but use new 'function' and 'color' properties
> instead of 'label'.
> 
> > > > +	int ret;
> > > > +
> > > > +	pr_info("bd71828 LED driver probed\n");
> > as a comment from myself - this print should be eliminated or by
> > minimum converted to dev_dbg.
> > 
> > > > +
> > > > +	bd71828 = dev_get_drvdata(pdev->dev.parent);
> > > > +	l = devm_kzalloc(&pdev->dev, sizeof(*l), GFP_KERNEL);
> > > > +	if (!l)
> > > > +		return -ENOMEM;
> > > > +	l->bd71828 = bd71828;
> > > > +	a = &l->amber;
> > > > +	g = &l->green;
> > > > +	a->id = ID_AMBER_LED;
> > > > +	g->id = ID_GREEN_LED;
> > > > +	a->force_mask = BD71828_MASK_LED_AMBER;
> > > > +	g->force_mask = BD71828_MASK_LED_GREEN;
> > > > +
> > > > +	a->l.name = ANAME;
> > > > +	g->l.name = GNAME;
> > > > +	a->l.brightness_set_blocking =
> > > > bd71828_led_brightness_set;
> > > > +	g->l.brightness_set_blocking =
> > > > bd71828_led_brightness_set;
> > > > +
> > > > +	ret = devm_led_classdev_register(&pdev->dev, &g->l);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	return devm_led_classdev_register(&pdev->dev, &a->l);
> 
> This way you force users to always register two LED class devices
> whereas they might need only one. Please compare how other LED class
> drivers handle DT parsing and LED class device registration.

I am not sure if I understand correctly what you mean by using only one
class device. As I (hopefully) somewhere said - users can't control
only one of these LEDs. If they decide to enable one led by SW, then
they inevitably control also the other. Thus it is better that user
gets control to both of the LEDs if they take the control for one.

Or do you mean I could achieve the control for both of these LEDs via
only one class device?

> 
> > > > +}
> > > > +
> > > 
> > > This looks different.  Not sure why you register both LEDs in
> > > this
> > > probe.
> > > 
> > > You can use the DT to define both LEDs and then each will be
> > > probed
> > > and 
> > > registered separately.
> > 
> > As I mentioned above, this driver is currently not using DT at all.
> > Reason why it does not is that I didn't know all the LEDs are
> > usually
> > having own DT entries/drivers.
> > 
> > But there is actually reason for bundling the LEDs to same driver.
> > What
> > is not shown in driver is that LEDs can be controlled by PMIC state
> > machine so that they are indicating charger states. Other option is
> 
> This can be handled by the LED trigger that your driver should
> expose.
> On activation the trigger would setup the hardware to control the
> LEDs. But that can be covered later.

Yet another thing for me to learn =) I looked at the trigger properties
in DT. That looked like a way to make the LED framework to "bind" the
LED state to some trigger. (For example make the LED framework to
toggle specific LED state when USB device is plugged?)

If this is the case then it might not be relevant for BD71828. Here the
LED is by-default controlled by HW. Eg, when charger starts charging
the battery, the PMIC will lit the LED. It will do so also when power
button is pressed or certain problems are detected. This reqires no SW
interaction.

What this driver intends to do is to allow SW to take over this. Eg, if
system is designed so that it is preferably to use these LEDs for some
other purpose it can be done by loading this LED driver and allowing
user-space to control these LEDs via sysfs.

> > driving LEDs using this driver - but forcing one of the LEDs will
> > cause
> > also the other LED to be under SW control. Eg, one can't control
> > just
> > single LED using SW, its both of them or none.
> 
> So this limitation will have to by documented in the trigger ABI.
> 
> > > This is how it is commonly done.
> > > 
> > > You can reference the LM36274 led driver as this is a MFD device
> > > to
> > > the 
> > > ti-lmu.c in the MFD directory.
> > 
> > Thanks for pointing this out. I will gladly see how others have it
> > implemented! I just would like to know if the DT binding is really
> > a
> > must? In this case I am unsure what LED related extra information
> > we
> > could really give from DT (for this specific device).
> > 
> > I just checked the lm36274 you mentioned. I see that also lm36274
> > do
> > parse the dt and set the name itself (so maybe the led_class is not
> > doing this after all?) - although the name setting code in lm36274
> > is a
> > bit peculiar as it loops through all child nodes and overwrites the
> > old
> > name if it finds more than one "label" properties from nodes (if I
> > read
> > the code correctly).
> > 
> > In any case I am unsure what is the benefit from using DT and
> > adding
> > the DT parsing code for this PMIC's LEDs. I could understand DT
> > usage
> > if LED class handled dt parsing and if there was something to
> > configure
> > in BD71828 LEDs - but I see no such benefits in this case.
> 
> I hope I was able to clarify most of your doubts.

Thanks again for taking the time to check this out. I appreciate the
effort you guys put into this! And what comes to my doubts - yes, I
still have a few ;) But I am sure we can work this out. Thanks a bunch
for this discussion Jacek!

Br,
	Matti Vaittinen




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

* Re: [RFC PATCH 10/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs
  2019-10-21 14:36       ` Bartosz Golaszewski
  2019-10-21 14:56         ` Vaittinen, Matti
@ 2019-10-22 13:19         ` Vaittinen, Matti
  1 sibling, 0 replies; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-22 13:19 UTC (permalink / raw)
  To: bgolaszewski
  Cc: dmurphy, linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mazziesaccount, mturquette, devicetree,
	jacek.anaszewski, linus.walleij, a.zummo, lgirdwood,
	mark.rutland, robh+dt, linux-clk, pavel, sboyd, broonie,
	lee.jones

Hello Bartosz,

Just a short note at this point.

On Mon, 2019-10-21 at 16:36 +0200, Bartosz Golaszewski wrote:
> pon., 21 paź 2019 o 09:00 Vaittinen, Matti
> <Matti.Vaittinen@fi.rohmeurope.com> napisał(a):
> > On Thu, 2019-10-17 at 14:45 +0200, Bartosz Golaszewski wrote:
> > > czw., 17 paź 2019 o 11:53 Matti Vaittinen
> > > 
> > > > +static struct platform_driver bd71828_gpio = {
> > > > +       .driver = {
> > > > +               .name = "bd71828-gpio"
> > > > +       },
> > > > +       .probe = bd71828_probe,
> > > > +};
> > > > +
> > > > +module_platform_driver(bd71828_gpio);
> > > > +
> > > > +MODULE_AUTHOR("Matti Vaittinen <
> > > > matti.vaittinen@fi.rohmeurope.com>
> > > > ");
> > > > +MODULE_DESCRIPTION("BD71828 voltage regulator driver");
> > > > +MODULE_LICENSE("GPL");
> > > 
> > > Don't you need a MODULE_ALIAS() here since this is an MFD sub-
> > > module?
> > 
> > I must admit I don't know the details of how module loading is
> > done. I
> > used system where modules are load by scripts. (I guess the module
> > alias could be used to allow automatic module loading [by udev?])
> > 
> > Can you please educate me - If I add module aliases matching the
> > sub-
> > device name given in in MFD cell - should the sub module loading be
> > automatic when MFD driver gets probed? For some reason I didn't get
> > that working on my test bed. Or maybe I misunderstood something.
> > 
> 
> If the gpio module is a sub-node on the device tree than you may need
> to use a sub-compatible to get the module loaded by udev.
> 
> Bart
> 
> > Eg, this should be enough for GPIO sub-module to be also load:
> > 
> > MFD:
> > static struct mfd_cell bd71828_mfd_cells[] = {
> >         { .name = "bd71828-pmic", },
> >         { .name = "bd71828-gpio", },
> > ...
> > ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
> >                            bd71828_mfd_cells,
> >                            ARRAY_SIZE(bd71828_mfd_cells), NULL, 0,
> >                            regmap_irq_get_domain(irq_data));
> > 
> > GPIO driver:
> > MODULE_ALIAS("platform:bd71828-gpio");
> > 
> > I had the sub-devices probed even without the MODULE_ALIAS - but
> > manual
> > loading is required. I will gladly add the alias if it should
> > enable
> > the automatic module loading.

I had some problems with my test setup. After I fixed those I was able
to test the automatic module loading. The sub-devices do not need own
compatible to be load - they only need the correct module_alias:
"platform:sub-device-name-used-in-mfd-cell". If this is added, the
module for sub-device is load when MFD cell with name matching the
alias is instantiated.

So to your original question - yes, MODULE_ALIAS() is needed for
automated loading. No compatible is required then :)

Thanks for pointing me to correct direction here! It's always nice to
learn something new!

Br,
	Matti Vaittinen

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-22 12:40         ` Vaittinen, Matti
@ 2019-10-22 17:40           ` Jacek Anaszewski
  2019-10-23  8:37             ` Vaittinen, Matti
  0 siblings, 1 reply; 43+ messages in thread
From: Jacek Anaszewski @ 2019-10-22 17:40 UTC (permalink / raw)
  To: Vaittinen, Matti, mazziesaccount, dmurphy
  Cc: linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mturquette, lgirdwood, devicetree,
	linus.walleij, a.zummo, mark.rutland, robh+dt, bgolaszewski,
	linux-clk, lee.jones, pavel, broonie, sboyd

Matti,

On 10/22/19 2:40 PM, Vaittinen, Matti wrote:
> Hello Jacek,
> 
> Thanks for the clarifications. I think I now understand the LED
> subsystem a bit better :)
> 
> On Mon, 2019-10-21 at 21:09 +0200, Jacek Anaszewski wrote:
>> Hi Matti,
>>
>> On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
>>> Hello Dan,
>>>
>>> Thanks for taking the time to check my driver :) I truly appreciate
>>> all
>>> the help!
>>>
>>> A "fundamental question" regarding these review comments is whether
>>> I
>>> should add DT entries for these LEDs or not. I thought I shouldn't
>>> but
>>> I would like to get a comment from Rob regarding it.
>>
>> If the LED controller is a part of MFD device probed from DT then
>> there is no doubt it should have corresponding DT sub-node.
> 
> Sorry but I still see no much benefit from adding this information in
> DT. Why should it have corresponding DT-node if the LED properties are
> fixed and if we only wish to allow user-space control and have no
> dependencies to other devices in DT? 
> 
> In this specific case the information we can provide from DT is
> supposed to be fixed. No board based variation. Furthermore, there is
> not much generic driver/led core functionality which would be able to
> parse and utilize relevant information from DT. I think we can only
> give the name (function) and colour. And they are supposed to be fixed
> and thus could be just hard-coded in driver. Hard-coding these would be
> simpler and less error prone for users (no DT bindings to write) and
> simpler to create and probably also to maintain (no separate binding
> documents needed for LEDs).

AFAICS it is possible to connect LED of arbitrary color to the iouts
of this device. If this is the case then it is justified to have DT
node only to allow for LED name customization.

> But assuming this is Ok to DT-folks and if you insist - I will add LED
> information to DT for the next patches. Hopefully this extra complexity
> helps in some oddball use-case which I can't foresee =)
> 
> Then what comes to the DT format.
> 
> Do you think LED subsystem should try to follow the convention with
> other sub-systems and not introduce multiple compatibles for single
> device? MFD can handle instantiating the sub-devices just fine even
> when sub-devices have no own compatible property or of_match. Maybe we
> should also avoid unnecessary sub-nodes when they are not really
> required.

This is beyond my scope of responsibility. It is MFD subsystem thing to
choose the way of LED class driver instantiation. When it comes to
LED subsystem - it expects single compatible pertaining to a physical
device.

Nonetheless, so far we used to have separate compatibles for drivers of
MFD devices' LED cells. If we are going to change that I'd like to see
explicit DT maintainer's statement confirming that.

And one benefit of having separate nodes per MFD cells is that we can
easily discern the support for which cells is to be turned on.

> If we look at clk and GPIOs it is nowadays preferred (as per my
> understanding after discussions with Stephen, Rob and some others -
> please correct me if I am wrong) to place the 'provider' information in
> the MFD node and obtain the relevant properties from parent->of_node in
> the sub-device driver (or for generic properties, in the core
> framework) - at least for simple cases. I guess rationale was that it
> reflects the real hardware better when no artificial sub-nodes are
> required?
> 
> I see the example bindings (like max77693 below) you pointed to me
> don't follow that convention but create own sub nodes with own
> compatible properties in MFD for all the logical blocks.
> 
> I am asking this as I was "strongly advieced" (a.k.a told to change my
> approach if I wish to get driver ion their trees ;]) by Rob and Stephen
> to not do that with previous PMIC drivers I upstreamed.
> 
> As example - the relevant bindings for BD71837 clock output were
> originally:
> 
> pmic: bd71837@4b {
> 	compatible = "rohm,bd71837";
> 
> 	regulators {
> 		...
> 	};
> 
> 	/* Clock node */
> 	clk: bd71837-32k-out {
> 		compatible = "rohm,bd71837-clk";
> 		#clock-cells = <0>;
> 		clock-frequency  = <32768>;
> 		clock-output-names = "bd71837-32k-out";
> 	};
> };
> 
> but it was preferred to not have the the clk sub-node and place the
> provider information directly in pmic node instead. Result was:
> 
> pmic: bd71837@4b {
> 	compatible = "rohm,bd71837";
> 
> 	#clock-cells = <0>;
> 	clock-frequency  = <32768>;
> 	clock-output-names = "bd71837-32k-out";
> 
> 	regulators {
> 		...
> 	};
> };
> 
> clk consumers can then live anywhere in DT and just refer to clock
> provider via reference.
> 
> The sub-device driver is instantiated by MFD and it does not need the
> DT (or compatible) for this. After that the sub-driver can fetch the
> parent DT node and looks for relevant information - or just passes that
> node to core if it expects no driver specific properties.
> 
> The bd70528 is another example. It is a MFD with drivers for
> regulators, clk-gate, few GPIOs, watchdog, charger and rtc. It still
> has only one compatible in DT, and only MFD node with regulators sub-
> node. There is no need for WDG, RTC or charger to be present in DT. (In
> my books LEDs for BD71828 would fall in the same category but I guess I
> already mentioned that xD ).
> 
> pmic: pmic@4b {
> 	compatible = "rohm,bd70528";
> 	reg = <0x4b>;
> 	interrupt-parent = <&gpio1>;
> 	interrupts = <29 GPIO_ACTIVE_LOW>;
> 	clocks = <&osc 0>;
> 	#clock-cells = <0>;
> 	clock-output-names = "bd70528-32k-out";
> 	#gpio-cells = <2>;
> 	gpio-controller;
> 	interrupt-controller;
> 	#interrupt-cells = <2>;
> 
> 	regulators {
> 		...
> 	};
> };
> 
> So, to make LED subsystem at least partially follow same principle I
> suggest I'll do following:
> 
> Where max77693 does:
> max77693@66 {
> 		compatible = "maxim,max77693";
> 		reg = <0x66>;
> 		interrupt-parent = <&gpx1>;
> 		interrupts = <5 2>;
> 
> 		regulators {
> 			esafeout@1 {
> 				regulator-compatible = "ESAFEOUT1";
> 				...
> 			};
> 		};
> 
> 		haptic {
> 			compatible = "maxim,max77693-haptic";
> 			...
> 		};
> 
> 		charger {
> 			compatible = "maxim,max77693-charger";
> 			...
> 		};
> 
> 		led {
> 			compatible = "maxim,max77693-led";
> 			...
> 
> 			camera_flash: flash-led {
> 				label = "max77693-flash";
> 				...
> 			};
> 		};
> 	};
> 
> I would go with:
> 
> 	pmic: pmic@4b {
> 		compatible = "rohm,bd71828";
> 		reg = <0x4b>;
> 		interrupt-parent = <&gpio1>;
> 		interrupts = <29 GPIO_ACTIVE_LOW>;
> 		clocks = <&osc 0>;
> 		#clock-cells = <0>;
> 		clock-output-names = "bd71828-32k-out";
> 		gpio-controller;
> 		#gpio-cells = <2>;
> 		ngpios = <4>;
> 		gpio-reserved-ranges = <0 1 2 1>;
> 		gpio-line-names = "EPDEN";
> 		rohm,dvs-vsel-gpios = <&gpio1 12 0>,
> 				      <&gpio1 13 0>;
> 		regulators {
> 			...
> 		};
> 		
> 		chg-led {
> 			function = LED_FUNCTION_CHARGING;
> 			color = LED_COLOR_ID_AMBER;
> 		};
> 
> 		pwr-led {
> 			function = LED_FUNCTION_POWER;
> 			color = LED_COLOR_ID_GREEN;
> 		};

This way you would probably need to probe LED class driver twice,
instead of letting it behave in a standard way and parse child LED
nodes.

> 	};
> 
> How do you see this? Or do you really wish to have this one extra node:
> 
> 	pmic: pmic@4b {
> 		compatible = "rohm,bd71828";
> 		
> reg = <0x4b>;
> 		interrupt-parent = <&gpio1>;
> 		interru
> pts = <29 GPIO_ACTIVE_LOW>;
> 		clocks = <&osc 0>;
> 		
> #clock-cells = <0>;
> 		clock-output-names = "bd71828-32k-out";
> 		gpio-controller;
> 		#gpio-cells = <2>;
> 	
> 	ngpios = <4>;
> 		gpio-reserved-ranges = <0 1 2 1>;
> 	
> 	gpio-line-names = "EPDEN";
> 		rohm,dvs-vsel-gpios =
> <&gpio1 12 0>,
> 				      <&gpio1 13 0>;
> 		
> regulators {
> 			...
> 		};
> 		
> 		leds-dummy {

Why leds-dummy ?

The convention is to have led-controller@unit-address as the parent LED
controller node.

> 			chg-led {
s/chg-led/led0/

> 				function = LED_FUNCTION_CHARGING;
> 				color = LED_COLOR_ID_AMBER;
> 			};
> 
> 			pwr-led {

s/pwr-led/led1/

This is ePAPR requirement that DT node name should describe the
general class of device.

> 				function = LED_FUNCTION_POWER;
> 				color = LED_COLOR_ID_GREEN;
> 			};

Common LED bindings say this is the proper way to go. However you
would need compatible to probe LED class driver in DT based way.
If you plan to do it otherwise then it makes no sense to have
DT nodes for LEDs.

> 		};
> 	};
> 
> 
>> We've recently added some standardization of LED naming so please
>> use the new 'function' and 'color' DT properties in the bindings.
>>
>> Please refer to Documentation/devicetree/bindings/leds/common.txt.
>>
>> You should register LED class devices using
>> devm_led_classdev_register_ext() API to benefit from generic support
>> for LED name composition. This support is available in Linus tree
>> starting from 5.4-rc1.
> 
> Thanks for the hint. So this API would make the name out of function
> and colour given via dt, right?

Right.

>>> On Thu, 2019-10-17 at 09:04 -0500, Dan Murphy wrote:
>>>> Matt
>>>>
>>>> On 10/17/19 4:53 AM, Matti Vaittinen wrote:
>>>>> ROHM BD71828 power management IC has two LED outputs for charge
>>>>> status
>>>>> and button pressing indications. The LED outputs can also be
>>>>> forced
>>>>> bs SW so add driver allowing to use these LEDs for other
>>>>> indications
>>>> s/bs/by
>>>>> as well.
>>>>>
>>>>> Leds are controlled by SW using 'Force ON' bits. Please note
>>>>> the
>>>>> constrains mentioned in data-sheet:
>>>>> 1. If one LED is forced ON - then also the other LED is forced.
>>>>> 	=> You can't use SW control to force ON one LED and
>>>>> allow HW
>>>>> 	   to control the other.
>>>>> 2. You can't force both LEDs OFF. If the FORCE bit for both
>>>>> LED's
>>>>> is
>>>>>     zero, then LEDs are controlled by HW and indicate
>>>>> button/charger
>>>>>     states as explained in data-sheet.
>>>>>
>>>>> Signed-off-by: Matti Vaittinen <
>>>>> matti.vaittinen@fi.rohmeurope.com>
>>>>> ---
>>>>>   drivers/leds/Kconfig        | 10 ++++
>>>>>   drivers/leds/Makefile       |  1 +
>>>>>   drivers/leds/leds-bd71828.c | 97
>>>>> +++++++++++++++++++++++++++++++++++++
>>>>>   3 files changed, 108 insertions(+)
>>>>>   create mode 100644 drivers/leds/leds-bd71828.c
>>>>>
>>>>> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
>>>>> index b0fdeef10bd9..ec59f28bcb39 100644
>>>>> --- a/drivers/leds/Kconfig
>>>>> +++ b/drivers/leds/Kconfig
>>>>> @@ -529,6 +529,16 @@ config LEDS_BD2802
>>>>>   	  This option enables support for BD2802GU RGB LED
>>>>> driver chips
>>>>>   	  accessed via the I2C bus.
>>>>>   
>>>>> +config LEDS_BD71828
>>>>> +	tristate "LED driver for LED pins on ROHM BD71828 PMIC"
>>>>> +	depends on LEDS_CLASS
>>>> doesn't this have a dependency on MFD_ROHM_BD71828
>>>>> +	depends on I2C
>>>>> +	help
>>>>> +	  This option enables support for LED outputs located
>>>>> on ROHM
>>>>> +	   BD71828 power management IC. ROHM BD71828 has two
>>>>> led output
>>>>> pins
>>>>> +	   which can be left to indicate HW states or
>>>>> controlled by SW.
>>>>> Say
>>>>> +	   yes here if you want to enable SW control for these
>>>>> LEDs.
>>>>> +
>>>>
>>>> Add module statement
>>>
>>> What is the module statement? Do you mean the 'if you compile this
>>> as a
>>> module it will be called blahblah' or 'choose M to blahblah'?
>>>
>>> I've never understood why some entries have those statements.
>>> 'Choose
>>> M' stuff is help for config system - why should each module explain
>>> how
>>> to use configs? This information should be in more generic
>>> documentation. Furthermore, the 'tristate' there already says you
>>> can
>>> compile this as a module. Module name on the other hand really is
>>> module's property but it may well change if one changes the name of
>>> the
>>> file. That should not require change in KConfig. Furthermore, where
>>> do
>>> you need the module name? And if you really need the module name
>>> you
>>> should check the config name from Makefile to be sure - module/file
>>> names in comments or docs tend to get outdated.
>>>
>>> After all this being said - I can add any boilerplate text in
>>> KConfig
>>> if necessary - I just see zero benefit from this. And if you didn't
>>> mean this - can you then please tell me what is the module
>>> statement?
>>
>> Yes, like you noticed, this is boilerplate so please follow the
>> convention. If you'd like to discuss its relevance please submit
>> a message to kernel-janitors@vger.kernel.org.
> 
> I did follow the convention. There is 67 tristated LED drivers which do
> NOT add this module building babbling in description. Then there is 14
> drivers which do. So common convention even in LED subsystem is to NOT
> include meaningless mumbojumbo there.
> 
> So even regarding convention it is better to have short description to
> the point. That actually makes the requiring boilerplate even more
> useless. But as I said, I can put any meaningless letters there.
> (again, if I can't convince you to reconsider how you like the LED
> subsystem to appear like). Knowing how hard it is to help people
> reducing waste - it's may be easier for me than discussing this further
> :(

I will not insist - it's up to you. Unless Dan who raised the
issue sees that differently.

>>>>>   config LEDS_INTEL_SS4200
>>>>>   	tristate "LED driver for Intel NAS SS4200 series"
>>>>>   	depends on LEDS_CLASS
>>>>> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
>>>>> index 41fb073a39c1..2a8f6a8e4c7c 100644
>>>>> --- a/drivers/leds/Makefile
>>>>> +++ b/drivers/leds/Makefile
>>>>> @@ -15,6 +15,7 @@ obj-$(CONFIG_LEDS_AN30259A)		+=
>>>>> leds-an30259a.o
>>>>>   obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
>>>>>   obj-$(CONFIG_LEDS_BCM6358)		+= leds-bcm6358.o
>>>>>   obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
>>>>> +obj-$(CONFIG_LEDS_BD71828)		+= leds-bd71828.o
>>>>>   obj-$(CONFIG_LEDS_CPCAP)		+= leds-cpcap.o
>>>>>   obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
>>>>>   obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
>>>>> diff --git a/drivers/leds/leds-bd71828.c b/drivers/leds/leds-
>>>>> bd71828.c
>>>>> new file mode 100644
>>>>> index 000000000000..2427619444f5
>>>>> --- /dev/null
>>>>> +++ b/drivers/leds/leds-bd71828.c
>>>>> @@ -0,0 +1,97 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0
>>>>> +// Copyright (C) 2019 ROHM Semiconductors
>>>>> +
>>>>> +#include <linux/device.h>
>>>>> +#include <linux/err.h>
>>>>> +#include <linux/kernel.h>
>>>>> +#include <linux/leds.h>
>>>>> +#include <linux/mfd/rohm-bd71828.h>
>>>>> +#include <linux/module.h>
>>>>> +#include <linux/platform_device.h>
>>>>> +#include <linux/regmap.h>
>>>>> +#include <linux/slab.h>
>>>>> +
>>>>> +#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ? \
>>>>> +	container_of((l), struct bd71828_leds, green) : \
>>>>> +	container_of((l), struct bd71828_leds, amber))
>>>>
>>>> I don't think we should be defining the color as the variable.
>>>> The 
>>>> outputs can drive any color LED.
>>>
>>> I used the colors mentioned in BD71828 data-sheet. It is true
>>> someone
>>> might use different LEDs on their board but at least this naming
>>> allows
>>> one to match the output to one in data-sheet. I can add comment
>>> explaining this if you thin it's worth mentioning.
>>
>> I see you've come up with below definitions in rohm-bd71828.h:
>>
>> #define BD71828_MASK_LED_AMBER		0x80
>> #define BD71828_MASK_LED_GREEN		0x40
>>
>> Is this how those bit fields are named in the data sheet?
> 
> The leds are through the document referred as "GRNLED" and "AMBLED".
> These specific bits are named "AMBLED_FORCE_ON" and "GRNLED_FORCE_ON".

OK, so then it's reasonable to use those names in the driver.
I would only add a comment next to the definitions, highlighting
that their names don't imply the scope of supported colors as this is
entirely irrelevant.

>>>>> +
>>>>> +enum {
>>>>> +	ID_GREEN_LED,
>>>>> +	ID_AMBER_LED,
>>>>> +	ID_NMBR_OF,
>>>>> +};
>>>>> +
>>>>
>>>> Please use the color_id in linux/include/dt-
>>>> bindings/leds/common.h
>>>
>>> Maybe I should not include anything from dt-bindings if I don't use
>>> DT
>>> for this sub-device? (Please see the comments below).
>>
>> But you should.
> 
> Is this your final say? You think I can't convince you to reconsider?
> 
> Anyways, this is LED ID, not color property. Eg, this is not defining
> the color, this is identifying the LED. Please see the macro:
> 
> #define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ? \
>         container_of((l), struct bd71828_leds, green) : \
>         container_of((l), struct bd71828_leds, amber))
> 
> which is used to obtain the pointer to driver data based on pointer to
> led structure. Eg, we get the correct offset by determining which LED
> was contolled (based on the LED ID).
> 
>>
>>>>> +struct bd71828_led {
>>>>> +	int id;
>>>>> +	struct led_classdev l;
>>>>> +	u8 force_mask;
>>>>> +};
>>>>> +
>>>>> +struct bd71828_leds {
>>>>> +	struct rohm_regmap_dev *bd71828;
>>>>> +	struct bd71828_led green;
>>>>> +	struct bd71828_led amber;
>>>>> +};
>>>>> +
>>>>> +static int bd71828_led_brightness_set(struct led_classdev
>>>>> *led_cdev,
>>>>> +				      enum led_brightness
>>>>> value)
>>>>> +{
>>>>> +	struct bd71828_led *l = container_of(led_cdev, struct
>>>>> bd71828_led, l);
>>>>> +	struct bd71828_leds *data;
>>>>> +	unsigned int val = BD71828_LED_OFF;
>>>>> +
>>>>> +	data = BD71828_LED_TO_DATA(l);
>>>>> +	if (value != LED_OFF)
>>>>> +		val = BD71828_LED_ON;
>>>>> +
>>>>> +	return regmap_update_bits(data->bd71828->regmap,
>>>>> BD71828_REG_LED_CTRL,
>>>>> +			    l->force_mask, val);
>>>>> +}
>>>>> +
>>>>> +static int bd71828_led_probe(struct platform_device *pdev)
>>>>> +{
>>>>> +	struct rohm_regmap_dev *bd71828;
>>>>> +	struct bd71828_leds *l;
>>>>> +	struct bd71828_led *g, *a;
>>>>> +	static const char *GNAME = "bd71828-green-led";
>>>>> +	static const char *ANAME = "bd71828-amber-led";
>>>> The LED class creates the name it can get it from the DT.
>>>
>>> I did not add DT node for LEDs as I thought this is preferred way
>>> when
>>> there is not much HW information to bring from DT. I am not sure as
>>> previous PMICs I did drivers for didn't have LEDs though. Currently
>>> this is a MFD sub-device and gets no data from DT. Actually the
>>> driver
>>> crashed when I first didn't explicitly give these names.
>>
>> Please compare below bindings of MFD devices with LED support:
>>
>> Documentation/devicetree/bindings/mfd/max77693.txt
>> Documentation/devicetree/bindings/mfd/ti-lmu.txt
>>
>> Please follow them but use new 'function' and 'color' properties
>> instead of 'label'.
>>
>>>>> +	int ret;
>>>>> +
>>>>> +	pr_info("bd71828 LED driver probed\n");
>>> as a comment from myself - this print should be eliminated or by
>>> minimum converted to dev_dbg.
>>>
>>>>> +
>>>>> +	bd71828 = dev_get_drvdata(pdev->dev.parent);
>>>>> +	l = devm_kzalloc(&pdev->dev, sizeof(*l), GFP_KERNEL);
>>>>> +	if (!l)
>>>>> +		return -ENOMEM;
>>>>> +	l->bd71828 = bd71828;
>>>>> +	a = &l->amber;
>>>>> +	g = &l->green;
>>>>> +	a->id = ID_AMBER_LED;
>>>>> +	g->id = ID_GREEN_LED;
>>>>> +	a->force_mask = BD71828_MASK_LED_AMBER;
>>>>> +	g->force_mask = BD71828_MASK_LED_GREEN;
>>>>> +
>>>>> +	a->l.name = ANAME;
>>>>> +	g->l.name = GNAME;
>>>>> +	a->l.brightness_set_blocking =
>>>>> bd71828_led_brightness_set;
>>>>> +	g->l.brightness_set_blocking =
>>>>> bd71828_led_brightness_set;
>>>>> +
>>>>> +	ret = devm_led_classdev_register(&pdev->dev, &g->l);
>>>>> +	if (ret)
>>>>> +		return ret;
>>>>> +
>>>>> +	return devm_led_classdev_register(&pdev->dev, &a->l);
>>
>> This way you force users to always register two LED class devices
>> whereas they might need only one. Please compare how other LED class
>> drivers handle DT parsing and LED class device registration.
> 
> I am not sure if I understand correctly what you mean by using only one
> class device. As I (hopefully) somewhere said - users can't control
> only one of these LEDs. If they decide to enable one led by SW, then
> they inevitably control also the other. Thus it is better that user
> gets control to both of the LEDs if they take the control for one.
> 
> Or do you mean I could achieve the control for both of these LEDs via
> only one class device?

AFAIU the LEDs, when in SW mode, can be controlled independently, right?
Because if not then there is no point in having separate LED class
devices.

But if I get it right, then allowing for registering only one LED class
device is entirely justifiable - think of a situation when the iout
remains not connected on the board.

>>
>>>>> +}
>>>>> +
>>>>
>>>> This looks different.  Not sure why you register both LEDs in
>>>> this
>>>> probe.
>>>>
>>>> You can use the DT to define both LEDs and then each will be
>>>> probed
>>>> and 
>>>> registered separately.
>>>
>>> As I mentioned above, this driver is currently not using DT at all.
>>> Reason why it does not is that I didn't know all the LEDs are
>>> usually
>>> having own DT entries/drivers.
>>>
>>> But there is actually reason for bundling the LEDs to same driver.
>>> What
>>> is not shown in driver is that LEDs can be controlled by PMIC state
>>> machine so that they are indicating charger states. Other option is
>>
>> This can be handled by the LED trigger that your driver should
>> expose.
>> On activation the trigger would setup the hardware to control the
>> LEDs. But that can be covered later.
> 
> Yet another thing for me to learn =) I looked at the trigger properties
> in DT. That looked like a way to make the LED framework to "bind" the
> LED state to some trigger. (For example make the LED framework to
> toggle specific LED state when USB device is plugged?)
> 
> If this is the case then it might not be relevant for BD71828. Here the
> LED is by-default controlled by HW. Eg, when charger starts charging
> the battery, the PMIC will lit the LED. It will do so also when power
> button is pressed or certain problems are detected. This reqires no SW
> interaction.

The trigger can be always deactivated from sysfs as well as set up
again.

> 
> What this driver intends to do is to allow SW to take over this. Eg, if
> system is designed so that it is preferably to use these LEDs for some
> other purpose it can be done by loading this LED driver and allowing
> user-space to control these LEDs via sysfs.

So the LED trigger interface would help to signalize in which state
the LED is. If the trigger is set then it means the LED is under hw
control.

>>> driving LEDs using this driver - but forcing one of the LEDs will
>>> cause
>>> also the other LED to be under SW control. Eg, one can't control
>>> just
>>> single LED using SW, its both of them or none.
>>
>> So this limitation will have to by documented in the trigger ABI.
>>
>>>> This is how it is commonly done.
>>>>
>>>> You can reference the LM36274 led driver as this is a MFD device
>>>> to
>>>> the 
>>>> ti-lmu.c in the MFD directory.
>>>
>>> Thanks for pointing this out. I will gladly see how others have it
>>> implemented! I just would like to know if the DT binding is really
>>> a
>>> must? In this case I am unsure what LED related extra information
>>> we
>>> could really give from DT (for this specific device).
>>>
>>> I just checked the lm36274 you mentioned. I see that also lm36274
>>> do
>>> parse the dt and set the name itself (so maybe the led_class is not
>>> doing this after all?) - although the name setting code in lm36274
>>> is a
>>> bit peculiar as it loops through all child nodes and overwrites the
>>> old
>>> name if it finds more than one "label" properties from nodes (if I
>>> read
>>> the code correctly).
>>>
>>> In any case I am unsure what is the benefit from using DT and
>>> adding
>>> the DT parsing code for this PMIC's LEDs. I could understand DT
>>> usage
>>> if LED class handled dt parsing and if there was something to
>>> configure
>>> in BD71828 LEDs - but I see no such benefits in this case.
>>
>> I hope I was able to clarify most of your doubts.
> 
> Thanks again for taking the time to check this out. I appreciate the
> effort you guys put into this! And what comes to my doubts - yes, I
> still have a few ;) But I am sure we can work this out. Thanks a bunch
> for this discussion Jacek!

You're welcome!

-- 
Best regards,
Jacek Anaszewski

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-22 17:40           ` Jacek Anaszewski
@ 2019-10-23  8:37             ` Vaittinen, Matti
  2019-10-23 21:59               ` Jacek Anaszewski
  0 siblings, 1 reply; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-23  8:37 UTC (permalink / raw)
  To: mazziesaccount, dmurphy, jacek.anaszewski
  Cc: linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mturquette, lgirdwood, devicetree,
	linus.walleij, a.zummo, mark.rutland, robh+dt, bgolaszewski,
	linux-clk, lee.jones, pavel, broonie, sboyd

Morning Jacek,

Thanks for the reply again. I did some cleaning to this mail as it was
getting lengthy.

On Tue, 2019-10-22 at 19:40 +0200, Jacek Anaszewski wrote:
> Matti,
> 
> On 10/22/19 2:40 PM, Vaittinen, Matti wrote:
> > Hello Jacek,
> > 
> > Thanks for the clarifications. I think I now understand the LED
> > subsystem a bit better :)
> > 
> > On Mon, 2019-10-21 at 21:09 +0200, Jacek Anaszewski wrote:
> > > Hi Matti,
> > > 
> > > On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
> > > > Hello Dan,
> > > > 
> > > > Thanks for taking the time to check my driver :) I truly
> > > > appreciate
> > > > all
> > > > the help!
> > > > 
> > > > A "fundamental question" regarding these review comments is
> > > > whether
> > > > I
> > > > should add DT entries for these LEDs or not. I thought I
> > > > shouldn't
> > > > but
> > > > I would like to get a comment from Rob regarding it.
> > > 
> > > If the LED controller is a part of MFD device probed from DT then
> > > there is no doubt it should have corresponding DT sub-node.
> > 
> > Sorry but I still see no much benefit from adding this information
> > in
> > DT. Why should it have corresponding DT-node if the LED properties
> > are
> > fixed and if we only wish to allow user-space control and have no
> > dependencies to other devices in DT? 
> > 
> > In this specific case the information we can provide from DT is
> > supposed to be fixed. No board based variation. Furthermore, there
> > is
> > not much generic driver/led core functionality which would be able
> > to
> > parse and utilize relevant information from DT. I think we can only
> > give the name (function) and colour. And they are supposed to be
> > fixed
> > and thus could be just hard-coded in driver. Hard-coding these
> > would be
> > simpler and less error prone for users (no DT bindings to write)
> > and
> > simpler to create and probably also to maintain (no separate
> > binding
> > documents needed for LEDs).
> 
> AFAICS it is possible to connect LED of arbitrary color to the iouts
> of this device. If this is the case then it is justified to have DT
> node only to allow for LED name customization.

In theory, yes. In practice (if I understand it correctly) the color in
this case is only visible in sysfs path name. I am not at all sure that
reflecting the (unlikely) color change in path name is worth the
hassle. Besides - if this happens, then the driver and DT can be
changed. It is easier to add DT entries than remove them. If you see
the color change support as really crucial - then I could even consider
defaulting the colours to amber and green if no colour property is
present in DT. I see no point in _requiring_ the DT entry to be there.
If we like being prepared for the theoretical possibilities - what if
x86 is used to control this PMIC? I guess we wouldn't have DT there
then (And no - I don't see such use-case).

> > But assuming this is Ok to DT-folks and if you insist - I will add
> > LED
> > information to DT for the next patches. Hopefully this extra
> > complexity
> > helps in some oddball use-case which I can't foresee =)
> > 
> > Then what comes to the DT format.
> > 
> > Do you think LED subsystem should try to follow the convention with
> > other sub-systems and not introduce multiple compatibles for single
> > device? MFD can handle instantiating the sub-devices just fine even
> > when sub-devices have no own compatible property or of_match. Maybe
> > we
> > should also avoid unnecessary sub-nodes when they are not really
> > required.
> 
> This is beyond my scope of responsibility. It is MFD subsystem thing
> to
> choose the way of LED class driver instantiation. When it comes to
> LED subsystem - it expects single compatible pertaining to a physical
> device.

Sorry but I don't quite follow. What the LED subsystem does with the
compatible property? How does it expect this?

> Nonetheless, so far we used to have separate compatibles for drivers
> of
> MFD devices' LED cells. If we are going to change that I'd like to
> see
> explicit DT maintainer's statement confirming that.

I don't expect that existing DTs would be changed. But as I said, the
consensus amongst most of the subsystenm maintainers and DT maintainers
seems to be that sub-devices should not have own compatibles. I hope
Rob acks this here - but knowing he is a busy guy I add some old
discussions from which I have gathered my understanding:

BD71837 - first patch where regulators had compatible - Mark (regulator
maintainer instructed me to drop it):
https://lore.kernel.org/linux-clk/20180524140118.GS4828@sirena.org.uk/

And here Stephen (the clk subsystem maintainer) told me to drop whole
clocks sub-node (including the compatible):
https://lore.kernel.org/linux-clk/152777867392.144038.18188452389972834689@swboyd.mtv.corp.google.com/


> And one benefit of having separate nodes per MFD cells is that we can
> easily discern the support for which cells is to be turned on.

We don't want to do DT modifications to drop some sub-device support
out. The DT is HW description and sub-blocks are still there. We drop
the support by KConfig. Only 'configuration' we could bring from DT is
the amount of connected LEDs (as you said). But on the other hand -
whether preparing for such unlikely design is reasonable (or needed) is
questionable.

> > 	pmic: pmic@4b {
> > 		compatible = "rohm,bd71828";
> > 		reg = <0x4b>;
> > 		interrupt-parent = <&gpio1>;
> > 		interrupts = <29 GPIO_ACTIVE_LOW>;
> > 		clocks = <&osc 0>;
> > 		#clock-cells = <0>;
> > 		clock-output-names = "bd71828-32k-out";
> > 		gpio-controller;
> > 		#gpio-cells = <2>;
> > 		ngpios = <4>;
> > 		gpio-reserved-ranges = <0 1 2 1>;
> > 		gpio-line-names = "EPDEN";
> > 		rohm,dvs-vsel-gpios = <&gpio1 12 0>,
> > 				      <&gpio1 13 0>;
> > 		regulators {
> > 			...
> > 		};
> > 		
> > 		chg-led {
> > 			function = LED_FUNCTION_CHARGING;
> > 			color = LED_COLOR_ID_AMBER;
> > 		};
> > 
> > 		pwr-led {
> > 			function = LED_FUNCTION_POWER;
> > 			color = LED_COLOR_ID_GREEN;
> > 		};
> 
> This way you would probably need to probe LED class driver twice,
> instead of letting it behave in a standard way and parse child LED
> nodes.

No. Please note that probing the MFD sub-drivers is _not_ bound to
device-tree nodes. MFD sub-devices can be probed just fine even if they
have no DT entries. When we add MFD cell for LED driver, the
corresponding LED driver is probed. No DT magic needed for this.

What the LED driver (as other sub-device drivers) is required to do is
to obtain the pointer to parent device's DT node and find information
which is relevant for it. Ideally, the subsystem framework can extract
the properties which are common for whole subsystem (like color and
function in case of LEDs) and driver only parses the DT if it has some
custom properties. Again, ideally the driver has sane defaults - or
some other 'platform data' mechanism if no DT information is found.
There is architectures which do not support DT.

In case of BD71828 LEDs my first idea was to go with only the 'sane
defaults' option as I saw no much configurability. The DT snippet above
contains LED information as per your suggestion.

What the LED sub driver for BD71828 would now do is calling 
devm_led_classdev_register_ext with the DT information of BD71828
device. Eg, it should use the MFD dt node (because this is the real
device) and not just part of it. devm_led_classdev_register_ext should
then extract the LED specific information. I have not checked the
implementation of devm_led_classdev_register_ext in details - but it
should ignore non led properties and just walk through the LED
information and create the sysfs interfaces etc. for all LEDs it finds.
(In my example this is the chg-led and pwr-led sub-nodes). Furthermore,
if no LED information is found from DT I would expect
devm_led_classdev_register_ext to fail with well-defined return value
so that the driver could do what it now does - Eg, use "sane defaults"
to register the default class-devices for green and amber LEDs. The
default led class dev naming should of course be same format as it
would be if DT was populated with green and amber led information. 

> > 	};
> > 
> > How do you see this? Or do you really wish to have this one extra
> > node:
> > 
> > 	pmic: pmic@4b {
> > 		compatible = "rohm,bd71828";
> > 		
> > reg = <0x4b>;
> > 		interrupt-parent = <&gpio1>;
> > 		interru
> > pts = <29 GPIO_ACTIVE_LOW>;
> > 		clocks = <&osc 0>;
> > 		
> > #clock-cells = <0>;
> > 		clock-output-names = "bd71828-32k-out";
> > 		gpio-controller;
> > 		#gpio-cells = <2>;
> > 	
> > 	ngpios = <4>;
> > 		gpio-reserved-ranges = <0 1 2 1>;
> > 	
> > 	gpio-line-names = "EPDEN";
> > 		rohm,dvs-vsel-gpios =
> > <&gpio1 12 0>,
> > 				      <&gpio1 13 0>;
> > 		
> > regulators {
> > 			...
> > 		};
> > 		
> > 		leds-dummy {
> 
> Why leds-dummy ?

Because there is no real led controller device in any "MFD bus". It is
just one MFD device with controls for two LEDs. 

> The convention is to have led-controller@unit-address as the parent
> LED
> controller node.

What is the unit address here? 0x4b is the I2C slave address and it is
the MFD node address. There is no addressing for LED controller as
there is no separate LED controller device. There is only one device,
the PMIC which is MFD device as it has multiple functions meld in. One
of these functions is LED control and requires LED driver.

> > 			chg-led {
> s/chg-led/led0/
> 
> > 				function = LED_FUNCTION_CHARGING;
> > 				color = LED_COLOR_ID_AMBER;
> > 			};
> > 
> > 			pwr-led {
> 
> s/pwr-led/led1/
> 
> This is ePAPR requirement that DT node name should describe the
> general class of device.

Thanks. I had some problems with these node names as I wanted to make
them generic (led) but also to include some information what leds they
are. A bit same idea as I see in node names like "chan1" and "chan345"
that are used in ti-lmu bindings I checked for the example. But I am
fine with renaming them in this example! I just don't think we should
have this extra node as I mentioned.

> 
> > 				function = LED_FUNCTION_POWER;
> > 				color = LED_COLOR_ID_GREEN;
> > 			};
> 
> Common LED bindings say this is the proper way to go. However you
> would need compatible to probe LED class driver in DT based way.

No. I don't. MFD will probe the LED class driver as long as the name of
the driver matches to MFD cell name. So we only need MFD driver to be
probed based on the compatible. Rest of the sub-device drivers will be
probed by MFD. What I am missing is MODULE_ALIAS in LED driver for
loading the module when MFD is searching for it if it is not modprobed
via scripts or built in-kernel. I have understood this is the standard
way with MFD nowadays - I am positive Lee will kick me if I am wrong ;)
(I think I have bullied him that much in the past :/ )

> If you plan to do it otherwise then it makes no sense to have
> DT nodes for LEDs.

That was my point. This is why I did not have LEDs in DT in first
place. But as I said above - as a result of this discussion I have
started thinking that maybe I could check if I can easily add support
for providing LED information also via DT and fall back to defaults if
no LED information is found. (to allow color change or to omit one of
the LEDs as you suggested)

> > > > > > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> > > > > > index b0fdeef10bd9..ec59f28bcb39 100644
> > > > > > --- a/drivers/leds/Kconfig
> > > > > > +++ b/drivers/leds/Kconfig
> > > > > > @@ -529,6 +529,16 @@ config LEDS_BD2802
> > > > > >   	  This option enables support for BD2802GU RGB LED
> > > > > > driver chips
> > > > > >   	  accessed via the I2C bus.
> > > > > >   
> > > > > > +config LEDS_BD71828
> > > > > > +	tristate "LED driver for LED pins on ROHM BD71828 PMIC"
> > > > > > +	depends on LEDS_CLASS
> > > > > doesn't this have a dependency on MFD_ROHM_BD71828
> > > > > > +	depends on I2C
> > > > > > +	help
> > > > > > +	  This option enables support for LED outputs located
> > > > > > on ROHM
> > > > > > +	   BD71828 power management IC. ROHM BD71828 has two
> > > > > > led output
> > > > > > pins
> > > > > > +	   which can be left to indicate HW states or
> > > > > > controlled by SW.
> > > > > > Say
> > > > > > +	   yes here if you want to enable SW control for these
> > > > > > LEDs.
> > > > > > +
> > > > > 
> > > > > Add module statement
> > > > 
> > > > What is the module statement? Do you mean the 'if you compile
> > > > this
> > > > as a
> > > > module it will be called blahblah' or 'choose M to blahblah'?
> > > > 
> > > > I've never understood why some entries have those statements.
> > > > 'Choose
> > > > M' stuff is help for config system - why should each module
> > > > explain
> > > > how
> > > > to use configs? This information should be in more generic
> > > > documentation. Furthermore, the 'tristate' there already says
> > > > you
> > > > can
> > > > compile this as a module. Module name on the other hand really
> > > > is
> > > > module's property but it may well change if one changes the
> > > > name of
> > > > the
> > > > file. That should not require change in KConfig. Furthermore,
> > > > where
> > > > do
> > > > you need the module name? And if you really need the module
> > > > name
> > > > you
> > > > should check the config name from Makefile to be sure -
> > > > module/file
> > > > names in comments or docs tend to get outdated.
> > > > 
> > > > After all this being said - I can add any boilerplate text in
> > > > KConfig
> > > > if necessary - I just see zero benefit from this. And if you
> > > > didn't
> > > > mean this - can you then please tell me what is the module
> > > > statement?
> > > 
> > > Yes, like you noticed, this is boilerplate so please follow the
> > > convention. If you'd like to discuss its relevance please submit
> > > a message to kernel-janitors@vger.kernel.org.
> > 
> > I did follow the convention. There is 67 tristated LED drivers
> > which do
> > NOT add this module building babbling in description. Then there is
> > 14
> > drivers which do. So common convention even in LED subsystem is to
> > NOT
> > include meaningless mumbojumbo there.
> > 
> > So even regarding convention it is better to have short description
> > to
> > the point. That actually makes the requiring boilerplate even more
> > useless. But as I said, I can put any meaningless letters there.
> > (again, if I can't convince you to reconsider how you like the LED
> > subsystem to appear like). Knowing how hard it is to help people
> > reducing waste - it's may be easier for me than discussing this
> > further
> > :(
> 
> I will not insist - it's up to you. Unless Dan who raised the
> issue sees that differently.

Thanks :) Dan, I would like to hear your thoughts on this - do you
still think this is a fatal issue for you?

> > > > > > +#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ?
> > > > > > \
> > > > > > +	container_of((l), struct bd71828_leds, green) : \
> > > > > > +	container_of((l), struct bd71828_leds, amber))
> > > > > 
> > > > > I don't think we should be defining the color as the
> > > > > variable.
> > > > > The 
> > > > > outputs can drive any color LED.
> > > > 
> > > > I used the colors mentioned in BD71828 data-sheet. It is true
> > > > someone
> > > > might use different LEDs on their board but at least this
> > > > naming
> > > > allows
> > > > one to match the output to one in data-sheet. I can add comment
> > > > explaining this if you thin it's worth mentioning.
> > > 
> > > I see you've come up with below definitions in rohm-bd71828.h:
> > > 
> > > #define BD71828_MASK_LED_AMBER		0x80
> > > #define BD71828_MASK_LED_GREEN		0x40
> > > 
> > > Is this how those bit fields are named in the data sheet?
> > 
> > The leds are through the document referred as "GRNLED" and
> > "AMBLED".
> > These specific bits are named "AMBLED_FORCE_ON" and
> > "GRNLED_FORCE_ON".
> 
> OK, so then it's reasonable to use those names in the driver.
> I would only add a comment next to the definitions, highlighting
> that their names don't imply the scope of supported colors as this is
> entirely irrelevant.

I'll add a note here. 

> 
> > > > > > +
> > > > > > +	bd71828 = dev_get_drvdata(pdev->dev.parent);
> > > > > > +	l = devm_kzalloc(&pdev->dev, sizeof(*l), GFP_KERNEL);
> > > > > > +	if (!l)
> > > > > > +		return -ENOMEM;
> > > > > > +	l->bd71828 = bd71828;
> > > > > > +	a = &l->amber;
> > > > > > +	g = &l->green;
> > > > > > +	a->id = ID_AMBER_LED;
> > > > > > +	g->id = ID_GREEN_LED;
> > > > > > +	a->force_mask = BD71828_MASK_LED_AMBER;
> > > > > > +	g->force_mask = BD71828_MASK_LED_GREEN;
> > > > > > +
> > > > > > +	a->l.name = ANAME;
> > > > > > +	g->l.name = GNAME;
> > > > > > +	a->l.brightness_set_blocking =
> > > > > > bd71828_led_brightness_set;
> > > > > > +	g->l.brightness_set_blocking =
> > > > > > bd71828_led_brightness_set;
> > > > > > +
> > > > > > +	ret = devm_led_classdev_register(&pdev->dev, &g->l);
> > > > > > +	if (ret)
> > > > > > +		return ret;
> > > > > > +
> > > > > > +	return devm_led_classdev_register(&pdev->dev, &a->l);
> > > 
> > > This way you force users to always register two LED class devices
> > > whereas they might need only one. Please compare how other LED
> > > class
> > > drivers handle DT parsing and LED class device registration.
> > 
> > I am not sure if I understand correctly what you mean by using only
> > one
> > class device. As I (hopefully) somewhere said - users can't control
> > only one of these LEDs. If they decide to enable one led by SW,
> > then
> > they inevitably control also the other. Thus it is better that user
> > gets control to both of the LEDs if they take the control for one.
> > 
> > Or do you mean I could achieve the control for both of these LEDs
> > via
> > only one class device?
> 
> AFAIU the LEDs, when in SW mode, can be controlled independently,
> right?

Yes and no. Both of the LEDs can be forced on/off individually - as
long as one of them is forced ON. If both LEDs are tried to be forced
OFF - then both LEDs are controlled by HW. If both are controlled by HW
and then one is forced ON - the other is also no longer controlled by
HW and is forced OFF.

Eg, bits 0x80 and 0x40 are conrols for these LEDs. 0x80 for one, 0x40
for the other. Setting bit means LED is on, clearing means LED is off -
with the HW control twist... If either of the bits is set - then both
leds are controlled by these bits (SW control). If both bits are
cleared, then LEDs are controlled by HW (likely to be off but not for
sure).

> Because if not then there is no point in having separate LED class
> devices.
> 
> But if I get it right, then allowing for registering only one LED
> class
> device is entirely justifiable - think of a situation when the iout
> remains not connected on the board.

Yes. This might be unlikely - but this is the reason why I consider
adding the DT support. I just am not sure if covering this scenario now
is worth the hassle. I tend to think we should only add the DT support
if someone actually produces a board where this LED is not connected.

> > Yet another thing for me to learn =) I looked at the trigger
> > properties
> > in DT. That looked like a way to make the LED framework to "bind"
> > the
> > LED state to some trigger. (For example make the LED framework to
> > toggle specific LED state when USB device is plugged?)
> > 
> > If this is the case then it might not be relevant for BD71828. Here
> > the
> > LED is by-default controlled by HW. Eg, when charger starts
> > charging
> > the battery, the PMIC will lit the LED. It will do so also when
> > power
> > button is pressed or certain problems are detected. This reqires no
> > SW
> > interaction.
> 
> The trigger can be always deactivated from sysfs as well as set up
> again.
> 
> > What this driver intends to do is to allow SW to take over this.
> > Eg, if
> > system is designed so that it is preferably to use these LEDs for
> > some
> > other purpose it can be done by loading this LED driver and
> > allowing
> > user-space to control these LEDs via sysfs.
> 
> So the LED trigger interface would help to signalize in which state
> the LED is. If the trigger is set then it means the LED is under hw
> control.

This might be handy. I need to check the data-sheet because I think
there was own control for using one of the LEDs for charge indicator.
It might be that by-default the HW control of LEDs means that only the
power button presses are signaled via these LEDs. This trigger thing
could be handy for enabling/disabling also the charge indication as
well as for checking if LEDs are in forced state - although this might
be somewhat complicated because the 'turn led on' bit is connected to
the 'trigger'. Eg, even if the trigger says that SW is controlling LED,
turning off the LED may mean that trigger changes. But well, this is
the HW design we are dealing with at this time :/ In any case, I'll
leave this as further dev item for now.

Again, thanks for all the help!

Br,
	Matti Vaittinen


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

* Re: [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver
  2019-10-17 10:48       ` Alexandre Belloni
  2019-10-21  5:29         ` Vaittinen, Matti
@ 2019-10-23 10:27         ` Vaittinen, Matti
  2019-10-29 13:50           ` Alexandre Belloni
  1 sibling, 1 reply; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-23 10:27 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: dmurphy, linux-leds, linux-rtc, linux-gpio, linux-kernel,
	mazziesaccount, mturquette, devicetree, jacek.anaszewski,
	a.zummo, linus.walleij, lgirdwood, mark.rutland, robh+dt,
	bgolaszewski, linux-clk, pavel, sboyd, broonie, lee.jones

Hello again Alexandre,

On Thu, 2019-10-17 at 12:48 +0200, Alexandre Belloni wrote:
> On 17/10/2019 10:36:44+0000, Vaittinen, Matti wrote:
> > Hello Alexandre,
> > 
> > Thanks for quick check! I'll be off for the rest of the week but I
> > will
> > re-work this patch at next week :) I agree with you regarding most
> > of
> > the comments.
> > 
> > > > +
> > > > +
> > > > +/*
> > > > + * RTC definitions shared between
> > > > + *
> > > > + * BD70528
> > > > + * and BD71828
> > > > + */
> > > > +
> > > > +#define ROHM_BD1_MASK_RTC_SEC		0x7f
> > > > +#define ROHM_BD1_MASK_RTC_MINUTE	0x7f
> > > > +#define ROHM_BD1_MASK_RTC_HOUR_24H	0x80
> > > > +#define ROHM_BD1_MASK_RTC_HOUR_PM	0x20
> > > > +#define ROHM_BD1_MASK_RTC_HOUR		0x3f
> > > > +#define ROHM_BD1_MASK_RTC_DAY		0x3f
> > > > +#define ROHM_BD1_MASK_RTC_WEEK		0x07
> > > > +#define ROHM_BD1_MASK_RTC_MONTH		0x1f
> > > > +#define ROHM_BD1_MASK_RTC_YEAR		0xff
> > > > +#define ROHM_BD1_MASK_ALM_EN		0x7
> > > > +
> > > 
> > > All that renaming is distracting and useless. Please resubmit
> > > without
> > > renaming defines, structs and functions to make it easier to
> > > review.
> > 
> > I would prefer renaming because it makes it clearly visible which
> > defines/structs/functions are common for both PMICs and which are
> > PMIC
> > specific. But I really understand the problem of spotting real
> > changes.
> > Would it be Ok if I did renaming in separate patch which does not
> > bring
> > in any other changes - and then the functional changes in separate
> > patch?
> > 
> 
> No, unless you can guarantee that all future PMICs from rohm matching
> the wildcard will use this driver.
> 
I started re-working this patch and remembered my original idea
regarding the naming :) I should have commented it as I had already
forgotten it. You are correct what comes to the difficulty of using
correct wild-cards. And I agree with you what comes to function and
struct names like bd7xx28 - those are somewhat fragile as next PMIC
which we want to support with this driver may be BD12345 - yielding our
wild-card useless.

But if we take a look of common definitions in header rohm-shared.h
which I added - those are prefixed as ROHM_BD1. My idea was introducing
this common RTC define group 1 - which would be common define group for
all devices which belong to BD1 group. Currently that would be BD71828
and BD70528. What was missing is the comment explaining this (and lack
of comment made this useless as even I forgot it already).

I already reverted this naming change and all BD70528 specific and
common defines/functions/enums are prefixed with the good old BD70528.
Only new definitions which I added for BD71828 are prefixed with
BD71828. But how do you see the grouping the common defines to format
ROHM_BD<group number>_FOO_BAR in the rohm-shared.h - with comment that
group BD1 consists of definitions which are common for BD70528 and
BD71828?

My only fear when using prefix BD70528 for common defines is that
someone changes some defines to match the BD70528 data-sheet without
evaluating if this impacts to other PMICs. It may be useless paranoia
though - hence I am asking for your opinion at this phase. I can do
this grouping in own patch - or just leave it as it is now in my local
repo - with the old BD70528 being common prefix.

Br,
	Matti Vaittinen


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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-23  8:37             ` Vaittinen, Matti
@ 2019-10-23 21:59               ` Jacek Anaszewski
  2019-10-24  8:15                 ` Vaittinen, Matti
  0 siblings, 1 reply; 43+ messages in thread
From: Jacek Anaszewski @ 2019-10-23 21:59 UTC (permalink / raw)
  To: Vaittinen, Matti, mazziesaccount, dmurphy
  Cc: linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mturquette, lgirdwood, devicetree,
	linus.walleij, a.zummo, mark.rutland, robh+dt, bgolaszewski,
	linux-clk, lee.jones, pavel, broonie, sboyd

Hi Matti,

On 10/23/19 10:37 AM, Vaittinen, Matti wrote:
> Morning Jacek,
> 
> Thanks for the reply again. I did some cleaning to this mail as it was
> getting lengthy.
> 
> On Tue, 2019-10-22 at 19:40 +0200, Jacek Anaszewski wrote:
>> Matti,
>>
>> On 10/22/19 2:40 PM, Vaittinen, Matti wrote:
>>> Hello Jacek,
>>>
>>> Thanks for the clarifications. I think I now understand the LED
>>> subsystem a bit better :)
>>>
>>> On Mon, 2019-10-21 at 21:09 +0200, Jacek Anaszewski wrote:
>>>> Hi Matti,
>>>>
>>>> On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
>>>>> Hello Dan,
>>>>>
>>>>> Thanks for taking the time to check my driver :) I truly
>>>>> appreciate
>>>>> all
>>>>> the help!
>>>>>
>>>>> A "fundamental question" regarding these review comments is
>>>>> whether
>>>>> I
>>>>> should add DT entries for these LEDs or not. I thought I
>>>>> shouldn't
>>>>> but
>>>>> I would like to get a comment from Rob regarding it.
>>>>
>>>> If the LED controller is a part of MFD device probed from DT then
>>>> there is no doubt it should have corresponding DT sub-node.
>>>
>>> Sorry but I still see no much benefit from adding this information
>>> in
>>> DT. Why should it have corresponding DT-node if the LED properties
>>> are
>>> fixed and if we only wish to allow user-space control and have no
>>> dependencies to other devices in DT? 
>>>
>>> In this specific case the information we can provide from DT is
>>> supposed to be fixed. No board based variation. Furthermore, there
>>> is
>>> not much generic driver/led core functionality which would be able
>>> to
>>> parse and utilize relevant information from DT. I think we can only
>>> give the name (function) and colour. And they are supposed to be
>>> fixed
>>> and thus could be just hard-coded in driver. Hard-coding these
>>> would be
>>> simpler and less error prone for users (no DT bindings to write)
>>> and
>>> simpler to create and probably also to maintain (no separate
>>> binding
>>> documents needed for LEDs).
>>
>> AFAICS it is possible to connect LED of arbitrary color to the iouts
>> of this device. If this is the case then it is justified to have DT
>> node only to allow for LED name customization.
> 
> In theory, yes. In practice (if I understand it correctly) the color in
> this case is only visible in sysfs path name. I am not at all sure that
> reflecting the (unlikely) color change in path name is worth the
> hassle. Besides - if this happens, then the driver and DT can be
> changed.

Driver should not be changed. We have DT for conveying board specific
parameters.

> It is easier to add DT entries than remove them. If you see
> the color change support as really crucial - then I could even consider
> defaulting the colours to amber and green if no colour property is
> present in DT.

You don't need to default to anything. The color section will be left
empty if the property is not provided.

> I see no point in _requiring_ the DT entry to be there.

I'm referring to this later in this message.

> If we like being prepared for the theoretical possibilities - what if
> x86 is used to control this PMIC? I guess we wouldn't have DT there
> then (And no - I don't see such use-case).

We have fwnode abstraction for that. You can also check:
Documentation/firmware-guide/acpi/dsd/leds.rst.

>>> But assuming this is Ok to DT-folks and if you insist - I will add
>>> LED
>>> information to DT for the next patches. Hopefully this extra
>>> complexity
>>> helps in some oddball use-case which I can't foresee =)
>>>
>>> Then what comes to the DT format.
>>>
>>> Do you think LED subsystem should try to follow the convention with
>>> other sub-systems and not introduce multiple compatibles for single
>>> device? MFD can handle instantiating the sub-devices just fine even
>>> when sub-devices have no own compatible property or of_match. Maybe
>>> we
>>> should also avoid unnecessary sub-nodes when they are not really
>>> required.
>>
>> This is beyond my scope of responsibility. It is MFD subsystem thing
>> to
>> choose the way of LED class driver instantiation. When it comes to
>> LED subsystem - it expects single compatible pertaining to a physical
>> device.
> 
> Sorry but I don't quite follow. What the LED subsystem does with the
> compatible property? How does it expect this?

In case of DT based MFD cell probing you must initialize of_compatible
property of struct mfd_cell element which will then be matched
with struct platform_driver -> driver -> of_match_table in the LED
class driver. Basing on that a relevant platform_device is passed
to the probe function. Its child struct device's of_node property comes
already initialized to the pointer to the corresponding child node
in MFD node.

>> Nonetheless, so far we used to have separate compatibles for drivers
>> of
>> MFD devices' LED cells. If we are going to change that I'd like to
>> see
>> explicit DT maintainer's statement confirming that.
> 
> I don't expect that existing DTs would be changed. 

I didn't suggest that.

> But as I said, the
> consensus amongst most of the subsystenm maintainers and DT maintainers
> seems to be that sub-devices should not have own compatibles. I hope
> Rob acks this here - but knowing he is a busy guy I add some old
> discussions from which I have gathered my understanding:
> 
> BD71837 - first patch where regulators had compatible - Mark (regulator
> maintainer instructed me to drop it):
> https://lore.kernel.org/linux-clk/20180524140118.GS4828@sirena.org.uk/
> 
> And here Stephen (the clk subsystem maintainer) told me to drop whole
> clocks sub-node (including the compatible):
> https://lore.kernel.org/linux-clk/152777867392.144038.18188452389972834689@swboyd.mtv.corp.google.com/

Still, there are MFD drivers using of_compatible for matching cell
drivers. I don't follow current trends on MFD subsystem side.
You've got to wait for review feedback from Lee Jones anyway
to find out how to proceed with MFD bindings.

>> And one benefit of having separate nodes per MFD cells is that we can
>> easily discern the support for which cells is to be turned on.
> 
> We don't want to do DT modifications to drop some sub-device support
> out. The DT is HW description and sub-blocks are still there. We drop
> the support by KConfig. 

How would you describe the purpose of 'status = "disabled"' DT
assignment then?

Anyway, I entirely disagree here - it is perfectly proper approach
to define platform capabilities by modifying dts file alone.
This way you can easily create multiple versions of platform
configurations. It may be often impractical to enable all available
platform features, at least from business point of view. And recompiling
dts is lightweight operation in comparison to kernel compilation.

Not saying that in some cases there are secret keys required for
encrypting kernel images, that may not always be at hand.

> Only 'configuration' we could bring from DT is
> the amount of connected LEDs (as you said). But on the other hand -
> whether preparing for such unlikely design is reasonable (or needed) is
> questionable.

LED naming related data is vital as well.

>>> 	pmic: pmic@4b {
>>> 		compatible = "rohm,bd71828";
>>> 		reg = <0x4b>;
>>> 		interrupt-parent = <&gpio1>;
>>> 		interrupts = <29 GPIO_ACTIVE_LOW>;
>>> 		clocks = <&osc 0>;
>>> 		#clock-cells = <0>;
>>> 		clock-output-names = "bd71828-32k-out";
>>> 		gpio-controller;
>>> 		#gpio-cells = <2>;
>>> 		ngpios = <4>;
>>> 		gpio-reserved-ranges = <0 1 2 1>;
>>> 		gpio-line-names = "EPDEN";
>>> 		rohm,dvs-vsel-gpios = <&gpio1 12 0>,
>>> 				      <&gpio1 13 0>;
>>> 		regulators {
>>> 			...
>>> 		};
>>> 		
>>> 		chg-led {
>>> 			function = LED_FUNCTION_CHARGING;
>>> 			color = LED_COLOR_ID_AMBER;
>>> 		};
>>>
>>> 		pwr-led {
>>> 			function = LED_FUNCTION_POWER;
>>> 			color = LED_COLOR_ID_GREEN;
>>> 		};
>>
>> This way you would probably need to probe LED class driver twice,
>> instead of letting it behave in a standard way and parse child LED
>> nodes.
> 
> No. Please note that probing the MFD sub-drivers is _not_ bound to
> device-tree nodes. MFD sub-devices can be probed just fine even if they
> have no DT entries. When we add MFD cell for LED driver, the
> corresponding LED driver is probed. No DT magic needed for this.
> 
> What the LED driver (as other sub-device drivers) is required to do is
> to obtain the pointer to parent device's DT node and find information
> which is relevant for it. Ideally, the subsystem framework can extract
> the properties which are common for whole subsystem (like color and
> function in case of LEDs) and driver only parses the DT if it has some
> custom properties. Again, ideally the driver has sane defaults - or
> some other 'platform data' mechanism if no DT information is found.
> There is architectures which do not support DT.

LED common bindings define that each LED should be described
using child node. And we've enforced sticking to this standard
for last two years strictly.

LED core mechanism for LED name composition also relies on this
DT design - it expects single 'color' and 'function' properties to
be present in the passed fwnode.

LED class registration function registers single LED and it has been
always LED class driver's responsibility to call it for every LED
connected to the LED controller iouts.

> In case of BD71828 LEDs my first idea was to go with only the 'sane
> defaults' option as I saw no much configurability. The DT snippet above
> contains LED information as per your suggestion.
> 
> What the LED sub driver for BD71828 would now do is calling 
> devm_led_classdev_register_ext with the DT information of BD71828
> device. Eg, it should use the MFD dt node (because this is the real
> device) and not just part of it. devm_led_classdev_register_ext should
> then extract the LED specific information. I have not checked the
> implementation of devm_led_classdev_register_ext in details - but it
> should ignore non led properties and just walk through the LED
> information and create the sysfs interfaces etc. for all LEDs it finds.

This function does not work like that, as explained above.
Please first get acquainted with the way how existing LED class drivers
approach LED registration. Because otherwise we're wasting each
others' time.

> (In my example this is the chg-led and pwr-led sub-nodes). Furthermore,
> if no LED information is found from DT I would expect
> devm_led_classdev_register_ext to fail with well-defined return value
> so that the driver could do what it now does - Eg, use "sane defaults"
> to register the default class-devices for green and amber LEDs. The
> default led class dev naming should of course be same format as it
> would be if DT was populated with green and amber led information. 

Please go through 5.4-rc1 patches related to LED naming improvements
You can also refer to Documentation/leds/leds-class.rst,
"LED Device Naming" section for starter.

>>> 	};
>>>
>>> How do you see this? Or do you really wish to have this one extra
>>> node:
>>>
>>> 	pmic: pmic@4b {
>>> 		compatible = "rohm,bd71828";
>>> 		
>>> reg = <0x4b>;
>>> 		interrupt-parent = <&gpio1>;
>>> 		interru
>>> pts = <29 GPIO_ACTIVE_LOW>;
>>> 		clocks = <&osc 0>;
>>> 		
>>> #clock-cells = <0>;
>>> 		clock-output-names = "bd71828-32k-out";
>>> 		gpio-controller;
>>> 		#gpio-cells = <2>;
>>> 	
>>> 	ngpios = <4>;
>>> 		gpio-reserved-ranges = <0 1 2 1>;
>>> 	
>>> 	gpio-line-names = "EPDEN";
>>> 		rohm,dvs-vsel-gpios =
>>> <&gpio1 12 0>,
>>> 				      <&gpio1 13 0>;
>>> 		
>>> regulators {
>>> 			...
>>> 		};
>>> 		
>>> 		leds-dummy {
>>
>> Why leds-dummy ?
> 
> Because there is no real led controller device in any "MFD bus". It is
> just one MFD device with controls for two LEDs. 
> 
>> The convention is to have led-controller@unit-address as the parent
>> LED
>> controller node.
> 
> What is the unit address here? 0x4b is the I2C slave address and it is
> the MFD node address. There is no addressing for LED controller as
> there is no separate LED controller device. There is only one device,
> the PMIC which is MFD device as it has multiple functions meld in. One
> of these functions is LED control and requires LED driver.

For MFD cell you can have just "led".

>>> 			chg-led {
>> s/chg-led/led0/
>>
>>> 				function = LED_FUNCTION_CHARGING;
>>> 				color = LED_COLOR_ID_AMBER;
>>> 			};
>>>
>>> 			pwr-led {
>>
>> s/pwr-led/led1/
>>
>> This is ePAPR requirement that DT node name should describe the
>> general class of device.
> 
> Thanks. I had some problems with these node names as I wanted to make
> them generic (led) but also to include some information what leds they
> are. A bit same idea as I see in node names like "chan1" and "chan345"
> that are used in ti-lmu bindings I checked for the example. But I am
> fine with renaming them in this example! I just don't think we should
> have this extra node as I mentioned.

I wonder what Rob and Lee will say here. I personally would
like to stick to LED common bindings and have this extra node.
We define standards for a reason after all.

>>> 				function = LED_FUNCTION_POWER;
>>> 				color = LED_COLOR_ID_GREEN;
>>> 			};
>>
>> Common LED bindings say this is the proper way to go. However you
>> would need compatible to probe LED class driver in DT based way.
> 
> No. I don't. MFD will probe the LED class driver as long as the name of
> the driver matches to MFD cell name. 

If you initialize only of_compatible in struct mfd_cell element then it
will use only that for matching. I bet I was checking that five years
ago while working on leds-max77693 driver.

> So we only need MFD driver to be
> probed based on the compatible. Rest of the sub-device drivers will be
> probed by MFD. What I am missing is MODULE_ALIAS in LED driver for
> loading the module when MFD is searching for it if it is not modprobed
> via scripts or built in-kernel. I have understood this is the standard
> way with MFD nowadays - I am positive Lee will kick me if I am wrong ;)
> (I think I have bullied him that much in the past :/ )

Last sentence confirms my observation that you're strongly inclined
to contest status quo :-)

>> If you plan to do it otherwise then it makes no sense to have
>> DT nodes for LEDs.
> 
> That was my point. This is why I did not have LEDs in DT in first
> place. But as I said above - as a result of this discussion I have
> started thinking that maybe I could check if I can easily add support
> for providing LED information also via DT and fall back to defaults if
> no LED information is found. (to allow color change or to omit one of
> the LEDs as you suggested)
> 
>>>>>>> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
>>>>>>> index b0fdeef10bd9..ec59f28bcb39 100644
>>>>>>> --- a/drivers/leds/Kconfig
>>>>>>> +++ b/drivers/leds/Kconfig
>>>>>>> @@ -529,6 +529,16 @@ config LEDS_BD2802
>>>>>>>   	  This option enables support for BD2802GU RGB LED
>>>>>>> driver chips
>>>>>>>   	  accessed via the I2C bus.
>>>>>>>   
>>>>>>> +config LEDS_BD71828
>>>>>>> +	tristate "LED driver for LED pins on ROHM BD71828 PMIC"
>>>>>>> +	depends on LEDS_CLASS
>>>>>> doesn't this have a dependency on MFD_ROHM_BD71828
>>>>>>> +	depends on I2C
>>>>>>> +	help
>>>>>>> +	  This option enables support for LED outputs located
>>>>>>> on ROHM
>>>>>>> +	   BD71828 power management IC. ROHM BD71828 has two
>>>>>>> led output
>>>>>>> pins
>>>>>>> +	   which can be left to indicate HW states or
>>>>>>> controlled by SW.
>>>>>>> Say
>>>>>>> +	   yes here if you want to enable SW control for these
>>>>>>> LEDs.
>>>>>>> +
>>>>>>
>>>>>> Add module statement
>>>>>
>>>>> What is the module statement? Do you mean the 'if you compile
>>>>> this
>>>>> as a
>>>>> module it will be called blahblah' or 'choose M to blahblah'?
>>>>>
>>>>> I've never understood why some entries have those statements.
>>>>> 'Choose
>>>>> M' stuff is help for config system - why should each module
>>>>> explain
>>>>> how
>>>>> to use configs? This information should be in more generic
>>>>> documentation. Furthermore, the 'tristate' there already says
>>>>> you
>>>>> can
>>>>> compile this as a module. Module name on the other hand really
>>>>> is
>>>>> module's property but it may well change if one changes the
>>>>> name of
>>>>> the
>>>>> file. That should not require change in KConfig. Furthermore,
>>>>> where
>>>>> do
>>>>> you need the module name? And if you really need the module
>>>>> name
>>>>> you
>>>>> should check the config name from Makefile to be sure -
>>>>> module/file
>>>>> names in comments or docs tend to get outdated.
>>>>>
>>>>> After all this being said - I can add any boilerplate text in
>>>>> KConfig
>>>>> if necessary - I just see zero benefit from this. And if you
>>>>> didn't
>>>>> mean this - can you then please tell me what is the module
>>>>> statement?
>>>>
>>>> Yes, like you noticed, this is boilerplate so please follow the
>>>> convention. If you'd like to discuss its relevance please submit
>>>> a message to kernel-janitors@vger.kernel.org.
>>>
>>> I did follow the convention. There is 67 tristated LED drivers
>>> which do
>>> NOT add this module building babbling in description. Then there is
>>> 14
>>> drivers which do. So common convention even in LED subsystem is to
>>> NOT
>>> include meaningless mumbojumbo there.
>>>
>>> So even regarding convention it is better to have short description
>>> to
>>> the point. That actually makes the requiring boilerplate even more
>>> useless. But as I said, I can put any meaningless letters there.
>>> (again, if I can't convince you to reconsider how you like the LED
>>> subsystem to appear like). Knowing how hard it is to help people
>>> reducing waste - it's may be easier for me than discussing this
>>> further
>>> :(
>>
>> I will not insist - it's up to you. Unless Dan who raised the
>> issue sees that differently.
> 
> Thanks :) Dan, I would like to hear your thoughts on this - do you
> still think this is a fatal issue for you?
> 
>>>>>>> +#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ?
>>>>>>> \
>>>>>>> +	container_of((l), struct bd71828_leds, green) : \
>>>>>>> +	container_of((l), struct bd71828_leds, amber))
>>>>>>
>>>>>> I don't think we should be defining the color as the
>>>>>> variable.
>>>>>> The 
>>>>>> outputs can drive any color LED.
>>>>>
>>>>> I used the colors mentioned in BD71828 data-sheet. It is true
>>>>> someone
>>>>> might use different LEDs on their board but at least this
>>>>> naming
>>>>> allows
>>>>> one to match the output to one in data-sheet. I can add comment
>>>>> explaining this if you thin it's worth mentioning.
>>>>
>>>> I see you've come up with below definitions in rohm-bd71828.h:
>>>>
>>>> #define BD71828_MASK_LED_AMBER		0x80
>>>> #define BD71828_MASK_LED_GREEN		0x40
>>>>
>>>> Is this how those bit fields are named in the data sheet?
>>>
>>> The leds are through the document referred as "GRNLED" and
>>> "AMBLED".
>>> These specific bits are named "AMBLED_FORCE_ON" and
>>> "GRNLED_FORCE_ON".
>>
>> OK, so then it's reasonable to use those names in the driver.
>> I would only add a comment next to the definitions, highlighting
>> that their names don't imply the scope of supported colors as this is
>> entirely irrelevant.
> 
> I'll add a note here. 
> 
>>
>>>>>>> +
>>>>>>> +	bd71828 = dev_get_drvdata(pdev->dev.parent);
>>>>>>> +	l = devm_kzalloc(&pdev->dev, sizeof(*l), GFP_KERNEL);
>>>>>>> +	if (!l)
>>>>>>> +		return -ENOMEM;
>>>>>>> +	l->bd71828 = bd71828;
>>>>>>> +	a = &l->amber;
>>>>>>> +	g = &l->green;
>>>>>>> +	a->id = ID_AMBER_LED;
>>>>>>> +	g->id = ID_GREEN_LED;
>>>>>>> +	a->force_mask = BD71828_MASK_LED_AMBER;
>>>>>>> +	g->force_mask = BD71828_MASK_LED_GREEN;
>>>>>>> +
>>>>>>> +	a->l.name = ANAME;
>>>>>>> +	g->l.name = GNAME;
>>>>>>> +	a->l.brightness_set_blocking =
>>>>>>> bd71828_led_brightness_set;
>>>>>>> +	g->l.brightness_set_blocking =
>>>>>>> bd71828_led_brightness_set;
>>>>>>> +
>>>>>>> +	ret = devm_led_classdev_register(&pdev->dev, &g->l);
>>>>>>> +	if (ret)
>>>>>>> +		return ret;
>>>>>>> +
>>>>>>> +	return devm_led_classdev_register(&pdev->dev, &a->l);
>>>>
>>>> This way you force users to always register two LED class devices
>>>> whereas they might need only one. Please compare how other LED
>>>> class
>>>> drivers handle DT parsing and LED class device registration.
>>>
>>> I am not sure if I understand correctly what you mean by using only
>>> one
>>> class device. As I (hopefully) somewhere said - users can't control
>>> only one of these LEDs. If they decide to enable one led by SW,
>>> then
>>> they inevitably control also the other. Thus it is better that user
>>> gets control to both of the LEDs if they take the control for one.
>>>
>>> Or do you mean I could achieve the control for both of these LEDs
>>> via
>>> only one class device?
>>
>> AFAIU the LEDs, when in SW mode, can be controlled independently,
>> right?
> 
> Yes and no. Both of the LEDs can be forced on/off individually - as
> long as one of them is forced ON. If both LEDs are tried to be forced
> OFF - then both LEDs are controlled by HW. If both are controlled by HW
> and then one is forced ON - the other is also no longer controlled by
> HW and is forced OFF.
> 
> Eg, bits 0x80 and 0x40 are conrols for these LEDs. 0x80 for one, 0x40
> for the other. Setting bit means LED is on, clearing means LED is off -
> with the HW control twist... If either of the bits is set - then both
> leds are controlled by these bits (SW control). If both bits are
> cleared, then LEDs are controlled by HW (likely to be off but not for
> sure).

Thank you for the explanation. So they can be represented by separate
LED class devices. Driver logic will just need to update the state of
the sibling LED if it will be affected.

>> Because if not then there is no point in having separate LED class
>> devices.
>>
>> But if I get it right, then allowing for registering only one LED
>> class
>> device is entirely justifiable - think of a situation when the iout
>> remains not connected on the board.
> 
> Yes. This might be unlikely - but this is the reason why I consider
> adding the DT support. I just am not sure if covering this scenario now
> is worth the hassle. I tend to think we should only add the DT support
> if someone actually produces a board where this LED is not connected.

Could you share what board you're working with?

>>> Yet another thing for me to learn =) I looked at the trigger
>>> properties
>>> in DT. That looked like a way to make the LED framework to "bind"
>>> the
>>> LED state to some trigger. (For example make the LED framework to
>>> toggle specific LED state when USB device is plugged?)
>>>
>>> If this is the case then it might not be relevant for BD71828. Here
>>> the
>>> LED is by-default controlled by HW. Eg, when charger starts
>>> charging
>>> the battery, the PMIC will lit the LED. It will do so also when
>>> power
>>> button is pressed or certain problems are detected. This reqires no
>>> SW
>>> interaction.
>>
>> The trigger can be always deactivated from sysfs as well as set up
>> again.
>>
>>> What this driver intends to do is to allow SW to take over this.
>>> Eg, if
>>> system is designed so that it is preferably to use these LEDs for
>>> some
>>> other purpose it can be done by loading this LED driver and
>>> allowing
>>> user-space to control these LEDs via sysfs.
>>
>> So the LED trigger interface would help to signalize in which state
>> the LED is. If the trigger is set then it means the LED is under hw
>> control.
> 
> This might be handy. I need to check the data-sheet because I think
> there was own control for using one of the LEDs for charge indicator.
> It might be that by-default the HW control of LEDs means that only the
> power button presses are signaled via these LEDs. This trigger thing
> could be handy for enabling/disabling also the charge indication as
> well as for checking if LEDs are in forced state - although this might
> be somewhat complicated because the 'turn led on' bit is connected to
> the 'trigger'. Eg, even if the trigger says that SW is controlling LED,
> turning off the LED may mean that trigger changes. But well, this is
> the HW design we are dealing with at this time :/ In any case, I'll
> leave this as further dev item for now.
> 
> Again, thanks for all the help!
> 
> Br,
> 	Matti Vaittinen
> 

-- 
Best regards,
Jacek Anaszewski

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-23 21:59               ` Jacek Anaszewski
@ 2019-10-24  8:15                 ` Vaittinen, Matti
  2019-10-24 22:04                   ` Jacek Anaszewski
  0 siblings, 1 reply; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-24  8:15 UTC (permalink / raw)
  To: mazziesaccount, dmurphy, jacek.anaszewski
  Cc: linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mturquette, lgirdwood, devicetree,
	linus.walleij, a.zummo, mark.rutland, robh+dt, bgolaszewski,
	linux-clk, lee.jones, pavel, broonie, sboyd

Hello Jacek,

On Wed, 2019-10-23 at 23:59 +0200, Jacek Anaszewski wrote:
> On 10/23/19 10:37 AM, Vaittinen, Matti wrote:
> > On Tue, 2019-10-22 at 19:40 +0200, Jacek Anaszewski wrote:
> > > On 10/22/19 2:40 PM, Vaittinen, Matti wrote:
> > > > On Mon, 2019-10-21 at 21:09 +0200, Jacek Anaszewski wrote:
> > > > > On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
> > > > > > Hello Dan,
> > > > > > 
> > > > > > Thanks for taking the time to check my driver :) I truly
> > > > > > appreciate
> > > > > > all
> > > > > > the help!
> > > > > > 
> > > > > > A "fundamental question" regarding these review comments is
> > > > > > whether
> > > > > > I
> > > > > > should add DT entries for these LEDs or not. I thought I
> > > > > > shouldn't
> > > > > > but
> > > > > > I would like to get a comment from Rob regarding it.
> > > > > 
> > > > > If the LED controller is a part of MFD device probed from DT
> > > > > then
> > > > > there is no doubt it should have corresponding DT sub-node.
> > > > 
> > > > Sorry but I still see no much benefit from adding this
> > > > information
> > > > in
> > > > DT. Why should it have corresponding DT-node if the LED
> > > > properties
> > > > are
> > > > fixed and if we only wish to allow user-space control and have
> > > > no
> > > > dependencies to other devices in DT? 
> > > > 
> > > > In this specific case the information we can provide from DT is
> > > > supposed to be fixed. No board based variation. Furthermore,
> > > > there
> > > > is
> > > > not much generic driver/led core functionality which would be
> > > > able
> > > > to
> > > > parse and utilize relevant information from DT. I think we can
> > > > only
> > > > give the name (function) and colour. And they are supposed to
> > > > be
> > > > fixed
> > > > and thus could be just hard-coded in driver. Hard-coding these
> > > > would be
> > > > simpler and less error prone for users (no DT bindings to
> > > > write)
> > > > and
> > > > simpler to create and probably also to maintain (no separate
> > > > binding
> > > > documents needed for LEDs).
> > > 
> > > AFAICS it is possible to connect LED of arbitrary color to the
> > > iouts
> > > of this device. If this is the case then it is justified to have
> > > DT
> > > node only to allow for LED name customization.
> > 
> > In theory, yes. In practice (if I understand it correctly) the
> > color in
> > this case is only visible in sysfs path name. I am not at all sure
> > that
> > reflecting the (unlikely) color change in path name is worth the
> > hassle. Besides - if this happens, then the driver and DT can be
> > changed.
> 
> Driver should not be changed. We have DT for conveying board specific
> parameters.

Driver needs to be changed if we add new feature to it. It is waste to
develop features that are never needed. DT support here may be such. So
I'd suggest we add DT support later if it is needed.

> > It is easier to add DT entries than remove them. If you see
> > the color change support as really crucial - then I could even
> > consider
> > defaulting the colours to amber and green if no colour property is
> > present in DT.
> 
> You don't need to default to anything. The color section will be left
> empty if the property is not provided.

Thanks for this info :) It makes sense.

And no need to explain me this if you are busy - but I don't really see
why we have led colour in sysfs path? I admit I am not too deep in the
world of LEDs (so I am sure there is just something I haven't been 
thinking about) but it intuitively feels terribly wrong. If I was
writing application to control - let's say a charger LED - I would
definitely prefer to always have the charger led control in same sysfs
path - no matter what the color is. If my application was interested in
knowing the colour, then I hoped to be able to read / change it via
file "colour" which resides in the charger sysfs path. (And yes, if I
had bunch of RGB leds, I would _definitely_ want to change the
'abstract' color - not brightnes of red, green and blue LEDs). But all
this is off-topic and not related to this discussion - I was just
curious as my limited brains don't see the reasoning behind this :)

> > I see no point in _requiring_ the DT entry to be there.
> 
> I'm referring to this later in this message.
> 
> > If we like being prepared for the theoretical possibilities - what
> > if
> > x86 is used to control this PMIC? I guess we wouldn't have DT there
> > then (And no - I don't see such use-case).
> 
> We have fwnode abstraction for that. You can also check:
> Documentation/firmware-guide/acpi/dsd/leds.rst.
> 
> > > > But assuming this is Ok to DT-folks and if you insist - I will
> > > > add
> > > > LED
> > > > information to DT for the next patches. Hopefully this extra
> > > > complexity
> > > > helps in some oddball use-case which I can't foresee =)
> > > > 
> > > > Then what comes to the DT format.
> > > > 
> > > > Do you think LED subsystem should try to follow the convention
> > > > with
> > > > other sub-systems and not introduce multiple compatibles for
> > > > single
> > > > device? MFD can handle instantiating the sub-devices just fine
> > > > even
> > > > when sub-devices have no own compatible property or of_match.
> > > > Maybe
> > > > we
> > > > should also avoid unnecessary sub-nodes when they are not
> > > > really
> > > > required.
> > > 
> > > This is beyond my scope of responsibility. It is MFD subsystem
> > > thing
> > > to
> > > choose the way of LED class driver instantiation. When it comes
> > > to
> > > LED subsystem - it expects single compatible pertaining to a
> > > physical
> > > device.
> > 
> > Sorry but I don't quite follow. What the LED subsystem does with
> > the
> > compatible property? How does it expect this?
> 
> In case of DT based MFD cell probing you must initialize
> of_compatible
> property of struct mfd_cell element which will then be matched
> with struct platform_driver -> driver -> of_match_table in the LED
> class driver. Basing on that a relevant platform_device is passed
> to the probe function. Its child struct device's of_node property
> comes
> already initialized to the pointer to the corresponding child node
> in MFD node.

I know. I did this at first with the BD71837 - and I was told to not do
that. The difference when we don't use of_compatible is that the
of_node pointer in sub-device (LEDs) is not populated. But when we have
pure MFD sub-device (like LEDs on BD71828), the sub-device knows it is
instantiated by MFD (parent) and it can get the relevant DT data from
parent's of_node - which kind of makes sense as there really is only
one physical device (the MFD). But I see you like to get opinion from
Lee and/or Rob - let's hope they help us to align our views. (It is
also definitely a good idea to correct my understanding if I have
misunderstood this!)

> > > Nonetheless, so far we used to have separate compatibles for
> > > drivers
> > > of
> > > MFD devices' LED cells. If we are going to change that I'd like
> > > to
> > > see
> > > explicit DT maintainer's statement confirming that.
> > 
> > I don't expect that existing DTs would be changed. 
> 
> I didn't suggest that.
> 
> > But as I said, the
> > consensus amongst most of the subsystenm maintainers and DT
> > maintainers
> > seems to be that sub-devices should not have own compatibles. I
> > hope
> > Rob acks this here - but knowing he is a busy guy I add some old
> > discussions from which I have gathered my understanding:
> > 
> > BD71837 - first patch where regulators had compatible - Mark
> > (regulator
> > maintainer instructed me to drop it):
> > https://lore.kernel.org/linux-clk/20180524140118.GS4828@sirena.org.uk/
> > 
> > And here Stephen (the clk subsystem maintainer) told me to drop
> > whole
> > clocks sub-node (including the compatible):
> > https://lore.kernel.org/linux-clk/152777867392.144038.18188452389972834689@swboyd.mtv.corp.google.com/
> 
> Still, there are MFD drivers using of_compatible for matching cell
> drivers. I don't follow current trends on MFD subsystem side.
> You've got to wait for review feedback from Lee Jones anyway
> to find out how to proceed with MFD bindings.

Sure. And as the subject states, this whole series is still RFC. I am
not expecting the regulator run-level control to be accepted as such -
I am hoping to get a push to right direction - although the basic
regulator control might go in without big changes. So my case does not
require super fast decision - but I think that if the general direction
should be more towards dropping own compatibles from MFD sub-devices,
then it might be good idea to get this sorted sooner than later :)

> > > And one benefit of having separate nodes per MFD cells is that we
> > > can
> > > easily discern the support for which cells is to be turned on.
> > 
> > We don't want to do DT modifications to drop some sub-device
> > support
> > out. The DT is HW description and sub-blocks are still there. We
> > drop
> > the support by KConfig. 
> 
> How would you describe the purpose of 'status = "disabled"' DT
> assignment then?
> 
> Anyway, I entirely disagree here - it is perfectly proper approach
> to define platform capabilities by modifying dts file alone.
> This way you can easily create multiple versions of platform
> configurations. It may be often impractical to enable all available
> platform features, at least from business point of view. And
> recompiling
> dts is lightweight operation in comparison to kernel compilation.

I guess we have different backgrounds here =) For quite a long time I
was working with devices that had really long life-span. They received
multiple SW updates - but changing a DT was rare. For some of the
products DT changes were impossible after the product was out of the
factory. For some of the products DT changes were possible - but rare -
and during the update the system often booted up in a state where it
had either new SW but old DT. In SW fall-back scenarios system often
had old SW and new DT. And at times there were systems running new SW
but years old DT - especially for those systems where DT was not
updated after the product left factory...

In that environment all the DT updates were a nightmare.

> Not saying that in some cases there are secret keys required for
> encrypting kernel images, that may not always be at hand.
> 
> > Only 'configuration' we could bring from DT is
> > the amount of connected LEDs (as you said). But on the other hand -
> > whether preparing for such unlikely design is reasonable (or
> > needed) is
> > questionable.
> 
> LED naming related data is vital as well.

Sure. But I don't think the LED names need to be changed. On the
contrary - I expect the user-space to hope the names stay constant.
Maybe I just don't understand something here :)

> > > > 	pmic: pmic@4b {
> > > > 		compatible = "rohm,bd71828";
> > > > 		reg = <0x4b>;
> > > > 		interrupt-parent = <&gpio1>;
> > > > 		interrupts = <29 GPIO_ACTIVE_LOW>;
> > > > 		clocks = <&osc 0>;
> > > > 		#clock-cells = <0>;
> > > > 		clock-output-names = "bd71828-32k-out";
> > > > 		gpio-controller;
> > > > 		#gpio-cells = <2>;
> > > > 		ngpios = <4>;
> > > > 		gpio-reserved-ranges = <0 1 2 1>;
> > > > 		gpio-line-names = "EPDEN";
> > > > 		rohm,dvs-vsel-gpios = <&gpio1 12 0>,
> > > > 				      <&gpio1 13 0>;
> > > > 		regulators {
> > > > 			...
> > > > 		};
> > > > 		
> > > > 		chg-led {
> > > > 			function = LED_FUNCTION_CHARGING;
> > > > 			color = LED_COLOR_ID_AMBER;
> > > > 		};
> > > > 
> > > > 		pwr-led {
> > > > 			function = LED_FUNCTION_POWER;
> > > > 			color = LED_COLOR_ID_GREEN;
> > > > 		};
> > > 
> > > This way you would probably need to probe LED class driver twice,
> > > instead of letting it behave in a standard way and parse child
> > > LED
> > > nodes.
> > 
> > No. Please note that probing the MFD sub-drivers is _not_ bound to
> > device-tree nodes. MFD sub-devices can be probed just fine even if
> > they
> > have no DT entries. When we add MFD cell for LED driver, the
> > corresponding LED driver is probed. No DT magic needed for this.
> > 
> > What the LED driver (as other sub-device drivers) is required to do
> > is
> > to obtain the pointer to parent device's DT node and find
> > information
> > which is relevant for it. Ideally, the subsystem framework can
> > extract
> > the properties which are common for whole subsystem (like color and
> > function in case of LEDs) and driver only parses the DT if it has
> > some
> > custom properties. Again, ideally the driver has sane defaults - or
> > some other 'platform data' mechanism if no DT information is found.
> > There is architectures which do not support DT.
> 
> LED common bindings define that each LED should be described
> using child node. And we've enforced sticking to this standard
> for last two years strictly.

I am not against that. If DT is used, then it is fine for me to have
properties of one LED in own node. But I don't think the DT should be
compulsory at all for cases where the LED information stays static.

> LED core mechanism for LED name composition also relies on this
> DT design - it expects single 'color' and 'function' properties to
> be present in the passed fwnode.

I am not against this either - although I don't fully understand this
as I said above. I believe that set of well defined LED names is a good
thing. And LED APIs should indeed force the name to follow specific
format. But I don't think that the DT should be only mechanism for
bringing the function and colour. I think we should allow LED name
composition for example by specifying the colour and function in LED
class registration API in cases where fwnode is not needed.

> LED class registration function registers single LED and it has been
> always LED class driver's responsibility to call it for every LED
> connected to the LED controller iouts.

This is fine for me too (especially when DT is not used). And my driver
draft did this, right? But I see that lots of code duplication in
drivers could be avoided if the LED framework provided function which
could extract all LEDs from a (MFD) device-tree node and did register
more than one of them. The typical "for_each_child_of_node" could be in
LED core. But this is currently some what irrelevant - let's first see
how the "compatible" discussion for sub-devices turns out ;)

> 
> > In case of BD71828 LEDs my first idea was to go with only the 'sane
> > defaults' option as I saw no much configurability. The DT snippet
> > above
> > contains LED information as per your suggestion.
> > 
> > What the LED sub driver for BD71828 would now do is calling 
> > devm_led_classdev_register_ext with the DT information of BD71828
> > device. Eg, it should use the MFD dt node (because this is the real
> > device) and not just part of it. devm_led_classdev_register_ext
> > should
> > then extract the LED specific information. I have not checked the
> > implementation of devm_led_classdev_register_ext in details - but
> > it
> > should ignore non led properties and just walk through the LED
> > information and create the sysfs interfaces etc. for all LEDs it
> > finds.
> 
> This function does not work like that, as explained above.
> Please first get acquainted with the way how existing LED class
> drivers
> approach LED registration. Because otherwise we're wasting each
> others' time.

Right. I see. So each LED driver must first parse the DT information in
order to find the LED node - or each LED node must be identified by
what-ever mechanism is invoking the LED driver... I see this could be
improved in the future by adding LED framework a mechanism to identify
LED nodes. But that discussion is (probably) out of the scope of this
driver. Thanks for pointing that out.

> > (In my example this is the chg-led and pwr-led sub-nodes).
> > Furthermore,
> > if no LED information is found from DT I would expect
> > devm_led_classdev_register_ext to fail with well-defined return
> > value
> > so that the driver could do what it now does - Eg, use "sane
> > defaults"
> > to register the default class-devices for green and amber LEDs. The
> > default led class dev naming should of course be same format as it
> > would be if DT was populated with green and amber led information. 
> 
> Please go through 5.4-rc1 patches related to LED naming improvements
> You can also refer to Documentation/leds/leds-class.rst,
> "LED Device Naming" section for starter.

I will. The naming should be coherent - and names in my current draft
were just pulled off from my hat. Thanks.

> > > > 	};
> > > > 
> > > > How do you see this? Or do you really wish to have this one
> > > > extra
> > > > node:
> > > > 
> > > > 	pmic: pmic@4b {
> > > > 		compatible = "rohm,bd71828";
> > > > 		
> > > > reg = <0x4b>;
> > > > 		interrupt-parent = <&gpio1>;
> > > > 		interru
> > > > pts = <29 GPIO_ACTIVE_LOW>;
> > > > 		clocks = <&osc 0>;
> > > > 		
> > > > #clock-cells = <0>;
> > > > 		clock-output-names = "bd71828-32k-out";
> > > > 		gpio-controller;
> > > > 		#gpio-cells = <2>;
> > > > 	
> > > > 	ngpios = <4>;
> > > > 		gpio-reserved-ranges = <0 1 2 1>;
> > > > 	
> > > > 	gpio-line-names = "EPDEN";
> > > > 		rohm,dvs-vsel-gpios =
> > > > <&gpio1 12 0>,
> > > > 				      <&gpio1 13 0>;
> > > > 		
> > > > regulators {
> > > > 			...
> > > > 		};
> > > > 		
> > > > 		leds-dummy {
> > > 
> > > Why leds-dummy ?
> > 
> > Because there is no real led controller device in any "MFD bus". It
> > is
> > just one MFD device with controls for two LEDs. 
> > 
> > > The convention is to have led-controller@unit-address as the
> > > parent
> > > LED
> > > controller node.
> > 
> > What is the unit address here? 0x4b is the I2C slave address and it
> > is
> > the MFD node address. There is no addressing for LED controller as
> > there is no separate LED controller device. There is only one
> > device,
> > the PMIC which is MFD device as it has multiple functions meld in.
> > One
> > of these functions is LED control and requires LED driver.
> 
> For MFD cell you can have just "led".
> 
> > > > 			chg-led {
> > > s/chg-led/led0/
> > > 
> > > > 				function =
> > > > LED_FUNCTION_CHARGING;
> > > > 				color = LED_COLOR_ID_AMBER;
> > > > 			};
> > > > 
> > > > 			pwr-led {
> > > 
> > > s/pwr-led/led1/
> > > 
> > > This is ePAPR requirement that DT node name should describe the
> > > general class of device.
> > 
> > Thanks. I had some problems with these node names as I wanted to
> > make
> > them generic (led) but also to include some information what leds
> > they
> > are. A bit same idea as I see in node names like "chan1" and
> > "chan345"
> > that are used in ti-lmu bindings I checked for the example. But I
> > am
> > fine with renaming them in this example! I just don't think we
> > should
> > have this extra node as I mentioned.
> 
> I wonder what Rob and Lee will say here. I personally would
> like to stick to LED common bindings and have this extra node.
> We define standards for a reason after all.

I don't understand what makes you think we shouldn't stick LED common
bindings? We definitely want to have common bindings and increase
amount of bindings handled by core instead of handling the bindings in
all of the LED drivers. It was just strange to me that LED subsystem
uses this "extra node" and "extra compatible" inside MFD whereas (I
have understood that) other subsystems seem to be giving up on that.
But maybe I am mistaken on that - wouldn't be first time - let's see :)

> > > > 				function = LED_FUNCTION_POWER;
> > > > 				color = LED_COLOR_ID_GREEN;
> > > > 			};
> > > 
> > > Common LED bindings say this is the proper way to go. However you
> > > would need compatible to probe LED class driver in DT based way.
> > 
> > No. I don't. MFD will probe the LED class driver as long as the
> > name of
> > the driver matches to MFD cell name. 
> 
> If you initialize only of_compatible in struct mfd_cell element then
> it
> will use only that for matching. I bet I was checking that five years
> ago while working on leds-max77693 driver.

Yes. It sure uses of_compatible for matching and populating the dt
node. This is different from probing though. Sub-device is probed just
fine even when there is no compatible for in DT - if the name matches.
What changes is that the of_node won't be populated and sub driver
needs to figure it out. So both approaches *work* - which is considered
as "right thing to do"(tm) needs to be figured out. I have no further
insight as to why the compatible should or should not be used for MFD
sub-devices - I was just told to avoid that in the past. But let's see
if we get Rob's or Lee's attention :)

> > So we only need MFD driver to be
> > probed based on the compatible. Rest of the sub-device drivers will
> > be
> > probed by MFD. What I am missing is MODULE_ALIAS in LED driver for
> > loading the module when MFD is searching for it if it is not
> > modprobed
> > via scripts or built in-kernel. I have understood this is the
> > standard
> > way with MFD nowadays - I am positive Lee will kick me if I am
> > wrong ;)
> > (I think I have bullied him that much in the past :/ )
> 
> Last sentence confirms my observation that you're strongly inclined
> to contest status quo :-)

Let's just say that I have had my moments - both in good and in bad :)
I am probably not the easiest guy to work with but not the worst
either. Actually, problems tend to start when I try to be funny
:rolleyes: I should learn when to stop.

> > > If you plan to do it otherwise then it makes no sense to have
> > > DT nodes for LEDs.
> > 
> > That was my point. This is why I did not have LEDs in DT in first
> > place. But as I said above - as a result of this discussion I have
> > started thinking that maybe I could check if I can easily add
> > support
> > for providing LED information also via DT and fall back to defaults
> > if
> > no LED information is found. (to allow color change or to omit one
> > of
> > the LEDs as you suggested)

> > > > > > > > +
> > > > > > > > +	bd71828 = dev_get_drvdata(pdev->dev.parent);
> > > > > > > > +	l = devm_kzalloc(&pdev->dev, sizeof(*l),
> > > > > > > > GFP_KERNEL);
> > > > > > > > +	if (!l)
> > > > > > > > +		return -ENOMEM;
> > > > > > > > +	l->bd71828 = bd71828;
> > > > > > > > +	a = &l->amber;
> > > > > > > > +	g = &l->green;
> > > > > > > > +	a->id = ID_AMBER_LED;
> > > > > > > > +	g->id = ID_GREEN_LED;
> > > > > > > > +	a->force_mask = BD71828_MASK_LED_AMBER;
> > > > > > > > +	g->force_mask = BD71828_MASK_LED_GREEN;
> > > > > > > > +
> > > > > > > > +	a->l.name = ANAME;
> > > > > > > > +	g->l.name = GNAME;
> > > > > > > > +	a->l.brightness_set_blocking =
> > > > > > > > bd71828_led_brightness_set;
> > > > > > > > +	g->l.brightness_set_blocking =
> > > > > > > > bd71828_led_brightness_set;
> > > > > > > > +
> > > > > > > > +	ret = devm_led_classdev_register(&pdev->dev,
> > > > > > > > &g->l);
> > > > > > > > +	if (ret)
> > > > > > > > +		return ret;
> > > > > > > > +
> > > > > > > > +	return devm_led_classdev_register(&pdev->dev,
> > > > > > > > &a->l);
> > > > > 
> > > > > This way you force users to always register two LED class
> > > > > devices
> > > > > whereas they might need only one. Please compare how other
> > > > > LED
> > > > > class
> > > > > drivers handle DT parsing and LED class device registration.
> > > > 
> > > > I am not sure if I understand correctly what you mean by using
> > > > only
> > > > one
> > > > class device. As I (hopefully) somewhere said - users can't
> > > > control
> > > > only one of these LEDs. If they decide to enable one led by SW,
> > > > then
> > > > they inevitably control also the other. Thus it is better that
> > > > user
> > > > gets control to both of the LEDs if they take the control for
> > > > one.
> > > > 
> > > > Or do you mean I could achieve the control for both of these
> > > > LEDs
> > > > via
> > > > only one class device?
> > > 
> > > AFAIU the LEDs, when in SW mode, can be controlled independently,
> > > right?
> > 
> > Yes and no. Both of the LEDs can be forced on/off individually - as
> > long as one of them is forced ON. If both LEDs are tried to be
> > forced
> > OFF - then both LEDs are controlled by HW. If both are controlled
> > by HW
> > and then one is forced ON - the other is also no longer controlled
> > by
> > HW and is forced OFF.
> > 
> > Eg, bits 0x80 and 0x40 are conrols for these LEDs. 0x80 for one,
> > 0x40
> > for the other. Setting bit means LED is on, clearing means LED is
> > off -
> > with the HW control twist... If either of the bits is set - then
> > both
> > leds are controlled by these bits (SW control). If both bits are
> > cleared, then LEDs are controlled by HW (likely to be off but not
> > for
> > sure).
> 
> Thank you for the explanation. So they can be represented by separate
> LED class devices. Driver logic will just need to update the state of
> the sibling LED if it will be affected.

Right. Or at first it might be enough (and simplest) to assume that if
LEDs are used via this driver, then colour for both LEDs is set
explicitly by user-space. I wouldn't try guessing if sibling LED state
changes to OFF when one LED is turned ON - or if LED states change to
ON if both are turned OFF. This would require exporting interfaces from
power-supply driver - and it would still be racy. The thing is that
when both LEDs are on board they are both either under HW or SW
control. So it makes no sense to control only one LED in such case.
Thus I think it is Ok if this LED driver is registering both class
devices at one probe. No need to instantiate own platform devices for
both of the LEDs.

> > > Because if not then there is no point in having separate LED
> > > class
> > > devices.
> > > 
> > > But if I get it right, then allowing for registering only one LED
> > > class
> > > device is entirely justifiable - think of a situation when the
> > > iout
> > > remains not connected on the board.
> > 
> > Yes. This might be unlikely - but this is the reason why I consider
> > adding the DT support. I just am not sure if covering this scenario
> > now
> > is worth the hassle. I tend to think we should only add the DT
> > support
> > if someone actually produces a board where this LED is not
> > connected.
> 
> Could you share what board you're working with?

Unfortunately I can't :( I am working for a component vendor and all
customer related information is usually strictly confidental - even in
cases where it can be guessed...




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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-24  8:15                 ` Vaittinen, Matti
@ 2019-10-24 22:04                   ` Jacek Anaszewski
  2019-10-25  7:07                     ` Vaittinen, Matti
  0 siblings, 1 reply; 43+ messages in thread
From: Jacek Anaszewski @ 2019-10-24 22:04 UTC (permalink / raw)
  To: Vaittinen, Matti, mazziesaccount, dmurphy
  Cc: linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mturquette, lgirdwood, devicetree,
	linus.walleij, a.zummo, mark.rutland, robh+dt, bgolaszewski,
	linux-clk, lee.jones, pavel, broonie, sboyd

Hi Matti,

On 10/24/19 10:15 AM, Vaittinen, Matti wrote:
> Hello Jacek,
> 
> On Wed, 2019-10-23 at 23:59 +0200, Jacek Anaszewski wrote:
>> On 10/23/19 10:37 AM, Vaittinen, Matti wrote:
>>> On Tue, 2019-10-22 at 19:40 +0200, Jacek Anaszewski wrote:
>>>> On 10/22/19 2:40 PM, Vaittinen, Matti wrote:
>>>>> On Mon, 2019-10-21 at 21:09 +0200, Jacek Anaszewski wrote:
>>>>>> On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
>>>>>>> Hello Dan,
>>>>>>>
>>>>>>> Thanks for taking the time to check my driver :) I truly
>>>>>>> appreciate
>>>>>>> all
>>>>>>> the help!
>>>>>>>
>>>>>>> A "fundamental question" regarding these review comments is
>>>>>>> whether
>>>>>>> I
>>>>>>> should add DT entries for these LEDs or not. I thought I
>>>>>>> shouldn't
>>>>>>> but
>>>>>>> I would like to get a comment from Rob regarding it.
>>>>>>
>>>>>> If the LED controller is a part of MFD device probed from DT
>>>>>> then
>>>>>> there is no doubt it should have corresponding DT sub-node.
>>>>>
>>>>> Sorry but I still see no much benefit from adding this
>>>>> information
>>>>> in
>>>>> DT. Why should it have corresponding DT-node if the LED
>>>>> properties
>>>>> are
>>>>> fixed and if we only wish to allow user-space control and have
>>>>> no
>>>>> dependencies to other devices in DT? 
>>>>>
>>>>> In this specific case the information we can provide from DT is
>>>>> supposed to be fixed. No board based variation. Furthermore,
>>>>> there
>>>>> is
>>>>> not much generic driver/led core functionality which would be
>>>>> able
>>>>> to
>>>>> parse and utilize relevant information from DT. I think we can
>>>>> only
>>>>> give the name (function) and colour. And they are supposed to
>>>>> be
>>>>> fixed
>>>>> and thus could be just hard-coded in driver. Hard-coding these
>>>>> would be
>>>>> simpler and less error prone for users (no DT bindings to
>>>>> write)
>>>>> and
>>>>> simpler to create and probably also to maintain (no separate
>>>>> binding
>>>>> documents needed for LEDs).
>>>>
>>>> AFAICS it is possible to connect LED of arbitrary color to the
>>>> iouts
>>>> of this device. If this is the case then it is justified to have
>>>> DT
>>>> node only to allow for LED name customization.
>>>
>>> In theory, yes. In practice (if I understand it correctly) the
>>> color in
>>> this case is only visible in sysfs path name. I am not at all sure
>>> that
>>> reflecting the (unlikely) color change in path name is worth the
>>> hassle. Besides - if this happens, then the driver and DT can be
>>> changed.
>>
>> Driver should not be changed. We have DT for conveying board specific
>> parameters.
> 
> Driver needs to be changed if we add new feature to it. It is waste to
> develop features that are never needed. DT support here may be such. So
> I'd suggest we add DT support later if it is needed.

If you add a driver to mainline kernel you have to think of its all
possible applications to make any prospective users' life easier.

And besides, I was referring to changes of hardcoded color. You
mentioned that color change is unlikely. This indicates that you don't
take into account other applications for this device than your current
board. Such approach is applied for platform drivers where LEDs are
controlled e.g. via MMIO. In this case LED names can be initialized
in a static way. You should look at drivers in arch directory.
I see that some of them include also headers of mfd devices so
it may be your area of interest.

If your device is really bound to this single platform then
this is different discussion.

>>> It is easier to add DT entries than remove them. If you see
>>> the color change support as really crucial - then I could even
>>> consider
>>> defaulting the colours to amber and green if no colour property is
>>> present in DT.
>>
>> You don't need to default to anything. The color section will be left
>> empty if the property is not provided.
> 
> Thanks for this info :) It makes sense.
> 
> And no need to explain me this if you are busy - but I don't really see
> why we have led colour in sysfs path? I admit I am not too deep in the
> world of LEDs (so I am sure there is just something I haven't been 
> thinking about) but it intuitively feels terribly wrong. If I was
> writing application to control - let's say a charger LED - I would
> definitely prefer to always have the charger led control in same sysfs
> path - no matter what the color is. If my application was interested in
> knowing the colour, then I hoped to be able to read / change it via
> file "colour" which resides in the charger sysfs path. (And yes, if I
> had bunch of RGB leds, I would _definitely_ want to change the
> 'abstract' color - not brightnes of red, green and blue LEDs). But all
> this is off-topic and not related to this discussion - I was just
> curious as my limited brains don't see the reasoning behind this :)

LED subsystem was founded in 2006 with this naming convention.
Here is an excerpt from the docs touching this issue:

"
There have been calls for LED properties such as color to be exported as
individual led class attributes. As a solution which doesn't incur as much
overhead, I suggest these become part of the device name. The naming scheme
above leaves scope for further attributes should they be needed. If sections
of the name don't apply, just leave that section blank.
"

I was opting for moving color to the sysfs file during LED naming
rework but there was no consensus, so we have to live with that.

>>> I see no point in _requiring_ the DT entry to be there.
>>
>> I'm referring to this later in this message.
>>
>>> If we like being prepared for the theoretical possibilities - what
>>> if
>>> x86 is used to control this PMIC? I guess we wouldn't have DT there
>>> then (And no - I don't see such use-case).
>>
>> We have fwnode abstraction for that. You can also check:
>> Documentation/firmware-guide/acpi/dsd/leds.rst.
>>
>>>>> But assuming this is Ok to DT-folks and if you insist - I will
>>>>> add
>>>>> LED
>>>>> information to DT for the next patches. Hopefully this extra
>>>>> complexity
>>>>> helps in some oddball use-case which I can't foresee =)
>>>>>
>>>>> Then what comes to the DT format.
>>>>>
>>>>> Do you think LED subsystem should try to follow the convention
>>>>> with
>>>>> other sub-systems and not introduce multiple compatibles for
>>>>> single
>>>>> device? MFD can handle instantiating the sub-devices just fine
>>>>> even
>>>>> when sub-devices have no own compatible property or of_match.
>>>>> Maybe
>>>>> we
>>>>> should also avoid unnecessary sub-nodes when they are not
>>>>> really
>>>>> required.
>>>>
>>>> This is beyond my scope of responsibility. It is MFD subsystem
>>>> thing
>>>> to
>>>> choose the way of LED class driver instantiation. When it comes
>>>> to
>>>> LED subsystem - it expects single compatible pertaining to a
>>>> physical
>>>> device.
>>>
>>> Sorry but I don't quite follow. What the LED subsystem does with
>>> the
>>> compatible property? How does it expect this?
>>
>> In case of DT based MFD cell probing you must initialize
>> of_compatible
>> property of struct mfd_cell element which will then be matched
>> with struct platform_driver -> driver -> of_match_table in the LED
>> class driver. Basing on that a relevant platform_device is passed
>> to the probe function. Its child struct device's of_node property
>> comes
>> already initialized to the pointer to the corresponding child node
>> in MFD node.
> 
> I know. I did this at first with the BD71837 - and I was told to not do
> that. The difference when we don't use of_compatible is that the
> of_node pointer in sub-device (LEDs) is not populated. But when we have
> pure MFD sub-device (like LEDs on BD71828), the sub-device knows it is
> instantiated by MFD (parent) and it can get the relevant DT data from
> parent's of_node - which kind of makes sense as there really is only
> one physical device (the MFD). But I see you like to get opinion from
> Lee and/or Rob - let's hope they help us to align our views. (It is
> also definitely a good idea to correct my understanding if I have
> misunderstood this!)
> 
>>>> Nonetheless, so far we used to have separate compatibles for
>>>> drivers
>>>> of
>>>> MFD devices' LED cells. If we are going to change that I'd like
>>>> to
>>>> see
>>>> explicit DT maintainer's statement confirming that.
>>>
>>> I don't expect that existing DTs would be changed. 
>>
>> I didn't suggest that.
>>
>>> But as I said, the
>>> consensus amongst most of the subsystenm maintainers and DT
>>> maintainers
>>> seems to be that sub-devices should not have own compatibles. I
>>> hope
>>> Rob acks this here - but knowing he is a busy guy I add some old
>>> discussions from which I have gathered my understanding:
>>>
>>> BD71837 - first patch where regulators had compatible - Mark
>>> (regulator
>>> maintainer instructed me to drop it):
>>> https://lore.kernel.org/linux-clk/20180524140118.GS4828@sirena.org.uk/
>>>
>>> And here Stephen (the clk subsystem maintainer) told me to drop
>>> whole
>>> clocks sub-node (including the compatible):
>>> https://lore.kernel.org/linux-clk/152777867392.144038.18188452389972834689@swboyd.mtv.corp.google.com/
>>
>> Still, there are MFD drivers using of_compatible for matching cell
>> drivers. I don't follow current trends on MFD subsystem side.
>> You've got to wait for review feedback from Lee Jones anyway
>> to find out how to proceed with MFD bindings.
> 
> Sure. And as the subject states, this whole series is still RFC. I am
> not expecting the regulator run-level control to be accepted as such -
> I am hoping to get a push to right direction - although the basic
> regulator control might go in without big changes. So my case does not
> require super fast decision - but I think that if the general direction
> should be more towards dropping own compatibles from MFD sub-devices,
> then it might be good idea to get this sorted sooner than later :)

All your doubts may stem from the fact that you look at drivers
from platform centric POV and you don't think of portability.

>>>> And one benefit of having separate nodes per MFD cells is that we
>>>> can
>>>> easily discern the support for which cells is to be turned on.
>>>
>>> We don't want to do DT modifications to drop some sub-device
>>> support
>>> out. The DT is HW description and sub-blocks are still there. We
>>> drop
>>> the support by KConfig. 
>>
>> How would you describe the purpose of 'status = "disabled"' DT
>> assignment then?
>>
>> Anyway, I entirely disagree here - it is perfectly proper approach
>> to define platform capabilities by modifying dts file alone.
>> This way you can easily create multiple versions of platform
>> configurations. It may be often impractical to enable all available
>> platform features, at least from business point of view. And
>> recompiling
>> dts is lightweight operation in comparison to kernel compilation.
> 
> I guess we have different backgrounds here =) For quite a long time I
> was working with devices that had really long life-span. They received
> multiple SW updates - but changing a DT was rare. For some of the
> products DT changes were impossible after the product was out of the
> factory. For some of the products DT changes were possible - but rare -
> and during the update the system often booted up in a state where it
> had either new SW but old DT. In SW fall-back scenarios system often
> had old SW and new DT. And at times there were systems running new SW
> but years old DT - especially for those systems where DT was not
> updated after the product left factory...
> 
> In that environment all the DT updates were a nightmare.
> 
>> Not saying that in some cases there are secret keys required for
>> encrypting kernel images, that may not always be at hand.
>>
>>> Only 'configuration' we could bring from DT is
>>> the amount of connected LEDs (as you said). But on the other hand -
>>> whether preparing for such unlikely design is reasonable (or
>>> needed) is
>>> questionable.
>>
>> LED naming related data is vital as well.
> 
> Sure. But I don't think the LED names need to be changed. On the
> contrary - I expect the user-space to hope the names stay constant.
> Maybe I just don't understand something here :)

So this is the gist of the problem - you are platform biased.
The same LED controller can be mounted on various platforms
and may have different functions, which needs to be reflected
in sysfs.

>>>>> 	pmic: pmic@4b {
>>>>> 		compatible = "rohm,bd71828";
>>>>> 		reg = <0x4b>;
>>>>> 		interrupt-parent = <&gpio1>;
>>>>> 		interrupts = <29 GPIO_ACTIVE_LOW>;
>>>>> 		clocks = <&osc 0>;
>>>>> 		#clock-cells = <0>;
>>>>> 		clock-output-names = "bd71828-32k-out";
>>>>> 		gpio-controller;
>>>>> 		#gpio-cells = <2>;
>>>>> 		ngpios = <4>;
>>>>> 		gpio-reserved-ranges = <0 1 2 1>;
>>>>> 		gpio-line-names = "EPDEN";
>>>>> 		rohm,dvs-vsel-gpios = <&gpio1 12 0>,
>>>>> 				      <&gpio1 13 0>;
>>>>> 		regulators {
>>>>> 			...
>>>>> 		};
>>>>> 		
>>>>> 		chg-led {
>>>>> 			function = LED_FUNCTION_CHARGING;
>>>>> 			color = LED_COLOR_ID_AMBER;
>>>>> 		};
>>>>>
>>>>> 		pwr-led {
>>>>> 			function = LED_FUNCTION_POWER;
>>>>> 			color = LED_COLOR_ID_GREEN;
>>>>> 		};
>>>>
>>>> This way you would probably need to probe LED class driver twice,
>>>> instead of letting it behave in a standard way and parse child
>>>> LED
>>>> nodes.
>>>
>>> No. Please note that probing the MFD sub-drivers is _not_ bound to
>>> device-tree nodes. MFD sub-devices can be probed just fine even if
>>> they
>>> have no DT entries. When we add MFD cell for LED driver, the
>>> corresponding LED driver is probed. No DT magic needed for this.
>>>
>>> What the LED driver (as other sub-device drivers) is required to do
>>> is
>>> to obtain the pointer to parent device's DT node and find
>>> information
>>> which is relevant for it. Ideally, the subsystem framework can
>>> extract
>>> the properties which are common for whole subsystem (like color and
>>> function in case of LEDs) and driver only parses the DT if it has
>>> some
>>> custom properties. Again, ideally the driver has sane defaults - or
>>> some other 'platform data' mechanism if no DT information is found.
>>> There is architectures which do not support DT.
>>
>> LED common bindings define that each LED should be described
>> using child node. And we've enforced sticking to this standard
>> for last two years strictly.
> 
> I am not against that. If DT is used, then it is fine for me to have
> properties of one LED in own node. But I don't think the DT should be
> compulsory at all for cases where the LED information stays static.
> 
>> LED core mechanism for LED name composition also relies on this
>> DT design - it expects single 'color' and 'function' properties to
>> be present in the passed fwnode.
> 
> I am not against this either - although I don't fully understand this
> as I said above. I believe that set of well defined LED names is a good
> thing. And LED APIs should indeed force the name to follow specific
> format. But I don't think that the DT should be only mechanism for
> bringing the function and colour. I think we should allow LED name
> composition for example by specifying the colour and function in LED
> class registration API in cases where fwnode is not needed.
> 
>> LED class registration function registers single LED and it has been
>> always LED class driver's responsibility to call it for every LED
>> connected to the LED controller iouts.
> 
> This is fine for me too (especially when DT is not used). And my driver
> draft did this, right? But I see that lots of code duplication in
> drivers could be avoided if the LED framework provided function which
> could extract all LEDs from a (MFD) device-tree node and did register
> more than one of them. The typical "for_each_child_of_node" could be in
> LED core. But this is currently some what irrelevant - let's first see
> how the "compatible" discussion for sub-devices turns out ;)

It is not trivial to come up with generic mechanism for registering
sub-LEDs due to various possible iout topologies and arrangements.

>>
>>> In case of BD71828 LEDs my first idea was to go with only the 'sane
>>> defaults' option as I saw no much configurability. The DT snippet
>>> above
>>> contains LED information as per your suggestion.
>>>
>>> What the LED sub driver for BD71828 would now do is calling 
>>> devm_led_classdev_register_ext with the DT information of BD71828
>>> device. Eg, it should use the MFD dt node (because this is the real
>>> device) and not just part of it. devm_led_classdev_register_ext
>>> should
>>> then extract the LED specific information. I have not checked the
>>> implementation of devm_led_classdev_register_ext in details - but
>>> it
>>> should ignore non led properties and just walk through the LED
>>> information and create the sysfs interfaces etc. for all LEDs it
>>> finds.
>>
>> This function does not work like that, as explained above.
>> Please first get acquainted with the way how existing LED class
>> drivers
>> approach LED registration. Because otherwise we're wasting each
>> others' time.
> 
> Right. I see. So each LED driver must first parse the DT information in
> order to find the LED node - or each LED node must be identified by
> what-ever mechanism is invoking the LED driver... I see this could be
> improved in the future by adding LED framework a mechanism to identify
> LED nodes. But that discussion is (probably) out of the scope of this
> driver. Thanks for pointing that out.
> 
>>> (In my example this is the chg-led and pwr-led sub-nodes).
>>> Furthermore,
>>> if no LED information is found from DT I would expect
>>> devm_led_classdev_register_ext to fail with well-defined return
>>> value
>>> so that the driver could do what it now does - Eg, use "sane
>>> defaults"
>>> to register the default class-devices for green and amber LEDs. The
>>> default led class dev naming should of course be same format as it
>>> would be if DT was populated with green and amber led information. 
>>
>> Please go through 5.4-rc1 patches related to LED naming improvements
>> You can also refer to Documentation/leds/leds-class.rst,
>> "LED Device Naming" section for starter.
> 
> I will. The naming should be coherent - and names in my current draft
> were just pulled off from my hat. Thanks.
> 
>>>>> 	};
>>>>>
>>>>> How do you see this? Or do you really wish to have this one
>>>>> extra
>>>>> node:
>>>>>
>>>>> 	pmic: pmic@4b {
>>>>> 		compatible = "rohm,bd71828";
>>>>> 		
>>>>> reg = <0x4b>;
>>>>> 		interrupt-parent = <&gpio1>;
>>>>> 		interru
>>>>> pts = <29 GPIO_ACTIVE_LOW>;
>>>>> 		clocks = <&osc 0>;
>>>>> 		
>>>>> #clock-cells = <0>;
>>>>> 		clock-output-names = "bd71828-32k-out";
>>>>> 		gpio-controller;
>>>>> 		#gpio-cells = <2>;
>>>>> 	
>>>>> 	ngpios = <4>;
>>>>> 		gpio-reserved-ranges = <0 1 2 1>;
>>>>> 	
>>>>> 	gpio-line-names = "EPDEN";
>>>>> 		rohm,dvs-vsel-gpios =
>>>>> <&gpio1 12 0>,
>>>>> 				      <&gpio1 13 0>;
>>>>> 		
>>>>> regulators {
>>>>> 			...
>>>>> 		};
>>>>> 		
>>>>> 		leds-dummy {
>>>>
>>>> Why leds-dummy ?
>>>
>>> Because there is no real led controller device in any "MFD bus". It
>>> is
>>> just one MFD device with controls for two LEDs. 
>>>
>>>> The convention is to have led-controller@unit-address as the
>>>> parent
>>>> LED
>>>> controller node.
>>>
>>> What is the unit address here? 0x4b is the I2C slave address and it
>>> is
>>> the MFD node address. There is no addressing for LED controller as
>>> there is no separate LED controller device. There is only one
>>> device,
>>> the PMIC which is MFD device as it has multiple functions meld in.
>>> One
>>> of these functions is LED control and requires LED driver.
>>
>> For MFD cell you can have just "led".
>>
>>>>> 			chg-led {
>>>> s/chg-led/led0/
>>>>
>>>>> 				function =
>>>>> LED_FUNCTION_CHARGING;
>>>>> 				color = LED_COLOR_ID_AMBER;
>>>>> 			};
>>>>>
>>>>> 			pwr-led {
>>>>
>>>> s/pwr-led/led1/
>>>>
>>>> This is ePAPR requirement that DT node name should describe the
>>>> general class of device.
>>>
>>> Thanks. I had some problems with these node names as I wanted to
>>> make
>>> them generic (led) but also to include some information what leds
>>> they
>>> are. A bit same idea as I see in node names like "chan1" and
>>> "chan345"
>>> that are used in ti-lmu bindings I checked for the example. But I
>>> am
>>> fine with renaming them in this example! I just don't think we
>>> should
>>> have this extra node as I mentioned.
>>
>> I wonder what Rob and Lee will say here. I personally would
>> like to stick to LED common bindings and have this extra node.
>> We define standards for a reason after all.
> 
> I don't understand what makes you think we shouldn't stick LED common
> bindings? We definitely want to have common bindings and increase
> amount of bindings handled by core instead of handling the bindings in
> all of the LED drivers. It was just strange to me that LED subsystem
> uses this "extra node" and "extra compatible" inside MFD whereas (I
> have understood that) other subsystems seem to be giving up on that.
> But maybe I am mistaken on that - wouldn't be first time - let's see :)
> 
>>>>> 				function = LED_FUNCTION_POWER;
>>>>> 				color = LED_COLOR_ID_GREEN;
>>>>> 			};
>>>>
>>>> Common LED bindings say this is the proper way to go. However you
>>>> would need compatible to probe LED class driver in DT based way.
>>>
>>> No. I don't. MFD will probe the LED class driver as long as the
>>> name of
>>> the driver matches to MFD cell name. 
>>
>> If you initialize only of_compatible in struct mfd_cell element then
>> it
>> will use only that for matching. I bet I was checking that five years
>> ago while working on leds-max77693 driver.
> 
> Yes. It sure uses of_compatible for matching and populating the dt
> node. This is different from probing though. Sub-device is probed just
> fine even when there is no compatible for in DT - if the name matches.
> What changes is that the of_node won't be populated and sub driver
> needs to figure it out. So both approaches *work* - which is considered
> as "right thing to do"(tm) needs to be figured out. I have no further
> insight as to why the compatible should or should not be used for MFD
> sub-devices - I was just told to avoid that in the past. But let's see
> if we get Rob's or Lee's attention :)
> 
>>> So we only need MFD driver to be
>>> probed based on the compatible. Rest of the sub-device drivers will
>>> be
>>> probed by MFD. What I am missing is MODULE_ALIAS in LED driver for
>>> loading the module when MFD is searching for it if it is not
>>> modprobed
>>> via scripts or built in-kernel. I have understood this is the
>>> standard
>>> way with MFD nowadays - I am positive Lee will kick me if I am
>>> wrong ;)
>>> (I think I have bullied him that much in the past :/ )
>>
>> Last sentence confirms my observation that you're strongly inclined
>> to contest status quo :-)
> 
> Let's just say that I have had my moments - both in good and in bad :)
> I am probably not the easiest guy to work with but not the worst
> either. Actually, problems tend to start when I try to be funny
> :rolleyes: I should learn when to stop.
> 
>>>> If you plan to do it otherwise then it makes no sense to have
>>>> DT nodes for LEDs.
>>>
>>> That was my point. This is why I did not have LEDs in DT in first
>>> place. But as I said above - as a result of this discussion I have
>>> started thinking that maybe I could check if I can easily add
>>> support
>>> for providing LED information also via DT and fall back to defaults
>>> if
>>> no LED information is found. (to allow color change or to omit one
>>> of
>>> the LEDs as you suggested)
> 
>>>>>>>>> +
>>>>>>>>> +	bd71828 = dev_get_drvdata(pdev->dev.parent);
>>>>>>>>> +	l = devm_kzalloc(&pdev->dev, sizeof(*l),
>>>>>>>>> GFP_KERNEL);
>>>>>>>>> +	if (!l)
>>>>>>>>> +		return -ENOMEM;
>>>>>>>>> +	l->bd71828 = bd71828;
>>>>>>>>> +	a = &l->amber;
>>>>>>>>> +	g = &l->green;
>>>>>>>>> +	a->id = ID_AMBER_LED;
>>>>>>>>> +	g->id = ID_GREEN_LED;
>>>>>>>>> +	a->force_mask = BD71828_MASK_LED_AMBER;
>>>>>>>>> +	g->force_mask = BD71828_MASK_LED_GREEN;
>>>>>>>>> +
>>>>>>>>> +	a->l.name = ANAME;
>>>>>>>>> +	g->l.name = GNAME;
>>>>>>>>> +	a->l.brightness_set_blocking =
>>>>>>>>> bd71828_led_brightness_set;
>>>>>>>>> +	g->l.brightness_set_blocking =
>>>>>>>>> bd71828_led_brightness_set;
>>>>>>>>> +
>>>>>>>>> +	ret = devm_led_classdev_register(&pdev->dev,
>>>>>>>>> &g->l);
>>>>>>>>> +	if (ret)
>>>>>>>>> +		return ret;
>>>>>>>>> +
>>>>>>>>> +	return devm_led_classdev_register(&pdev->dev,
>>>>>>>>> &a->l);
>>>>>>
>>>>>> This way you force users to always register two LED class
>>>>>> devices
>>>>>> whereas they might need only one. Please compare how other
>>>>>> LED
>>>>>> class
>>>>>> drivers handle DT parsing and LED class device registration.
>>>>>
>>>>> I am not sure if I understand correctly what you mean by using
>>>>> only
>>>>> one
>>>>> class device. As I (hopefully) somewhere said - users can't
>>>>> control
>>>>> only one of these LEDs. If they decide to enable one led by SW,
>>>>> then
>>>>> they inevitably control also the other. Thus it is better that
>>>>> user
>>>>> gets control to both of the LEDs if they take the control for
>>>>> one.
>>>>>
>>>>> Or do you mean I could achieve the control for both of these
>>>>> LEDs
>>>>> via
>>>>> only one class device?
>>>>
>>>> AFAIU the LEDs, when in SW mode, can be controlled independently,
>>>> right?
>>>
>>> Yes and no. Both of the LEDs can be forced on/off individually - as
>>> long as one of them is forced ON. If both LEDs are tried to be
>>> forced
>>> OFF - then both LEDs are controlled by HW. If both are controlled
>>> by HW
>>> and then one is forced ON - the other is also no longer controlled
>>> by
>>> HW and is forced OFF.
>>>
>>> Eg, bits 0x80 and 0x40 are conrols for these LEDs. 0x80 for one,
>>> 0x40
>>> for the other. Setting bit means LED is on, clearing means LED is
>>> off -
>>> with the HW control twist... If either of the bits is set - then
>>> both
>>> leds are controlled by these bits (SW control). If both bits are
>>> cleared, then LEDs are controlled by HW (likely to be off but not
>>> for
>>> sure).
>>
>> Thank you for the explanation. So they can be represented by separate
>> LED class devices. Driver logic will just need to update the state of
>> the sibling LED if it will be affected.
> 
> Right. Or at first it might be enough (and simplest) to assume that if
> LEDs are used via this driver, then colour for both LEDs is set
> explicitly by user-space. I wouldn't try guessing if sibling LED state
> changes to OFF when one LED is turned ON - or if LED states change to
> ON if both are turned OFF. This would require exporting interfaces from
> power-supply driver - and it would still be racy. The thing is that
> when both LEDs are on board they are both either under HW or SW
> control. So it makes no sense to control only one LED in such case.
> Thus I think it is Ok if this LED driver is registering both class
> devices at one probe. No need to instantiate own platform devices for
> both of the LEDs.

We always register all LEDs originating from the same device in one
probe.

>>>> Because if not then there is no point in having separate LED
>>>> class
>>>> devices.
>>>>
>>>> But if I get it right, then allowing for registering only one LED
>>>> class
>>>> device is entirely justifiable - think of a situation when the
>>>> iout
>>>> remains not connected on the board.
>>>
>>> Yes. This might be unlikely - but this is the reason why I consider
>>> adding the DT support. I just am not sure if covering this scenario
>>> now
>>> is worth the hassle. I tend to think we should only add the DT
>>> support
>>> if someone actually produces a board where this LED is not
>>> connected.
>>
>> Could you share what board you're working with?
> 
> Unfortunately I can't :( I am working for a component vendor and all
> customer related information is usually strictly confidental - even in
> cases where it can be guessed...
> 
> 
> 

-- 
Best regards,
Jacek Anaszewski

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-24 22:04                   ` Jacek Anaszewski
@ 2019-10-25  7:07                     ` Vaittinen, Matti
  2019-10-25 13:24                       ` Rob Herring
  0 siblings, 1 reply; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-25  7:07 UTC (permalink / raw)
  To: mazziesaccount, dmurphy, jacek.anaszewski
  Cc: linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mturquette, lgirdwood, devicetree,
	linus.walleij, a.zummo, mark.rutland, robh+dt, bgolaszewski,
	linux-clk, lee.jones, pavel, broonie, sboyd

Hi Again Jacek,

This has been a nice conversation. I guess I have learned something
from this all but I think this is no longer going forward too much :)
I'll cook up second version - where I add LEDs to DT (even if I don't
see the value for it now). I won't add own compatible for the LED (for
now) - as it is part of MFD - and I'll add the LEDs also to binding
docs. I think that will get the attention from Lee/Rob better than the
LED driver discussion. We can continue discussion there. I hope this is
Ok to you. (And then just few compulsory notes about my view on your
replies - after all, I can't let you to have the final say xD - you can
ignore them or respond just as you like)

On Fri, 2019-10-25 at 00:04 +0200, Jacek Anaszewski wrote:
> Hi Matti,
> 
> On 10/24/19 10:15 AM, Vaittinen, Matti wrote:
> > Hello Jacek,
> > 
> > On Wed, 2019-10-23 at 23:59 +0200, Jacek Anaszewski wrote:
> > > On 10/23/19 10:37 AM, Vaittinen, Matti wrote:
> > > > On Tue, 2019-10-22 at 19:40 +0200, Jacek Anaszewski wrote:
> > > > > On 10/22/19 2:40 PM, Vaittinen, Matti wrote:
> > > > > > On Mon, 2019-10-21 at 21:09 +0200, Jacek Anaszewski wrote:
> > > > > > > On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
> > > > > > > > Hello Dan,
> > > > > > > > 
> > > > > > > > Thanks for taking the time to check my driver :) I
> > > > > > > > truly
> > > > > > > > appreciate
> > > > > > > > all
> > > > > > > > the help!
> > > > > > > > 
> > > > > > > > A "fundamental question" regarding these review
> > > > > > > > comments is
> > > > > > > > whether
> > > > > > > > I
> > > > > > > > should add DT entries for these LEDs or not. I thought
> > > > > > > > I
> > > > > > > > shouldn't
> > > > > > > > but
> > > > > > > > I would like to get a comment from Rob regarding it.
> > > > > > > 
> > > > > > > If the LED controller is a part of MFD device probed from
> > > > > > > DT
> > > > > > > then
> > > > > > > there is no doubt it should have corresponding DT sub-
> > > > > > > node.
> > > > > > 
> > > > > > Sorry but I still see no much benefit from adding this
> > > > > > information
> > > > > > in
> > > > > > DT. Why should it have corresponding DT-node if the LED
> > > > > > properties
> > > > > > are
> > > > > > fixed and if we only wish to allow user-space control and
> > > > > > have
> > > > > > no
> > > > > > dependencies to other devices in DT? 
> > > > > > 
> > > > > > In this specific case the information we can provide from
> > > > > > DT is
> > > > > > supposed to be fixed. No board based variation.
> > > > > > Furthermore,
> > > > > > there
> > > > > > is
> > > > > > not much generic driver/led core functionality which would
> > > > > > be
> > > > > > able
> > > > > > to
> > > > > > parse and utilize relevant information from DT. I think we
> > > > > > can
> > > > > > only
> > > > > > give the name (function) and colour. And they are supposed
> > > > > > to
> > > > > > be
> > > > > > fixed
> > > > > > and thus could be just hard-coded in driver. Hard-coding
> > > > > > these
> > > > > > would be
> > > > > > simpler and less error prone for users (no DT bindings to
> > > > > > write)
> > > > > > and
> > > > > > simpler to create and probably also to maintain (no
> > > > > > separate
> > > > > > binding
> > > > > > documents needed for LEDs).
> > > > > 
> > > > > AFAICS it is possible to connect LED of arbitrary color to
> > > > > the
> > > > > iouts
> > > > > of this device. If this is the case then it is justified to
> > > > > have
> > > > > DT
> > > > > node only to allow for LED name customization.
> > > > 
> > > > In theory, yes. In practice (if I understand it correctly) the
> > > > color in
> > > > this case is only visible in sysfs path name. I am not at all
> > > > sure
> > > > that
> > > > reflecting the (unlikely) color change in path name is worth
> > > > the
> > > > hassle. Besides - if this happens, then the driver and DT can
> > > > be
> > > > changed.
> > > 
> > > Driver should not be changed. We have DT for conveying board
> > > specific
> > > parameters.
> > 
> > Driver needs to be changed if we add new feature to it. It is waste
> > to
> > develop features that are never needed. DT support here may be
> > such. So
> > I'd suggest we add DT support later if it is needed.
> 
> If you add a driver to mainline kernel you have to think of its all
> possible applications to make any prospective users' life easier.

Also, when you add stuff to mainline kernel you want to cut out
everything unnecessary. Being prepared for unknown future is often
waste. We should add code to support what we have NOW - but do it so
that it can be later changed. Good thing of open source is that if it
does not meet your needs you can improve it.

> And besides, I was referring to changes of hardcoded color. You
> mentioned that color change is unlikely. This indicates that you
> don't
> take into account other applications for this device than your
> current
> board.

PMICs tend to be targeted for specific SoCs. There are few generic
PMICs but many are quite SoC specific. Still, I of course hope more
SoCs would be using this PMIC in the future - but right now we should
consider what we have right now. It may be this is never used to power
anything else - who knows. See my previous chapter

>  Such approach is applied for platform drivers where LEDs are
> controlled e.g. via MMIO. In this case LED names can be initialized
> in a static way. You should look at drivers in arch directory.
> I see that some of them include also headers of mfd devices so
> it may be your area of interest.

This boils down to ruling out other possibilities. Even if I don't see
this PMIC being used in other projects I don't want to ensure that by
bolting this PMIC in specific arch code - besides I guess it wouldn't
be welcomed there :P

> If your device is really bound to this single platform then
> this is different discussion.

At the moment it is - but we want to pretend it is not in order to keep
the doors open :]

> > > > It is easier to add DT entries than remove them. If you see
> > > > the color change support as really crucial - then I could even
> > > > consider
> > > > defaulting the colours to amber and green if no colour property
> > > > is
> > > > present in DT.
> > > 
> > > You don't need to default to anything. The color section will be
> > > left
> > > empty if the property is not provided.
> > 
> > Thanks for this info :) It makes sense.
> > 
> > And no need to explain me this if you are busy - but I don't really
> > see
> > why we have led colour in sysfs path? I admit I am not too deep in
> > the
> > world of LEDs (so I am sure there is just something I haven't been 
> > thinking about) but it intuitively feels terribly wrong. If I was
> > writing application to control - let's say a charger LED - I would
> > definitely prefer to always have the charger led control in same
> > sysfs
> > path - no matter what the color is. If my application was
> > interested in
> > knowing the colour, then I hoped to be able to read / change it via
> > file "colour" which resides in the charger sysfs path. (And yes, if
> > I
> > had bunch of RGB leds, I would _definitely_ want to change the
> > 'abstract' color - not brightnes of red, green and blue LEDs). But
> > all
> > this is off-topic and not related to this discussion - I was just
> > curious as my limited brains don't see the reasoning behind this :)
> 
> LED subsystem was founded in 2006 with this naming convention.
> Here is an excerpt from the docs touching this issue:
> 
> "
> There have been calls for LED properties such as color to be exported
> as
> individual led class attributes. As a solution which doesn't incur as
> much
> overhead, I suggest these become part of the device name. The naming
> scheme
> above leaves scope for further attributes should they be needed. If
> sections
> of the name don't apply, just leave that section blank.
> "
> 
> I was opting for moving color to the sysfs file during LED naming
> rework but there was no consensus, so we have to live with that.

I understand that choices have been done under different circumstances.
They always are. But knowing how much LEDs have been developed - I
wonder how long we can stick with old ways. Actually, I wonder how the
devices which control larger amount of RGB leds are done... I could
think that having fixed colour makes colour control quite horrible. I
wouldn't be surprized if we wouldn't have to live with that rest of our
careers :) But as I said, this is not relevant here, I was just
curious. Thank you for the explanation! I again appreciate you
educating me :)

> > > > 
> > 
> > > > > Nonetheless, so far we used to have separate compatibles for
> > > > > drivers
> > > > > of
> > > > > MFD devices' LED cells. If we are going to change that I'd
> > > > > like
> > > > > to
> > > > > see
> > > > > explicit DT maintainer's statement confirming that.
> > > > 
> > > > I don't expect that existing DTs would be changed. 
> > > 
> > > I didn't suggest that.
> > > 
> > > > But as I said, the
> > > > consensus amongst most of the subsystenm maintainers and DT
> > > > maintainers
> > > > seems to be that sub-devices should not have own compatibles. I
> > > > hope
> > > > Rob acks this here - but knowing he is a busy guy I add some
> > > > old
> > > > discussions from which I have gathered my understanding:
> > > > 
> > > > BD71837 - first patch where regulators had compatible - Mark
> > > > (regulator
> > > > maintainer instructed me to drop it):
> > > > https://lore.kernel.org/linux-clk/20180524140118.GS4828@sirena.org.uk/
> > > > 
> > > > And here Stephen (the clk subsystem maintainer) told me to drop
> > > > whole
> > > > clocks sub-node (including the compatible):
> > > > https://lore.kernel.org/linux-clk/152777867392.144038.18188452389972834689@swboyd.mtv.corp.google.com/
> > > 
> > > Still, there are MFD drivers using of_compatible for matching
> > > cell
> > > drivers. I don't follow current trends on MFD subsystem side.
> > > You've got to wait for review feedback from Lee Jones anyway
> > > to find out how to proceed with MFD bindings.
> > 
> > Sure. And as the subject states, this whole series is still RFC. I
> > am
> > not expecting the regulator run-level control to be accepted as
> > such -
> > I am hoping to get a push to right direction - although the basic
> > regulator control might go in without big changes. So my case does
> > not
> > require super fast decision - but I think that if the general
> > direction
> > should be more towards dropping own compatibles from MFD sub-
> > devices,
> > then it might be good idea to get this sorted sooner than later :)
> 
> All your doubts may stem from the fact that you look at drivers
> from platform centric POV and you don't think of portability.

In this specific case my doubts stem from the fact that I was
previously told to drop the sub-device compatibles and meld the
properties in MFD node where possible.

> > > > Only 'configuration' we could bring from DT is
> > > > the amount of connected LEDs (as you said). But on the other
> > > > hand -
> > > > whether preparing for such unlikely design is reasonable (or
> > > > needed) is
> > > > questionable.
> > > 
> > > LED naming related data is vital as well.
> > 
> > Sure. But I don't think the LED names need to be changed. On the
> > contrary - I expect the user-space to hope the names stay constant.
> > Maybe I just don't understand something here :)
> 
> So this is the gist of the problem - you are platform biased.
> The same LED controller can be mounted on various platforms
> and may have different functions, which needs to be reflected
> in sysfs.

Right. I understand this need very well for any generic LED controller.
But at this time we are not dealing with generic LED controlled but a
PMIC. And even in this case I see no problem adding DT support later if
it is needed.

> > This is fine for me too (especially when DT is not used). And my
> > driver
> > draft did this, right? But I see that lots of code duplication in
> > drivers could be avoided if the LED framework provided function
> > which
> > could extract all LEDs from a (MFD) device-tree node and did
> > register
> > more than one of them. The typical "for_each_child_of_node" could
> > be in
> > LED core. But this is currently some what irrelevant - let's first
> > see
> > how the "compatible" discussion for sub-devices turns out ;)
> 
> It is not trivial to come up with generic mechanism for registering
> sub-LEDs due to various possible iout topologies and arrangements.

Not knowing the field in great depths I can't really tell how
complicated it would be. For a LED novice like me it just does not
sound harder than it is for some other systems which do similar thing.
For example the regulator subsystem does this just fine even though the
supply chains may be complex. For example the regulator core does parse
all the regulator sub-nodes. The simple regulator drivers don't need to
look at the DT node at all. Regulator core even allows registering a DT
parsing call-back for individual regulators and pass pointer to very
specific regulator sub-node to this call-back during regulator
registration - if specific handling is required. But this is just
babbling until someone attempts on implementing this. Actually, this
might be a fun challenge if I found the time :)

Please, don't take this as criticism - I know that I don't _know_
unless I try doing it myself :) It's always easy to shout towards the
government when you are in the opposition ;)

> > Right. Or at first it might be enough (and simplest) to assume that
> > if
> > LEDs are used via this driver, then colour for both LEDs is set
> > explicitly by user-space. I wouldn't try guessing if sibling LED
> > state
> > changes to OFF when one LED is turned ON - or if LED states change
> > to
> > ON if both are turned OFF. This would require exporting interfaces
> > from
> > power-supply driver - and it would still be racy. The thing is that
> > when both LEDs are on board they are both either under HW or SW
> > control. So it makes no sense to control only one LED in such case.
> > Thus I think it is Ok if this LED driver is registering both class
> > devices at one probe. No need to instantiate own platform devices
> > for
> > both of the LEDs.
> 
> We always register all LEDs originating from the same device in one
> probe.
> 

Then I see little benefit from of_compatible or leds subnode for MFD
devices with many LEDs. The driver or core must in any ways parse the
DT in order to find the sub nodes with information for individual LEDs.
I don't think that would be much different from just using the MFD node
as controller node and walking through the MFD child nodes to locate
LED sub nodes (at least for MFDs with simple LED controller).


Br,
	Matti Vaittinen

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-25  7:07                     ` Vaittinen, Matti
@ 2019-10-25 13:24                       ` Rob Herring
  2019-10-25 14:37                         ` Vaittinen, Matti
  0 siblings, 1 reply; 43+ messages in thread
From: Rob Herring @ 2019-10-25 13:24 UTC (permalink / raw)
  To: Vaittinen, Matti
  Cc: mazziesaccount, dmurphy, jacek.anaszewski, linux-leds, linux-rtc,
	linux-gpio, linux-kernel, alexandre.belloni, mturquette,
	lgirdwood, devicetree, linus.walleij, a.zummo, mark.rutland,
	bgolaszewski, linux-clk, lee.jones, pavel, broonie, sboyd

On Fri, Oct 25, 2019 at 2:07 AM Vaittinen, Matti
<Matti.Vaittinen@fi.rohmeurope.com> wrote:
>
> Hi Again Jacek,
>
> This has been a nice conversation. I guess I have learned something
> from this all but I think this is no longer going forward too much :)
> I'll cook up second version - where I add LEDs to DT (even if I don't
> see the value for it now). I won't add own compatible for the LED (for
> now) - as it is part of MFD - and I'll add the LEDs also to binding
> docs. I think that will get the attention from Lee/Rob better than the
> LED driver discussion. We can continue discussion there. I hope this is
> Ok to you. (And then just few compulsory notes about my view on your
> replies - after all, I can't let you to have the final say xD - you can
> ignore them or respond just as you like)
>
> On Fri, 2019-10-25 at 00:04 +0200, Jacek Anaszewski wrote:
> > Hi Matti,
> >
> > On 10/24/19 10:15 AM, Vaittinen, Matti wrote:
> > > Hello Jacek,
> > >
> > > On Wed, 2019-10-23 at 23:59 +0200, Jacek Anaszewski wrote:
> > > > On 10/23/19 10:37 AM, Vaittinen, Matti wrote:
> > > > > On Tue, 2019-10-22 at 19:40 +0200, Jacek Anaszewski wrote:
> > > > > > On 10/22/19 2:40 PM, Vaittinen, Matti wrote:
> > > > > > > On Mon, 2019-10-21 at 21:09 +0200, Jacek Anaszewski wrote:
> > > > > > > > On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
> > > > > > > > > Hello Dan,
> > > > > > > > >
> > > > > > > > > Thanks for taking the time to check my driver :) I
> > > > > > > > > truly
> > > > > > > > > appreciate
> > > > > > > > > all
> > > > > > > > > the help!
> > > > > > > > >
> > > > > > > > > A "fundamental question" regarding these review
> > > > > > > > > comments is
> > > > > > > > > whether
> > > > > > > > > I
> > > > > > > > > should add DT entries for these LEDs or not. I thought
> > > > > > > > > I
> > > > > > > > > shouldn't
> > > > > > > > > but
> > > > > > > > > I would like to get a comment from Rob regarding it.
> > > > > > > >
> > > > > > > > If the LED controller is a part of MFD device probed from
> > > > > > > > DT
> > > > > > > > then
> > > > > > > > there is no doubt it should have corresponding DT sub-
> > > > > > > > node.

Agreed.

[...]

> > > Right. Or at first it might be enough (and simplest) to assume that
> > > if
> > > LEDs are used via this driver, then colour for both LEDs is set
> > > explicitly by user-space. I wouldn't try guessing if sibling LED
> > > state
> > > changes to OFF when one LED is turned ON - or if LED states change
> > > to
> > > ON if both are turned OFF. This would require exporting interfaces
> > > from
> > > power-supply driver - and it would still be racy. The thing is that
> > > when both LEDs are on board they are both either under HW or SW
> > > control. So it makes no sense to control only one LED in such case.
> > > Thus I think it is Ok if this LED driver is registering both class
> > > devices at one probe. No need to instantiate own platform devices
> > > for
> > > both of the LEDs.
> >
> > We always register all LEDs originating from the same device in one
> > probe.
> >
>
> Then I see little benefit from of_compatible or leds subnode for MFD
> devices with many LEDs. The driver or core must in any ways parse the
> DT in order to find the sub nodes with information for individual LEDs.
> I don't think that would be much different from just using the MFD node
> as controller node and walking through the MFD child nodes to locate
> LED sub nodes (at least for MFDs with simple LED controller).

The cases for not having child nodes are when you have child nodes
with nothing more than a compatible and possibly provider properties
(e.g. #gpio-cells for gpio providers). If you have other resource
dependencies (e.g. clocks) or data to define (e.g. voltages for
regulators), then child nodes absolutely make sense. Once we have
child nodes, then generally it is easier for every function to be a
child node and not mix the two. I'm sure I have told people
incorrectly to not do child nodes because they define incomplete
bindings.

I would group the led nodes under an led-controller node (with a
compatible). The simple reason is each level only has one
number/address space and you can't mix different ones. You're not
numbering the leds here, but could you (with numbers that correspond
to something in the h/w, not just 0..N)? The other reason is modern
LED bindings define a controller node for the controller and led nodes
for the actual led on the board. While the MFD node could be the
controller node, that gets into mixing.

Rob

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-25 13:24                       ` Rob Herring
@ 2019-10-25 14:37                         ` Vaittinen, Matti
  2019-10-25 15:47                           ` Rob Herring
  0 siblings, 1 reply; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-25 14:37 UTC (permalink / raw)
  To: robh+dt
  Cc: sboyd, dmurphy, linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mazziesaccount, mturquette, lgirdwood,
	devicetree, linus.walleij, a.zummo, mark.rutland, bgolaszewski,
	linux-clk, lee.jones, pavel, jacek.anaszewski, broonie

Hello Peeps,

On Fri, 2019-10-25 at 08:24 -0500, Rob Herring wrote:
> On Fri, Oct 25, 2019 at 2:07 AM Vaittinen, Matti
> <Matti.Vaittinen@fi.rohmeurope.com> wrote:
> > Hi Again Jacek,
> > 
> > This has been a nice conversation. I guess I have learned something
> > from this all but I think this is no longer going forward too much
> > :)
> > I'll cook up second version - where I add LEDs to DT (even if I
> > don't
> > see the value for it now). I won't add own compatible for the LED
> > (for
> > now) - as it is part of MFD - and I'll add the LEDs also to binding
> > docs. I think that will get the attention from Lee/Rob better than
> > the
> > LED driver discussion. We can continue discussion there. I hope
> > this is
> > Ok to you. (And then just few compulsory notes about my view on
> > your
> > replies - after all, I can't let you to have the final say xD - you
> > can
> > ignore them or respond just as you like)
> > 
> > On Fri, 2019-10-25 at 00:04 +0200, Jacek Anaszewski wrote:
> > > Hi Matti,
> > > 
> > > On 10/24/19 10:15 AM, Vaittinen, Matti wrote:
> > > > Hello Jacek,
> > > > 
> > > > On Wed, 2019-10-23 at 23:59 +0200, Jacek Anaszewski wrote:
> > > > > On 10/23/19 10:37 AM, Vaittinen, Matti wrote:
> > > > > > On Tue, 2019-10-22 at 19:40 +0200, Jacek Anaszewski wrote:
> > > > > > > On 10/22/19 2:40 PM, Vaittinen, Matti wrote:
> > > > > > > > On Mon, 2019-10-21 at 21:09 +0200, Jacek Anaszewski
> > > > > > > > wrote:
> > > > > > > > > On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
> > > > > > > > > > Hello Dan,
> > > > > > > > > > 
> > > > > > > > > > Thanks for taking the time to check my driver :) I
> > > > > > > > > > truly
> > > > > > > > > > appreciate
> > > > > > > > > > all
> > > > > > > > > > the help!
> > > > > > > > > > 
> > > > > > > > > > A "fundamental question" regarding these review
> > > > > > > > > > comments is
> > > > > > > > > > whether
> > > > > > > > > > I
> > > > > > > > > > should add DT entries for these LEDs or not. I
> > > > > > > > > > thought
> > > > > > > > > > I
> > > > > > > > > > shouldn't
> > > > > > > > > > but
> > > > > > > > > > I would like to get a comment from Rob regarding
> > > > > > > > > > it.
> > > > > > > > > 
> > > > > > > > > If the LED controller is a part of MFD device probed
> > > > > > > > > from
> > > > > > > > > DT
> > > > > > > > > then
> > > > > > > > > there is no doubt it should have corresponding DT
> > > > > > > > > sub-
> > > > > > > > > node.
> 
> Agreed.

Ouch. That annoying feeling when you notice you have been wrong...

> [...]
> 
> > > > Right. Or at first it might be enough (and simplest) to assume
> > > > that
> > > > if
> > > > LEDs are used via this driver, then colour for both LEDs is set
> > > > explicitly by user-space. I wouldn't try guessing if sibling
> > > > LED
> > > > state
> > > > changes to OFF when one LED is turned ON - or if LED states
> > > > change
> > > > to
> > > > ON if both are turned OFF. This would require exporting
> > > > interfaces
> > > > from
> > > > power-supply driver - and it would still be racy. The thing is
> > > > that
> > > > when both LEDs are on board they are both either under HW or SW
> > > > control. So it makes no sense to control only one LED in such
> > > > case.
> > > > Thus I think it is Ok if this LED driver is registering both
> > > > class
> > > > devices at one probe. No need to instantiate own platform
> > > > devices
> > > > for
> > > > both of the LEDs.
> > > 
> > > We always register all LEDs originating from the same device in
> > > one
> > > probe.
> > > 
> > 
> > Then I see little benefit from of_compatible or leds subnode for
> > MFD
> > devices with many LEDs. The driver or core must in any ways parse
> > the
> > DT in order to find the sub nodes with information for individual
> > LEDs.
> > I don't think that would be much different from just using the MFD
> > node
> > as controller node and walking through the MFD child nodes to
> > locate
> > LED sub nodes (at least for MFDs with simple LED controller).
> 
> The cases for not having child nodes are when you have child nodes
> with nothing more than a compatible and possibly provider properties
> (e.g. #gpio-cells for gpio providers). If you have other resource
> dependencies (e.g. clocks) or data to define (e.g. voltages for
> regulators), then child nodes absolutely make sense.

Thanks for telling the reasoning behind. Makes sense.

> Once we have
> child nodes, then generally it is easier for every function to be a
> child node and not mix the two. I'm sure I have told people
> incorrectly to not do child nodes because they define incomplete
> bindings.

Does this mean that if I add LED controlled node with LED nodes inside
- then I should actually add sub nodes for clk and GPIO too? I would
prefer still having the clk provider information in MFD node as adding
a sub-node for clk would probably require changes in the bd718x7_clk
driver. (Not big ones but avoidable if clk provider information can
still dwell in MFD node).

> I would group the led nodes under an led-controller node (with a
> compatible). The simple reason is each level only has one
> number/address space and you can't mix different ones. You're not
> numbering the leds here, but could you (with numbers that correspond
> to something in the h/w, not just 0..N)?

I don't know what that would be. The LED controller resides in MFD
device in I2C bus and has no meaningful numbers I can think of. The
actual LEDs (on my board) are dummy devices and I really don't know how
to invent meaningfull numbers for them either.

>  The other reason is modern
> LED bindings define a controller node for the controller and led
> nodes
> for the actual led on the board. While the MFD node could be the
> controller node, that gets into mixing.

My idea (if we use DT) was to use the MFD as controller - but that
really would be "mixing" then.

I'll see what I can prepare for v3.

Oh, and sorry Jacek for taking the time - I guess it was frustrating
for you - I really thought I knew how this should be done. Being wrong
is hard job at times, but so must be being right ;)


Br,
	Matti



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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-25 14:37                         ` Vaittinen, Matti
@ 2019-10-25 15:47                           ` Rob Herring
  2019-10-29 13:29                             ` Vaittinen, Matti
  0 siblings, 1 reply; 43+ messages in thread
From: Rob Herring @ 2019-10-25 15:47 UTC (permalink / raw)
  To: Vaittinen, Matti
  Cc: sboyd, dmurphy, linux-leds, linux-rtc, linux-gpio, linux-kernel,
	alexandre.belloni, mazziesaccount, mturquette, lgirdwood,
	devicetree, linus.walleij, a.zummo, mark.rutland, bgolaszewski,
	linux-clk, lee.jones, pavel, jacek.anaszewski, broonie

On Fri, Oct 25, 2019 at 9:37 AM Vaittinen, Matti
<Matti.Vaittinen@fi.rohmeurope.com> wrote:
>
> Hello Peeps,
>
> On Fri, 2019-10-25 at 08:24 -0500, Rob Herring wrote:
> > On Fri, Oct 25, 2019 at 2:07 AM Vaittinen, Matti
> > <Matti.Vaittinen@fi.rohmeurope.com> wrote:
> > > Hi Again Jacek,
> > >
> > > This has been a nice conversation. I guess I have learned something
> > > from this all but I think this is no longer going forward too much
> > > :)
> > > I'll cook up second version - where I add LEDs to DT (even if I
> > > don't
> > > see the value for it now). I won't add own compatible for the LED
> > > (for
> > > now) - as it is part of MFD - and I'll add the LEDs also to binding
> > > docs. I think that will get the attention from Lee/Rob better than
> > > the
> > > LED driver discussion. We can continue discussion there. I hope
> > > this is
> > > Ok to you. (And then just few compulsory notes about my view on
> > > your
> > > replies - after all, I can't let you to have the final say xD - you
> > > can
> > > ignore them or respond just as you like)
> > >
> > > On Fri, 2019-10-25 at 00:04 +0200, Jacek Anaszewski wrote:
> > > > Hi Matti,
> > > >
> > > > On 10/24/19 10:15 AM, Vaittinen, Matti wrote:
> > > > > Hello Jacek,
> > > > >
> > > > > On Wed, 2019-10-23 at 23:59 +0200, Jacek Anaszewski wrote:
> > > > > > On 10/23/19 10:37 AM, Vaittinen, Matti wrote:
> > > > > > > On Tue, 2019-10-22 at 19:40 +0200, Jacek Anaszewski wrote:
> > > > > > > > On 10/22/19 2:40 PM, Vaittinen, Matti wrote:
> > > > > > > > > On Mon, 2019-10-21 at 21:09 +0200, Jacek Anaszewski
> > > > > > > > > wrote:
> > > > > > > > > > On 10/21/19 10:00 AM, Vaittinen, Matti wrote:
> > > > > > > > > > > Hello Dan,
> > > > > > > > > > >
> > > > > > > > > > > Thanks for taking the time to check my driver :) I
> > > > > > > > > > > truly
> > > > > > > > > > > appreciate
> > > > > > > > > > > all
> > > > > > > > > > > the help!
> > > > > > > > > > >
> > > > > > > > > > > A "fundamental question" regarding these review
> > > > > > > > > > > comments is
> > > > > > > > > > > whether
> > > > > > > > > > > I
> > > > > > > > > > > should add DT entries for these LEDs or not. I
> > > > > > > > > > > thought
> > > > > > > > > > > I
> > > > > > > > > > > shouldn't
> > > > > > > > > > > but
> > > > > > > > > > > I would like to get a comment from Rob regarding
> > > > > > > > > > > it.
> > > > > > > > > >
> > > > > > > > > > If the LED controller is a part of MFD device probed
> > > > > > > > > > from
> > > > > > > > > > DT
> > > > > > > > > > then
> > > > > > > > > > there is no doubt it should have corresponding DT
> > > > > > > > > > sub-
> > > > > > > > > > node.
> >
> > Agreed.
>
> Ouch. That annoying feeling when you notice you have been wrong...
>
> > [...]
> >
> > > > > Right. Or at first it might be enough (and simplest) to assume
> > > > > that
> > > > > if
> > > > > LEDs are used via this driver, then colour for both LEDs is set
> > > > > explicitly by user-space. I wouldn't try guessing if sibling
> > > > > LED
> > > > > state
> > > > > changes to OFF when one LED is turned ON - or if LED states
> > > > > change
> > > > > to
> > > > > ON if both are turned OFF. This would require exporting
> > > > > interfaces
> > > > > from
> > > > > power-supply driver - and it would still be racy. The thing is
> > > > > that
> > > > > when both LEDs are on board they are both either under HW or SW
> > > > > control. So it makes no sense to control only one LED in such
> > > > > case.
> > > > > Thus I think it is Ok if this LED driver is registering both
> > > > > class
> > > > > devices at one probe. No need to instantiate own platform
> > > > > devices
> > > > > for
> > > > > both of the LEDs.
> > > >
> > > > We always register all LEDs originating from the same device in
> > > > one
> > > > probe.
> > > >
> > >
> > > Then I see little benefit from of_compatible or leds subnode for
> > > MFD
> > > devices with many LEDs. The driver or core must in any ways parse
> > > the
> > > DT in order to find the sub nodes with information for individual
> > > LEDs.
> > > I don't think that would be much different from just using the MFD
> > > node
> > > as controller node and walking through the MFD child nodes to
> > > locate
> > > LED sub nodes (at least for MFDs with simple LED controller).
> >
> > The cases for not having child nodes are when you have child nodes
> > with nothing more than a compatible and possibly provider properties
> > (e.g. #gpio-cells for gpio providers). If you have other resource
> > dependencies (e.g. clocks) or data to define (e.g. voltages for
> > regulators), then child nodes absolutely make sense.
>
> Thanks for telling the reasoning behind. Makes sense.
>
> > Once we have
> > child nodes, then generally it is easier for every function to be a
> > child node and not mix the two. I'm sure I have told people
> > incorrectly to not do child nodes because they define incomplete
> > bindings.
>
> Does this mean that if I add LED controlled node with LED nodes inside
> - then I should actually add sub nodes for clk and GPIO too? I would
> prefer still having the clk provider information in MFD node as adding
> a sub-node for clk would probably require changes in the bd718x7_clk
> driver. (Not big ones but avoidable if clk provider information can
> still dwell in MFD node).

Probably not, if there's an existing structure to follow, then
continue doing that.

> > I would group the led nodes under an led-controller node (with a
> > compatible). The simple reason is each level only has one
> > number/address space and you can't mix different ones. You're not
> > numbering the leds here, but could you (with numbers that correspond
> > to something in the h/w, not just 0..N)?
>
> I don't know what that would be. The LED controller resides in MFD
> device in I2C bus and has no meaningful numbers I can think of. The
> actual LEDs (on my board) are dummy devices and I really don't know how
> to invent meaningfull numbers for them either.

If you have something like "led control registers 1, 2, 3" where 1,2,3
is each LED channel, then use that. Or if the LED supplies (or supply
pins) have some numbering, use that. If there's none of that, then
following standard node names kind of falls apart. '<generic name>-N'
is what I've been defining for some schema.

Rob

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

* Re: [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
  2019-10-25 15:47                           ` Rob Herring
@ 2019-10-29 13:29                             ` Vaittinen, Matti
  0 siblings, 0 replies; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-29 13:29 UTC (permalink / raw)
  To: robh+dt
  Cc: broonie, dmurphy, linux-leds, linux-rtc, linux-gpio,
	linux-kernel, alexandre.belloni, mazziesaccount, mturquette,
	lgirdwood, devicetree, linus.walleij, a.zummo, mark.rutland,
	bgolaszewski, linux-clk, sboyd, lee.jones, jacek.anaszewski,
	pavel

Hello Rob,
On Fri, 2019-10-25 at 10:47 -0500, Rob Herring wrote:
> On Fri, Oct 25, 2019 at 9:37 AM Vaittinen, Matti
> <Matti.Vaittinen@fi.rohmeurope.com> wrote:
> > Hello Peeps,
> > 
> > On Fri, 2019-10-25 at 08:24 -0500, Rob Herring wrote:
> > > On Fri, Oct 25, 2019 at 2:07 AM Vaittinen, Matti
> > > <Matti.Vaittinen@fi.rohmeurope.com> wrote:
> > > The cases for not having child nodes are when you have child
> > > nodes
> > > with nothing more than a compatible and possibly provider
> > > properties
> > > (e.g. #gpio-cells for gpio providers). If you have other resource
> > > dependencies (e.g. clocks) or data to define (e.g. voltages for
> > > regulators), then child nodes absolutely make sense.
> > 
> > Thanks for telling the reasoning behind. Makes sense.
> > 
> > > Once we have
> > > child nodes, then generally it is easier for every function to be
> > > a
> > > child node and not mix the two. I'm sure I have told people
> > > incorrectly to not do child nodes because they define incomplete
> > > bindings.
> > 
> > Does this mean that if I add LED controlled node with LED nodes
> > inside
> > - then I should actually add sub nodes for clk and GPIO too? I
> > would
> > prefer still having the clk provider information in MFD node as
> > adding
> > a sub-node for clk would probably require changes in the
> > bd718x7_clk
> > driver. (Not big ones but avoidable if clk provider information can
> > still dwell in MFD node).
> 
> Probably not, if there's an existing structure to follow, then
> continue doing that.

Ok, thanks.

> 
> > > I would group the led nodes under an led-controller node (with a
> > > compatible). The simple reason is each level only has one
> > > number/address space and you can't mix different ones. You're not
> > > numbering the leds here, but could you (with numbers that
> > > correspond
> > > to something in the h/w, not just 0..N)?
> > 
> > I don't know what that would be. The LED controller resides in MFD
> > device in I2C bus and has no meaningful numbers I can think of. The
> > actual LEDs (on my board) are dummy devices and I really don't know
> > how
> > to invent meaningfull numbers for them either.
> 
> If you have something like "led control registers 1, 2, 3" where
> 1,2,3
> is each LED channel, then use that.

Unfortunately, no. LED controls are in same register.

>  Or if the LED supplies (or supply
> pins) have some numbering, use that.

I don't know how to format the numbering either. Currently planned PMIC
package is so called "UCSP55M3C" meaning the pins are in a matrix -
columns having numbers from 1 to 8 and rows having letters from A to J.
In this case the LED outputs are F6 and H6. I don't know if different
packaging is planned. Only 'constant' I can find is the output pin
naming 'GRNLED' and 'AMBLED' :/

> If there's none of that, then
> following standard node names kind of falls apart. '<generic name>-N'
> is what I've been defining for some schema.

So I could use node names led-1 and led-2 in the example, right?

Br,
	Matti Vaittinen

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

* Re: [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver
  2019-10-23 10:27         ` Vaittinen, Matti
@ 2019-10-29 13:50           ` Alexandre Belloni
  2019-10-29 14:08             ` Vaittinen, Matti
  0 siblings, 1 reply; 43+ messages in thread
From: Alexandre Belloni @ 2019-10-29 13:50 UTC (permalink / raw)
  To: Vaittinen, Matti
  Cc: dmurphy, linux-leds, linux-rtc, linux-gpio, linux-kernel,
	mazziesaccount, mturquette, devicetree, jacek.anaszewski,
	a.zummo, linus.walleij, lgirdwood, mark.rutland, robh+dt,
	bgolaszewski, linux-clk, pavel, sboyd, broonie, lee.jones

On 23/10/2019 10:27:43+0000, Vaittinen, Matti wrote:
> Hello again Alexandre,
> 
> On Thu, 2019-10-17 at 12:48 +0200, Alexandre Belloni wrote:
> > On 17/10/2019 10:36:44+0000, Vaittinen, Matti wrote:
> > > Hello Alexandre,
> > > 
> > > Thanks for quick check! I'll be off for the rest of the week but I
> > > will
> > > re-work this patch at next week :) I agree with you regarding most
> > > of
> > > the comments.
> > > 
> > > > > +
> > > > > +
> > > > > +/*
> > > > > + * RTC definitions shared between
> > > > > + *
> > > > > + * BD70528
> > > > > + * and BD71828
> > > > > + */
> > > > > +
> > > > > +#define ROHM_BD1_MASK_RTC_SEC		0x7f
> > > > > +#define ROHM_BD1_MASK_RTC_MINUTE	0x7f
> > > > > +#define ROHM_BD1_MASK_RTC_HOUR_24H	0x80
> > > > > +#define ROHM_BD1_MASK_RTC_HOUR_PM	0x20
> > > > > +#define ROHM_BD1_MASK_RTC_HOUR		0x3f
> > > > > +#define ROHM_BD1_MASK_RTC_DAY		0x3f
> > > > > +#define ROHM_BD1_MASK_RTC_WEEK		0x07
> > > > > +#define ROHM_BD1_MASK_RTC_MONTH		0x1f
> > > > > +#define ROHM_BD1_MASK_RTC_YEAR		0xff
> > > > > +#define ROHM_BD1_MASK_ALM_EN		0x7
> > > > > +
> > > > 
> > > > All that renaming is distracting and useless. Please resubmit
> > > > without
> > > > renaming defines, structs and functions to make it easier to
> > > > review.
> > > 
> > > I would prefer renaming because it makes it clearly visible which
> > > defines/structs/functions are common for both PMICs and which are
> > > PMIC
> > > specific. But I really understand the problem of spotting real
> > > changes.
> > > Would it be Ok if I did renaming in separate patch which does not
> > > bring
> > > in any other changes - and then the functional changes in separate
> > > patch?
> > > 
> > 
> > No, unless you can guarantee that all future PMICs from rohm matching
> > the wildcard will use this driver.
> > 
> I started re-working this patch and remembered my original idea
> regarding the naming :) I should have commented it as I had already
> forgotten it. You are correct what comes to the difficulty of using
> correct wild-cards. And I agree with you what comes to function and
> struct names like bd7xx28 - those are somewhat fragile as next PMIC
> which we want to support with this driver may be BD12345 - yielding our
> wild-card useless.
> 
> But if we take a look of common definitions in header rohm-shared.h
> which I added - those are prefixed as ROHM_BD1. My idea was introducing
> this common RTC define group 1 - which would be common define group for
> all devices which belong to BD1 group. Currently that would be BD71828
> and BD70528. What was missing is the comment explaining this (and lack
> of comment made this useless as even I forgot it already).
> 
> I already reverted this naming change and all BD70528 specific and
> common defines/functions/enums are prefixed with the good old BD70528.
> Only new definitions which I added for BD71828 are prefixed with
> BD71828. But how do you see the grouping the common defines to format
> ROHM_BD<group number>_FOO_BAR in the rohm-shared.h - with comment that
> group BD1 consists of definitions which are common for BD70528 and
> BD71828?
> 
> My only fear when using prefix BD70528 for common defines is that
> someone changes some defines to match the BD70528 data-sheet without
> evaluating if this impacts to other PMICs. It may be useless paranoia
> though - hence I am asking for your opinion at this phase. I can do
> this grouping in own patch - or just leave it as it is now in my local
> repo - with the old BD70528 being common prefix.
> 

I don't think those masks will ever change, all the BCD RTCs are using
the same.

Note that ROHM_BD1_MASK_RTC_HOUR_24H, ROHM_BD1_MASK_RTC_HOUR_PM and
ROHM_BD1_MASK_ALM_EN are bits and should use BIT() to make that clear.
Those may change later but I don't see how someone looking at the
BD70528 datasheet would get those wrong.

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver
  2019-10-29 13:50           ` Alexandre Belloni
@ 2019-10-29 14:08             ` Vaittinen, Matti
  0 siblings, 0 replies; 43+ messages in thread
From: Vaittinen, Matti @ 2019-10-29 14:08 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: dmurphy, linux-leds, linux-rtc, linux-gpio, linux-kernel,
	mazziesaccount, mturquette, devicetree, jacek.anaszewski,
	a.zummo, linus.walleij, lgirdwood, mark.rutland, robh+dt,
	bgolaszewski, linux-clk, pavel, sboyd, broonie, lee.jones


On Tue, 2019-10-29 at 14:50 +0100, Alexandre Belloni wrote:
> On 23/10/2019 10:27:43+0000, Vaittinen, Matti wrote:
> > Hello again Alexandre,
> > 
> > On Thu, 2019-10-17 at 12:48 +0200, Alexandre Belloni wrote:
> > > On 17/10/2019 10:36:44+0000, Vaittinen, Matti wrote:
> > > > Hello Alexandre,
> > > > 
> > > > Thanks for quick check! I'll be off for the rest of the week
> > > > but I
> > > > will
> > > > re-work this patch at next week :) I agree with you regarding
> > > > most
> > > > of
> > > > the comments.
> > > > 
> > > > > > +
> > > > > > +
> > > > > > +/*
> > > > > > + * RTC definitions shared between
> > > > > > + *
> > > > > > + * BD70528
> > > > > > + * and BD71828
> > > > > > + */
> > > > > > +
> > > > > > +#define ROHM_BD1_MASK_RTC_SEC		0x7f
> > > > > > +#define ROHM_BD1_MASK_RTC_MINUTE	0x7f
> > > > > > +#define ROHM_BD1_MASK_RTC_HOUR_24H	0x80
> > > > > > +#define ROHM_BD1_MASK_RTC_HOUR_PM	0x20
> > > > > > +#define ROHM_BD1_MASK_RTC_HOUR		0x3f
> > > > > > +#define ROHM_BD1_MASK_RTC_DAY		0x3f
> > > > > > +#define ROHM_BD1_MASK_RTC_WEEK		0x07
> > > > > > +#define ROHM_BD1_MASK_RTC_MONTH		0x1f
> > > > > > +#define ROHM_BD1_MASK_RTC_YEAR		0xff
> > > > > > +#define ROHM_BD1_MASK_ALM_EN		0x7
> > > > > > +
> > > > > 
> > > > > All that renaming is distracting and useless. Please resubmit
> > > > > without
> > > > > renaming defines, structs and functions to make it easier to
> > > > > review.
> > > > 
> > > > I would prefer renaming because it makes it clearly visible
> > > > which
> > > > defines/structs/functions are common for both PMICs and which
> > > > are
> > > > PMIC
> > > > specific. But I really understand the problem of spotting real
> > > > changes.
> > > > Would it be Ok if I did renaming in separate patch which does
> > > > not
> > > > bring
> > > > in any other changes - and then the functional changes in
> > > > separate
> > > > patch?
> > > > 
> > > 
> > > No, unless you can guarantee that all future PMICs from rohm
> > > matching
> > > the wildcard will use this driver.
> > > 
> > I started re-working this patch and remembered my original idea
> > regarding the naming :) I should have commented it as I had already
> > forgotten it. You are correct what comes to the difficulty of using
> > correct wild-cards. And I agree with you what comes to function and
> > struct names like bd7xx28 - those are somewhat fragile as next PMIC
> > which we want to support with this driver may be BD12345 - yielding
> > our
> > wild-card useless.
> > 
> > But if we take a look of common definitions in header rohm-shared.h
> > which I added - those are prefixed as ROHM_BD1. My idea was
> > introducing
> > this common RTC define group 1 - which would be common define group
> > for
> > all devices which belong to BD1 group. Currently that would be
> > BD71828
> > and BD70528. What was missing is the comment explaining this (and
> > lack
> > of comment made this useless as even I forgot it already).
> > 
> > I already reverted this naming change and all BD70528 specific and
> > common defines/functions/enums are prefixed with the good old
> > BD70528.
> > Only new definitions which I added for BD71828 are prefixed with
> > BD71828. But how do you see the grouping the common defines to
> > format
> > ROHM_BD<group number>_FOO_BAR in the rohm-shared.h - with comment
> > that
> > group BD1 consists of definitions which are common for BD70528 and
> > BD71828?
> > 
> > My only fear when using prefix BD70528 for common defines is that
> > someone changes some defines to match the BD70528 data-sheet
> > without
> > evaluating if this impacts to other PMICs. It may be useless
> > paranoia
> > though - hence I am asking for your opinion at this phase. I can do
> > this grouping in own patch - or just leave it as it is now in my
> > local
> > repo - with the old BD70528 being common prefix.
> > 
> 
> I don't think those masks will ever change, all the BCD RTCs are
> using
> the same.

I guess this is very true. And to follow this further - If then next
ROHM RTC I work with is not using BCD - then I am probably not trying
to support it with this driver. So I'd say you are correct.

> Note that ROHM_BD1_MASK_RTC_HOUR_24H, ROHM_BD1_MASK_RTC_HOUR_PM and
> ROHM_BD1_MASK_ALM_EN are bits and should use BIT() to make that
> clear.

Ok. I'll use BIT() for new/moved one bit defines in next versions.

> Those may change later but I don't see how someone looking at the
> BD70528 datasheet would get those wrong.

I admit this is unlikely and I don't see the scenario how this can
break - assuming these masks are now correct for BD70528 ;) If there
was an error then having prefix BD70528 for define which is used by
BD71828 (and possibly others) might be error prone as one could fix the
define without checking the BD71828. When we limit this to those three
defines it is _really_ unlikely (and probably not a problem) - but this
was the reason why I wanted to do the renaming of common defines.

By the way, I sent patch v2 couple of days ago - and I kept the BD70528
prefix as you suggested. I'll do that also in next versions so we can
probably say this case is closed :]

Thanks for taking the time to read the patches!

Br,
	Matti Vaittinen


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

end of thread, back to index

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-17  9:40 [RFC PATCH 00/13] Support ROHM BD71828 PMIC Matti Vaittinen
2019-10-17  9:41 ` [RFC PATCH 01/13] mfd: bd71828: Support ROHM BD71828 PMIC - core Matti Vaittinen
2019-10-17  9:42 ` [RFC PATCH 02/13] mfd: input: bd71828: Add power-key support Matti Vaittinen
2019-10-17  9:43 ` [RFC PATCH 03/13] clk: bd718x7: Support ROHM BD71828 clk block Matti Vaittinen
2019-10-17  9:44 ` [RFC PATCH 04/13] regulator: bd718x7: Split driver to common and bd718x7 specific parts Matti Vaittinen
2019-10-17  9:44 ` [RFC PATCH 05/13] regulator: bd71828: Basic support for ROHM bd71828 PMIC regulators Matti Vaittinen
2019-10-17  9:48 ` [RFC PATCH 06/13] regulator: bd71828: Add GPIO based run-level control for regulators Matti Vaittinen
2019-10-17  9:50 ` [RFC PATCH 07/13] regulator: bd71828: enhanced run-level support Matti Vaittinen
2019-10-17  9:51 ` [RFC PATCH 08/13] regulator: bd71828: Support in-kernel APIs to change run-level Matti Vaittinen
2019-10-17  9:52 ` [RFC PATCH 09/13] mfd: rtc: support RTC on ROHM BD71828 with BD70528 driver Matti Vaittinen
2019-10-17 10:12   ` Alexandre Belloni
2019-10-17 10:36     ` Vaittinen, Matti
2019-10-17 10:48       ` Alexandre Belloni
2019-10-21  5:29         ` Vaittinen, Matti
2019-10-23 10:27         ` Vaittinen, Matti
2019-10-29 13:50           ` Alexandre Belloni
2019-10-29 14:08             ` Vaittinen, Matti
2019-10-17  9:53 ` [RFC PATCH 10/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs Matti Vaittinen
2019-10-17 12:45   ` Bartosz Golaszewski
2019-10-21  7:00     ` Vaittinen, Matti
2019-10-21 14:36       ` Bartosz Golaszewski
2019-10-21 14:56         ` Vaittinen, Matti
2019-10-22 13:19         ` Vaittinen, Matti
2019-10-17  9:53 ` [RFC PATCH 11/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC Matti Vaittinen
2019-10-17 14:04   ` Dan Murphy
2019-10-17 14:28     ` Alexandre Belloni
2019-10-21  8:00     ` Vaittinen, Matti
2019-10-21 19:09       ` Jacek Anaszewski
2019-10-22 12:40         ` Vaittinen, Matti
2019-10-22 17:40           ` Jacek Anaszewski
2019-10-23  8:37             ` Vaittinen, Matti
2019-10-23 21:59               ` Jacek Anaszewski
2019-10-24  8:15                 ` Vaittinen, Matti
2019-10-24 22:04                   ` Jacek Anaszewski
2019-10-25  7:07                     ` Vaittinen, Matti
2019-10-25 13:24                       ` Rob Herring
2019-10-25 14:37                         ` Vaittinen, Matti
2019-10-25 15:47                           ` Rob Herring
2019-10-29 13:29                             ` Vaittinen, Matti
2019-10-17  9:55 ` [RFC PATCH 12/13] dt-bindings: mfd: Document ROHM BD71282 bindings Matti Vaittinen
2019-10-17 14:18   ` Dan Murphy
2019-10-21  8:03     ` Vaittinen, Matti
2019-10-17  9:57 ` [RFC PATCH 13/13] dt-bindings: regulator: Document ROHM BD71282 regulator bindings Matti Vaittinen

Linux-RTC Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-rtc/0 linux-rtc/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-rtc linux-rtc/ https://lore.kernel.org/linux-rtc \
		linux-rtc@vger.kernel.org
	public-inbox-index linux-rtc

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-rtc


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git