2019-11-18 06:55:21

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 00/16] Support ROHM BD71828 PMIC

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

The series also adds LED DT-node lookup based on node name or given
property name/value pair in LED core. It also adds generic default-state
and default-trigger property handling to LED core. Follow-up patches
simplifying few other LED drivers should follow.

In GPIO framework this series adds devm-support for gpio_array getting
for MFD sub-devices whose GPIO consumer information may be in parent
device's DT node. And while I was at it I also added few missing GPIO devm
functions to the documentaton listing.

Changelog v5:
Only LED patch (patch 15) changed, rest as in v4.
LED:
- Fixed issues reported by Dan Carpenter and kbuild-bot static
analysis.
Changelog v4 (first non RFC):
General:
- Changed subdevice loading and chip version identification to use
platform ID.
- License identifiers changed to GPL-2.0-only
MFD:
- Styling fixes mostly
DT-Bindings:
- a few more checks as suggested by Rob Herring.
- Order of DT patches changed.
- me as maintainer
- standard units to new properties (microvolts, ohms)
- runlevel values in an array
LED:
- BD71828 driver added (back)
- Added DT support
- Added LED DT node lookup in led framework when init_data is given
with DT node match information.
- Added common property parsing for default-state and
default-trigger.
Regulators:
- dropped sysfs interfaces
- fixed module unload/reload by binding gpio consumer information to
regulator device not to MFD.
GPIO:
- Added devm_gpiod_get_parent_array
- added few missing devm functions to documentation

Changelog v3:
DT-Bindings:
- yamlify
- add LED binding doc
CLK:
- Move clk register definitions from MFD headers to clk driver
GPIO:
- Add generic direction define and use it.
LED:
- Drop LED driver from the series (for now).

Changelog v2: Mainly RTC and GPIO fixes suggested by Alexandre and Bartosz
General:
-Patch ordering changed to provide dt binding documents right after the
MFD core.
DT-Bindings for regulators (Patch 3)
-Fix typo in PMIC model number
RTC (patch 11)
-Reverted renaming in order to reduce patch size.
-Reworded commit message
BD71828 regulator (patch 7)
-Add MODULE_ALIAS
GPIO (patch 12)
-Remove file-name from comment
-prefix IN and OUT defines with chip type
-improved documentation for the INPUT only pin.
-removed empty left-over function
-removed unnecessary #ifdef CONFIG_OF_GPIO
-removed unnecessary error print
-Add MODULE_ALIAS

Patch 1:
dt-bindings for regulators on BD71828 PMIC
Patch 2:
dt-bindings for LEDs on BD71828 PMIC
Patch 3:
dt-bindings for BD71828 PMIC
Patch 4:
Convert rohm PMICs with common sub-devices to use platform_
device_id to match MFD sub-devices
Patch 5:
BD71828 MFD core.
Patch 6:
Power button support using GPIO keys.
Patch 7:
CLK gate support using existing clk-bd718x7
Patch 8:
Split existing bd718x7 regulator driver to generic ROHM dt
parsing portion (used by more than one ROHM drivers) and
bd718x8 specific parts
Patch 9:
Basic regulator support (individual control via I2C). This
should be pretty standard stuff.
Patch 10:
Add devm_gpiod_get_parent_array
Patch 11:
Add missing managed GPIO array get functions to documentation
Patch 12:
Add support for getting regulator voltages when run-levels are
used. Allow specifying voltages for run-levels via DT and
in-kernel API. Support changing run-levels via in-kernel API.
Patch 13:
Support BD71828 RTC block using BD70528 RTC driver
Patch 14:
Allow control of GP(I)O pins on BD71828 via GPIO subsystem
Patch 15:
Add LED node lookup and common LED binding parsing support
to LED class/core
Patch 16:
Support toggling the LEDs on BD71828.

This patch series is based on v5.4-rc7

---

Matti Vaittinen (16):
dt-bindings: regulator: Document ROHM BD71282 regulator bindings
dt-bindings: leds: ROHM BD71282 PMIC LED driver
dt-bindings: mfd: Document ROHM BD71828 bindings
mfd: rohm PMICs - use platform_device_id to match MFD sub-devices
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
gpio: devres: Add devm_gpiod_get_parent_array
docs: driver-model: Add missing managed GPIO array get functions
regulator: bd71828: Add GPIO based run-level control for regulators
rtc: bd70528 add BD71828 support
gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs
leds: Add common LED binding parsing support to LED class/core
led: bd71828: Support LED outputs on ROHM BD71828 PMIC

.../bindings/leds/rohm,bd71828-leds.yaml | 49 +
.../bindings/mfd/rohm,bd71828-pmic.yaml | 249 +++
.../regulator/rohm,bd71828-regulator.yaml | 122 ++
.../driver-api/driver-model/devres.rst | 3 +
drivers/clk/Kconfig | 6 +-
drivers/clk/clk-bd718x7.c | 50 +-
drivers/gpio/Kconfig | 12 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-bd71828.c | 159 ++
drivers/gpio/gpiolib-devres.c | 65 +-
drivers/leds/Kconfig | 10 +
drivers/leds/Makefile | 1 +
drivers/leds/led-class.c | 88 +-
drivers/leds/led-core.c | 246 ++-
drivers/leds/leds-bd71828.c | 104 ++
drivers/mfd/Kconfig | 15 +
drivers/mfd/Makefile | 2 +-
drivers/mfd/rohm-bd70528.c | 3 +-
drivers/mfd/rohm-bd71828.c | 345 +++++
drivers/mfd/rohm-bd718x7.c | 39 +-
drivers/regulator/Kconfig | 16 +
drivers/regulator/Makefile | 2 +
drivers/regulator/bd71828-regulator.c | 1374 +++++++++++++++++
drivers/regulator/bd718x7-regulator.c | 200 +--
drivers/regulator/rohm-regulator.c | 95 ++
drivers/rtc/Kconfig | 3 +-
drivers/rtc/rtc-bd70528.c | 167 +-
include/linux/gpio/consumer.h | 5 +
include/linux/leds.h | 90 +-
include/linux/mfd/rohm-bd70528.h | 19 +-
include/linux/mfd/rohm-bd71828.h | 428 +++++
include/linux/mfd/rohm-bd718x7.h | 6 -
include/linux/mfd/rohm-generic.h | 48 +-
include/linux/mfd/rohm-shared.h | 27 +
34 files changed, 3771 insertions(+), 278 deletions(-)
create mode 100644 Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
create mode 100644 Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml
create mode 100644 Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.yaml
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


base-commit: 31f4f5b495a62c9a8b15b1c3581acd5efeb9af8c
--
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 =]


2019-11-18 06:55:59

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

Document ROHM BD71828 PMIC regulator device tree bindings.

Signed-off-by: Matti Vaittinen <[email protected]>
---

No changes from v4

.../regulator/rohm,bd71828-regulator.yaml | 122 ++++++++++++++++++
1 file changed, 122 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.yaml

diff --git a/Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.yaml b/Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.yaml
new file mode 100644
index 000000000000..c23ec4d8584b
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.yaml
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/rohm,bd71828-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ROHM BD71828 Power Management Integrated Circuit regulators
+
+maintainers:
+ - Matti Vaittinen <[email protected]>
+
+description: |
+ This module is part of the ROHM BD71828 MFD device. For more details
+ see Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml.
+
+ The regulator controller is represented as a sub-node of the PMIC node
+ on the device tree.
+
+ Regulator nodes should be named to BUCK_<number> and LDO_<number>.
+ The valid names for BD71828 regulator nodes are
+ BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6, BUCK7
+ LDO1, LDO2, LDO3, LDO4, LDO5, LDO6, LDO7
+
+patternProperties:
+ "^LDO[1-7]$":
+ type: object
+ allOf:
+ - $ref: regulator.yaml#
+ description:
+ Properties for single LDO regulator.
+
+ properties:
+ #Is there a nice way to check the name is same as node name but lower case
+ regulator-name:
+ pattern: "^ldo[1-7]$"
+ description:
+ should be "ldo1", ..., "ldo7"
+
+ "^BUCK[1-7]$":
+ type: object
+ allOf:
+ - $ref: regulator.yaml#
+ description:
+ Properties for single BUCK regulator.
+
+ properties:
+ #Is there a nice way to check the name is same as node name but lower case
+ regulator-name:
+ pattern: "^buck[1-7]$"
+ description:
+ should be "buck1", ..., "buck7"
+
+ rohm,dvs-run-voltage:
+ allOf:
+ - $ref: "/schemas/types.yaml#/definitions/uint32"
+ - minimum: 0
+ maximum: 3300000
+ description:
+ PMIC default "RUN" state voltage in uV. See below table for
+ bucks which support this. 0 means disabled.
+
+ rohm,dvs-idle-voltage:
+ allOf:
+ - $ref: "/schemas/types.yaml#/definitions/uint32"
+ - minimum: 0
+ maximum: 3300000
+ description:
+ PMIC default "IDLE" state voltage in uV. See below table for
+ bucks which support this. 0 means disabled.
+
+ rohm,dvs-suspend-voltage:
+ allOf:
+ - $ref: "/schemas/types.yaml#/definitions/uint32"
+ - minimum: 0
+ maximum: 3300000
+ description:
+ PMIC default "SUSPEND" state voltage in uV. See below table for
+ bucks which support this. 0 means disabled.
+
+ rohm,dvs-lpsr-voltage:
+ allOf:
+ - $ref: "/schemas/types.yaml#/definitions/uint32"
+ - minimum: 0
+ maximum: 3300000
+ description:
+ PMIC default "LPSR" state voltage in uV. See below table for
+ bucks which support this. 0 means disabled.
+
+#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.
+
+ rohm,dvs-runlvl-ctrl:
+ description: |
+ buck control is done based on run-level. Regulator is not
+ individually controllable. See ../mfd/rohm,bd71828-pmic.yaml for
+ how to specify run-level control mechanism. Only bucks 1, 2, 6
+ and 7 support this.
+ type: boolean
+
+ rohm,dvs-runlevel-microvolts:
+ minimum: 0
+ maximum: 2000000
+ maxItems: 4
+ description:
+ Array of voltages for run-levels. First value is for run-level 0,
+ second for run-level 1 etc. Microvolts.
+
+ required:
+ - regulator-name
+ additionalProperties: false
+additionalProperties: false
--
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 =]

2019-11-18 06:57:04

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 03/16] dt-bindings: mfd: Document ROHM BD71828 bindings

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

Document the dt bindings drivers are using.

Signed-off-by: Matti Vaittinen <[email protected]>
---

No changes from v4

.../bindings/mfd/rohm,bd71828-pmic.yaml | 249 ++++++++++++++++++
1 file changed, 249 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml

diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml
new file mode 100644
index 000000000000..17ea9fa463e8
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml
@@ -0,0 +1,249 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/rohm,bd71828-pmic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ROHM BD71828 Power Management Integrated Circuit bindings
+
+maintainers:
+ - Matti Vaittinen <[email protected]>
+
+description: |
+ 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.
+
+ 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.yaml for how to define regulator voltages
+ for run-levels. Run-levels can be changed by I2C or GPIO depending on PMIC's
+ OTP configuration.
+
+properties:
+ compatible:
+ const: rohm,bd71828
+
+ reg:
+ description:
+ I2C slave address.
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+ description:
+ The interrupt line the device is connected to.
+
+ gpio-controller: true
+ # Indicate BD71828 acts as a GPIO controller.
+
+ "#gpio-cells":
+ const: 2
+ description: |
+ The first cell is the pin number and the second cell is used to specify
+ flags. See ../gpio/gpio.txt for more information.
+
+ clocks:
+ maxItems: 1
+ description:
+ The parent clock connected to PMIC.
+
+ "#clock-cells":
+ const: 0
+
+ rohm,charger-sense-resistor-ohms:
+ minimum: 10000000
+ maximum: 50000000
+ description: |
+ BD71827 and BD71828 have SAR ADC for measuring charging currents.
+ External sense resistor (RSENSE in data sheet) should be used. If some
+ other but 30MOhm resistor is used the resistance value should be given
+ here in Ohms.
+
+ regulators:
+ $ref: ../regulator/rohm,bd71828-regulator.yaml
+ description:
+ List of child nodes that specify the regulators.
+
+ leds:
+ $ref: ../leds/rohm,bd71828-leds.yaml
+
+ rohm,dvs-vsel-gpios:
+ maxItems: 2
+ description: |
+ 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:
+ description: |
+ Usage of BD71828 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.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - "#clock-cells"
+ - regulators
+ - gpio-controller
+ - "#gpio-cells"
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/leds/common.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pmic: pmic@4b {
+ compatible = "rohm,bd71828";
+ reg = <0x4b>;
+
+ interrupt-parent = <&gpio1>;
+ interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
+
+ clocks = <&osc 0>;
+ #clock-cells = <0>;
+ clock-output-names = "bd71828-32k-out";
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-reserved-ranges = <0 1>, <2 1>;
+
+ rohm,dvs-vsel-gpios = <&gpio1 12 0>, <&gpio1 13 0>;
+ rohm,charger-sense-resistor-ohms = <10000000>;
+
+ regulators {
+ buck1: BUCK1 {
+ regulator-name = "buck1";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-ramp-delay = <2500>;
+ rohm,dvs-runlvl-ctrl;
+ rohm,dvs-runlevel-microvolts = <500000>,
+ <506250>,
+ <512500>,
+ <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-runlevel-microvolts = <500000>,
+ <506250>,
+ <512500>,
+ <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-runlevel-microvolts = <500000>,
+ <506250>,
+ <512500>,
+ <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-runlevel-microvolts = <500000>,
+ <506250>,
+ <512500>,
+ <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;
+ };
+ };
+
+ leds {
+ compatible = "rohm,bd71828-leds";
+
+ led-1 {
+ rohm,led-compatible = "bd71828-grnled";
+ function = LED_FUNCTION_INDICATOR;
+ color = <LED_COLOR_ID_GREEN>;
+ };
+ led-2 {
+ rohm,led-compatible = "bd71828-ambled";
+ function = LED_FUNCTION_CHARGING;
+ color = <LED_COLOR_ID_AMBER>;
+ };
+ };
+ };
+ };
--
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 =]

2019-11-18 06:57:29

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 02/16] dt-bindings: leds: ROHM BD71282 PMIC LED driver

Document ROHM BD71828 PMIC LED driver device tree bindings.

Signed-off-by: Matti Vaittinen <[email protected]>
---

No changes from v4

.../bindings/leds/rohm,bd71828-leds.yaml | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml

diff --git a/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml b/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
new file mode 100644
index 000000000000..e72c9ae0fc2a
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/rohm,bd71828-leds.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ROHM BD71828 Power Management Integrated Circuit LED driver
+
+maintainers:
+ - Matti Vaittinen <[email protected]>
+
+description: |
+ This module is part of the ROHM BD71828 MFD device. For more details
+ see Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml.
+
+ The LED controller is represented as a sub-node of the PMIC node on the device
+ tree.
+
+ The device has two LED outputs referred as GRNLED and AMBLED in data-sheet.
+
+properties:
+ compatible:
+ const: rohm,bd71828-leds
+
+patternProperties:
+ "^led-[1-2]$":
+ type: object
+ description:
+ Properties for a single LED.
+ properties:
+ #$ref: "common.yaml#"
+ rohm,led-compatible:
+ description: LED identification string
+ allOf:
+ - $ref: "/schemas/types.yaml#/definitions/string"
+ - oneOf:
+ - const: bd71828-ambled
+ - const: bd71828-grnled
+ function:
+ description:
+ Purpose of LED as defined in dt-bindings/leds/common.h
+ $ref: "/schemas/types.yaml#/definitions/string"
+ color:
+ description:
+ LED colour as defined in dt-bindings/leds/common.h
+ $ref: "/schemas/types.yaml#/definitions/uint32"
+
+required:
+ - compatible
--
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 =]

2019-11-18 06:58:16

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 04/16] mfd: rohm PMICs - use platform_device_id to match MFD sub-devices

Thanks to Stephen Boyd I today learned we can use platform_device_id
to do device and module matching for MFD sub-devices!

Do device matching using the platform_device_id instead of using
explicit module_aliases to load modules and custom parent-data field
to do module loading and sub-device matching.

Signed-off-by: Matti Vaittinen <[email protected]>
---

No changes from v4

drivers/clk/clk-bd718x7.c | 12 ++++++++-
drivers/mfd/rohm-bd70528.c | 3 +--
drivers/mfd/rohm-bd718x7.c | 39 ++++++++++++++++++++++-----
drivers/regulator/bd718x7-regulator.c | 17 +++++++++---
include/linux/mfd/rohm-generic.h | 3 +--
5 files changed, 58 insertions(+), 16 deletions(-)

diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c
index ae6e5baee330..1c1764f74d0a 100644
--- a/drivers/clk/clk-bd718x7.c
+++ b/drivers/clk/clk-bd718x7.c
@@ -74,6 +74,7 @@ static int bd71837_clk_probe(struct platform_device *pdev)
.name = "bd718xx-32k-out",
.ops = &bd71837_clk_ops,
};
+ enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data;

c = devm_kzalloc(&pdev->dev, sizeof(*c), GFP_KERNEL);
if (!c)
@@ -87,7 +88,7 @@ static int bd71837_clk_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "No parent clk found\n");
return -EINVAL;
}
- switch (mfd->chip_type) {
+ switch (chip) {
case ROHM_CHIP_TYPE_BD71837:
case ROHM_CHIP_TYPE_BD71847:
c->reg = BD718XX_REG_OUT32K;
@@ -121,11 +122,20 @@ static int bd71837_clk_probe(struct platform_device *pdev)
return rval;
}

+static const struct platform_device_id bd718x7_clk_id[] = {
+ { "bd71837-clk", ROHM_CHIP_TYPE_BD71837 },
+ { "bd71847-clk", ROHM_CHIP_TYPE_BD71847 },
+ { "bd70528-clk", ROHM_CHIP_TYPE_BD70528 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bd718x7_clk_id);
+
static struct platform_driver bd71837_clk = {
.driver = {
.name = "bd718xx-clk",
},
.probe = bd71837_clk_probe,
+ .id_table = bd718x7_clk_id,
};

module_platform_driver(bd71837_clk);
diff --git a/drivers/mfd/rohm-bd70528.c b/drivers/mfd/rohm-bd70528.c
index 55599d5c5c86..e66a4a1c3731 100644
--- a/drivers/mfd/rohm-bd70528.c
+++ b/drivers/mfd/rohm-bd70528.c
@@ -48,7 +48,7 @@ static struct mfd_cell bd70528_mfd_cells[] = {
* 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 = "bd70528-clk", },
{ .name = "bd70528-wdt", },
{
.name = "bd70528-power",
@@ -237,7 +237,6 @@ static int bd70528_i2c_probe(struct i2c_client *i2c,

dev_set_drvdata(&i2c->dev, &bd70528->chip);

- bd70528->chip.chip_type = ROHM_CHIP_TYPE_BD70528;
bd70528->chip.regmap = devm_regmap_init_i2c(i2c, &bd70528_regmap);
if (IS_ERR(bd70528->chip.regmap)) {
dev_err(&i2c->dev, "Failed to initialize Regmap\n");
diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
index 85e7f5133365..bb86ec829079 100644
--- a/drivers/mfd/rohm-bd718x7.c
+++ b/drivers/mfd/rohm-bd718x7.c
@@ -30,14 +30,24 @@ static struct gpio_keys_platform_data bd718xx_powerkey_data = {
.name = "bd718xx-pwrkey",
};

-static struct mfd_cell bd718xx_mfd_cells[] = {
+static struct mfd_cell bd71837_mfd_cells[] = {
{
.name = "gpio-keys",
.platform_data = &bd718xx_powerkey_data,
.pdata_size = sizeof(bd718xx_powerkey_data),
},
- { .name = "bd718xx-clk", },
- { .name = "bd718xx-pmic", },
+ { .name = "bd71837-clk", },
+ { .name = "bd71837-pmic", },
+};
+
+static struct mfd_cell bd71847_mfd_cells[] = {
+ {
+ .name = "gpio-keys",
+ .platform_data = &bd718xx_powerkey_data,
+ .pdata_size = sizeof(bd718xx_powerkey_data),
+ },
+ { .name = "bd71847-clk", },
+ { .name = "bd71847-pmic", },
};

static const struct regmap_irq bd718xx_irqs[] = {
@@ -124,6 +134,9 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
{
struct bd718xx *bd718xx;
int ret;
+ unsigned int chip_type;
+ struct mfd_cell *mfd;
+ int cells;

if (!i2c->irq) {
dev_err(&i2c->dev, "No IRQ configured\n");
@@ -136,8 +149,21 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;

bd718xx->chip_irq = i2c->irq;
- bd718xx->chip.chip_type = (unsigned int)(uintptr_t)
- of_device_get_match_data(&i2c->dev);
+ chip_type = (unsigned int)(uintptr_t)
+ of_device_get_match_data(&i2c->dev);
+ switch (chip_type) {
+ case ROHM_CHIP_TYPE_BD71837:
+ mfd = bd71837_mfd_cells;
+ cells = ARRAY_SIZE(bd71837_mfd_cells);
+ break;
+ case ROHM_CHIP_TYPE_BD71847:
+ mfd = bd71847_mfd_cells;
+ cells = ARRAY_SIZE(bd71847_mfd_cells);
+ break;
+ default:
+ dev_err(&i2c->dev, "Unknown device type");
+ return -EINVAL;
+ }
bd718xx->chip.dev = &i2c->dev;
dev_set_drvdata(&i2c->dev, bd718xx);

@@ -170,8 +196,7 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
button.irq = ret;

ret = devm_mfd_add_devices(bd718xx->chip.dev, PLATFORM_DEVID_AUTO,
- bd718xx_mfd_cells,
- ARRAY_SIZE(bd718xx_mfd_cells), NULL, 0,
+ mfd, cells, NULL, 0,
regmap_irq_get_domain(bd718xx->irq_data));
if (ret)
dev_err(&i2c->dev, "Failed to create subdevices\n");
diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c
index bdab46a5c461..2058d972c7b7 100644
--- a/drivers/regulator/bd718x7-regulator.c
+++ b/drivers/regulator/bd718x7-regulator.c
@@ -1164,6 +1164,7 @@ static int bd718xx_probe(struct platform_device *pdev)

int i, j, err;
bool use_snvs;
+ enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data;

mfd = dev_get_drvdata(pdev->dev.parent);
if (!mfd) {
@@ -1172,8 +1173,8 @@ static int bd718xx_probe(struct platform_device *pdev)
goto err;
}

- if (mfd->chip.chip_type >= ROHM_CHIP_TYPE_AMOUNT ||
- !pmic_regulators[mfd->chip.chip_type].r_datas) {
+ if (chip >= ROHM_CHIP_TYPE_AMOUNT || chip < 0 ||
+ !pmic_regulators[chip].r_datas) {
dev_err(&pdev->dev, "Unsupported chip type\n");
err = -EINVAL;
goto err;
@@ -1215,13 +1216,13 @@ static int bd718xx_probe(struct platform_device *pdev)
}
}

- for (i = 0; i < pmic_regulators[mfd->chip.chip_type].r_amount; i++) {
+ for (i = 0; i < pmic_regulators[chip].r_amount; i++) {

const struct regulator_desc *desc;
struct regulator_dev *rdev;
const struct bd718xx_regulator_data *r;

- r = &pmic_regulators[mfd->chip.chip_type].r_datas[i];
+ r = &pmic_regulators[chip].r_datas[i];
desc = &r->desc;

config.dev = pdev->dev.parent;
@@ -1281,11 +1282,19 @@ static int bd718xx_probe(struct platform_device *pdev)
return err;
}

+static const struct platform_device_id bd718x7_pmic_id[] = {
+ { "bd71837-pmic", ROHM_CHIP_TYPE_BD71837 },
+ { "bd71847-pmic", ROHM_CHIP_TYPE_BD71847 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bd718x7_pmic_id);
+
static struct platform_driver bd718xx_regulator = {
.driver = {
.name = "bd718xx-pmic",
},
.probe = bd718xx_probe,
+ .id_table = bd718x7_pmic_id,
};

module_platform_driver(bd718xx_regulator);
diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h
index bff15ac26f2c..922f88008232 100644
--- a/include/linux/mfd/rohm-generic.h
+++ b/include/linux/mfd/rohm-generic.h
@@ -4,7 +4,7 @@
#ifndef __LINUX_MFD_ROHM_H__
#define __LINUX_MFD_ROHM_H__

-enum {
+enum rohm_chip_type {
ROHM_CHIP_TYPE_BD71837 = 0,
ROHM_CHIP_TYPE_BD71847,
ROHM_CHIP_TYPE_BD70528,
@@ -12,7 +12,6 @@ enum {
};

struct rohm_regmap_dev {
- unsigned int chip_type;
struct device *dev;
struct regmap *regmap;
};
--
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 =]

2019-11-18 06:58:29

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 06/16] mfd: input: bd71828: Add power-key support

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

Signed-off-by: Matti Vaittinen <[email protected]>
Acked-for-MFD-by: Lee Jones <[email protected]>
---

No changes from v4

drivers/mfd/rohm-bd71828.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)

diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
index 7f445d699fd9..b892b50ec555 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"),
@@ -35,6 +49,10 @@ static struct mfd_cell bd71828_mfd_cells[] = {
.name = "bd71828-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,6 +306,14 @@ static int bd71828_i2c_probe(struct i2c_client *i2c)
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 =]

2019-11-18 06:59:36

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 08/16] regulator: bd718x7: Split driver to common and bd718x7 specific parts

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 <[email protected]>
---

No changes from v4

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 3ee63531f6d5..5182a3d5660c 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -197,6 +197,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
@@ -790,6 +791,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 2210ba56f9bd..6bcab72c1fc7 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -99,6 +99,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 2058d972c7b7..76aef64cf793 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 <[email protected]>");
+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 ff3dd7578fd3..8037421cc6a1 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 {
ROHM_CHIP_TYPE_BD71837 = 0,
ROHM_CHIP_TYPE_BD71847,
@@ -17,4 +20,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 =]

2019-11-18 07:00:02

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 10/16] gpio: devres: Add devm_gpiod_get_parent_array

Bunch of MFD sub-devices which are instantiated by MFD do not have
own device-tree nodes but have (for example) the GPIO consumer
information in parent device's DT node. Add resource managed
devm_gpiod_get_array() for such devices so that they can get the
consumer information from parent DT while still binding the GPIO
reservation life-time to this sub-device life time.

If devm_gpiod_get_array is used as such - then unloading and then
re-loading the child device fails as the GPIOs reserved during first
load are not freed when driver for sub-device is unload (if parent
stays there).

Signed-off-by: Matti Vaittinen <[email protected]>
---

No changes from v4

.../driver-api/driver-model/devres.rst | 1 +
drivers/gpio/gpiolib-devres.c | 65 ++++++++++++++-----
include/linux/gpio/consumer.h | 5 ++
3 files changed, 56 insertions(+), 15 deletions(-)

diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index a100bef54952..1b8302ba405b 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -270,6 +270,7 @@ GPIO
devm_gpiod_get_index()
devm_gpiod_get_index_optional()
devm_gpiod_get_optional()
+ devm_gpiod_get_parent_array()
devm_gpiod_put()
devm_gpiod_unhinge()
devm_gpiochip_add_data()
diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c
index 98e3c20d9730..584fa6edfd20 100644
--- a/drivers/gpio/gpiolib-devres.c
+++ b/drivers/gpio/gpiolib-devres.c
@@ -270,19 +270,11 @@ struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_gpiod_get_index_optional);

-/**
- * devm_gpiod_get_array - Resource-managed gpiod_get_array()
- * @dev: GPIO consumer
- * @con_id: function within the GPIO consumer
- * @flags: optional GPIO initialization flags
- *
- * Managed gpiod_get_array(). GPIO descriptors returned from this function are
- * automatically disposed on driver detach. See gpiod_get_array() for detailed
- * information about behavior and return values.
- */
-struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
- const char *con_id,
- enum gpiod_flags flags)
+static struct gpio_descs *__must_check
+__devm_gpiod_get_array(struct device *gpiodev,
+ struct device *managed,
+ const char *con_id,
+ enum gpiod_flags flags)
{
struct gpio_descs **dr;
struct gpio_descs *descs;
@@ -292,19 +284,62 @@ struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
if (!dr)
return ERR_PTR(-ENOMEM);

- descs = gpiod_get_array(dev, con_id, flags);
+ descs = gpiod_get_array(gpiodev, con_id, flags);
if (IS_ERR(descs)) {
devres_free(dr);
return descs;
}

*dr = descs;
- devres_add(dev, dr);
+ if (managed)
+ devres_add(managed, dr);
+ else
+ devres_add(gpiodev, dr);

return descs;
}
+
+/**
+ * devm_gpiod_get_array - Resource-managed gpiod_get_array()
+ * @dev: GPIO consumer
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * Managed gpiod_get_array(). GPIO descriptors returned from this function are
+ * automatically disposed on driver detach. See gpiod_get_array() for detailed
+ * information about behavior and return values.
+ */
+struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
+ const char *con_id,
+ enum gpiod_flags flags)
+{
+ return __devm_gpiod_get_array(dev, NULL, con_id, flags);
+}
EXPORT_SYMBOL_GPL(devm_gpiod_get_array);

+/**
+ * devm_gpiod_get_parent_array - Resource-managed gpiod_get_array for subdevices
+ * @dev: Managed device whose parent is the GPIO consumer
+ * @con_id: function within the GPIO consumer
+ * @flags: optional GPIO initialization flags
+ *
+ * Managed gpiod_get_array() for subdevices. This function is intended to be
+ * used by MFD sub-devices whose GPIO bindings are in parent (MFD) device but
+ * whose GPIO reservation should last only for the dub-device life time.
+ * Returns EINVAL if no parent device is found. Rest of the behaviour and
+ * return values are as documented for gpiod_get_array()
+ */
+struct gpio_descs *__must_check
+devm_gpiod_get_parent_array(struct device *dev,
+ const char *con_id,
+ enum gpiod_flags flags)
+{
+ if (!dev | !dev->parent)
+ return ERR_PTR(-EINVAL);
+ return __devm_gpiod_get_array(dev->parent, dev, con_id, flags);
+}
+EXPORT_SYMBOL_GPL(devm_gpiod_get_parent_array);
+
/**
* devm_gpiod_get_array_optional - Resource-managed gpiod_get_array_optional()
* @dev: GPIO consumer
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index b70af921c614..01a82b1c6828 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -103,6 +103,11 @@ struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
struct gpio_descs *__must_check
devm_gpiod_get_array_optional(struct device *dev, const char *con_id,
enum gpiod_flags flags);
+struct gpio_descs *__must_check
+devm_gpiod_get_parent_array(struct device *dev,
+ const char *con_id,
+ enum gpiod_flags flags);
+
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);
void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc);
void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs);
--
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 =]

2019-11-18 07:00:03

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 05/16] mfd: bd71828: Support ROHM BD71828 PMIC - core

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 <[email protected]>
---

No changes from v4

drivers/mfd/Kconfig | 15 ++
drivers/mfd/Makefile | 2 +-
drivers/mfd/rohm-bd71828.c | 319 +++++++++++++++++++++++
include/linux/mfd/rohm-bd71828.h | 425 +++++++++++++++++++++++++++++++
include/linux/mfd/rohm-generic.h | 1 +
5 files changed, 761 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 ae24d3ea68ea..ff06e853f799 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1913,6 +1913,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 c1067ea46204..437c47ffd315 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -253,6 +253,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..7f445d699fd9
--- /dev/null
+++ b/drivers/mfd/rohm-bd71828.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// 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", .of_compatible = "rohm,bd71828-leds" },
+ /*
+ * We use BD71837 driver to drive the clock block. Only differences to
+ * BD70528 clock gate are the register address and mask.
+ */
+ { .name = "bd71828-clk", },
+ { .name = "bd71827-power", },
+ {
+ .name = "bd71828-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.
+ */
+ .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.
+ */
+
+static unsigned int bit0_offsets[] = {11}; /* RTC IRQ */
+static unsigned int bit1_offsets[] = {10}; /* TEMP IRQ */
+static unsigned int bit2_offsets[] = {6, 7, 8, 9}; /* BAT MON IRQ */
+static unsigned int bit3_offsets[] = {5}; /* BAT IRQ */
+static unsigned int bit4_offsets[] = {4}; /* CHG IRQ */
+static unsigned int bit5_offsets[] = {3}; /* VSYS IRQ */
+static unsigned int bit6_offsets[] = {1, 2}; /* DCIN IRQ */
+static unsigned int bit7_offsets[] = {0}; /* BUCK IRQ */
+
+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)
+{
+ 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->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_new = &bd71828_i2c_probe,
+};
+
+module_i2c_driver(bd71828_drv);
+
+MODULE_AUTHOR("Matti Vaittinen <[email protected]>");
+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 922f88008232..ff3dd7578fd3 100644
--- a/include/linux/mfd/rohm-generic.h
+++ b/include/linux/mfd/rohm-generic.h
@@ -8,6 +8,7 @@ enum rohm_chip_type {
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 =]

2019-11-18 07:00:48

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 07/16] clk: bd718x7: Support ROHM BD71828 clk block

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 <[email protected]>
---

No changes from v4

drivers/clk/Kconfig | 6 ++---
drivers/clk/clk-bd718x7.c | 38 +++++++++++++++++++++++---------
include/linux/mfd/rohm-bd70528.h | 6 -----
include/linux/mfd/rohm-bd71828.h | 4 ----
include/linux/mfd/rohm-bd718x7.h | 6 -----
5 files changed, 31 insertions(+), 29 deletions(-)

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c44247d0b83e..71c5dfdc78f4 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -293,10 +293,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 1c1764f74d0a..853f484b758a 100644
--- a/drivers/clk/clk-bd718x7.c
+++ b/drivers/clk/clk-bd718x7.c
@@ -7,12 +7,25 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/mfd/rohm-bd718x7.h>
-#include <linux/mfd/rohm-bd70528.h>
+#include <linux/mfd/rohm-generic.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/regmap.h>

+/* clk control registers */
+/* BD70528 */
+#define BD70528_REG_OUT32K 0x2c
+/* BD71828 */
+#define BD71828_REG_OUT32K 0x4B
+/* BD71837 and BD71847 */
+#define BD718XX_REG_OUT32K 0x2E
+
+/*
+ * BD71837, BD71847, BD70528 and BD71828 all use bit [0] to clk output control
+ */
+#define CLK_OUT_EN_MASK BIT(0)
+
+
struct bd718xx_clk {
struct clk_hw hw;
u8 reg;
@@ -21,10 +34,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, unsigned 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 +44,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)
@@ -92,11 +105,15 @@ static int bd71837_clk_probe(struct platform_device *pdev)
case ROHM_CHIP_TYPE_BD71837:
case ROHM_CHIP_TYPE_BD71847:
c->reg = BD718XX_REG_OUT32K;
- c->mask = BD718XX_OUT32K_EN;
+ c->mask = CLK_OUT_EN_MASK;
+ break;
+ case ROHM_CHIP_TYPE_BD71828:
+ c->reg = BD71828_REG_OUT32K;
+ c->mask = CLK_OUT_EN_MASK;
break;
case ROHM_CHIP_TYPE_BD70528:
- c->reg = BD70528_REG_CLK_OUT;
- c->mask = BD70528_CLK_OUT_EN_MASK;
+ c->reg = BD70528_REG_OUT32K;
+ c->mask = CLK_OUT_EN_MASK;
break;
default:
dev_err(&pdev->dev, "Unknown clk chip\n");
@@ -126,6 +143,7 @@ static const struct platform_device_id bd718x7_clk_id[] = {
{ "bd71837-clk", ROHM_CHIP_TYPE_BD71837 },
{ "bd71847-clk", ROHM_CHIP_TYPE_BD71847 },
{ "bd70528-clk", ROHM_CHIP_TYPE_BD70528 },
+ { "bd71828-clk", ROHM_CHIP_TYPE_BD71828 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd718x7_clk_id);
diff --git a/include/linux/mfd/rohm-bd70528.h b/include/linux/mfd/rohm-bd70528.h
index 1013e60c5b25..2ad2320d0a96 100644
--- a/include/linux/mfd/rohm-bd70528.h
+++ b/include/linux/mfd/rohm-bd70528.h
@@ -89,10 +89,6 @@ struct bd70528_data {
#define BD70528_REG_GPIO3_OUT 0x52
#define BD70528_REG_GPIO4_OUT 0x54

-/* clk control */
-
-#define BD70528_REG_CLK_OUT 0x2c
-
/* RTC */

#define BD70528_REG_RTC_COUNT_H 0x2d
@@ -309,8 +305,6 @@ enum {

#define BD70528_GPIO_IN_STATE_BASE 1

-#define BD70528_CLK_OUT_EN_MASK 0x1
-
/* RTC masks to mask out reserved bits */

#define BD70528_MASK_RTC_SEC 0x7f
diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h
index eb0557eb5314..d013e03f742d 100644
--- a/include/linux/mfd/rohm-bd71828.h
+++ b/include/linux/mfd/rohm-bd71828.h
@@ -183,9 +183,6 @@ enum {
#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
@@ -417,7 +414,6 @@ enum {
#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
diff --git a/include/linux/mfd/rohm-bd718x7.h b/include/linux/mfd/rohm-bd718x7.h
index 7f2dbde402a1..bee2474a8f9f 100644
--- a/include/linux/mfd/rohm-bd718x7.h
+++ b/include/linux/mfd/rohm-bd718x7.h
@@ -191,12 +191,6 @@ enum {
#define IRQ_ON_REQ 0x02
#define IRQ_STBY_REQ 0x01

-/* BD718XX_REG_OUT32K bits */
-#define BD718XX_OUT32K_EN 0x01
-
-/* BD7183XX gated clock rate */
-#define BD718XX_CLK_RATE 32768
-
/* ROHM BD718XX irqs */
enum {
BD718XX_INT_STBY_REQ,
--
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 =]

2019-11-18 07:01:47

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 12/16] regulator: bd71828: Add GPIO based run-level control for regulators

Bucks 1,2,6 and 7 on ROHM 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:
- configuring regulators to be controlled via run-levels
- allow getting/setting the current run-level via in-kernel API
using either GPIO or I2C underneath
- Allow run-time changes to run-level voltages via in-kernel API
Signed-off-by: Matti Vaittinen <[email protected]>
---

No changes from v4


drivers/regulator/bd71828-regulator.c | 561 +++++++++++++++++++++++++-
include/linux/mfd/rohm-bd71828.h | 5 +
2 files changed, 556 insertions(+), 10 deletions(-)

diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c
index b113cec41969..411e6a297da6 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,37 @@
#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
+#define BD71828_RUNLVL_DEFAULT_UV 500000
+
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 mutex dvs_lock;
+ struct gpio_descs *gps;
+ struct regmap *regmap;
+ bool allow_runlvl;
};

static const struct reg_init buck1_inits[] = {
@@ -41,6 +64,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 +80,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 +96,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 +112,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 +210,83 @@ 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_runlvl_hw_dvs_levels(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *cfg)
+{
+ struct bd71828_regulator_data *data;
+ /* Default to 'disabled' (0 uV) if no property found */
+ uint32_t uv[DVS_RUN_LEVELS] = {0};
+ int i, ret;
+ const char *prop = "rohm,dvs-runlevel-microvolts";
+ /* On BD71828 the RUN level control reg is next to enable reg */
+ u8 en_reg = desc->enable_reg + 1;
+ 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);
+
+ ret = of_property_read_variable_u32_array(np, prop, uv, 1,
+ DVS_RUN_LEVELS);
+ if (ret < 0 && ret != -EINVAL)
+ return ret;
+
+ mutex_lock(&data->dvs_lock);
+ for (i = 0; i < DVS_RUN_LEVELS; i++) {
+ if (uv[i]) {
+ data->run_lvl[i].voltage = uv[i];
+ data->run_lvl[i].enabled = true;
+
+ ret = set_runlevel_voltage(cfg->regmap, desc, uv[i], i);
+ if (ret)
+ goto unlock_out;
+
+ ret = regmap_update_bits(cfg->regmap, en_reg,
+ en_masks[i], en_masks[i]);
+ } else {
+ data->run_lvl[i].voltage = BD71828_RUNLVL_DEFAULT_UV;
+ ret = regmap_update_bits(cfg->regmap, en_reg,
+ en_masks[i], 0);
+ }
+ if (ret)
+ goto unlock_out;
+ }
+
+ 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,
const struct regulator_desc *desc,
struct regulator_config *cfg)
@@ -189,6 +321,265 @@ 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);
+}
+
+/* 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;
+
+ 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;
+}
+
+/*
+ * 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;
+
+ mutex_lock(&data->dvs_lock);
+ ret = bd71828_dvs_gpio_get_run_level(data);
+ if (ret < 0 || ret >= DVS_RUN_LEVELS)
+ 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 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);
+
+ mutex_lock(&data->dvs_lock);
+ ret = bd71828_dvs_gpio_get_run_level(data);
+ if (ret < 0 || DVS_RUN_LEVELS <= ret)
+ goto unlock_out;
+
+ ret = data->run_lvl[ret].voltage;
+
+unlock_out:
+ 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);
+
+/**
+ * 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,
+};
+
+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,
@@ -773,6 +1164,127 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
},
};

+struct bd71828_gpio_cfg {
+ 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;
+
+ 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_parent_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;
+
+ g->use_gpio = true;
+
+ return 0;
+}
+
+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:
+ 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->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 int bd71828_probe(struct platform_device *pdev)
{
struct rohm_regmap_dev *bd71828;
@@ -780,6 +1292,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,30 +1301,57 @@ 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];
+
+ 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;

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;
}
}
diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h
index d013e03f742d..fc0955f0662c 100644
--- a/include/linux/mfd/rohm-bd71828.h
+++ b/include/linux/mfd/rohm-bd71828.h
@@ -418,4 +418,9 @@ 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);
+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 =]

2019-11-18 07:02:10

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 09/16] regulator: bd71828: Basic support for ROHM bd71828 PMIC regulators

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 <[email protected]>
---

No changes from v4

drivers/regulator/Kconfig | 12 +
drivers/regulator/Makefile | 1 +
drivers/regulator/bd71828-regulator.c | 833 ++++++++++++++++++++++++++
3 files changed, 846 insertions(+)
create mode 100644 drivers/regulator/bd71828-regulator.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 5182a3d5660c..838bbfcf11ea 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -194,6 +194,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 6bcab72c1fc7..cf0e08c5f3cd 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..b113cec41969
--- /dev/null
+++ b/drivers/regulator/bd71828-regulator.c
@@ -0,0 +1,833 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// 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 <[email protected]>");
+MODULE_DESCRIPTION("BD71828 voltage regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bd71828-pmic");
--
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 =]

2019-11-18 07:02:37

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 13/16] rtc: bd70528 add BD71828 support

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 <[email protected]>
---

No changes from v4

drivers/rtc/Kconfig | 3 +-
drivers/rtc/rtc-bd70528.c | 167 ++++++++++++++++++++++++++++---
include/linux/mfd/rohm-bd70528.h | 13 +--
include/linux/mfd/rohm-bd71828.h | 4 +-
include/linux/mfd/rohm-shared.h | 27 +++++
5 files changed, 185 insertions(+), 29 deletions(-)
create mode 100644 include/linux/mfd/rohm-shared.h

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 1adf9f815652..d03636ec7311 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -509,12 +509,13 @@ 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"
depends on MFD_ROHM_BD70528 && (BD70528_WATCHDOG || !BD70528_WATCHDOG)
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 7744333b0f40..09524b2712ba 100644
--- a/drivers/rtc/rtc-bd70528.c
+++ b/drivers/rtc/rtc-bd70528.c
@@ -6,6 +6,7 @@

#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,7 +16,7 @@
/*
* 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 {
u8 sec;
@@ -36,6 +37,13 @@ struct bd70528_rtc_wake {
u8 ctrl;
} __packed;

+struct bd71828_rtc_alm {
+ struct bd70528_rtc_data alm0;
+ struct bd70528_rtc_data alm1;
+ u8 alm_mask;
+ u8 alm1_mask;
+} __packed;
+
struct bd70528_rtc_alm {
struct bd70528_rtc_data data;
u8 alm_mask;
@@ -45,6 +53,8 @@ struct bd70528_rtc_alm {
struct bd70528_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,
@@ -152,12 +162,18 @@ static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state,
static int bd70528_re_enable_rtc_based_timers(struct bd70528_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,
int *old_state)
{
+ if (!r->has_rtc_timers)
+ return 0;
+
return bd70528_set_rtc_based_timers(r, 0, old_state);
}

@@ -213,6 +229,36 @@ static inline void rtc2tm(struct bd70528_rtc_data *r, struct rtc_time *t)
t->tm_wday = bcd2bin(r->week & BD70528_MASK_RTC_WEEK);
}

+static int bd71828_set_alarm(struct device *dev, struct rtc_wkalrm *a)
+{
+ int ret;
+ struct bd71828_rtc_alm alm;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ 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(dev, "Failed to read alarm regs\n");
+ return ret;
+ }
+
+ tm2rtc(&a->time, &alm.alm0);
+
+ if (!a->enabled)
+ alm.alm_mask &= ~BD70528_MASK_ALM_EN;
+ else
+ alm.alm_mask |= BD70528_MASK_ALM_EN;
+
+ ret = regmap_bulk_write(bd71828->regmap, BD71828_REG_RTC_ALM_START,
+ &alm, sizeof(alm));
+ if (ret)
+ dev_err(dev, "Failed to set alarm time\n");
+
+ return ret;
+
+}
+
static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
{
struct bd70528_rtc_wake wake;
@@ -261,6 +307,30 @@ static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
return ret;
}

+static int bd71828_read_alarm(struct device *dev, struct rtc_wkalrm *a)
+{
+ int ret;
+ struct bd71828_rtc_alm alm;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ 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(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 & BD70528_MASK_ALM_EN);
+ a->pending = 0;
+
+ return 0;
+}
+
static int bd70528_read_alarm(struct device *dev, struct rtc_wkalrm *a)
{
struct bd70528_rtc_alm alm;
@@ -297,7 +367,7 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
return ret;

tmpret = regmap_bulk_read(bd70528->regmap,
- BD70528_REG_RTC_START, &rtc_data,
+ r->reg_time_start, &rtc_data,
sizeof(rtc_data));
if (tmpret) {
dev_err(dev, "Failed to read RTC time registers\n");
@@ -306,7 +376,7 @@ 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,
+ r->reg_time_start, &rtc_data,
sizeof(rtc_data));
if (tmpret) {
dev_err(dev, "Failed to set RTC time\n");
@@ -321,6 +391,11 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
return ret;
}

+static int bd71828_set_time(struct device *dev, struct rtc_time *t)
+{
+ return bd70528_set_time_locked(dev, t);
+}
+
static int bd70528_set_time(struct device *dev, struct rtc_time *t)
{
int ret;
@@ -341,7 +416,7 @@ static int bd70528_get_time(struct device *dev, struct rtc_time *t)

/* read the RTC date and time registers all at once */
ret = regmap_bulk_read(bd70528->regmap,
- BD70528_REG_RTC_START, &rtc_data,
+ r->reg_time_start, &rtc_data,
sizeof(rtc_data));
if (ret) {
dev_err(dev, "Failed to read RTC time (err %d)\n", ret);
@@ -378,6 +453,23 @@ static int bd70528_alm_enable(struct device *dev, unsigned int enabled)
return ret;
}

+static int bd71828_alm_enable(struct device *dev, unsigned int enabled)
+{
+ int ret;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ unsigned int enableval = BD70528_MASK_ALM_EN;
+
+ if (!enabled)
+ enableval = 0;
+
+ ret = regmap_update_bits(r->mfd->regmap, BD71828_REG_RTC_ALM0_MASK,
+ BD70528_MASK_ALM_EN, enableval);
+ if (ret)
+ dev_err(dev, "Failed to change alarm state\n");
+
+ return ret;
+}
+
static const struct rtc_class_ops bd70528_rtc_ops = {
.read_time = bd70528_get_time,
.set_time = bd70528_set_time,
@@ -386,6 +478,14 @@ static const struct rtc_class_ops bd70528_rtc_ops = {
.alarm_irq_enable = bd70528_alm_enable,
};

+static const struct rtc_class_ops bd71828_rtc_ops = {
+ .read_time = bd70528_get_time,
+ .set_time = bd71828_set_time,
+ .read_alarm = bd71828_read_alarm,
+ .set_alarm = bd71828_set_alarm,
+ .alarm_irq_enable = bd71828_alm_enable,
+};
+
static irqreturn_t alm_hndlr(int irq, void *data)
{
struct rtc_device *rtc = data;
@@ -397,11 +497,16 @@ static irqreturn_t alm_hndlr(int irq, void *data)
static int bd70528_probe(struct platform_device *pdev)
{
struct bd70528_rtc *bd_rtc;
+ const struct rtc_class_ops *rtc_ops;
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;
+ enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data;

mfd = dev_get_drvdata(pdev->dev.parent);
if (!mfd) {
@@ -415,13 +520,36 @@ 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");
- if (irq < 0)
+ switch (chip) {
+ 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;
+ rtc_ops = &bd70528_rtc_ops;
+ 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;
+ rtc_ops = &bd71828_rtc_ops;
+ 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");
return irq;
+ }

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");
@@ -431,10 +559,10 @@ static int bd70528_probe(struct platform_device *pdev)
if (!(hr & BD70528_MASK_RTC_HOUR_24H)) {
struct rtc_time t;

- ret = bd70528_get_time(&pdev->dev, &t);
+ ret = rtc_ops->read_time(&pdev->dev, &t);

if (!ret)
- ret = bd70528_set_time(&pdev->dev, &t);
+ ret = rtc_ops->set_time(&pdev->dev, &t);

if (ret) {
dev_err(&pdev->dev,
@@ -454,7 +582,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 = rtc_ops;

/* Request alarm IRQ prior to registerig the RTC */
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, &alm_hndlr,
@@ -468,26 +596,35 @@ 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;
+ }
}

return rtc_register_device(rtc);
}
+static const struct platform_device_id bd718x7_rtc_id[] = {
+ { "bd70528-rtc", ROHM_CHIP_TYPE_BD70528 },
+ { "bd71828-rtc", ROHM_CHIP_TYPE_BD71828 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id);

static struct platform_driver bd70528_rtc = {
.driver = {
.name = "bd70528-rtc"
},
.probe = bd70528_probe,
+ .id_table = bd718x7_rtc_id,
};

module_platform_driver(bd70528_rtc);

MODULE_AUTHOR("Matti Vaittinen <[email protected]>");
-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 2ad2320d0a96..a57af878fd0c 100644
--- a/include/linux/mfd/rohm-bd70528.h
+++ b/include/linux/mfd/rohm-bd70528.h
@@ -7,6 +7,7 @@
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/mfd/rohm-generic.h>
+#include <linux/mfd/rohm-shared.h>
#include <linux/regmap.h>

enum {
@@ -307,17 +308,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
* HW would support ALM irq for over 24h
@@ -326,7 +316,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 fc0955f0662c..093662f3e867 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
@@ -204,7 +207,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..f16fc3b5000e
--- /dev/null
+++ b/include/linux/mfd/rohm-shared.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2018 ROHM Semiconductors */
+
+
+#ifndef __LINUX_MFD_ROHM_SHARED_H__
+#define __LINUX_MFD_ROHM_SHARED_H__
+
+/*
+ * RTC definitions shared between
+ *
+ * BD70528
+ * and BD71828
+ */
+
+
+#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 0x3f
+#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_ALM_EN 0x7
+
+#endif /* __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 =]

2019-11-18 07:02:52

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 11/16] docs: driver-model: Add missing managed GPIO array get functions

devm_gpiod_get_array and devm_gpiod_get_array_optional were missing
from the list. Add them.

Signed-off-by: Matti Vaittinen <[email protected]>
---
Documentation/driver-api/driver-model/devres.rst | 2 ++
1 file changed, 2 insertions(+)

diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index 1b8302ba405b..b60d0702412d 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -267,6 +267,8 @@ DRM

GPIO
devm_gpiod_get()
+ devm_gpiod_get_array()
+ devm_gpiod_get_array_optional()
devm_gpiod_get_index()
devm_gpiod_get_index_optional()
devm_gpiod_get_optional()
--
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 =]

2019-11-18 07:02:54

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 14/16] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs

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
trusts 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 selected by OTP settings.

Signed-off-by: Matti Vaittinen <[email protected]>
---

No changes from v4

drivers/gpio/Kconfig | 12 +++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-bd71828.c | 159 ++++++++++++++++++++++++++++++++++++
3 files changed, 172 insertions(+)
create mode 100644 drivers/gpio/gpio-bd71828.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 38e096e6925f..b4089096f7f2 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -994,6 +994,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 d2fd19c15bae..034b38996579 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..04aade9e0a4d
--- /dev/null
+++ b/drivers/gpio/gpio-bd71828.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2018 ROHM Semiconductors
+
+#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 GPIO_OUT_REG(off) (BD71828_REG_GPIO_CTRL1 + (off))
+#define HALL_GPIO_OFFSET 3
+
+/*
+ * These defines can be removed when
+ * "gpio: Add definition for GPIO direction"
+ * (9208b1e77d6e8e9776f34f46ef4079ecac9c3c25 in GPIO tree) gets merged,
+ */
+#ifndef GPIO_LINE_DIRECTION_IN
+ #define GPIO_LINE_DIRECTION_IN 1
+ #define GPIO_LINE_DIRECTION_OUT 0
+#endif
+
+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;
+
+ /*
+ * The HALL input pin can only be used as input. If this is the pin
+ * we are dealing with - then we are done
+ */
+ 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 GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int bd71828_probe(struct platform_device *pdev)
+{
+ struct bd71828_gpio *bdgpio;
+ struct rohm_regmap_dev *bd71828;
+
+ 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;
+
+ 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;
+
+ /*
+ * 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
+ */
+ bdgpio->gpio.ngpio = 4;
+ bdgpio->gpio.of_node = pdev->dev.parent->of_node;
+ bdgpio->chip.regmap = bd71828->regmap;
+
+ return devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio,
+ bdgpio);
+}
+
+static struct platform_driver bd71828_gpio = {
+ .driver = {
+ .name = "bd71828-gpio"
+ },
+ .probe = bd71828_probe,
+};
+
+module_platform_driver(bd71828_gpio);
+
+MODULE_AUTHOR("Matti Vaittinen <[email protected]>");
+MODULE_DESCRIPTION("BD71828 voltage regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bd71828-gpio");
--
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 =]

2019-11-18 07:04:58

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 15/16] leds: Add common LED binding parsing support to LED class/core

Qucik grep for 'for_each' or 'linux,default-trigger' or
'default-state' under drivers/leds can tell quite a lot. It seems
multiple LED controller drivers implement the very similar looping
through the child nodes in order to locate the LED nodes and read
and support the common LED dt bindings. Implementing this same
stuff for all LED controllers gets old pretty fast.

This commit adds support for locating the LED node (based on known
node names - or linux,led-compatible property) and handling of
few common LED properties.

linux,default-trigger,
default-state (with the exception of keep),

(in addition to already handled
function-enumerator,
function,
color
and label).

Regarding the node look-up: If no init_data is given, then no
node-lookup is done and cdev name is used as such.

If init_data is goven but no starting point for node lookup - then
(parent) device's own DT node is used. If no led-compatible is given,
then of_match is searched for. If neither led-compatible not of_match
is given then device's own node or passed starting point are used as
such.

Signed-off-by: Matti Vaittinen <[email protected]>
---

Changes from v4:
Fixed issues reported by Dan Carpenter and kbuild-bot.
(leftover kfree and uninitialized return value)

drivers/leds/led-class.c | 88 ++++++++++++--
drivers/leds/led-core.c | 246 +++++++++++++++++++++++++++++++--------
include/linux/leds.h | 90 ++++++++++++--
3 files changed, 359 insertions(+), 65 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 647b1263c579..98173b64a597 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -235,6 +235,29 @@ static int led_classdev_next_name(const char *init_name, char *name,
return i;
}

+static void led_add_props(struct led_classdev *ld, struct led_properties *props)
+{
+ if (props->default_trigger)
+ ld->default_trigger = props->default_trigger;
+ /*
+ * According to binding docs the LED is by-default turned OFF unless
+ * default_state is used to indicate it should be ON or that state
+ * should be kept as is
+ */
+ if (props->default_state) {
+ ld->brightness = LED_OFF;
+ if (!strcmp(props->default_state, "on"))
+ ld->brightness = LED_FULL;
+ /*
+ * We probably should not call the brightness_get prior calling
+ * the of_parse_cb if one is provided.
+ * Add a flag to advertice that state should be queried and kept as-is.
+ */
+ else if (!strcmp(props->default_state, "keep"))
+ props->brightness_keep = true;
+ }
+}
+
/**
* led_classdev_register_ext - register a new object of led_classdev class
* with init data.
@@ -251,22 +274,58 @@ int led_classdev_register_ext(struct device *parent,
char final_name[LED_MAX_NAME_SIZE];
const char *proposed_name = composed_name;
int ret;
-
+ struct led_properties props = {0};
+ struct fwnode_handle *fw;
+
+ /*
+ * We don't try getting the name based on DT node if init-data is not
+ * given. We could see if we find LED properties from the device's node
+ * but that might change LED names for old users of
+ * led_classdev_register who have been providing the LED name in
+ * cdev->name. So we seek fwnode for names only if init_data is given
+ */
if (init_data) {
+ led_cdev->init_data = init_data;
if (init_data->devname_mandatory && !init_data->devicename) {
dev_err(parent, "Mandatory device name is missing");
return -EINVAL;
}
- ret = led_compose_name(parent, init_data, composed_name);
+
+ fw = led_find_fwnode(parent, init_data);
+ if (!fw) {
+ dev_err(parent, "No fwnode found\n");
+ return -ENOENT;
+ }
+ /*
+ * We did increase refcount for fwnode. Let's set a flag so we
+ * can decrease it during deregistration
+ */
+ led_cdev->fwnode_owned = true;
+
+ ret = led_parse_fwnode_props(parent, fw, &props);
+ if (ret)
+ goto err_out;
+
+ if (init_data->of_parse_cb)
+ ret = init_data->of_parse_cb(led_cdev, fw, &props);
if (ret < 0)
- return ret;
+ goto err_out;
+
+ led_add_props(led_cdev, &props);
+
+ ret = led_compose_name(parent, init_data, &props,
+ composed_name);
+ if (ret < 0)
+ goto err_out;
} else {
proposed_name = led_cdev->name;
+ led_cdev->fwnode_owned = false;
+ fw = NULL;
}

ret = led_classdev_next_name(proposed_name, final_name, sizeof(final_name));
if (ret < 0)
- return ret;
+ goto err_out;

mutex_init(&led_cdev->led_access);
mutex_lock(&led_cdev->led_access);
@@ -274,22 +333,28 @@ int led_classdev_register_ext(struct device *parent,
led_cdev, led_cdev->groups, "%s", final_name);
if (IS_ERR(led_cdev->dev)) {
mutex_unlock(&led_cdev->led_access);
- return PTR_ERR(led_cdev->dev);
+ ret = PTR_ERR(led_cdev->dev);
+ goto err_out;
}
- if (init_data && init_data->fwnode)
- led_cdev->dev->fwnode = init_data->fwnode;
+ if (fw)
+ led_cdev->dev->fwnode = fw;

if (ret)
dev_warn(parent, "Led %s renamed to %s due to name collision",
led_cdev->name, dev_name(led_cdev->dev));

+ if (props.brightness_keep)
+ if (led_cdev->brightness_get)
+ led_cdev->brightness =
+ led_cdev->brightness_get(led_cdev);
+
if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
ret = led_add_brightness_hw_changed(led_cdev);
if (ret) {
device_unregister(led_cdev->dev);
led_cdev->dev = NULL;
mutex_unlock(&led_cdev->led_access);
- return ret;
+ goto err_out;
}
}

@@ -322,6 +387,10 @@ int led_classdev_register_ext(struct device *parent,
led_cdev->name);

return 0;
+err_out:
+ if (led_cdev->fwnode_owned)
+ fwnode_handle_put(fw);
+ return ret;
}
EXPORT_SYMBOL_GPL(led_classdev_register_ext);

@@ -355,6 +424,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
led_remove_brightness_hw_changed(led_cdev);

+ if (led_cdev->fwnode_owned)
+ fwnode_handle_put(led_cdev->dev->fwnode);
+
device_unregister(led_cdev->dev);

down_write(&leds_list_lock);
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index f1f718dbe0f8..9369f03ee540 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -365,70 +365,214 @@ void led_sysfs_enable(struct led_classdev *led_cdev)
}
EXPORT_SYMBOL_GPL(led_sysfs_enable);

-static void led_parse_fwnode_props(struct device *dev,
- struct fwnode_handle *fwnode,
- struct led_properties *props)
+static int fw_is_match(struct fwnode_handle *fw,
+ struct led_fw_match_property *mp, void *val)
{
- int ret;
+ void *cmp = mp->raw_val;
+ int ret = -EINVAL;
+
+ if (mp->raw_val) {
+ ret = fwnode_property_read_u8_array(fw, mp->name, val,
+ mp->size);
+ } else if (mp->intval) {
+ cmp = mp->intval;
+ switch (mp->size) {
+ case 1:
+ ret = fwnode_property_read_u8_array(fw, mp->name, val,
+ mp->size);
+ break;
+ case 2:
+ ret = fwnode_property_read_u16_array(fw, mp->name, val,
+ mp->size);
+ break;
+ case 4:
+ ret = fwnode_property_read_u32_array(fw, mp->name, val,
+ mp->size);
+ break;
+ case 8:
+ ret = fwnode_property_read_u64_array(fw, mp->name, val,
+ mp->size);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ if (!ret && cmp)
+ if (!memcmp(val, cmp, mp->size))
+ return 1;
+
+ return 0;
+}
+/**
+ * led_find_fwnode - find fwnode for led
+ * @parent LED controller device
+ * @init_data led init data with match information
+ *
+ * Scans the firmware nodes and returns node matching the given init_data.
+ * NOTE: Function increases refcount for found node. Caller must decrease
+ * refcount using fwnode_handle_put when finished with node.
+ */
+struct fwnode_handle *led_find_fwnode(struct device *parent,
+ struct led_init_data *init_data)
+{
+ struct fwnode_handle *fw;
+
+ /*
+ * This should never be called W/O init data. We could always return
+ * dev_fwnode() - but then we should pump-up the refcount
+ */
+ if (!init_data)
+ return NULL;
+
+ if (!init_data->fwnode)
+ fw = dev_fwnode(parent);
+ else
+ fw = init_data->fwnode;
+
+ if (!fw)
+ return NULL;
+
+ /*
+ * Simple things are pretty. I think simplest is to use DT node-name
+ * for matching the node with LED - same way regulators use the node
+ * name to match with desc.
+ *
+ * This may not work with existing LED DT entries if the node name has
+ * been freely selectible. In order to this to work the binding doc
+ * for LED driver should define usable node names.
+ *
+ * If this is not working we can define specific match property which
+ * value we scan and use for matching for LEDs connected to the
+ * controller.
+ */
+ if (init_data->match_property.name && init_data->match_property.size) {
+ u8 *val;
+ int ret;
+ struct fwnode_handle *child;
+ struct led_fw_match_property *mp;
+
+ mp = &init_data->match_property;
+
+ val = kzalloc(mp->size, GFP_KERNEL);
+ if (!val)
+ return ERR_PTR(-ENOMEM);
+
+ fwnode_for_each_child_node(fw, child) {
+ ret = fw_is_match(child, mp, val);
+ if (ret > 0) {
+ kfree(val);
+ return child;
+ }
+ if (ret < 0) {
+ dev_err(parent,
+ "invalid fw match. Use raw_val?\n");
+ fwnode_handle_put(child);
+ break;
+ }
+ }
+ kfree(val);
+ }
+ if (init_data->of_match)
+ fw = fwnode_get_named_child_node(fw, init_data->of_match);
+ else
+ fw = fwnode_handle_get(fw);
+
+ return fw;
+}
+EXPORT_SYMBOL(led_find_fwnode);
+
+int led_parse_fwnode_props(struct device *dev, struct fwnode_handle *fwnode,
+ struct led_properties *props)
+{
+ int ret = 0;

if (!fwnode)
- return;
+ return 0;

if (fwnode_property_present(fwnode, "label")) {
ret = fwnode_property_read_string(fwnode, "label", &props->label);
if (ret)
dev_err(dev, "Error parsing 'label' property (%d)\n", ret);
- return;
+ return ret;
}

+ /**
+ * Please note, logic changed - if invalid property is found we bail
+ * early out without parsing the rest of the properties. Originally
+ * this was the case only for 'label' property. I don't know the
+ * rationale behind original logic allowing invalid properties to be
+ * given. If there is a reason then we should reconsider this.
+ * Intuitively it feels correct to just yell and quit if we hit value we
+ * don't understand - but intuition may be wrong at times :)
+ */
if (fwnode_property_present(fwnode, "color")) {
ret = fwnode_property_read_u32(fwnode, "color", &props->color);
- if (ret)
+ if (ret) {
dev_err(dev, "Error parsing 'color' property (%d)\n", ret);
- else if (props->color >= LED_COLOR_ID_MAX)
+ return ret;
+ } else if (props->color >= LED_COLOR_ID_MAX) {
dev_err(dev, "LED color identifier out of range\n");
- else
- props->color_present = true;
+ return ret;
+ }
+ props->color_present = true;
}

+ if (fwnode_property_present(fwnode, "function")) {
+ ret = fwnode_property_read_string(fwnode, "function",
+ &props->function);
+ if (ret) {
+ dev_err(dev,
+ "Error parsing 'function' property (%d)\n",
+ ret);
+ return ret;
+ }
+ }

- if (!fwnode_property_present(fwnode, "function"))
- return;
-
- ret = fwnode_property_read_string(fwnode, "function", &props->function);
- if (ret) {
- dev_err(dev,
- "Error parsing 'function' property (%d)\n",
- ret);
+ if (fwnode_property_present(fwnode, "function-enumerator")) {
+ ret = fwnode_property_read_u32(fwnode, "function-enumerator",
+ &props->func_enum);
+ if (ret) {
+ dev_err(dev,
+ "Error parsing 'function-enumerator' property (%d)\n",
+ ret);
+ return ret;
+ }
+ props->func_enum_present = true;
}

- if (!fwnode_property_present(fwnode, "function-enumerator"))
- return;
+ if (fwnode_property_present(fwnode, "default-state")) {
+ ret = fwnode_property_read_string(fwnode, "default-state",
+ &props->default_state);
+ if (ret) {
+ dev_err(dev,
+ "Error parsing 'default-state' property (%d)\n",
+ ret);
+ return ret;
+ }
+ }

- ret = fwnode_property_read_u32(fwnode, "function-enumerator",
- &props->func_enum);
- if (ret) {
- dev_err(dev,
- "Error parsing 'function-enumerator' property (%d)\n",
- ret);
- } else {
- props->func_enum_present = true;
+ if (fwnode_property_present(fwnode, "linux,default-trigger")) {
+ ret = fwnode_property_read_string(fwnode,
+ "linux,default-trigger",
+ &props->default_trigger);
+ if (ret)
+ dev_err(dev,
+ "Error parsing 'linux,default-trigger' property (%d)\n",
+ ret);
}
+ return ret;
}
+EXPORT_SYMBOL_GPL(led_parse_fwnode_props);

int led_compose_name(struct device *dev, struct led_init_data *init_data,
- char *led_classdev_name)
+ struct led_properties *props, char *led_classdev_name)
{
- struct led_properties props = {};
- struct fwnode_handle *fwnode = init_data->fwnode;
const char *devicename = init_data->devicename;

if (!led_classdev_name)
return -EINVAL;

- led_parse_fwnode_props(dev, fwnode, &props);
-
- if (props.label) {
+ if (props->label) {
/*
* If init_data.devicename is NULL, then it indicates that
* DT label should be used as-is for LED class device name.
@@ -436,23 +580,23 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data,
* the final LED class device name.
*/
if (!devicename) {
- strscpy(led_classdev_name, props.label,
+ strscpy(led_classdev_name, props->label,
LED_MAX_NAME_SIZE);
} else {
snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
- devicename, props.label);
+ devicename, props->label);
}
- } else if (props.function || props.color_present) {
+ } else if (props->function || props->color_present) {
char tmp_buf[LED_MAX_NAME_SIZE];

- if (props.func_enum_present) {
+ if (props->func_enum_present) {
snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-%d",
- props.color_present ? led_colors[props.color] : "",
- props.function ?: "", props.func_enum);
+ props->color_present ? led_colors[props->color] : "",
+ props->function ?: "", props->func_enum);
} else {
snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
- props.color_present ? led_colors[props.color] : "",
- props.function ?: "");
+ props->color_present ? led_colors[props->color] : "",
+ props->function ?: "");
}
if (init_data->devname_mandatory) {
snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
@@ -468,11 +612,19 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data,
}
snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
devicename, init_data->default_label);
- } else if (is_of_node(fwnode)) {
- strscpy(led_classdev_name, to_of_node(fwnode)->name,
- LED_MAX_NAME_SIZE);
- } else
- return -EINVAL;
+ } else {
+ struct fwnode_handle *fwnode = led_find_fwnode(dev, init_data);
+ int ret = -EINVAL;
+
+ if (is_of_node(fwnode)) {
+ ret = 0;
+ strscpy(led_classdev_name, to_of_node(fwnode)->name,
+ LED_MAX_NAME_SIZE);
+ }
+ fwnode_handle_put(fwnode);
+
+ return ret;
+ }

return 0;
}
diff --git a/include/linux/leds.h b/include/linux/leds.h
index efb309dba914..aea7d6bc7294 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -13,6 +13,7 @@
#include <linux/kernfs.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/property.h>
#include <linux/rwsem.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
@@ -20,6 +21,7 @@

struct device;
struct led_pattern;
+struct led_classdev;
/*
* LED Core
*/
@@ -31,8 +33,47 @@ enum led_brightness {
LED_FULL = 255,
};

+struct led_properties {
+ u32 color;
+ bool color_present;
+ const char *function;
+ u32 func_enum;
+ bool func_enum_present;
+ const char *label;
+ const char *default_trigger;
+ const char *default_state;
+ bool brightness_keep;
+};
+
+struct led_fw_match_property {
+ const char *name; /* Name of the property to match */
+ void *raw_val; /* Raw property value as present in fwnode */
+ void *intval; /* Property value if 8,16,32 or 64bit integer */
+ size_t size; /* Size of value in bytes */
+};
+
struct led_init_data {
- /* device fwnode handle */
+ /*
+ * If DT binding dictates the node name the driver can fill of_match
+ * corresponding to node name describing this LED. If fwnode is given
+ * the match is searched from it's child nodes. If not, the match is
+ * searched from device's own child nodes.
+ */
+ const char *of_match;
+ /*
+ * If fwnode contains property with known value the driver can specify
+ * correct propertty-value pair here to do the matching. This has higher
+ * priority than of_match. If fwnode is given the match is searched
+ * from it's child nodes. If not match is searched from device's
+ * own child nodes.
+ */
+ struct led_fw_match_property match_property;
+ /*
+ * device fwnode handle. If of_match or led_compatible are not given
+ * this is used for LED as given. If of_match or led_compatible are
+ * given then this is used as a parent node whose child nodes are
+ * scanned for given match.
+ */
struct fwnode_handle *fwnode;
/*
* default <color:function> tuple, for backward compatibility
@@ -53,9 +94,17 @@ struct led_init_data {
* set it to true
*/
bool devname_mandatory;
+ /*
+ * Callback which a LED driver can register if it has own non-standard
+ * DT properties. Core calls this with the located DT node during
+ * class_device registration
+ */
+ int (*of_parse_cb)(struct led_classdev *ld, struct fwnode_handle *fw,
+ struct led_properties *props);
};

struct led_classdev {
+ struct led_init_data *init_data;
const char *name;
enum led_brightness brightness;
enum led_brightness max_brightness;
@@ -148,6 +197,7 @@ struct led_classdev {

/* Ensures consistent access to the LED Flash Class device */
struct mutex led_access;
+ bool fwnode_owned;
};

/**
@@ -302,6 +352,7 @@ extern void led_sysfs_enable(struct led_classdev *led_cdev);
* led_compose_name - compose LED class device name
* @dev: LED controller device object
* @init_data: the LED class device initialization data
+ * @props: LED properties as parsed from fwnode.
* @led_classdev_name: composed LED class device name
*
* Create LED class device name basing on the provided init_data argument.
@@ -311,6 +362,7 @@ extern void led_sysfs_enable(struct led_classdev *led_cdev);
* Returns: 0 on success or negative error value on failure
*/
extern int led_compose_name(struct device *dev, struct led_init_data *init_data,
+ struct led_properties *props,
char *led_classdev_name);

/**
@@ -324,6 +376,33 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev)
return led_cdev->flags & LED_SYSFS_DISABLE;
}

+/**
+ * led_find_fwnode - find fwnode matching given LED init data
+ * @parent: LED controller device this LED is driven by
+ * @init_data: the LED class device initialization data
+ *
+ * Find the fw node matching given LED init data.
+ * NOTE: Function increases refcount for found node. Caller must decrease
+ * refcount using fwnode_handle_put when finished with node.
+ *
+ * Returns: node handle or NULL if matching fw node was not found
+ */
+struct fwnode_handle *led_find_fwnode(struct device *parent,
+ struct led_init_data *init_data);
+
+/**
+ * led_parse_fwnode_props - parse LED common properties from fwnode
+ * @dev: pointer to LED device.
+ * @fwnode: LED node containing the properties
+ * @props: structure where found property data is stored.
+ *
+ * Parse the common LED properties from fwnode.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int led_parse_fwnode_props(struct device *dev, struct fwnode_handle *fwnode,
+ struct led_properties *props);
+
/*
* LED Triggers
*/
@@ -490,15 +569,6 @@ struct led_platform_data {
struct led_info *leds;
};

-struct led_properties {
- u32 color;
- bool color_present;
- const char *function;
- u32 func_enum;
- bool func_enum_present;
- const char *label;
-};
-
struct gpio_desc;
typedef int (*gpio_blink_set_t)(struct gpio_desc *desc, int state,
unsigned long *delay_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 =]

2019-11-18 07:05:11

by Matti Vaittinen

[permalink] [raw]
Subject: [PATCH v5 16/16] led: bd71828: Support LED outputs on ROHM BD71828 PMIC

ROHM BD71828 power management IC has two LED outputs for charge status
and button pressing indications. The LED outputs can also be forced
by 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 <[email protected]>
---

No changes from v4

drivers/leds/Kconfig | 10 ++++
drivers/leds/Makefile | 1 +
drivers/leds/leds-bd71828.c | 104 ++++++++++++++++++++++++++++++++++++
3 files changed, 115 insertions(+)
create mode 100644 drivers/leds/leds-bd71828.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 1988de1d64c0..cafcbae7c084 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -532,6 +532,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 LEDS on ROHM BD71828 PMIC"
+ depends on LEDS_CLASS
+ depends on MFD_ROHM_BD71828
+ 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..f032e24485e9
--- /dev/null
+++ b/drivers/leds/leds-bd71828.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// 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))
+
+/* Names for led identification - these match the data sheet names */
+enum {
+ ID_GREEN_LED,
+ ID_AMBER_LED,
+ ID_NMBR_OF,
+};
+
+struct bd71828_led {
+ struct led_init_data init_data;
+ 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;
+ int ret;
+
+ 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;
+
+ /* Fill in details for 'AMBLED' */
+ a->init_data.match_property.name = "rohm,led-compatible";
+ a->init_data.match_property.raw_val = "bd71828-ambled";
+ a->init_data.match_property.size = strlen("bd71828-ambled");
+ a->id = ID_AMBER_LED;
+ a->force_mask = BD71828_MASK_LED_AMBER;
+
+ /* Fill in details for 'GRNLED' */
+ g->init_data.match_property.name = "rohm,led-compatible";
+ g->init_data.match_property.raw_val = "bd71828-grnled";
+ g->init_data.match_property.size = strlen("bd71828-grnled");
+ g->id = ID_GREEN_LED;
+ g->force_mask = BD71828_MASK_LED_GREEN;
+
+ a->l.brightness_set_blocking = bd71828_led_brightness_set;
+ g->l.brightness_set_blocking = bd71828_led_brightness_set;
+
+ ret = devm_led_classdev_register_ext(&pdev->dev, &g->l, &g->init_data);
+ if (ret)
+ return ret;
+
+ return devm_led_classdev_register_ext(&pdev->dev, &a->l, &a->init_data);
+}
+
+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 <[email protected]>");
+MODULE_DESCRIPTION("ROHM BD71828 LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bd71828-led");
--
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 =]

2019-11-18 09:25:15

by Bartosz Golaszewski

[permalink] [raw]
Subject: Re: [PATCH v5 14/16] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs

pon., 18 lis 2019 o 08:01 Matti Vaittinen
<[email protected]> 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
> trusts 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 selected by OTP settings.
>
> Signed-off-by: Matti Vaittinen <[email protected]>
> ---
>
> No changes from v4
>
> drivers/gpio/Kconfig | 12 +++
> drivers/gpio/Makefile | 1 +
> drivers/gpio/gpio-bd71828.c | 159 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 172 insertions(+)
> create mode 100644 drivers/gpio/gpio-bd71828.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 38e096e6925f..b4089096f7f2 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -994,6 +994,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 d2fd19c15bae..034b38996579 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..04aade9e0a4d
> --- /dev/null
> +++ b/drivers/gpio/gpio-bd71828.c
> @@ -0,0 +1,159 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +// Copyright (C) 2018 ROHM Semiconductors
> +
> +#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 GPIO_OUT_REG(off) (BD71828_REG_GPIO_CTRL1 + (off))
> +#define HALL_GPIO_OFFSET 3
> +
> +/*
> + * These defines can be removed when
> + * "gpio: Add definition for GPIO direction"
> + * (9208b1e77d6e8e9776f34f46ef4079ecac9c3c25 in GPIO tree) gets merged,
> + */
> +#ifndef GPIO_LINE_DIRECTION_IN
> + #define GPIO_LINE_DIRECTION_IN 1
> + #define GPIO_LINE_DIRECTION_OUT 0
> +#endif
> +
> +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;
> +
> + /*
> + * The HALL input pin can only be used as input. If this is the pin
> + * we are dealing with - then we are done
> + */
> + 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 GPIO_LINE_DIRECTION_IN;
> +
> + return GPIO_LINE_DIRECTION_OUT;
> +}
> +
> +static int bd71828_probe(struct platform_device *pdev)
> +{
> + struct bd71828_gpio *bdgpio;
> + struct rohm_regmap_dev *bd71828;
> +
> + 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;
> +
> + 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;
> +
> + /*
> + * 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
> + */
> + bdgpio->gpio.ngpio = 4;
> + bdgpio->gpio.of_node = pdev->dev.parent->of_node;
> + bdgpio->chip.regmap = bd71828->regmap;
> +
> + return devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio,
> + bdgpio);
> +}
> +
> +static struct platform_driver bd71828_gpio = {
> + .driver = {
> + .name = "bd71828-gpio"
> + },
> + .probe = bd71828_probe,
> +};
> +
> +module_platform_driver(bd71828_gpio);
> +
> +MODULE_AUTHOR("Matti Vaittinen <[email protected]>");
> +MODULE_DESCRIPTION("BD71828 voltage regulator driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:bd71828-gpio");
> --
> 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 =]

Reviewed-by: Bartosz Golaszewski <[email protected]>

2019-11-18 16:24:51

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 09/16] regulator: bd71828: Basic support for ROHM bd71828 PMIC regulators

On Mon, Nov 18, 2019 at 08:57:57AM +0200, Matti Vaittinen wrote:

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

Rather than doing this it's better to just not provide the operation for
devices that don't support it, that makes the handling in the core
easier.


Attachments:
(No filename) (758.00 B)
signature.asc (499.00 B)
Download all attachments

2019-11-18 16:26:35

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

On Mon, Nov 18, 2019 at 08:53:57AM +0200, Matti Vaittinen wrote:

> +#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.
> +
> + rohm,dvs-runlvl-ctrl:
> + description: |
> + buck control is done based on run-level. Regulator is not
> + individually controllable. See ../mfd/rohm,bd71828-pmic.yaml for
> + how to specify run-level control mechanism. Only bucks 1, 2, 6
> + and 7 support this.
> + type: boolean

I don't think I saw this having the effect on set_voltage() that I'd
have expected in the driver?

> + rohm,dvs-runlevel-microvolts:
> + minimum: 0
> + maximum: 2000000
> + maxItems: 4
> + description:
> + Array of voltages for run-levels. First value is for run-level 0,
> + second for run-level 1 etc. Microvolts.

What's the mapping from array indexes to the names used elsewhere to
support runlevels?


Attachments:
(No filename) (1.66 kB)
signature.asc (499.00 B)
Download all attachments

2019-11-18 18:05:20

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

Hello Mark,

It's nice to hear from you again :) I hope you're enjoying all the new
things :)

On Mon, 2019-11-18 at 16:25 +0000, Mark Brown wrote:
> On Mon, Nov 18, 2019 at 08:53:57AM +0200, Matti Vaittinen wrote:
>
> > +#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.
> > +
> > + rohm,dvs-runlvl-ctrl:
> > + description: |
> > + buck control is done based on run-level. Regulator is
> > not
> > + individually controllable. See ../mfd/rohm,bd71828-
> > pmic.yaml for
> > + how to specify run-level control mechanism. Only bucks
> > 1, 2, 6
> > + and 7 support this.
> > + type: boolean
>
> I don't think I saw this having the effect on set_voltage() that I'd
> have expected in the driver?

The support for this is added in patch 12. I should've ordered the
patch series so that all regulator patches were one after another.
Sorry for that.
The patch 12 adds the run-level support. Please see the functions
get_runcontrolled_bucks_dt(),
mark_regulator_runlvl_controlled() (sets the g->runlvl)
and set_buck_runlvl_controlled() (called based on g->runlvl)
which changes the ops to disallow setters and to get voltage based on
current runlevel - and different ops depending on if runlevels are
controlled by GPIO or I2C. Additionally set_buck_runlvl_controlled()
adds DT parsing call-back for setting the initial voltages.

Complex, I know. I'm open to hints,tips and suggestions :)

> > + rohm,dvs-runlevel-microvolts:
> > + minimum: 0
> > + maximum: 2000000
> > + maxItems: 4
> > + description:
> > + Array of voltages for run-levels. First value is for
> > run-level 0,
> > + second for run-level 1 etc. Microvolts.
>
> What's the mapping from array indexes to the names used elsewhere to
> support runlevels?

Hmm. Sorry Mark, I don't think I follow your question. Do you mean
names like LPSR, SUSPEND, IDLE, RUN? If so, then I might need to
rephrase this. The runlevels referred here are different from LPSR,
SUSPEND, IDLE etc. They are actually 'sub-levels' for PMIC's RUN state.
Eg, kind of a 'fast way' to change voltages for multiple power rails
when SoC is at RUN state. The names I have seen are RUN0, RUN1, RUN2
and RUN3. That mapping is described in description above.

Br,
Matti Vaittinen

2019-11-18 21:57:19

by Jacek Anaszewski

[permalink] [raw]
Subject: Re: [PATCH v5 15/16] leds: Add common LED binding parsing support to LED class/core

Hi Matti,

Thank you for the patch. If your driver does not depend
on it then please send is separately. Besides, we would require
to convert many of current LED drivers to verify how the
proposed parsing mechanism will work with them. I've been testing
my LED name composition series using QEMU and stubbing things in
drivers where necessary and I propose to use the same approach
in this case.

Best regards,
Jacek Anaszewski

On 11/18/19 8:03 AM, Matti Vaittinen wrote:
> Qucik grep for 'for_each' or 'linux,default-trigger' or
> 'default-state' under drivers/leds can tell quite a lot. It seems
> multiple LED controller drivers implement the very similar looping
> through the child nodes in order to locate the LED nodes and read
> and support the common LED dt bindings. Implementing this same
> stuff for all LED controllers gets old pretty fast.
>
> This commit adds support for locating the LED node (based on known
> node names - or linux,led-compatible property) and handling of
> few common LED properties.
>
> linux,default-trigger,
> default-state (with the exception of keep),
>
> (in addition to already handled
> function-enumerator,
> function,
> color
> and label).
>
> Regarding the node look-up: If no init_data is given, then no
> node-lookup is done and cdev name is used as such.
>
> If init_data is goven but no starting point for node lookup - then
> (parent) device's own DT node is used. If no led-compatible is given,
> then of_match is searched for. If neither led-compatible not of_match
> is given then device's own node or passed starting point are used as
> such.
>
> Signed-off-by: Matti Vaittinen <[email protected]>
> ---
>
> Changes from v4:
> Fixed issues reported by Dan Carpenter and kbuild-bot.
> (leftover kfree and uninitialized return value)
>
> drivers/leds/led-class.c | 88 ++++++++++++--
> drivers/leds/led-core.c | 246 +++++++++++++++++++++++++++++++--------
> include/linux/leds.h | 90 ++++++++++++--
> 3 files changed, 359 insertions(+), 65 deletions(-)
>

--
Best regards,
Jacek Anaszewski

2019-11-19 07:22:49

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 15/16] leds: Add common LED binding parsing support to LED class/core

Hello Jacek,

On Mon, 2019-11-18 at 22:55 +0100, Jacek Anaszewski wrote:
> Hi Matti,
>
> Thank you for the patch. If your driver does not depend
> on it then please send is separately.

The BD71828 depends on device-tree node look-up. It does not utilize
the common property parsing. I could of course do the child dt-node
walking in BD71828 driver - but it kind of breaks my motivation to do
the LED core improvement if I anyways need to do the parsing in BD71828
driver ;)

> Besides, we would require
> to convert many of current LED drivers to verify how the
> proposed parsing mechanism will work with them.

I see the risk you are pointing out. And I actually think we could
default to old mechanism if of_match or match_property is not given
(for now). I could then see the existing drivers who use init_data -
and ensure those are initializing the new match_property and of_match
in init_data with 0. That would be quite trivial task.

That would allow us to convert and test existing drivers one-by-one
while allowing new drivers to offload the LED node look-up and common
property parsing to LED core. No risk, but less drivers to convert in
the future - and simpler drivers to maintain when all of them do not
need to duplicate node look-up or basic property parsing ;)

To make this more concrete:

We can only do the new DT node look-up if either
if (init_data->match_property.name && init_data->match_property.size)
or
if (init_data->of_match)
That would keep the node-lookup same for all existing drivers.

Eg,
led_find_fwnode could for now just do:

struct fwnode_handle *led_find_fwnode(struct device *parent,
struct led_init_data *init_data)
{
/*
* This should never be called W/O init data.
*/
if (!init_data)
return NULL;

/*
* For old drivers which do not populate new match information
* just directly use the given init_data->fwnode no matter if
* it is NULL or not. - as old functionality did.
*/
if ( (!init_data->match_property.name ||
!init_data->match_property.size) && !init_data->of_match)
return init_data->fwnode;

/* match information was given - do node look-up */
...
}

Furthermore, the common property parsing could also be (for now) done
only if match data is given:

/*
* For now only allow core to parse DT properties if
* parsing is explicitly requested by driver or if core has
* found new match data from init_data and then set the flag
*/
if (INVENT_A_COOL_NEW_FLAG_NAME_HERE)
led_add_props(led_cdev, &props);

or just simply:
if ((init_data->match_property.name &&
init_data->match_property.size) || init_data->of_match)
led_add_props(led_cdev, &props);

(but this won't allow driver to ask for common parsing even if it was
verified for this drv to work - hence I like the flag better)

And if you don't feel confident I can even drop the "common property
parsing" from the series and leave only the "node look-up if match-data
was given" to it.

Anyways, I would like to introduce this support while I am working with
the BD71828 driver which really has the LEDs - but I can modify the
patch series so that it only impacts to drivers which implement the new
match data in init_data and leave old drivers to be converted one-by-
one when they can be tested.

> I've been testing
> my LED name composition series using QEMU and stubbing things in
> drivers where necessary and I propose to use the same approach
> in this case.

I don't plan to do any mass-conversion as it is somewhat risky. I can
do conversion to some of the drivers (simple ones which I can
understand without too much of pain) - and ask if anyone having access
to actual HW with LEDs could be kind enough to test the patch for the
device. Tested drivers can then be taken in-tree as examples. And who
knows, maybe there is some developers looking for a hobby project with
access to LED controller to help with the rest ;) I don't have the
ambition to change all of the LED drivers but I think I can give my 10
cents by contributing the mechanism and doing few examples :)

Anyways, please let me know if you think you could accept patch which
won't change existing driver functionality - but allows new drivers to
not duplicate the code. Else I'll just duplicate the lookup code in one
more driver and hope I don't have another PMIC with LED controller on
my table too soon...

(I am having "some" pressure to do few other tasks I recently got. So I
am afraid I won't have too much time to invest on LEDs this year :(
Thus setting up the qemu and starting with stubbing is really not an
option for me at this phase).

Br,
Matti Vaittinen


>
> Best regards,
> Jacek Anaszewski
>
> On 11/18/19 8:03 AM, Matti Vaittinen wrote:
> > Qucik grep for 'for_each' or 'linux,default-trigger' or
> > 'default-state' under drivers/leds can tell quite a lot. It seems
> > multiple LED controller drivers implement the very similar looping
> > through the child nodes in order to locate the LED nodes and read
> > and support the common LED dt bindings. Implementing this same
> > stuff for all LED controllers gets old pretty fast.
> >
> > This commit adds support for locating the LED node (based on known
> > node names - or linux,led-compatible property) and handling of
> > few common LED properties.
> >
> > linux,default-trigger,
> > default-state (with the exception of keep),
> >
> > (in addition to already handled
> > function-enumerator,
> > function,
> > color
> > and label).
> >
> > Regarding the node look-up: If no init_data is given, then no
> > node-lookup is done and cdev name is used as such.
> >
> > If init_data is goven but no starting point for node lookup - then
> > (parent) device's own DT node is used. If no led-compatible is
> > given,
> > then of_match is searched for. If neither led-compatible not
> > of_match
> > is given then device's own node or passed starting point are used
> > as
> > such.
> >
> > Signed-off-by: Matti Vaittinen <[email protected]>
> > ---
> >
> > Changes from v4:
> > Fixed issues reported by Dan Carpenter and kbuild-bot.
> > (leftover kfree and uninitialized return value)
> >
> > drivers/leds/led-class.c | 88 ++++++++++++--
> > drivers/leds/led-core.c | 246 +++++++++++++++++++++++++++++++--
> > ------
> > include/linux/leds.h | 90 ++++++++++++--
> > 3 files changed, 359 insertions(+), 65 deletions(-)
> >

2019-11-19 09:16:12

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 09/16] regulator: bd71828: Basic support for ROHM bd71828 PMIC regulators

Thanks Mark,

On Mon, 2019-11-18 at 16:20 +0000, Mark Brown wrote:
> On Mon, Nov 18, 2019 at 08:57:57AM +0200, Matti Vaittinen wrote:
>
> > +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;
>
> Rather than doing this it's better to just not provide the operation
> for
> devices that don't support it, that makes the handling in the core
> easier.

Makes sense. I'll change this in next version.

Br,
Matti Vaittinen

2019-11-19 14:27:19

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 15/16] leds: Add common LED binding parsing support to LED class/core


On Mon, 2019-11-18 at 09:03 +0200, Matti Vaittinen wrote:
> Qucik grep for 'for_each' or 'linux,default-trigger' or
> 'default-state' under drivers/leds can tell quite a lot. It seems
> multiple LED controller drivers implement the very similar looping
> through the child nodes in order to locate the LED nodes and read
> and support the common LED dt bindings. Implementing this same
> stuff for all LED controllers gets old pretty fast.
>
> This commit adds support for locating the LED node (based on known
> node names - or linux,led-compatible property) and handling of
> few common LED properties.

This is actually not completely true. I originally thought of adding
led-compatible - but I changed my mind after I saw that at least some
of the existing drivers used value of "reg"-property to do the matching
in-driver. So, to make it simple for them, I did add property
name/value pair in init data for matching. This allows one to do led-
compatible, or to use existing fixed reg (or other) property value. I'd
better to change the commit message to reflect this too.

>
> linux,default-trigger,
> default-state (with the exception of keep),
>
> (in addition to already handled
> function-enumerator,
> function,
> color
> and label).
>
> Regarding the node look-up: If no init_data is given, then no
> node-lookup is done and cdev name is used as such.
>
> If init_data is goven but no starting point for node lookup - then
> (parent) device's own DT node is used. If no led-compatible is given,
> then of_match is searched for. If neither led-compatible not of_match
> is given then device's own node or passed starting point are used as
> such.

And same here.

>
> Signed-off-by: Matti Vaittinen <[email protected]>
> ---
>
> Changes from v4:
> Fixed issues reported by Dan Carpenter and kbuild-bot.
> (leftover kfree and uninitialized return value)
>
> drivers/leds/led-class.c | 88 ++++++++++++--
> drivers/leds/led-core.c | 246 +++++++++++++++++++++++++++++++----
> ----
> include/linux/leds.h | 90 ++++++++++++--
> 3 files changed, 359 insertions(+), 65 deletions(-)
>
> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index 647b1263c579..98173b64a597 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -235,6 +235,29 @@ static int led_classdev_next_name(const char
> *init_name, char *name,
> return i;
> }
>
> +static void led_add_props(struct led_classdev *ld, struct
> led_properties *props)
> +{
> + if (props->default_trigger)
> + ld->default_trigger = props->default_trigger;
> + /*
> + * According to binding docs the LED is by-default turned OFF
> unless
> + * default_state is used to indicate it should be ON or that
> state
> + * should be kept as is
> + */
> + if (props->default_state) {
> + ld->brightness = LED_OFF;
> + if (!strcmp(props->default_state, "on"))
> + ld->brightness = LED_FULL;
> + /*
> + * We probably should not call the brightness_get prior calling
> + * the of_parse_cb if one is provided.
> + * Add a flag to advertice that state should be queried and
> kept as-is.
> + */
> + else if (!strcmp(props->default_state, "keep"))
> + props->brightness_keep = true;
> + }
> +}
> +
> /**
> * led_classdev_register_ext - register a new object of led_classdev
> class
> * with init data.
> @@ -251,22 +274,58 @@ int led_classdev_register_ext(struct device
> *parent,
> char final_name[LED_MAX_NAME_SIZE];
> const char *proposed_name = composed_name;
> int ret;
> -
> + struct led_properties props = {0};
> + struct fwnode_handle *fw;
> +
> + /*
> + * We don't try getting the name based on DT node if init-data
> is not
> + * given. We could see if we find LED properties from the
> device's node
> + * but that might change LED names for old users of
> + * led_classdev_register who have been providing the LED name
> in
> + * cdev->name. So we seek fwnode for names only if init_data is
> given
> + */
> if (init_data) {
> + led_cdev->init_data = init_data;
> if (init_data->devname_mandatory && !init_data-
> >devicename) {
> dev_err(parent, "Mandatory device name is
> missing");
> return -EINVAL;
> }
> - ret = led_compose_name(parent, init_data,
> composed_name);
> +
> + fw = led_find_fwnode(parent, init_data);
> + if (!fw) {
> + dev_err(parent, "No fwnode found\n");
> + return -ENOENT;
> + }
> + /*
> + * We did increase refcount for fwnode. Let's set a
> flag so we
> + * can decrease it during deregistration
> + */
> + led_cdev->fwnode_owned = true;
> +
> + ret = led_parse_fwnode_props(parent, fw, &props);
> + if (ret)
> + goto err_out;
> +
> + if (init_data->of_parse_cb)
> + ret = init_data->of_parse_cb(led_cdev, fw,
> &props);
> if (ret < 0)
> - return ret;
> + goto err_out;
> +
> + led_add_props(led_cdev, &props);
> +
> + ret = led_compose_name(parent, init_data, &props,
> + composed_name);
> + if (ret < 0)
> + goto err_out;
> } else {
> proposed_name = led_cdev->name;
> + led_cdev->fwnode_owned = false;
> + fw = NULL;
> }
>
> ret = led_classdev_next_name(proposed_name, final_name,
> sizeof(final_name));
> if (ret < 0)
> - return ret;
> + goto err_out;
>
> mutex_init(&led_cdev->led_access);
> mutex_lock(&led_cdev->led_access);
> @@ -274,22 +333,28 @@ int led_classdev_register_ext(struct device
> *parent,
> led_cdev, led_cdev->groups, "%s",
> final_name);
> if (IS_ERR(led_cdev->dev)) {
> mutex_unlock(&led_cdev->led_access);
> - return PTR_ERR(led_cdev->dev);
> + ret = PTR_ERR(led_cdev->dev);
> + goto err_out;
> }
> - if (init_data && init_data->fwnode)
> - led_cdev->dev->fwnode = init_data->fwnode;
> + if (fw)
> + led_cdev->dev->fwnode = fw;
>
> if (ret)
> dev_warn(parent, "Led %s renamed to %s due to name
> collision",
> led_cdev->name, dev_name(led_cdev-
> >dev));
>
> + if (props.brightness_keep)
> + if (led_cdev->brightness_get)
> + led_cdev->brightness =
> + led_cdev->brightness_get(led_cdev);
> +
> if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
> ret = led_add_brightness_hw_changed(led_cdev);
> if (ret) {
> device_unregister(led_cdev->dev);
> led_cdev->dev = NULL;
> mutex_unlock(&led_cdev->led_access);
> - return ret;
> + goto err_out;
> }
> }
>
> @@ -322,6 +387,10 @@ int led_classdev_register_ext(struct device
> *parent,
> led_cdev->name);
>
> return 0;
> +err_out:
> + if (led_cdev->fwnode_owned)
> + fwnode_handle_put(fw);
> + return ret;
> }
> EXPORT_SYMBOL_GPL(led_classdev_register_ext);
>
> @@ -355,6 +424,9 @@ void led_classdev_unregister(struct led_classdev
> *led_cdev)
> if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
> led_remove_brightness_hw_changed(led_cdev);
>
> + if (led_cdev->fwnode_owned)
> + fwnode_handle_put(led_cdev->dev->fwnode);
> +
> device_unregister(led_cdev->dev);
>
> down_write(&leds_list_lock);
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index f1f718dbe0f8..9369f03ee540 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -365,70 +365,214 @@ void led_sysfs_enable(struct led_classdev
> *led_cdev)
> }
> EXPORT_SYMBOL_GPL(led_sysfs_enable);
>
> -static void led_parse_fwnode_props(struct device *dev,
> - struct fwnode_handle *fwnode,
> - struct led_properties *props)
> +static int fw_is_match(struct fwnode_handle *fw,
> + struct led_fw_match_property *mp, void *val)
> {
> - int ret;
> + void *cmp = mp->raw_val;
> + int ret = -EINVAL;
> +
> + if (mp->raw_val) {
> + ret = fwnode_property_read_u8_array(fw, mp->name, val,
> + mp->size);
> + } else if (mp->intval) {
> + cmp = mp->intval;
> + switch (mp->size) {
> + case 1:
> + ret = fwnode_property_read_u8_array(fw, mp-
> >name, val,
> + mp->size);
> + break;
> + case 2:
> + ret = fwnode_property_read_u16_array(fw, mp-
> >name, val,
> + mp->size);
> + break;
> + case 4:
> + ret = fwnode_property_read_u32_array(fw, mp-
> >name, val,
> + mp->size);
> + break;
> + case 8:
> + ret = fwnode_property_read_u64_array(fw, mp-
> >name, val,
> + mp->size);
> + break;
> + default:
> + return -EINVAL;
> + }
> + }
> + if (!ret && cmp)
> + if (!memcmp(val, cmp, mp->size))
> + return 1;
> +
> + return 0;
> +}
> +/**
> + * led_find_fwnode - find fwnode for led
> + * @parent LED controller device
> + * @init_data led init data with match information
> + *
> + * Scans the firmware nodes and returns node matching the given
> init_data.
> + * NOTE: Function increases refcount for found node. Caller must
> decrease
> + * refcount using fwnode_handle_put when finished with node.
> + */
> +struct fwnode_handle *led_find_fwnode(struct device *parent,
> + struct led_init_data *init_data)
> +{
> + struct fwnode_handle *fw;
> +
> + /*
> + * This should never be called W/O init data. We could always
> return
> + * dev_fwnode() - but then we should pump-up the refcount
> + */
> + if (!init_data)
> + return NULL;
> +
> + if (!init_data->fwnode)
> + fw = dev_fwnode(parent);
> + else
> + fw = init_data->fwnode;
> +
> + if (!fw)
> + return NULL;
> +
> + /*
> + * Simple things are pretty. I think simplest is to use DT
> node-name
> + * for matching the node with LED - same way regulators use the
> node
> + * name to match with desc.
> + *
> + * This may not work with existing LED DT entries if the node
> name has
> + * been freely selectible. In order to this to work the binding
> doc
> + * for LED driver should define usable node names.
> + *
> + * If this is not working we can define specific match property
> which
> + * value we scan and use for matching for LEDs connected to the
> + * controller.
> + */
> + if (init_data->match_property.name && init_data-
> >match_property.size) {
> + u8 *val;
> + int ret;
> + struct fwnode_handle *child;
> + struct led_fw_match_property *mp;
> +
> + mp = &init_data->match_property;
> +
> + val = kzalloc(mp->size, GFP_KERNEL);
> + if (!val)
> + return ERR_PTR(-ENOMEM);
> +
> + fwnode_for_each_child_node(fw, child) {
> + ret = fw_is_match(child, mp, val);
> + if (ret > 0) {
> + kfree(val);
> + return child;
> + }
> + if (ret < 0) {
> + dev_err(parent,
> + "invalid fw match. Use
> raw_val?\n");
> + fwnode_handle_put(child);
> + break;
> + }
> + }
> + kfree(val);
> + }
> + if (init_data->of_match)
> + fw = fwnode_get_named_child_node(fw, init_data-
> >of_match);
> + else
> + fw = fwnode_handle_get(fw);
> +
> + return fw;
> +}
> +EXPORT_SYMBOL(led_find_fwnode);
> +
> +int led_parse_fwnode_props(struct device *dev, struct fwnode_handle
> *fwnode,
> + struct led_properties *props)
> +{
> + int ret = 0;
>
> if (!fwnode)
> - return;
> + return 0;
>
> if (fwnode_property_present(fwnode, "label")) {
> ret = fwnode_property_read_string(fwnode, "label",
> &props->label);
> if (ret)
> dev_err(dev, "Error parsing 'label' property
> (%d)\n", ret);
> - return;
> + return ret;
> }
>
> + /**
> + * Please note, logic changed - if invalid property is found we
> bail
> + * early out without parsing the rest of the properties.
> Originally
> + * this was the case only for 'label' property. I don't know
> the
> + * rationale behind original logic allowing invalid properties
> to be
> + * given. If there is a reason then we should reconsider this.
> + * Intuitively it feels correct to just yell and quit if we hit
> value we
> + * don't understand - but intuition may be wrong at times :)
> + */
> if (fwnode_property_present(fwnode, "color")) {
> ret = fwnode_property_read_u32(fwnode, "color", &props-
> >color);
> - if (ret)
> + if (ret) {
> dev_err(dev, "Error parsing 'color' property
> (%d)\n", ret);
> - else if (props->color >= LED_COLOR_ID_MAX)
> + return ret;
> + } else if (props->color >= LED_COLOR_ID_MAX) {
> dev_err(dev, "LED color identifier out of
> range\n");
> - else
> - props->color_present = true;
> + return ret;
> + }
> + props->color_present = true;
> }
>
> + if (fwnode_property_present(fwnode, "function")) {
> + ret = fwnode_property_read_string(fwnode, "function",
> + &props->function);
> + if (ret) {
> + dev_err(dev,
> + "Error parsing 'function' property
> (%d)\n",
> + ret);
> + return ret;
> + }
> + }
>
> - if (!fwnode_property_present(fwnode, "function"))
> - return;
> -
> - ret = fwnode_property_read_string(fwnode, "function", &props-
> >function);
> - if (ret) {
> - dev_err(dev,
> - "Error parsing 'function' property (%d)\n",
> - ret);
> + if (fwnode_property_present(fwnode, "function-enumerator")) {
> + ret = fwnode_property_read_u32(fwnode, "function-
> enumerator",
> + &props->func_enum);
> + if (ret) {
> + dev_err(dev,
> + "Error parsing 'function-enumerator'
> property (%d)\n",
> + ret);
> + return ret;
> + }
> + props->func_enum_present = true;
> }
>
> - if (!fwnode_property_present(fwnode, "function-enumerator"))
> - return;
> + if (fwnode_property_present(fwnode, "default-state")) {
> + ret = fwnode_property_read_string(fwnode, "default-
> state",
> + &props-
> >default_state);
> + if (ret) {
> + dev_err(dev,
> + "Error parsing 'default-state' property
> (%d)\n",
> + ret);
> + return ret;
> + }
> + }
>
> - ret = fwnode_property_read_u32(fwnode, "function-enumerator",
> - &props->func_enum);
> - if (ret) {
> - dev_err(dev,
> - "Error parsing 'function-enumerator' property
> (%d)\n",
> - ret);
> - } else {
> - props->func_enum_present = true;
> + if (fwnode_property_present(fwnode, "linux,default-trigger")) {
> + ret = fwnode_property_read_string(fwnode,
> + "linux,default-
> trigger",
> + &props-
> >default_trigger);
> + if (ret)
> + dev_err(dev,
> + "Error parsing 'linux,default-trigger'
> property (%d)\n",
> + ret);
> }
> + return ret;
> }
> +EXPORT_SYMBOL_GPL(led_parse_fwnode_props);
>
> int led_compose_name(struct device *dev, struct led_init_data
> *init_data,
> - char *led_classdev_name)
> + struct led_properties *props, char
> *led_classdev_name)
> {
> - struct led_properties props = {};
> - struct fwnode_handle *fwnode = init_data->fwnode;
> const char *devicename = init_data->devicename;
>
> if (!led_classdev_name)
> return -EINVAL;
>
> - led_parse_fwnode_props(dev, fwnode, &props);
> -
> - if (props.label) {
> + if (props->label) {
> /*
> * If init_data.devicename is NULL, then it indicates
> that
> * DT label should be used as-is for LED class device
> name.
> @@ -436,23 +580,23 @@ int led_compose_name(struct device *dev, struct
> led_init_data *init_data,
> * the final LED class device name.
> */
> if (!devicename) {
> - strscpy(led_classdev_name, props.label,
> + strscpy(led_classdev_name, props->label,
> LED_MAX_NAME_SIZE);
> } else {
> snprintf(led_classdev_name, LED_MAX_NAME_SIZE,
> "%s:%s",
> - devicename, props.label);
> + devicename, props->label);
> }
> - } else if (props.function || props.color_present) {
> + } else if (props->function || props->color_present) {
> char tmp_buf[LED_MAX_NAME_SIZE];
>
> - if (props.func_enum_present) {
> + if (props->func_enum_present) {
> snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-
> %d",
> - props.color_present ?
> led_colors[props.color] : "",
> - props.function ?: "",
> props.func_enum);
> + props->color_present ?
> led_colors[props->color] : "",
> + props->function ?: "", props-
> >func_enum);
> } else {
> snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
> - props.color_present ?
> led_colors[props.color] : "",
> - props.function ?: "");
> + props->color_present ?
> led_colors[props->color] : "",
> + props->function ?: "");
> }
> if (init_data->devname_mandatory) {
> snprintf(led_classdev_name, LED_MAX_NAME_SIZE,
> "%s:%s",
> @@ -468,11 +612,19 @@ int led_compose_name(struct device *dev, struct
> led_init_data *init_data,
> }
> snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
> devicename, init_data->default_label);
> - } else if (is_of_node(fwnode)) {
> - strscpy(led_classdev_name, to_of_node(fwnode)->name,
> - LED_MAX_NAME_SIZE);
> - } else
> - return -EINVAL;
> + } else {
> + struct fwnode_handle *fwnode = led_find_fwnode(dev,
> init_data);
> + int ret = -EINVAL;
> +
> + if (is_of_node(fwnode)) {
> + ret = 0;
> + strscpy(led_classdev_name, to_of_node(fwnode)-
> >name,
> + LED_MAX_NAME_SIZE);
> + }
> + fwnode_handle_put(fwnode);
> +
> + return ret;
> + }
>
> return 0;
> }
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index efb309dba914..aea7d6bc7294 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -13,6 +13,7 @@
> #include <linux/kernfs.h>
> #include <linux/list.h>
> #include <linux/mutex.h>
> +#include <linux/property.h>
> #include <linux/rwsem.h>
> #include <linux/spinlock.h>
> #include <linux/timer.h>
> @@ -20,6 +21,7 @@
>
> struct device;
> struct led_pattern;
> +struct led_classdev;
> /*
> * LED Core
> */
> @@ -31,8 +33,47 @@ enum led_brightness {
> LED_FULL = 255,
> };
>
> +struct led_properties {
> + u32 color;
> + bool color_present;
> + const char *function;
> + u32 func_enum;
> + bool func_enum_present;
> + const char *label;
> + const char *default_trigger;
> + const char *default_state;
> + bool brightness_keep;
> +};
> +
> +struct led_fw_match_property {
> + const char *name; /* Name of the property to match */
> + void *raw_val; /* Raw property value as present in
> fwnode */
> + void *intval; /* Property value if 8,16,32 or 64bit
> integer */
> + size_t size; /* Size of value in bytes */
> +};
> +
> struct led_init_data {
> - /* device fwnode handle */
> + /*
> + * If DT binding dictates the node name the driver can fill
> of_match
> + * corresponding to node name describing this LED. If fwnode is
> given
> + * the match is searched from it's child nodes. If not, the
> match is
> + * searched from device's own child nodes.
> + */
> + const char *of_match;
> + /*
> + * If fwnode contains property with known value the driver can
> specify
> + * correct propertty-value pair here to do the matching. This
> has higher
> + * priority than of_match. If fwnode is given the match is
> searched
> + * from it's child nodes. If not match is searched from
> device's
> + * own child nodes.
> + */
> + struct led_fw_match_property match_property;
> + /*
> + * device fwnode handle. If of_match or led_compatible are not
> given
> + * this is used for LED as given. If of_match or led_compatible
> are
> + * given then this is used as a parent node whose child nodes
> are
> + * scanned for given match.
> + */
> struct fwnode_handle *fwnode;
> /*
> * default <color:function> tuple, for backward compatibility
> @@ -53,9 +94,17 @@ struct led_init_data {
> * set it to true
> */
> bool devname_mandatory;
> + /*
> + * Callback which a LED driver can register if it has own non-
> standard
> + * DT properties. Core calls this with the located DT node
> during
> + * class_device registration
> + */
> + int (*of_parse_cb)(struct led_classdev *ld, struct
> fwnode_handle *fw,
> + struct led_properties *props);
> };
>
> struct led_classdev {
> + struct led_init_data *init_data;
> const char *name;
> enum led_brightness brightness;
> enum led_brightness max_brightness;
> @@ -148,6 +197,7 @@ struct led_classdev {
>
> /* Ensures consistent access to the LED Flash Class device */
> struct mutex led_access;
> + bool fwnode_owned;
> };
>
> /**
> @@ -302,6 +352,7 @@ extern void led_sysfs_enable(struct led_classdev
> *led_cdev);
> * led_compose_name - compose LED class device name
> * @dev: LED controller device object
> * @init_data: the LED class device initialization data
> + * @props: LED properties as parsed from fwnode.
> * @led_classdev_name: composed LED class device name
> *
> * Create LED class device name basing on the provided init_data
> argument.
> @@ -311,6 +362,7 @@ extern void led_sysfs_enable(struct led_classdev
> *led_cdev);
> * Returns: 0 on success or negative error value on failure
> */
> extern int led_compose_name(struct device *dev, struct led_init_data
> *init_data,
> + struct led_properties *props,
> char *led_classdev_name);
>
> /**
> @@ -324,6 +376,33 @@ static inline bool led_sysfs_is_disabled(struct
> led_classdev *led_cdev)
> return led_cdev->flags & LED_SYSFS_DISABLE;
> }
>
> +/**
> + * led_find_fwnode - find fwnode matching given LED init data
> + * @parent: LED controller device this LED is driven by
> + * @init_data: the LED class device initialization data
> + *
> + * Find the fw node matching given LED init data.
> + * NOTE: Function increases refcount for found node. Caller must
> decrease
> + * refcount using fwnode_handle_put when finished with node.
> + *
> + * Returns: node handle or NULL if matching fw node was not found
> + */
> +struct fwnode_handle *led_find_fwnode(struct device *parent,
> + struct led_init_data *init_data);
> +
> +/**
> + * led_parse_fwnode_props - parse LED common properties from fwnode
> + * @dev: pointer to LED device.
> + * @fwnode: LED node containing the properties
> + * @props: structure where found property data is stored.
> + *
> + * Parse the common LED properties from fwnode.
> + *
> + * Returns: 0 on success or negative error value on failure
> + */
> +int led_parse_fwnode_props(struct device *dev, struct fwnode_handle
> *fwnode,
> + struct led_properties *props);
> +
> /*
> * LED Triggers
> */
> @@ -490,15 +569,6 @@ struct led_platform_data {
> struct led_info *leds;
> };
>
> -struct led_properties {
> - u32 color;
> - bool color_present;
> - const char *function;
> - u32 func_enum;
> - bool func_enum_present;
> - const char *label;
> -};
> -
> struct gpio_desc;
> typedef int (*gpio_blink_set_t)(struct gpio_desc *desc, int state,
> unsigned long *delay_on,
> --
> 2.21.0
>
>

2019-11-19 14:48:36

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v5 10/16] gpio: devres: Add devm_gpiod_get_parent_array

On Mon, Nov 18, 2019 at 7:58 AM Matti Vaittinen
<[email protected]> wrote:

> Bunch of MFD sub-devices which are instantiated by MFD do not have
> own device-tree nodes but have (for example) the GPIO consumer
> information in parent device's DT node. Add resource managed
> devm_gpiod_get_array() for such devices so that they can get the
> consumer information from parent DT while still binding the GPIO
> reservation life-time to this sub-device life time.
>
> If devm_gpiod_get_array is used as such - then unloading and then
> re-loading the child device fails as the GPIOs reserved during first
> load are not freed when driver for sub-device is unload (if parent
> stays there).
>
> Signed-off-by: Matti Vaittinen <[email protected]>
(...)
> +static struct gpio_descs *__must_check
> +__devm_gpiod_get_array(struct device *gpiodev,
> + struct device *managed,
> + const char *con_id,
> + enum gpiod_flags flags)

I'm opposed to functions named __underscore_something()
so find a proper name for this function.
devm_gpiod_get_array_common() works if nothing else.

> @@ -292,19 +284,62 @@ struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
> if (!dr)
> return ERR_PTR(-ENOMEM);
>
> - descs = gpiod_get_array(dev, con_id, flags);
> + descs = gpiod_get_array(gpiodev, con_id, flags);
> if (IS_ERR(descs)) {
> devres_free(dr);
> return descs;
> }
>
> *dr = descs;
> - devres_add(dev, dr);
> + if (managed)
> + devres_add(managed, dr);
> + else
> + devres_add(gpiodev, dr);

So we only get managed resources if the "managed" device is
passed in.

> +/**
> + * devm_gpiod_get_array - Resource-managed gpiod_get_array()

And this function is supposed to be resource managed for sure.

> + * @dev: GPIO consumer
> + * @con_id: function within the GPIO consumer
> + * @flags: optional GPIO initialization flags
> + *
> + * Managed gpiod_get_array(). GPIO descriptors returned from this function are
> + * automatically disposed on driver detach. See gpiod_get_array() for detailed
> + * information about behavior and return values.
> + */
> +struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
> + const char *con_id,
> + enum gpiod_flags flags)
> +{
> + return __devm_gpiod_get_array(dev, NULL, con_id, flags);

So what is this? NULL?

Doesn't that mean you just removed all resource management for this
call?

Or am I reading it wrong?

Yours,
Linus Walleij

2019-11-19 17:59:04

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 10/16] gpio: devres: Add devm_gpiod_get_parent_array

Hello Linus,

On Tue, 2019-11-19 at 15:43 +0100, Linus Walleij wrote:
> On Mon, Nov 18, 2019 at 7:58 AM Matti Vaittinen
> <[email protected]> wrote:
>
> > Bunch of MFD sub-devices which are instantiated by MFD do not have
> > own device-tree nodes but have (for example) the GPIO consumer
> > information in parent device's DT node. Add resource managed
> > devm_gpiod_get_array() for such devices so that they can get the
> > consumer information from parent DT while still binding the GPIO
> > reservation life-time to this sub-device life time.
> >
> > If devm_gpiod_get_array is used as such - then unloading and then
> > re-loading the child device fails as the GPIOs reserved during
> > first
> > load are not freed when driver for sub-device is unload (if parent
> > stays there).
> >
> > Signed-off-by: Matti Vaittinen <[email protected]>
> (...)
> > +static struct gpio_descs *__must_check
> > +__devm_gpiod_get_array(struct device *gpiodev,
> > + struct device *managed,
> > + const char *con_id,
> > + enum gpiod_flags flags)
>
> I'm opposed to functions named __underscore_something()
> so find a proper name for this function.
> devm_gpiod_get_array_common() works if nothing else.

Ok. We all have our own pecularitie... I mean preferences :) But as
this is definitely your territory - I'll change the name :) No problem.

>
> > @@ -292,19 +284,62 @@ struct gpio_descs *__must_check
> > devm_gpiod_get_array(struct device *dev,
> > if (!dr)
> > return ERR_PTR(-ENOMEM);
> >
> > - descs = gpiod_get_array(dev, con_id, flags);
> > + descs = gpiod_get_array(gpiodev, con_id, flags);
> > if (IS_ERR(descs)) {
> > devres_free(dr);
> > return descs;
> > }
> >
> > *dr = descs;
> > - devres_add(dev, dr);
> > + if (managed)
> > + devres_add(managed, dr);
> > + else
> > + devres_add(gpiodev, dr);
>
> So we only get managed resources if the "managed" device is
> passed in.

Hmm. Actually, no if I am not misreading this. We get managed devices
in any case, but the lifetime is either bound to exitence of the gpio
consumer device (which is standard way) - or existence of specific
'managed' device.

In case of MFD sub-device (like BD71828 regulator subdev) which has
GPIO consumer properties in MFD node - the GPIO consumer information is
in parent device's (BD71828 MFD device) data - but the lifetime should
be bound to sub-devices (BD71828 regulator device) lifetime. Thus we
need two different devices here.

>
> > +/**
> > + * devm_gpiod_get_array - Resource-managed gpiod_get_array()
>
> And this function is supposed to be resource managed for sure.

Yes. This is the standard case where device which has the consumer data
is also the one who 'manages' the GPIO.

>
> > + * @dev: GPIO consumer
> > + * @con_id: function within the GPIO consumer
> > + * @flags: optional GPIO initialization flags
> > + *
> > + * Managed gpiod_get_array(). GPIO descriptors returned from this
> > function are
> > + * automatically disposed on driver detach. See gpiod_get_array()
> > for detailed
> > + * information about behavior and return values.
> > + */
> > +struct gpio_descs *__must_check devm_gpiod_get_array(struct device
> > *dev,
> > + const char
> > *con_id,
> > + enum
> > gpiod_flags flags)
> > +{
> > + return __devm_gpiod_get_array(dev, NULL, con_id, flags);
>
> So what is this? NULL?

Here we don't have separate manager device - thus the manager is NULL
-and the consumer device ("dev" here) is what we use to manage GPIO.

>
> Doesn't that mean you just removed all resource management for this
> call?

No :)

>
> Or am I reading it wrong?

Either you are reading it wrong or I am writing it wrong xD. In any
case this means I need to drop few comments in code :) Thanks.

Br,
Matti Vaittinen

2019-11-19 18:16:08

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

On Mon, Nov 18, 2019 at 06:03:42PM +0000, Vaittinen, Matti wrote:

> It's nice to hear from you again :) I hope you're enjoying all the new
> things :)

:)

> On Mon, 2019-11-18 at 16:25 +0000, Mark Brown wrote:
> > On Mon, Nov 18, 2019 at 08:53:57AM +0200, Matti Vaittinen wrote:

> > I don't think I saw this having the effect on set_voltage() that I'd
> > have expected in the driver?

> The support for this is added in patch 12. I should've ordered the
> patch series so that all regulator patches were one after another.
> Sorry for that.
> The patch 12 adds the run-level support. Please see the functions
> get_runcontrolled_bucks_dt(),
> mark_regulator_runlvl_controlled() (sets the g->runlvl)
> and set_buck_runlvl_controlled() (called based on g->runlvl)
> which changes the ops to disallow setters and to get voltage based on
> current runlevel - and different ops depending on if runlevels are
> controlled by GPIO or I2C. Additionally set_buck_runlvl_controlled()
> adds DT parsing call-back for setting the initial voltages.

Ah, OK. I didn't even notice that patch when I scanned the series.
I'll look out for this next time around but that sounds like it's
generally going in the right direction, especially if it's integrated
with the suspend mode regulator bindings that Chunyan did.

> > > + minimum: 0
> > > + maximum: 2000000
> > > + maxItems: 4
> > > + description:
> > > + Array of voltages for run-levels. First value is for
> > > run-level 0,
> > > + second for run-level 1 etc. Microvolts.

> > What's the mapping from array indexes to the names used elsewhere to
> > support runlevels?

> Hmm. Sorry Mark, I don't think I follow your question. Do you mean
> names like LPSR, SUSPEND, IDLE, RUN? If so, then I might need to
> rephrase this. The runlevels referred here are different from LPSR,
> SUSPEND, IDLE etc. They are actually 'sub-levels' for PMIC's RUN state.
> Eg, kind of a 'fast way' to change voltages for multiple power rails
> when SoC is at RUN state. The names I have seen are RUN0, RUN1, RUN2
> and RUN3. That mapping is described in description above.

Yes, I think this needs clarification as I completely failed to pick up
on this and did indeed read this as referring to the modes. "Voltages
that can be set in RUN mode" or something? I take it these voltages are
fixed and the OS can't change them?


Attachments:
(No filename) (2.39 kB)
signature.asc (499.00 B)
Download all attachments

2019-11-19 18:54:06

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings


On Tue, 2019-11-19 at 18:13 +0000, Mark Brown wrote:
> On Mon, Nov 18, 2019 at 06:03:42PM +0000, Vaittinen, Matti wrote:
> > On Mon, 2019-11-18 at 16:25 +0000, Mark Brown wrote:
> > > On Mon, Nov 18, 2019 at 08:53:57AM +0200, Matti Vaittinen wrote:
> > > I don't think I saw this having the effect on set_voltage() that
> > > I'd
> > > have expected in the driver?
> > The support for this is added in patch 12. I should've ordered the
> > patch series so that all regulator patches were one after another.
> > Sorry for that.
> > The patch 12 adds the run-level support. Please see the functions
> > get_runcontrolled_bucks_dt(),
> > mark_regulator_runlvl_controlled() (sets the g->runlvl)
> > and set_buck_runlvl_controlled() (called based on g->runlvl)
> > which changes the ops to disallow setters and to get voltage based
> > on
> > current runlevel - and different ops depending on if runlevels are
> > controlled by GPIO or I2C. Additionally
> > set_buck_runlvl_controlled()
> > adds DT parsing call-back for setting the initial voltages.
>
> Ah, OK. I didn't even notice that patch when I scanned the series.
> I'll look out for this next time around but that sounds like it's
> generally going in the right direction, especially if it's integrated
> with the suspend mode regulator bindings that Chunyan did.

Probably it is not as I am not familiar with Chunyan's work. I'll try
looking what has been done on that front :) And I am pretty sure you
might not be happy with that patch - but perhaps you can give me a
nudge to better direction...

> > > > + minimum: 0
> > > > + maximum: 2000000
> > > > + maxItems: 4
> > > > + description:
> > > > + Array of voltages for run-levels. First value is for
> > > > run-level 0,
> > > > + second for run-level 1 etc. Microvolts.
> > > What's the mapping from array indexes to the names used elsewhere
> > > to
> > > support runlevels?
> > Hmm. Sorry Mark, I don't think I follow your question. Do you mean
> > names like LPSR, SUSPEND, IDLE, RUN? If so, then I might need to
> > rephrase this. The runlevels referred here are different from LPSR,
> > SUSPEND, IDLE etc. They are actually 'sub-levels' for PMIC's RUN
> > state.
> > Eg, kind of a 'fast way' to change voltages for multiple power
> > rails
> > when SoC is at RUN state. The names I have seen are RUN0, RUN1,
> > RUN2
> > and RUN3. That mapping is described in description above.
>
> Yes, I think this needs clarification as I completely failed to pick
> up
> on this and did indeed read this as referring to the
> modes. "Voltages
> that can be set in RUN mode" or something? I take it these voltages
> are
> fixed and the OS can't change them?

Unfortunately they are not. Voltages and enable/disable statuses for
each run-level (and individually for each run-level capable buck) can
be changed at runtime via I2C. And a customer requested me also to
support this - hence the in-kernel API - but I am sure you have some
nice words when you check the patch 12. :]

Br,
Matti Vaittinen

2019-11-19 19:33:57

by Jacek Anaszewski

[permalink] [raw]
Subject: Re: [PATCH v5 15/16] leds: Add common LED binding parsing support to LED class/core

Hi Matti,

On 11/19/19 8:21 AM, Vaittinen, Matti wrote:
> Hello Jacek,
>
> On Mon, 2019-11-18 at 22:55 +0100, Jacek Anaszewski wrote:
>> Hi Matti,
>>
>> Thank you for the patch. If your driver does not depend
>> on it then please send is separately.
>
> The BD71828 depends on device-tree node look-up. It does not utilize
> the common property parsing. I could of course do the child dt-node
> walking in BD71828 driver - but it kind of breaks my motivation to do
> the LED core improvement if I anyways need to do the parsing in BD71828
> driver ;)

If you do not plan on spending too much time on contributing this
set then I propose adhering to the currently used parsing schema :-)

And you have to know that from this development cycle I handed
over LED tree maintenance to Pavel Machek, so you will require
to have his acceptance in the first place.

>> Besides, we would require
>> to convert many of current LED drivers to verify how the
>> proposed parsing mechanism will work with them.
>
> I see the risk you are pointing out. And I actually think we could
> default to old mechanism if of_match or match_property is not given
> (for now). I could then see the existing drivers who use init_data -
> and ensure those are initializing the new match_property and of_match
> in init_data with 0. That would be quite trivial task.
>
> That would allow us to convert and test existing drivers one-by-one
> while allowing new drivers to offload the LED node look-up and common
> property parsing to LED core. No risk, but less drivers to convert in
> the future - and simpler drivers to maintain when all of them do not
> need to duplicate node look-up or basic property parsing ;)

I personally would prefer to do the massive driver update to using
the new mechanism. I know that this is time consuming but we are not
in a hurry.

> To make this more concrete:
>
> We can only do the new DT node look-up if either
> if (init_data->match_property.name && init_data->match_property.size)
> or
> if (init_data->of_match)
> That would keep the node-lookup same for all existing drivers.
>
> Eg,
> led_find_fwnode could for now just do:
>
> struct fwnode_handle *led_find_fwnode(struct device *parent,
> struct led_init_data *init_data)
> {
> /*
> * This should never be called W/O init data.
> */
> if (!init_data)
> return NULL;
>
> /*
> * For old drivers which do not populate new match information
> * just directly use the given init_data->fwnode no matter if
> * it is NULL or not. - as old functionality did.
> */
> if ( (!init_data->match_property.name ||
> !init_data->match_property.size) && !init_data->of_match)
> return init_data->fwnode;
>
> /* match information was given - do node look-up */
> ...
> }
>
> Furthermore, the common property parsing could also be (for now) done
> only if match data is given:
>
> /*
> * For now only allow core to parse DT properties if
> * parsing is explicitly requested by driver or if core has
> * found new match data from init_data and then set the flag
> */
> if (INVENT_A_COOL_NEW_FLAG_NAME_HERE)
> led_add_props(led_cdev, &props);
>
> or just simply:
> if ((init_data->match_property.name &&
> init_data->match_property.size) || init_data->of_match)
> led_add_props(led_cdev, &props);
>
> (but this won't allow driver to ask for common parsing even if it was
> verified for this drv to work - hence I like the flag better)
>
> And if you don't feel confident I can even drop the "common property
> parsing" from the series and leave only the "node look-up if match-data
> was given" to it.
>
> Anyways, I would like to introduce this support while I am working with
> the BD71828 driver which really has the LEDs - but I can modify the
> patch series so that it only impacts to drivers which implement the new
> match data in init_data and leave old drivers to be converted one-by-
> one when they can be tested.
>
>> I've been testing
>> my LED name composition series using QEMU and stubbing things in
>> drivers where necessary and I propose to use the same approach
>> in this case.
>
> I don't plan to do any mass-conversion as it is somewhat risky. I can

You do not need hardware to test DT parsing as I mentioned before,
so I don't see too much risk involved.

> do conversion to some of the drivers (simple ones which I can
> understand without too much of pain) - and ask if anyone having access
> to actual HW with LEDs could be kind enough to test the patch for the
> device. Tested drivers can then be taken in-tree as examples. And who
> knows, maybe there is some developers looking for a hobby project with
> access to LED controller to help with the rest ;) I don't have the
> ambition to change all of the LED drivers but I think I can give my 10
> cents by contributing the mechanism and doing few examples :)

If you want to introduce good, robust mechanism, then it should be
tested against widest possible spectrum of use cases.

> Anyways, please let me know if you think you could accept patch which
> won't change existing driver functionality - but allows new drivers to
> not duplicate the code. Else I'll just duplicate the lookup code in one
> more driver and hope I don't have another PMIC with LED controller on
> my table too soon...
>
> (I am having "some" pressure to do few other tasks I recently got. So I
> am afraid I won't have too much time to invest on LEDs this year :(
> Thus setting up the qemu and starting with stubbing is really not an
> option for me at this phase).

As mentioned before - I no longer apply patches so you will need to
consult Pavel, but I bet he will have similar opinion.

--
Best regards,
Jacek Anaszewski

2019-11-19 19:38:12

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

On Tue, Nov 19, 2019 at 06:51:37PM +0000, Vaittinen, Matti wrote:
> On Tue, 2019-11-19 at 18:13 +0000, Mark Brown wrote:

> > Ah, OK. I didn't even notice that patch when I scanned the series.
> > I'll look out for this next time around but that sounds like it's
> > generally going in the right direction, especially if it's integrated
> > with the suspend mode regulator bindings that Chunyan did.

> Probably it is not as I am not familiar with Chunyan's work. I'll try
> looking what has been done on that front :) And I am pretty sure you
> might not be happy with that patch - but perhaps you can give me a
> nudge to better direction...

The driver interface was added in "regulator: add PM suspend and resume
hooks".

> > Yes, I think this needs clarification as I completely failed to pick
> > up
> > on this and did indeed read this as referring to the
> > modes. "Voltages
> > that can be set in RUN mode" or something? I take it these voltages
> > are
> > fixed and the OS can't change them?

> Unfortunately they are not. Voltages and enable/disable statuses for
> each run-level (and individually for each run-level capable buck) can
> be changed at runtime via I2C. And a customer requested me also to
> support this - hence the in-kernel API - but I am sure you have some
> nice words when you check the patch 12. :]

Ah, that's actually better. It opens up possiblities for making use of
the feature without encoding voltages in DT. For example, you can cache
the last however many voltages that were set and jump quickly to them or
do something like put the top of the constraints in to help with
governors like ondemand. I'd recommend trying for something like that
rather than encoding in DT, it'll probably be more robust with things
like cpufreq changing.


Attachments:
(No filename) (1.78 kB)
signature.asc (499.00 B)
Download all attachments

2019-11-20 07:32:44

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 15/16] leds: Add common LED binding parsing support to LED class/core


On Tue, 2019-11-19 at 20:30 +0100, Jacek Anaszewski wrote:
> Hi Matti,
>
> On 11/19/19 8:21 AM, Vaittinen, Matti wrote:
> > Hello Jacek,
> >
> > On Mon, 2019-11-18 at 22:55 +0100, Jacek Anaszewski wrote:
> > > Hi Matti,
> > >
> > > Thank you for the patch. If your driver does not depend
> > > on it then please send is separately.
> >
> > The BD71828 depends on device-tree node look-up. It does not
> > utilize
> > the common property parsing. I could of course do the child dt-node
> > walking in BD71828 driver - but it kind of breaks my motivation to
> > do
> > the LED core improvement if I anyways need to do the parsing in
> > BD71828
> > driver ;)
>
> If you do not plan on spending too much time on contributing this
> set then I propose adhering to the currently used parsing schema :-)

I have no objections on doing few iterations of the patches. And I tend
to take care of problems my changes cause. So I am prepared to spend
the required time fixing the node look-up and common property parsing
for drivers I do break. What I am not prepared is to change and test
all of the existing drivers - so it's better to not promise such :)

> And you have to know that from this development cycle I handed
> over LED tree maintenance to Pavel Machek, so you will require
> to have his acceptance in the first place.

Well, then I for sure wait for Pavel's take on this. In general I have
had some positive feedback about doing the DT node look-up and common
property parsing in a centralized manner in LED core. So maybe also
Pavel sees the value of adding this now for new drivers - instead of
adding one more driver with copy-paste node look-up code. I want to
thank you for all the comments though, it's nice that you have been
active on this topic!

> > > Besides, we would require
> > > to convert many of current LED drivers to verify how the
> > > proposed parsing mechanism will work with them.
> >
> > I see the risk you are pointing out. And I actually think we could
> > default to old mechanism if of_match or match_property is not given
> > (for now). I could then see the existing drivers who use init_data
> > -
> > and ensure those are initializing the new match_property and
> > of_match
> > in init_data with 0. That would be quite trivial task.
> >
> > That would allow us to convert and test existing drivers one-by-one
> > while allowing new drivers to offload the LED node look-up and
> > common
> > property parsing to LED core. No risk, but less drivers to convert
> > in
> > the future - and simpler drivers to maintain when all of them do
> > not
> > need to duplicate node look-up or basic property parsing ;)
>
> I personally would prefer to do the massive driver update to using
> the new mechanism. I know that this is time consuming but we are not
> in a hurry.

I understand the preference of massive update - but I also know that if
we wait for someone to do a massive update and neglect improvements
done in small steps, then there is a risk that there won't be any
updates at all...

> > To make this more concrete:
> >
> > We can only do the new DT node look-up if either
> > if (init_data->match_property.name && init_data-
> > >match_property.size)
> > or
> > if (init_data->of_match)
> > That would keep the node-lookup same for all existing drivers.
> >
> > Eg,
> > led_find_fwnode could for now just do:
> >
> > struct fwnode_handle *led_find_fwnode(struct device *parent,
> > struct led_init_data *init_data)
> > {
> > /*
> > * This should never be called W/O init data.
> > */
> > if (!init_data)
> > return NULL;
> >
> > /*
> > * For old drivers which do not populate new match information
> > * just directly use the given init_data->fwnode no matter if
> > * it is NULL or not. - as old functionality did.
> > */
> > if ( (!init_data->match_property.name ||
> > !init_data->match_property.size) && !init_data->of_match)
> > return init_data->fwnode;
> >
> > /* match information was given - do node look-up */
> > ...
> > }
> >
> > Furthermore, the common property parsing could also be (for now)
> > done
> > only if match data is given:
> >
> > /*
> > * For now only allow core to parse DT properties if
> > * parsing is explicitly requested by driver or if core has
> > * found new match data from init_data and then set the flag
> > */
> > if (INVENT_A_COOL_NEW_FLAG_NAME_HERE)
> > led_add_props(led_cdev, &props);
> >
> > or just simply:
> > if ((init_data->match_property.name &&
> > init_data->match_property.size) || init_data->of_match)
> > led_add_props(led_cdev, &props);
> >
> > (but this won't allow driver to ask for common parsing even if it
> > was
> > verified for this drv to work - hence I like the flag better)
> >
> > And if you don't feel confident I can even drop the "common
> > property
> > parsing" from the series and leave only the "node look-up if match-
> > data
> > was given" to it.
> >
> > Anyways, I would like to introduce this support while I am working
> > with
> > the BD71828 driver which really has the LEDs - but I can modify the
> > patch series so that it only impacts to drivers which implement the
> > new
> > match data in init_data and leave old drivers to be converted one-
> > by-
> > one when they can be tested.
> >
> > > I've been testing
> > > my LED name composition series using QEMU and stubbing things in
> > > drivers where necessary and I propose to use the same approach
> > > in this case.
> >
> > I don't plan to do any mass-conversion as it is somewhat risky. I
> > can
>
> You do not need hardware to test DT parsing as I mentioned before,
> so I don't see too much risk involved.

Yes - if you have the time to test all the drivers at once - and
assuming you don't do some silly mistake there. Final verification
should always be done in HW. But as I said, I want to ensure all the
drivers I convert to new mechanism will work (the best I can) - and as
I can't test all the drivers I won't do mass-conversion. I have offered
a initial solution in the patch (and suggested reduced version to
mitigate the risk of breaking anything in this email) - and I see this
beneficial and as a good starting point enabling the rest of the
improvements. But as you said, we need also Pavel's take on this.

> > do conversion to some of the drivers (simple ones which I can
> > understand without too much of pain) - and ask if anyone having
> > access
> > to actual HW with LEDs could be kind enough to test the patch for
> > the
> > device. Tested drivers can then be taken in-tree as examples. And
> > who
> > knows, maybe there is some developers looking for a hobby project
> > with
> > access to LED controller to help with the rest ;) I don't have the
> > ambition to change all of the LED drivers but I think I can give my
> > 10
> > cents by contributing the mechanism and doing few examples :)
>
> If you want to introduce good, robust mechanism, then it should be
> tested against widest possible spectrum of use cases.

Yes. OTOH, if the mechanism is sub-optimal, then the beauty of open
source is that it can be improved. Preparing in advance for something
that never happens is also a waste. But I guess we don't need to
discuss this philosphy here :)

> > Anyways, please let me know if you think you could accept patch
> > which
> > won't change existing driver functionality - but allows new drivers
> > to
> > not duplicate the code. Else I'll just duplicate the lookup code in
> > one
> > more driver and hope I don't have another PMIC with LED controller
> > on
> > my table too soon...
> >
> > (I am having "some" pressure to do few other tasks I recently got.
> > So I
> > am afraid I won't have too much time to invest on LEDs this year :(
> > Thus setting up the qemu and starting with stubbing is really not
> > an
> > option for me at this phase).
>
> As mentioned before - I no longer apply patches so you will need to
> consult Pavel, but I bet he will have similar opinion.

Who knows, maybe he can see this differently :) Thanks anyways!

Br,
Matti Vaittinen

2019-11-21 14:16:12

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v5 10/16] gpio: devres: Add devm_gpiod_get_parent_array

On Tue, Nov 19, 2019 at 6:54 PM Vaittinen, Matti
<[email protected]> wrote:
> [Me]
> > So what is this? NULL?
>
> Here we don't have separate manager device - thus the manager is NULL
> -and the consumer device ("dev" here) is what we use to manage GPIO.
>
> >
> > Doesn't that mean you just removed all resource management for this
> > call?
>
> No :)
>
> >
> > Or am I reading it wrong?
>
> Either you are reading it wrong or I am writing it wrong xD. In any
> case this means I need to drop few comments in code :) Thanks.

I was reading it wrong, so not your bad. I guess lack of focus on
my side, this part is fine!

Yours,
Linus Walleij

2019-11-22 22:50:03

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

On Mon, Nov 18, 2019 at 08:53:57AM +0200, Matti Vaittinen wrote:
> Document ROHM BD71828 PMIC regulator device tree bindings.
>
> Signed-off-by: Matti Vaittinen <[email protected]>
> ---
>
> No changes from v4
>
> .../regulator/rohm,bd71828-regulator.yaml | 122 ++++++++++++++++++
> 1 file changed, 122 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.yaml
>
> diff --git a/Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.yaml b/Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.yaml
> new file mode 100644
> index 000000000000..c23ec4d8584b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.yaml
> @@ -0,0 +1,122 @@
> +# SPDX-License-Identifier: GPL-2.0-only

Please dual license new bindings:

(GPL-2.0-only OR BSD-2-Clause)

> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/regulator/rohm,bd71828-regulator.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ROHM BD71828 Power Management Integrated Circuit regulators
> +
> +maintainers:
> + - Matti Vaittinen <[email protected]>
> +
> +description: |
> + This module is part of the ROHM BD71828 MFD device. For more details
> + see Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml.
> +
> + The regulator controller is represented as a sub-node of the PMIC node
> + on the device tree.
> +
> + Regulator nodes should be named to BUCK_<number> and LDO_<number>.
> + The valid names for BD71828 regulator nodes are
> + BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6, BUCK7
> + LDO1, LDO2, LDO3, LDO4, LDO5, LDO6, LDO7
> +
> +patternProperties:
> + "^LDO[1-7]$":
> + type: object
> + allOf:
> + - $ref: regulator.yaml#
> + description:
> + Properties for single LDO regulator.
> +
> + properties:
> + #Is there a nice way to check the name is same as node name but lower case

Nope.

Why not make the node names lower case? That's the preference though
the regulator binding is special.

> + regulator-name:
> + pattern: "^ldo[1-7]$"
> + description:
> + should be "ldo1", ..., "ldo7"
> +
> + "^BUCK[1-7]$":
> + type: object
> + allOf:
> + - $ref: regulator.yaml#
> + description:
> + Properties for single BUCK regulator.
> +
> + properties:
> + #Is there a nice way to check the name is same as node name but lower case
> + regulator-name:
> + pattern: "^buck[1-7]$"
> + description:
> + should be "buck1", ..., "buck7"
> +
> + rohm,dvs-run-voltage:
> + allOf:
> + - $ref: "/schemas/types.yaml#/definitions/uint32"
> + - minimum: 0
> + maximum: 3300000
> + description:
> + PMIC default "RUN" state voltage in uV. See below table for
> + bucks which support this. 0 means disabled.
> +
> + rohm,dvs-idle-voltage:
> + allOf:
> + - $ref: "/schemas/types.yaml#/definitions/uint32"
> + - minimum: 0
> + maximum: 3300000
> + description:
> + PMIC default "IDLE" state voltage in uV. See below table for
> + bucks which support this. 0 means disabled.
> +
> + rohm,dvs-suspend-voltage:
> + allOf:
> + - $ref: "/schemas/types.yaml#/definitions/uint32"
> + - minimum: 0
> + maximum: 3300000
> + description:
> + PMIC default "SUSPEND" state voltage in uV. See below table for
> + bucks which support this. 0 means disabled.
> +
> + rohm,dvs-lpsr-voltage:
> + allOf:
> + - $ref: "/schemas/types.yaml#/definitions/uint32"
> + - minimum: 0
> + maximum: 3300000
> + description:
> + PMIC default "LPSR" state voltage in uV. See below table for
> + bucks which support this. 0 means disabled.
> +
> +#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.

Would be nicer if indented to the same level.

> +
> + rohm,dvs-runlvl-ctrl:
> + description: |
> + buck control is done based on run-level. Regulator is not
> + individually controllable. See ../mfd/rohm,bd71828-pmic.yaml for
> + how to specify run-level control mechanism. Only bucks 1, 2, 6
> + and 7 support this.
> + type: boolean
> +
> + rohm,dvs-runlevel-microvolts:
> + minimum: 0
> + maximum: 2000000
> + maxItems: 4

Mixing array and scalar constraints.

maxItems: 4
items:
minimum: 0
maximum: 2000000


> + description:
> + Array of voltages for run-levels. First value is for run-level 0,
> + second for run-level 1 etc. Microvolts.
> +
> + required:
> + - regulator-name
> + additionalProperties: false
> +additionalProperties: false
> --
> 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 =]

2019-11-22 23:04:54

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v5 02/16] dt-bindings: leds: ROHM BD71282 PMIC LED driver

On Mon, Nov 18, 2019 at 08:54:25AM +0200, Matti Vaittinen wrote:
> Document ROHM BD71828 PMIC LED driver device tree bindings.
>
> Signed-off-by: Matti Vaittinen <[email protected]>
> ---
>
> No changes from v4
>
> .../bindings/leds/rohm,bd71828-leds.yaml | 49 +++++++++++++++++++
> 1 file changed, 49 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
>
> diff --git a/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml b/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
> new file mode 100644
> index 000000000000..e72c9ae0fc2a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
> @@ -0,0 +1,49 @@
> +# SPDX-License-Identifier: GPL-2.0-only

Dual license please.

> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/leds/rohm,bd71828-leds.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ROHM BD71828 Power Management Integrated Circuit LED driver
> +
> +maintainers:
> + - Matti Vaittinen <[email protected]>
> +
> +description: |
> + This module is part of the ROHM BD71828 MFD device. For more details
> + see Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml.
> +
> + The LED controller is represented as a sub-node of the PMIC node on the device
> + tree.
> +
> + The device has two LED outputs referred as GRNLED and AMBLED in data-sheet.

Since you include this from the MFD schema, add a 'select: false' here
or this node gets checked twice.

> +
> +properties:
> + compatible:
> + const: rohm,bd71828-leds
> +
> +patternProperties:
> + "^led-[1-2]$":
> + type: object
> + description:
> + Properties for a single LED.
> + properties:
> + #$ref: "common.yaml#"

Will need to be:

allOf:
- $ref: ...

> + rohm,led-compatible:
> + description: LED identification string
> + allOf:
> + - $ref: "/schemas/types.yaml#/definitions/string"
> + - oneOf:
> + - const: bd71828-ambled
> + - const: bd71828-grnled

Use enum rather than oneOf+const

> + function:
> + description:
> + Purpose of LED as defined in dt-bindings/leds/common.h
> + $ref: "/schemas/types.yaml#/definitions/string"
> + color:
> + description:
> + LED colour as defined in dt-bindings/leds/common.h
> + $ref: "/schemas/types.yaml#/definitions/uint32"
> +
> +required:
> + - compatible
> --
> 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 =]

2019-11-22 23:10:52

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v5 03/16] dt-bindings: mfd: Document ROHM BD71828 bindings

On Mon, Nov 18, 2019 at 08:54:56AM +0200, 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
> and a 32.768 kHz clock gate.
>
> Document the dt bindings drivers are using.
>
> Signed-off-by: Matti Vaittinen <[email protected]>
> ---
>
> No changes from v4
>
> .../bindings/mfd/rohm,bd71828-pmic.yaml | 249 ++++++++++++++++++
> 1 file changed, 249 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml
>
> diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml
> new file mode 100644
> index 000000000000..17ea9fa463e8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml
> @@ -0,0 +1,249 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/rohm,bd71828-pmic.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ROHM BD71828 Power Management Integrated Circuit bindings
> +
> +maintainers:
> + - Matti Vaittinen <[email protected]>
> +
> +description: |
> + 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.
> +
> + 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.yaml for how to define regulator voltages
> + for run-levels. Run-levels can be changed by I2C or GPIO depending on PMIC's
> + OTP configuration.
> +
> +properties:
> + compatible:
> + const: rohm,bd71828
> +
> + reg:
> + description:
> + I2C slave address.
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> + description:
> + The interrupt line the device is connected to.

The description here is not too useful, I'd drop it.

> +
> + gpio-controller: true
> + # Indicate BD71828 acts as a GPIO controller.
> +
> + "#gpio-cells":
> + const: 2
> + description: |
> + The first cell is the pin number and the second cell is used to specify
> + flags. See ../gpio/gpio.txt for more information.
> +
> + clocks:
> + maxItems: 1
> + description:
> + The parent clock connected to PMIC.

The description here is not too useful, I'd drop it.

> +
> + "#clock-cells":
> + const: 0
> +
> + rohm,charger-sense-resistor-ohms:
> + minimum: 10000000
> + maximum: 50000000
> + description: |
> + BD71827 and BD71828 have SAR ADC for measuring charging currents.
> + External sense resistor (RSENSE in data sheet) should be used. If some
> + other but 30MOhm resistor is used the resistance value should be given
> + here in Ohms.
> +
> + regulators:
> + $ref: ../regulator/rohm,bd71828-regulator.yaml
> + description:
> + List of child nodes that specify the regulators.
> +
> + leds:
> + $ref: ../leds/rohm,bd71828-leds.yaml
> +
> + rohm,dvs-vsel-gpios:
> + maxItems: 2
> + description: |
> + 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:
> + description: |
> + Usage of BD71828 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.
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - clocks
> + - "#clock-cells"
> + - regulators
> + - gpio-controller
> + - "#gpio-cells"

2019-11-29 07:49:46

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

Hello Again Mark,

Sorry for long delay - I am really having too many things on my table
right now :/

On Tue, 2019-11-19 at 19:36 +0000, Mark Brown wrote:
> On Tue, Nov 19, 2019 at 06:51:37PM +0000, Vaittinen, Matti wrote:
> > On Tue, 2019-11-19 at 18:13 +0000, Mark Brown wrote:
> > > Ah, OK. I didn't even notice that patch when I scanned the
> > > series.
> > > I'll look out for this next time around but that sounds like it's
> > > generally going in the right direction, especially if it's
> > > integrated
> > > with the suspend mode regulator bindings that Chunyan did.
> > Probably it is not as I am not familiar with Chunyan's work. I'll
> > try
> > looking what has been done on that front :) And I am pretty sure
> > you
> > might not be happy with that patch - but perhaps you can give me a
> > nudge to better direction...
>
> The driver interface was added in "regulator: add PM suspend and
> resume
> hooks".

I looked through the set but didn't spot any new interface towards the
regulator driver (which accesses the HW). I saw interface towards
regulator consumer driver which can be used to set the constrains
though.

Specifically, I don't see voltage setting callback for different run-
modes. Nor do I see voltage setting (or differentiation) of more than
one suspend state.

To explain it further - my assumption is that the BD71828 'run-levels'
(RUN0, ... RUN3) could be mapped to regulator modes
REGULATOR_MODE_FAST, REGULATOR_MODE_NORMAL, REGULATOR_MODE_IDLE and
REGULATOR_MODE_STANDBY. But regulators which are controlled by these
run-levels, can't be individually controlled. If state for one is
changed, the state is changed for all of them. The DVS bucks 1,2,6 and
7 support this. Rest of the LDOs and BUCKs (and also those DVS bucks
which are not configured to be controlled by run-levels) have modes RUN
and IDLE, where the processor stays powered.

In addition to these (active) modes/states, there is few states where
processor is not powered. I guess these could be mapped to 'different'
suspend states. At least LPSR, HBNT and SHIP states are such. These are
again global PMIC states - not per regulator states. They can be either
controlled by driving a pin on PMIC - or by I2C register setting. I
don't see how I could differentiate these states when using standard
APIs - nor do I know if these should be changed via regulator
interfaces at all.

All in all - I am also a bit unsure how I should do the mapping of the
PMIC low-power modes to the modes used by Linux - the curse of working
for component vendor is that I have limited visibility to actual end-
products - if they are not in-tree. :( And I don't think we have any
in-tree boards which use these low-power states (at least for now) - So
if you or Rob do not object - I will leave these bindings in this doc -
but I need to consider the value of adding stuff presented in patch 12
in-tree kernel... Guess I'll drop it out unless I get some better
understanding how run-levels and low-power modes are handled in some of
the actual devices. We can always add this support later :)

> > > Yes, I think this needs clarification as I completely failed to
> > > pick
> > > up
> > > on this and did indeed read this as referring to the
> > > modes. "Voltages
> > > that can be set in RUN mode" or something? I take it these
> > > voltages
> > > are
> > > fixed and the OS can't change them?
> > Unfortunately they are not. Voltages and enable/disable statuses
> > for
> > each run-level (and individually for each run-level capable buck)
> > can
> > be changed at runtime via I2C. And a customer requested me also to
> > support this - hence the in-kernel API - but I am sure you have
> > some
> > nice words when you check the patch 12. :]
>
> Ah, that's actually better. It opens up possiblities for making use
> of
> the feature without encoding voltages in DT. For example, you can
> cache
> the last however many voltages that were set and jump quickly to them
> or
> do something like put the top of the constraints in to help with
> governors like ondemand. I'd recommend trying for something like
> that
> rather than encoding in DT, it'll probably be more robust with things
> like cpufreq changing.

I wish I was working with the full product so that I could see and
learn a proper example on how the cpufreq actually uses these
interfaces :) I'd really like to understand this much better. Maybe
this could be a topic for you to present in some Linux conference ;)
Just please ping me when you are doing that and I'll be listening there
for sure ;)

Anyways, my idea was to set the inital voltage values for these states
via DT - but allow the voltages to be changed at run-time too (I guess
this idea is visible in the patch 12).

Br,
Matti Vaittinen

2019-11-29 12:10:58

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

On Fri, Nov 29, 2019 at 07:48:13AM +0000, Vaittinen, Matti wrote:
> On Tue, 2019-11-19 at 19:36 +0000, Mark Brown wrote:

> > The driver interface was added in "regulator: add PM suspend and
> > resume
> > hooks".

> I looked through the set but didn't spot any new interface towards the
> regulator driver (which accesses the HW). I saw interface towards
> regulator consumer driver which can be used to set the constrains
> though.

The regulator driver has a bunch fo set_suspend_ operations.

> Specifically, I don't see voltage setting callback for different run-
> modes. Nor do I see voltage setting (or differentiation) of more than
> one suspend state.

set_suspend_voltage.

> To explain it further - my assumption is that the BD71828 'run-levels'
> (RUN0, ... RUN3) could be mapped to regulator modes
> REGULATOR_MODE_FAST, REGULATOR_MODE_NORMAL, REGULATOR_MODE_IDLE and
> REGULATOR_MODE_STANDBY. But regulators which are controlled by these

That doesn't make sense at all, the modes affect the quality of
regulation not the voltage that is set.

> run-levels, can't be individually controlled. If state for one is
> changed, the state is changed for all of them. The DVS bucks 1,2,6 and

We don't really have anything that'd only work for group configuration
except for the suspend modes.

> > Ah, that's actually better. It opens up possiblities for making use
> > of
> > the feature without encoding voltages in DT. For example, you can
> > cache
> > the last however many voltages that were set and jump quickly to them
> > or
> > do something like put the top of the constraints in to help with
> > governors like ondemand. I'd recommend trying for something like
> > that
> > rather than encoding in DT, it'll probably be more robust with things
> > like cpufreq changing.

> I wish I was working with the full product so that I could see and
> learn a proper example on how the cpufreq actually uses these
> interfaces :) I'd really like to understand this much better. Maybe
> this could be a topic for you to present in some Linux conference ;)
> Just please ping me when you are doing that and I'll be listening there
> for sure ;)

The cpufreq code is all there in kernel - drivers/cpufreq. I can't
remember if Android still has a custom governor in their trees but it
doesn't really make much difference in terms of how it interacts with
the regulator drivers.

> Anyways, my idea was to set the inital voltage values for these states
> via DT - but allow the voltages to be changed at run-time too (I guess
> this idea is visible in the patch 12).

It'd be much better if you could avoid putting the voltages in the
binding if they're not strictly required.


Attachments:
(No filename) (2.69 kB)
signature.asc (499.00 B)
Download all attachments

2019-12-02 08:01:32

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

Hello Mark!

On Fri, 2019-11-29 at 12:09 +0000, Mark Brown wrote:
> On Fri, Nov 29, 2019 at 07:48:13AM +0000, Vaittinen, Matti wrote:
> > On Tue, 2019-11-19 at 19:36 +0000, Mark Brown wrote:
> > > The driver interface was added in "regulator: add PM suspend and
> > > resume
> > > hooks".
> > I looked through the set but didn't spot any new interface towards
> > the
> > regulator driver (which accesses the HW). I saw interface towards
> > regulator consumer driver which can be used to set the constrains
> > though.
>
> The regulator driver has a bunch fo set_suspend_ operations.

Hmm. I saw these. But unless I am mistaken linux only knows one
'suspend' state whereas the PMIC has a few separate states I can see as
'suspend' states. As far as I understood the set_suspend_voltage does
not allow setting separate suspend voltages depending on the "type of
suspend" (as there is only one 'suspend' state).

For example, from CPU point of view the BD71828 PMIC states HIBERNATE
and LPSR are probably both just "suspend" - but the PMIC could set
different voltages or ON/OFF states for some regulators depending on
the 'suspend' target (LPSR or HIBERNATE or STANDBY).

Yet, as I said, I haven't seen how these states are used by real
devices - we don't currently have any in-tree SoCs which use BD71828
and utilize these states (as far as I know). Hence I can't really
figure out how to add support for these PMIC features if there is no
'de-facto' mechanism in place :(
>
> > Specifically, I don't see voltage setting callback for different
> > run-
> > modes. Nor do I see voltage setting (or differentiation) of more
> > than
> > one suspend state.
>
> set_suspend_voltage.

Yes. But this does only allow setting one suspend voltage for
regulator, not own voltage for HIBERNATE, LPSR, STANDBY etc - or am I
mistaken?

> > To explain it further - my assumption is that the BD71828 'run-
> > levels'
> > (RUN0, ... RUN3) could be mapped to regulator modes
> > REGULATOR_MODE_FAST, REGULATOR_MODE_NORMAL, REGULATOR_MODE_IDLE
> > and
> > REGULATOR_MODE_STANDBY. But regulators which are controlled by
> > these
>
> That doesn't make sense at all, the modes affect the quality of
> regulation not the voltage that is set.

Thanks. I misunderstood this. I thought these states could be used for
some adaptive voltages. My understanding is that the RUN0,...RUN3 are
designed for that - but I didn't know if regulator framework is
designed for this.

> > run-levels, can't be individually controlled. If state for one is
> > changed, the state is changed for all of them. The DVS bucks 1,2,6
> > and
>
> We don't really have anything that'd only work for group
> configuration
> except for the suspend modes.

Thanks. As I said, I thought the 'quality of regulation' states could
have been supporting also changing the voltages.

>
> > > Ah, that's actually better. It opens up possiblities for making
> > > use
> > > of
> > > the feature without encoding voltages in DT. For example, you
> > > can
> > > cache
> > > the last however many voltages that were set and jump quickly to
> > > them
> > > or
> > > do something like put the top of the constraints in to help with
> > > governors like ondemand. I'd recommend trying for something like
> > > that
> > > rather than encoding in DT, it'll probably be more robust with
> > > things
> > > like cpufreq changing.
> > I wish I was working with the full product so that I could see and
> > learn a proper example on how the cpufreq actually uses these
> > interfaces :) I'd really like to understand this much better. Maybe
> > this could be a topic for you to present in some Linux conference
> > ;)
> > Just please ping me when you are doing that and I'll be listening
> > there
> > for sure ;)
>
> The cpufreq code is all there in kernel - drivers/cpufreq. I can't
> remember if Android still has a custom governor in their trees but it
> doesn't really make much difference in terms of how it interacts with
> the regulator drivers.

Right. I guess your answers mean that there is no "regulator group
control" for "adaptive voltage changes" supported by regulator
framework / cpufreq - as of now. Furthermore, I have understood that it
is different story depending on PMIC and CPU/SoC capabilities. (Like
PMIC inbuilt states and state change mechanisms, SoC voltage scaling
support etc.)

> Anyways, my idea was to set the inital voltage values for these
> states
> > via DT - but allow the voltages to be changed at run-time too (I
> > guess
> > this idea is visible in the patch 12).
>
> It'd be much better if you could avoid putting the voltages in the
> binding if they're not strictly required.

Hmm.. I guess I can omit that from in-tree driver for now. I can try
maintaining own set of patches for existing customers for now. I think
adding these to bindings later is not impossible :) Removing them would
probably be harder.

Thanks again Mark. I truly appreciate your help and guidance :)

Br,
Matti


2019-12-02 13:14:23

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

On Mon, Dec 02, 2019 at 07:57:13AM +0000, Vaittinen, Matti wrote:
> On Fri, 2019-11-29 at 12:09 +0000, Mark Brown wrote:

> > The regulator driver has a bunch fo set_suspend_ operations.

> Hmm. I saw these. But unless I am mistaken linux only knows one
> 'suspend' state whereas the PMIC has a few separate states I can see as
> 'suspend' states. As far as I understood the set_suspend_voltage does
> not allow setting separate suspend voltages depending on the "type of
> suspend" (as there is only one 'suspend' state).

No, look at the bindings - we support a bunch of different
suspend states matching the different suspend states that the
kernel as a whole supports. We don't assume that the device will
know this but you can always use the current suspend we're going
for to decide where to update.

> > > (RUN0, ... RUN3) could be mapped to regulator modes
> > > REGULATOR_MODE_FAST, REGULATOR_MODE_NORMAL, REGULATOR_MODE_IDLE
> > > and
> > > REGULATOR_MODE_STANDBY. But regulators which are controlled by
> > > these

> > That doesn't make sense at all, the modes affect the quality of
> > regulation not the voltage that is set.

> Thanks. I misunderstood this. I thought these states could be used for
> some adaptive voltages. My understanding is that the RUN0,...RUN3 are
> designed for that - but I didn't know if regulator framework is
> designed for this.

The framework doesn't care how a device is controlled, that's up
to the device. Like I said I recommend figuring out what
voltages are useful to have quick access to at runtime, for
example it's likely that it's good to have quick access to the
highest voltage that's been set (and/or the top of the
constraints).

> > The cpufreq code is all there in kernel - drivers/cpufreq. I can't
> > remember if Android still has a custom governor in their trees but it
> > doesn't really make much difference in terms of how it interacts with
> > the regulator drivers.

> Right. I guess your answers mean that there is no "regulator group
> control" for "adaptive voltage changes" supported by regulator

I can't parse the above, sorry. What is "regulator group
control"?


Attachments:
(No filename) (2.14 kB)
signature.asc (499.00 B)
Download all attachments

2019-12-02 14:08:57

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings


On Mon, 2019-12-02 at 13:11 +0000, Mark Brown wrote:
> On Mon, Dec 02, 2019 at 07:57:13AM +0000, Vaittinen, Matti wrote:
> > On Fri, 2019-11-29 at 12:09 +0000, Mark Brown wrote:
> > > The regulator driver has a bunch fo set_suspend_ operations.
> > Hmm. I saw these. But unless I am mistaken linux only knows one
> > 'suspend' state whereas the PMIC has a few separate states I can
> > see as
> > 'suspend' states. As far as I understood the set_suspend_voltage
> > does
> > not allow setting separate suspend voltages depending on the "type
> > of
> > suspend" (as there is only one 'suspend' state).
>
> No, look at the bindings - we support a bunch of different
> suspend states matching the different suspend states that the
> kernel as a whole supports. We don't assume that the device will
> know this but you can always use the current suspend we're going
> for to decide where to update.

Hm. So if I understand this correctly, you mean user should set the
suspend 'target' - and then call the set_suspend_voltage for this
state. To set voltages for all states one should do loop

get_current_mode()

for_all_modes() {
set_mode()
set_voltage()
}

restore_original_mode()

am I on a right track? I'll try to see if I can find some examples of
this - thanks.

>
> > > > (RUN0, ... RUN3) could be mapped to regulator modes
> > > > REGULATOR_MODE_FAST, REGULATOR_MODE_NORMAL, REGULATOR_MODE_IDLE
> > > > and
> > > > REGULATOR_MODE_STANDBY. But regulators which are controlled by
> > > > these
> > > That doesn't make sense at all, the modes affect the quality of
> > > regulation not the voltage that is set.
> > Thanks. I misunderstood this. I thought these states could be used
> > for
> > some adaptive voltages. My understanding is that the RUN0,...RUN3
> > are
> > designed for that - but I didn't know if regulator framework is
> > designed for this.
>
> The framework doesn't care how a device is controlled, that's up
> to the device. Like I said I recommend figuring out what
> voltages are useful to have quick access to at runtime, for
> example it's likely that it's good to have quick access to the
> highest voltage that's been set (and/or the top of the
> constraints).

Problem is that the run-level controlled regulator can't be
individually controlled (unless it is only regulator in the group). I
misunderstood these REGULATOR_MODE_FAST, ...,REGULATOR_MODE_STANDBY to
be global 'states' rather than states of individual regulators. And I
thought these were also designed for voltage scaling. But as I said, I
misunderstood them - so thanks for correcting me on this.

>
> > > The cpufreq code is all there in kernel - drivers/cpufreq. I
> > > can't
> > > remember if Android still has a custom governor in their trees
> > > but it
> > > doesn't really make much difference in terms of how it interacts
> > > with
> > > the regulator drivers.
> > Right. I guess your answers mean that there is no "regulator group
> > control" for "adaptive voltage changes" supported by regulator
>
> I can't parse the above, sorry. What is "regulator group
> control"?

I mean bundling the regulators in a group - and changing state for all
of the bundled regulators in one go. The thing I mentioned earlier -
and I guess you did already confirm it's not doable. I think you said
that only 'mass operation' or 'group operation' is the suspend.

But just to confirm, I meant for example assigning bucks 1,2,6 and 7
into a group which 'state' is changed via GPIO line. Say 'states' are
RUN0, RUN1. For each of these bucks we can define a voltage and
enable/disable status which is to be used on RUN0, and another
voltage/state tuple for RUN1.

When certain 'trigger' is detected (I assume CPU load here and adaptive
voltage scaling - but this is just my assumption of the use-case for
now) the PMIC state can be quickly changed via this GPIO toggle.

In realty, we have two GPIOs and 4 states - but that does not change
the princible. I don't think there is any 'de-facto' mechanism to
control such groups.


Br,
Matti Vaittinen

2019-12-04 12:48:24

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

On Mon, Dec 02, 2019 at 02:02:41PM +0000, Vaittinen, Matti wrote:
> On Mon, 2019-12-02 at 13:11 +0000, Mark Brown wrote:

> > No, look at the bindings - we support a bunch of different
> > suspend states matching the different suspend states that the
> > kernel as a whole supports. We don't assume that the device will
> > know this but you can always use the current suspend we're going
> > for to decide where to update.

> Hm. So if I understand this correctly, you mean user should set the
> suspend 'target' - and then call the set_suspend_voltage for this
> state. To set voltages for all states one should do loop

The general idea is that we set the suspend state during the
process of suspending rather than in advance - that way when the
hardware doesn't understand different types of suspsend things
work fine.

> get_current_mode()

> for_all_modes() {
> set_mode()
> set_voltage()
> }

> restore_original_mode()

> am I on a right track? I'll try to see if I can find some examples of
> this - thanks.

I don't understand the save and restore of mode? If setting the
suspend configuration affects the runtime state then the hardware
doesn't support suspend configuration.

> > The framework doesn't care how a device is controlled, that's up
> > to the device. Like I said I recommend figuring out what
> > voltages are useful to have quick access to at runtime, for
> > example it's likely that it's good to have quick access to the
> > highest voltage that's been set (and/or the top of the
> > constraints).

> Problem is that the run-level controlled regulator can't be
> individually controlled (unless it is only regulator in the group). I

Regulators that have to be controlled en masse aren't really
supported by the API, it only understands regulators that are
individually controllable.

> But just to confirm, I meant for example assigning bucks 1,2,6 and 7
> into a group which 'state' is changed via GPIO line. Say 'states' are
> RUN0, RUN1. For each of these bucks we can define a voltage and
> enable/disable status which is to be used on RUN0, and another
> voltage/state tuple for RUN1.

So you could also just create a group consisting of a single
regulator? That would be fine for the API.


Attachments:
(No filename) (2.23 kB)
signature.asc (499.00 B)
Download all attachments

2019-12-04 13:13:58

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings


On Wed, 2019-12-04 at 12:47 +0000, Mark Brown wrote:
> On Mon, Dec 02, 2019 at 02:02:41PM +0000, Vaittinen, Matti wrote:
> > On Mon, 2019-12-02 at 13:11 +0000, Mark Brown wrote:
> > > No, look at the bindings - we support a bunch of different
> > > suspend states matching the different suspend states that the
> > > kernel as a whole supports. We don't assume that the device will
> > > know this but you can always use the current suspend we're going
> > > for to decide where to update.
> > Hm. So if I understand this correctly, you mean user should set the
> > suspend 'target' - and then call the set_suspend_voltage for this
> > state. To set voltages for all states one should do loop
>
> The general idea is that we set the suspend state during the
> process of suspending rather than in advance - that way when the
> hardware doesn't understand different types of suspsend things
> work fine.

Ok. So voltage for the specific suspend state is set just before going
to suspend - when the target suspend state is already selected. Makes
sense. Thanks.

> > get_current_mode()
> > for_all_modes() {
> > set_mode()
> > set_voltage()
> > }
> > restore_original_mode()
> > am I on a right track? I'll try to see if I can find some examples
> > of
> > this - thanks.
>
> I don't understand the save and restore of mode?

I was thinking that there is some 'cahced suspend target state' in
framework. This get mode is pseudo-code for getting the suspend mode
from framework - "restore mode" is returning back the "original mode"
after voltages for all modes are set. But please ignore this - I think
I captured your message already :)

> If setting the
> suspend configuration affects the runtime state then the hardware
> doesn't support suspend configuration.

No, it does not. I probably explained this badly.

>
> > > The framework doesn't care how a device is controlled, that's up
> > > to the device. Like I said I recommend figuring out what
> > > voltages are useful to have quick access to at runtime, for
> > > example it's likely that it's good to have quick access to the
> > > highest voltage that's been set (and/or the top of the
> > > constraints).
> > Problem is that the run-level controlled regulator can't be
> > individually controlled (unless it is only regulator in the group).
> > I
>
> Regulators that have to be controlled en masse aren't really
> supported by the API, it only understands regulators that are
> individually controllable.

Thanks. This was the piece of information I wanted and assumed. Thus
the patch 12 did introduce new in-kernel APIs - but as I said, we don't
have any in-tree socs using BD71828 RUN0, ..., RUN3 states for now so
I'll drop the patch 12 and leave only the basic support for
individually controllable regulators.

> > But just to confirm, I meant for example assigning bucks 1,2,6 and
> > 7
> > into a group which 'state' is changed via GPIO line. Say 'states'
> > are
> > RUN0, RUN1. For each of these bucks we can define a voltage and
> > enable/disable status which is to be used on RUN0, and another
> > voltage/state tuple for RUN1.
>
> So you could also just create a group consisting of a single
> regulator? That would be fine for the API.

I think I once again explained myself badly. There can be only one
group with 4 RUN states selected by combination of 2 GPIO lines. bucks
1,2,6 and 7 can each either be assigned into this one group or
controlled individually via I2C. But I doubt assigning only one of the
bucks in this group is the typical use-case. What we would need would
really be the 'en-masse' control - either via GPIO or I2C - but I don't
want to suggest any framework expansion as I don't have proper in-tree
use-case for this as of now :) So I'll forget this for now (and thanks
for all the help!) and drop the patch 12 from series. I'll see if I can
send updated series tomorrow - or latest early next week :)

Br,
Matti Vaittinen

2019-12-04 14:15:43

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

On Wed, Dec 04, 2019 at 01:13:08PM +0000, Vaittinen, Matti wrote:

> I think I once again explained myself badly. There can be only one
> group with 4 RUN states selected by combination of 2 GPIO lines. bucks
> 1,2,6 and 7 can each either be assigned into this one group or
> controlled individually via I2C. But I doubt assigning only one of the
> bucks in this group is the typical use-case. What we would need would

I don't think this is as unusual as you're thinking - the
regulators people want to control quickly are usually the main
CPU supply regulators and these often vary independently of
anything else.


Attachments:
(No filename) (628.00 B)
signature.asc (499.00 B)
Download all attachments

2019-12-10 10:40:49

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings


On Wed, 2019-12-04 at 14:14 +0000, Mark Brown wrote:
> On Wed, Dec 04, 2019 at 01:13:08PM +0000, Vaittinen, Matti wrote:
>
> > I think I once again explained myself badly. There can be only one
> > group with 4 RUN states selected by combination of 2 GPIO lines.
> > bucks
> > 1,2,6 and 7 can each either be assigned into this one group or
> > controlled individually via I2C. But I doubt assigning only one of
> > the
> > bucks in this group is the typical use-case. What we would need
> > would
>
> I don't think this is as unusual as you're thinking - the
> regulators people want to control quickly are usually the main
> CPU supply regulators and these often vary independently of
> anything else.

Hmm. I see your point. You might be correct. Allowing only one buck to
be assigned in 'run-level group' (to be controlled by GPIOs) would be
totally possible with current regulator API - and it might be useful
for scaling the CPU voltage. I appreciate your help and experience here
:) Implementing it would be also pretty simple, caching and controlling
the run-level voltages is already there in patch 12, I just should
restrict the group size to one buck. I will see how it works and also
ask if my colleagues know whether this is valuable with our current
customer's SOCs. Thanks a bunch!

Br,
Matti Vaittinen

2019-12-10 11:16:42

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

Hello Mark,

On Fri, 2019-11-29 at 12:09 +0000, Mark Brown wrote:
> On Fri, Nov 29, 2019 at 07:48:13AM +0000, Vaittinen, Matti wrote:
> > On Tue, 2019-11-19 at 19:36 +0000, Mark Brown wrote:
> The cpufreq code is all there in kernel - drivers/cpufreq. I can't
> remember if Android still has a custom governor in their trees but it
> doesn't really make much difference in terms of how it interacts with
> the regulator drivers.
>
> > Anyways, my idea was to set the inital voltage values for these
> > states
> > via DT - but allow the voltages to be changed at run-time too (I
> > guess
> > this idea is visible in the patch 12).
>
> It'd be much better if you could avoid putting the voltages in the
> binding if they're not strictly required.

You suggested in the other mail that it might be worth making a run-
level 'group' consisting only one buck for fast voltage changes via
GPIOs. So I am back to adding the run-level support to the BD71828
driver. Which lead me back to this.

The PMIC supports wide range of voltages for these DVS bucks - but only
4 of these voltages can be selected to be switched fast via GPIO line
states. Eg, in HW level we can set RUN0 voltage (selected when both
GPIO lines are LOW) to one register. RUN1 voltage (selected when one
GPIO is high, other low) to second register. Same for RUN2 and RUN3
voltages.

I could make this so that initially there is the HW default voltages
are read up by driver and cached to be used for each level. When new
voltage is requested by the consumer, correct RUN level is selected or
if matching voltage is not set to any RUN level, then it is written to
one of the run level registers (and cache).

Problem is that if no default voltages are given from DT, the the first
voltage changes are likely to be slow (require register access - I
guess the HW defaults are not working for many use-cases) - which may
be undesirable.

So I still think the DT bindings for setting these initial values might
be the most convenient way if we are not adding custom API for this.
Hence I plan to keep this binding. Please let me know if you have
better ideas or if this is absolutely a no go.

Br,
Matti Vaittinen

2019-12-10 12:12:21

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

On Tue, Dec 10, 2019 at 11:14:48AM +0000, Vaittinen, Matti wrote:

> Problem is that if no default voltages are given from DT, the the first
> voltage changes are likely to be slow (require register access - I
> guess the HW defaults are not working for many use-cases) - which may
> be undesirable.

I don't think that's likely to be a practical problem, and it's not
likely it'd be worse than always doing writes. A lot of things are
slower the first time you do them and you're still going to have to
do the writes no matter what.


Attachments:
(No filename) (546.00 B)
signature.asc (499.00 B)
Download all attachments

2019-12-10 12:42:43

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

Hello Mark,

On Tue, 2019-12-10 at 12:11 +0000, Mark Brown wrote:
> On Tue, Dec 10, 2019 at 11:14:48AM +0000, Vaittinen, Matti wrote:
>
> > Problem is that if no default voltages are given from DT, the the
> > first
> > voltage changes are likely to be slow (require register access - I
> > guess the HW defaults are not working for many use-cases) - which
> > may
> > be undesirable.
>
> I don't think that's likely to be a practical problem, and it's not
> likely it'd be worse than always doing writes. A lot of things are
> slower the first time you do them and you're still going to have to
> do the writes no matter what.

The thing is that if we do initial setting of voltages (based on
binding data) we can set the voltages to registers before we switch to
that run-level. If we don't do initial setting then we will only do
setting when voltage change is actually requested - which may be too
late. (I actually heard somewhere that there is 40 uS time limit - but
I don't see how this is counted. Starting from what? - and I don't see
how this is guaranteed even with GPIO if interrupts are to be served).

OTOH, I also heard that the SoC probably require at least two of the
bucks to be controlled as a group (bucks connected to SRAM and CORE) -
probably all four.

So, I am again wondering if I should just upstream the basic control
with I2C for SoCs which do not require fast DVS voltage changes and
perhaps maintain/provide own set of patches with additional interface
for run-level control for those customers who require it... Sorry for
being such a difficult guy. Decision making seems to not be my strong
point :/

Br,
Matti Vaittinen

2019-12-10 12:46:27

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings

On Tue, Dec 10, 2019 at 12:41:47PM +0000, Vaittinen, Matti wrote:

> The thing is that if we do initial setting of voltages (based on
> binding data) we can set the voltages to registers before we switch to
> that run-level. If we don't do initial setting then we will only do
> setting when voltage change is actually requested - which may be too
> late. (I actually heard somewhere that there is 40 uS time limit - but
> I don't see how this is counted. Starting from what? - and I don't see
> how this is guaranteed even with GPIO if interrupts are to be served).

I suspect that if that limit is a real thing it's from some runtime
performance metrics where people are doing benchmarking to verify that
everything is working fine rather than an absolute thing that is a basic
requirement for operation.

> So, I am again wondering if I should just upstream the basic control
> with I2C for SoCs which do not require fast DVS voltage changes and
> perhaps maintain/provide own set of patches with additional interface
> for run-level control for those customers who require it... Sorry for
> being such a difficult guy. Decision making seems to not be my strong
> point :/

Yes, definitely submit the basic stuff separately - the GPIO changes can
be reviewed as a separate, incremental patch.


Attachments:
(No filename) (1.29 kB)
signature.asc (499.00 B)
Download all attachments

2019-12-10 13:08:29

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/16] dt-bindings: regulator: Document ROHM BD71282 regulator bindings


On Tue, 2019-12-10 at 12:45 +0000, Mark Brown wrote:
> On Tue, Dec 10, 2019 at 12:41:47PM +0000, Vaittinen, Matti wrote:
>
> > The thing is that if we do initial setting of voltages (based on
> > binding data) we can set the voltages to registers before we switch
> > to
> > that run-level. If we don't do initial setting then we will only do
> > setting when voltage change is actually requested - which may be
> > too
> > late. (I actually heard somewhere that there is 40 uS time limit -
> > but
> > I don't see how this is counted. Starting from what? - and I don't
> > see
> > how this is guaranteed even with GPIO if interrupts are to be
> > served).
>
> I suspect that if that limit is a real thing it's from some runtime
> performance metrics where people are doing benchmarking to verify
> that
> everything is working fine rather than an absolute thing that is a
> basic
> requirement for operation.
>
> > So, I am again wondering if I should just upstream the basic
> > control
> > with I2C for SoCs which do not require fast DVS voltage changes and
> > perhaps maintain/provide own set of patches with additional
> > interface
> > for run-level control for those customers who require it... Sorry
> > for
> > being such a difficult guy. Decision making seems to not be my
> > strong
> > point :/
>
> Yes, definitely submit the basic stuff separately - the GPIO changes
> can
> be reviewed as a separate, incremental patch.

Right. That was rationale behind splitting the regulator support in two
patches. I was just unsure if I should add all the DT bindings already
here. Well, I guess I will drop the run-level ones for now. Adding new
bindings later might not be as hard as removing them. Thanks for
support! Discussing this with someone is definitely helpful :)

Br,
Matti

2019-12-10 13:26:14

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH v5 13/16] rtc: bd70528 add BD71828 support

Hi,

On 18/11/2019 09:00:47+0200, Matti Vaittinen wrote:
> @@ -468,26 +596,35 @@ 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;
> + }
> }
>
> return rtc_register_device(rtc);
> }

Missing blank line here.

> +static const struct platform_device_id bd718x7_rtc_id[] = {
> + { "bd70528-rtc", ROHM_CHIP_TYPE_BD70528 },
> + { "bd71828-rtc", ROHM_CHIP_TYPE_BD71828 },
> + { },
> +};
> +MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id);
>

Else, Acked-by: Alexandre Belloni <[email protected]>

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