From: pascal paillet <[email protected]>
The goal of this patch-set is to propose a driver for the STPMIC1 PMIC from
STMicroelectronics.
The STPMIC1 regulators supply power to an application processor as well as
to external system peripherals such as DDR, Flash memories and system
devices. It also features onkey button input and an hardware watchdog.
The STPMIC1 is controlled via I2C.
Main driver is drivers/mfd/stpmic1 that handle I2C regmap configuration and
irqchip. stpmic1_regulator, stpmic1_onkey and stpmic1_wdt need stpmic1 mfd
as parent.
stpmic1 mfd and regulator drivers maybe mandatory at boot time.
changes in v2:
* the hardware component has been renamed from stpmu1 to stpmic1 !
* change headers
* handles remarks from the reviews.
pascal paillet (8):
dt-bindings: mfd: document stpmic1
mfd: stpmic1: add stpmic1 driver
dt-bindings: regulator: document stpmic1 pmic regulators
regulator: stpmic1: add stpmic1 regulator driver
dt-bindings: input: document stpmic1 pmic onkey
input: stpmic1: add stpmic1 onkey driver
dt-bindings: watchdog: document stpmic1 pmic watchdog
watchdog: stpmic1: add stpmic1 watchdog driver
.../devicetree/bindings/input/st,stpmic1-onkey.txt | 31 +
.../devicetree/bindings/mfd/st,stpmic1.txt | 137 ++++
.../bindings/regulator/st,stpmic1-regulator.txt | 68 ++
.../bindings/watchdog/st,stpmic1-wdt.txt | 11 +
drivers/input/misc/Kconfig | 11 +
drivers/input/misc/Makefile | 2 +
drivers/input/misc/stpmic1_onkey.c | 257 ++++++++
drivers/mfd/Kconfig | 13 +
drivers/mfd/Makefile | 1 +
drivers/mfd/stpmic1.c | 457 ++++++++++++++
drivers/regulator/Kconfig | 12 +
drivers/regulator/Makefile | 1 +
drivers/regulator/stpmic1_regulator.c | 692 +++++++++++++++++++++
drivers/watchdog/Kconfig | 12 +
drivers/watchdog/Makefile | 1 +
drivers/watchdog/stpmic1_wdt.c | 130 ++++
include/dt-bindings/mfd/st,stpmic1.h | 46 ++
include/linux/mfd/stpmic1.h | 220 +++++++
18 files changed, 2102 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt
create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmic1.txt
create mode 100644 Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt
create mode 100644 Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
create mode 100644 drivers/input/misc/stpmic1_onkey.c
create mode 100644 drivers/mfd/stpmic1.c
create mode 100644 drivers/regulator/stpmic1_regulator.c
create mode 100644 drivers/watchdog/stpmic1_wdt.c
create mode 100644 include/dt-bindings/mfd/st,stpmic1.h
create mode 100644 include/linux/mfd/stpmic1.h
--
1.9.1
From: pascal paillet <[email protected]>
The stpmic1 PMIC embeds a watchdog which is disabled by default.
In case of watchdog, the PMIC goes off.
Signed-off-by: pascal paillet <[email protected]>
---
Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
diff --git a/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt b/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
new file mode 100644
index 0000000..7cc1407
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
@@ -0,0 +1,11 @@
+STMicroelectronics STPMIC1 Watchdog
+
+Required properties:
+
+- compatible : should be "st,stpmic1-wdt"
+
+Example:
+
+watchdog {
+ compatible = "st,stpmic1-wdt";
+};
--
1.9.1
From: pascal paillet <[email protected]>
The stpmic1 PMIC embeds a watchdog which is disabled by default. As soon
as the watchdog is started, it must be refreshed periodically otherwise
the PMIC goes off.
Signed-off-by: pascal paillet <[email protected]>
---
changes in v2:
* the hardware component has been renamed from stpmu1 to stpmic1 !
* change headers
* handle remarks from Guenter
* fix set timeout
drivers/watchdog/Kconfig | 12 ++++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/stpmic1_wdt.c | 130 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 143 insertions(+)
create mode 100644 drivers/watchdog/stpmic1_wdt.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9af07fd..11c5937 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -796,6 +796,18 @@ config STM32_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called stm32_iwdg.
+config STPMIC1_WATCHDOG
+ tristate "STPMIC1 PMIC watchdog support"
+ depends on MFD_STPMIC1
+ select WATCHDOG_CORE
+ help
+ Say Y here to include watchdog support embedded into STPMIC1 PMIC.
+ If the watchdog timer expires, stpmic1 shut-down all its power
+ supplies.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spmic1_wdt.
+
config UNIPHIER_WATCHDOG
tristate "UniPhier watchdog support"
depends on ARCH_UNIPHIER || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 1d3c6b0..a898d8f 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -216,3 +216,4 @@ obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
+obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c
new file mode 100644
index 0000000..391e544
--- /dev/null
+++ b/drivers/watchdog/stpmic1_wdt.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <[email protected]> for STMicroelectronics.
+
+#include <linux/kernel.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/watchdog.h>
+
+/* WATCHDOG CONTROL REGISTER bit */
+#define WDT_START BIT(0)
+#define WDT_PING BIT(1)
+#define WDT_START_MASK BIT(0)
+#define WDT_PING_MASK BIT(1)
+
+#define PMIC_WDT_MIN_TIMEOUT 1
+#define PMIC_WDT_MAX_TIMEOUT 256
+
+struct stpmic1_wdt {
+ struct stpmic1_dev *pmic;
+ struct watchdog_device wdtdev;
+};
+
+static int pmic_wdt_start(struct watchdog_device *wdd)
+{
+ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ return regmap_update_bits(wdt->pmic->regmap,
+ WCHDG_CR, WDT_START_MASK, WDT_START);
+}
+
+static int pmic_wdt_stop(struct watchdog_device *wdd)
+{
+ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ return regmap_update_bits(wdt->pmic->regmap,
+ WCHDG_CR, WDT_START_MASK, ~WDT_START);
+}
+
+static int pmic_wdt_ping(struct watchdog_device *wdd)
+{
+ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ return regmap_update_bits(wdt->pmic->regmap,
+ WCHDG_CR, WDT_PING_MASK, WDT_PING);
+}
+
+static int pmic_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1);
+}
+
+static const struct watchdog_info pmic_watchdog_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "STPMIC1 PMIC Watchdog",
+};
+
+static const struct watchdog_ops pmic_watchdog_ops = {
+ .owner = THIS_MODULE,
+ .start = pmic_wdt_start,
+ .stop = pmic_wdt_stop,
+ .ping = pmic_wdt_ping,
+ .set_timeout = pmic_wdt_set_timeout,
+};
+
+static int pmic_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct stpmic1_dev *pmic;
+ struct stpmic1_wdt *wdt;
+
+ if (!pdev->dev.parent)
+ return -EINVAL;
+
+ pmic = dev_get_drvdata(pdev->dev.parent);
+ if (!pmic)
+ return -EINVAL;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(struct stpmic1_wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->pmic = pmic;
+
+ wdt->wdtdev.info = &pmic_watchdog_info;
+ wdt->wdtdev.ops = &pmic_watchdog_ops;
+ wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT;
+ wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT;
+ wdt->wdtdev.timeout = PMIC_WDT_MIN_TIMEOUT;
+
+ wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
+
+ watchdog_set_drvdata(&wdt->wdtdev, wdt);
+ dev_set_drvdata(&pdev->dev, wdt);
+
+ ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
+ if (ret)
+ return ret;
+
+ dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n");
+ return 0;
+}
+
+static const struct of_device_id of_pmic_wdt_match[] = {
+ { .compatible = "st,stpmic1-wdt" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, of_pmic_wdt_match);
+
+static struct platform_driver stpmic1_wdt_driver = {
+ .probe = pmic_wdt_probe,
+ .driver = {
+ .name = "stpmic1-wdt",
+ .of_match_table = of_pmic_wdt_match,
+ },
+};
+module_platform_driver(stpmic1_wdt_driver);
+
+MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device");
+MODULE_AUTHOR("Pascal Paillet");
+MODULE_LICENSE("GPL v2");
--
1.9.1
From: pascal paillet <[email protected]>
The STPMIC1 regulators supply power to the application processor as well as
to the external system peripherals such as DDR, Flash memories and system
devices.
Signed-off-by: pascal paillet <[email protected]>
---
changes in v2:
* the hardware component has been renamed from stpmu1 to stpmic1 !
* replace _ with - in properties name
* remove deprecated regulator-compatible from the example
Rob, the st,mask_reset name is taken from the data-sheet. I have modified the
description. I hope there is no confusion with bit masking know.
.../bindings/regulator/st,stpmic1-regulator.txt | 68 ++++++++++++++++++++++
1 file changed, 68 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt
diff --git a/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt b/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt
new file mode 100644
index 0000000..a3f4762
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt
@@ -0,0 +1,68 @@
+STMicroelectronics STPMIC1 Voltage regulators
+
+Regulator Nodes are optional depending on needs.
+
+Available Regulators in STPMIC1 device are:
+ - buck1 for Buck BUCK1
+ - buck2 for Buck BUCK2
+ - buck3 for Buck BUCK3
+ - buck4 for Buck BUCK4
+ - ldo1 for LDO LDO1
+ - ldo2 for LDO LDO2
+ - ldo3 for LDO LDO3
+ - ldo4 for LDO LDO4
+ - ldo5 for LDO LDO5
+ - ldo6 for LDO LDO6
+ - vref_ddr for LDO Vref DDR
+ - boost for Buck BOOST
+ - pwr_sw1 for VBUS_OTG switch
+ - pwr_sw2 for SW_OUT switch
+
+Switches are fixed voltage regulators with only enable/disable capability.
+
+Optional properties:
+- st,mask-reset: mask reset for this regulator: the regulator configuration
+ is maintained during pmic reset.
+- regulator-pull-down: enable high pull down
+ if not specified light pull down is used
+- regulator-over-current-protection:
+ if set, all regulators are switched off in case of over-current detection
+ on this regulator,
+ if not set, the driver only sends an over-current event.
+- interrupt-parent: phandle to the parent interrupt controller
+- interrupts: index of current limit detection interrupt
+- <regulator>-supply: phandle to the parent supply/regulator node
+ each regulator supply can be described except vref_ddr.
+
+Example:
+regulators {
+ compatible = "st,stpmic1-regulators";
+
+ ldo6-supply = <&v3v3>;
+
+ vdd_core: buck1 {
+ regulator-name = "vdd_core";
+ interrupts = <IT_CURLIM_BUCK1 0>;
+ interrupt-parent = <&pmic>;
+ st,mask-reset;
+ regulator-pull-down;
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <1200000>;
+ };
+
+ v3v3: buck4 {
+ regulator-name = "v3v3";
+ interrupts = <IT_CURLIM_BUCK4 0>;
+ interrupt-parent = <&mypmic>;
+
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ v1v8: ldo6 {
+ regulator-name = "v1v8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-over-current-protection;
+ };
+};
--
1.9.1
From: pascal paillet <[email protected]>
stpmic1 is a pmic from STMicroelectronics. The stpmic1 integrates 10
regulators and 3 switches with various capabilities.
Signed-off-by: pascal paillet <[email protected]>
---
changes in v2:
* the hardware component has been renamed from stpmu1 to stpmic1 !
* replace _ with - in properties name
* fix node names in example
* remove regulator compatibles in example
* add st,stpmic1.h to the patch
Rob, I did not change the usage of the properties because it would lead to a lot
of st properties; for example st,main-control-register would be replaced by:
st,power_cycling_on_turn_off
st,pwrctrl_enabled
st,pwrctrl_active_high
should I go this way ?
Rob, I did not found the standard property for st,onkey-press-seconds = <10>;
.../devicetree/bindings/mfd/st,stpmic1.txt | 137 +++++++++++++++++++++
include/dt-bindings/mfd/st,stpmic1.h | 46 +++++++
2 files changed, 183 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmic1.txt
create mode 100644 include/dt-bindings/mfd/st,stpmic1.h
diff --git a/Documentation/devicetree/bindings/mfd/st,stpmic1.txt b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt
new file mode 100644
index 0000000..9f2c516
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt
@@ -0,0 +1,137 @@
+* STMicroelectronics STPMIC1 Power Management IC
+
+Required parent device properties:
+- compatible: "st,stpmic1"
+- reg: the I2C slave address for the stpmic1 chip
+- interrupts-extended: interrupt lines to use: second irq is for wakeup.
+- #interrupt-cells: should be 2.
+- interrupt-controller: describes the STPMIC1 as an interrupt
+ controller (has its own domain). interrupt number are the following:
+ /* Interrupt Register 1 (0x50 for latch) */
+ IT_SWOUT_R=0
+ IT_SWOUT_F=1
+ IT_VBUS_OTG_R=2
+ IT_VBUS_OTG_F=3
+ IT_WAKEUP_R=4
+ IT_WAKEUP_F=5
+ IT_PONKEY_R=6
+ IT_PONKEY_F=7
+ /* Interrupt Register 2 (0x51 for latch) */
+ IT_OVP_BOOST=8
+ IT_OCP_BOOST=9
+ IT_OCP_SWOUT=10
+ IT_OCP_OTG=11
+ IT_CURLIM_BUCK4=12
+ IT_CURLIM_BUCK3=13
+ IT_CURLIM_BUCK2=14
+ IT_CURLIM_BUCK1=15
+ /* Interrupt Register 3 (0x52 for latch) */
+ IT_SHORT_SWOUT=16
+ IT_SHORT_SWOTG=17
+ IT_CURLIM_LDO6=18
+ IT_CURLIM_LDO5=19
+ IT_CURLIM_LDO4=20
+ IT_CURLIM_LDO3=21
+ IT_CURLIM_LDO2=22
+ IT_CURLIM_LDO1=23
+ /* Interrupt Register 3 (0x52 for latch) */
+ IT_SWIN_R=24
+ IT_SWIN_F=25
+ IT_RESERVED_1=26
+ IT_RESERVED_2=27
+ IT_VINLOW_R=28
+ IT_VINLOW_F=29
+ IT_TWARN_R=30
+ IT_TWARN_F=31
+
+Optional parent device properties:
+- st,main-control-register:
+ -bit 1: Power cycling will be performed on turn OFF condition
+ -bit 2: PWRCTRL is functional
+ -bit 3: PWRCTRL active high
+- st,pads-pull-register:
+ -bit 1: WAKEUP pull down is not active
+ -bit 2: PWRCTRL pull up is active
+ -bit 3: PWRCTRL pull down is active
+ -bit 4: WAKEUP detector is disabled
+- st,vin-control-register:
+ -bit 0: VINLOW monitoring is enabled
+ -bit [1...3]: VINLOW rising threshold
+ 000 VINOK_f + 50mV
+ 001 VINOK_f + 100mV
+ 010 VINOK_f + 150mV
+ 011 VINOK_f + 200mV
+ 100 VINOK_f + 250mV
+ 101 VINOK_f + 300mV
+ 110 VINOK_f + 350mV
+ 111 VINOK_f + 400mV
+ -bit [4...5]: VINLOW hyst
+ 00 100mV
+ 01 200mV
+ 10 300mV
+ 11 400mV
+ -bit 6: SW_OUT detector is disabled
+ -bit 7: SW_IN detector is enabled.
+- st,usb-control-register:
+ -bit 3: SW_OUT current limit
+ 0: 600mA
+ 1: 1.1A
+ -bit 4: VBUS_OTG discharge is enabled
+ -bit 5: SW_OUT discharge is enabled
+ -bit 6: VBUS_OTG detection is enabled
+ -bit 7: BOOST_OVP is disabled
+
+
+stpmic1 consists is a varied group of sub-devices:
+
+Device Description
+------ ------------
+st,stpmic1-onkey : On key
+st,stpmic1-regulators : Regulators
+st,stpmic1-wdt : Watchdog
+
+each sub-device bindings is be described in associated driver
+documentation section.
+
+Example:
+
+pmic: stpmic1@33 {
+ compatible = "st,stpmic1";
+ reg = <0x33>;
+ interrupts = <0 2>;
+ interrupts-extended = <&intc GIC_SPI 149 IRQ_TYPE_NONE>,
+ <&exti 55 1>;
+ st,version_status = <0x10>;
+ st,main-control-register=<0x0c>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ onkey {
+ compatible = "st,stpmic1-onkey";
+ interrupt-parent = <&pmic>;
+ interrupts = <IT_PONKEY_F 0>,<IT_PONKEY_R 1>;
+ interrupt-names = "onkey-falling", "onkey-rising";
+ st,onkey-pwroff-enabled;
+ st,onkey-long-press-seconds = <10>;
+ };
+
+ watchdog {
+ compatible = "st,stpmic1-wdt";
+ };
+
+ regulators {
+ compatible = "st,stpmic1-regulators";
+
+ vdd_core: buck1 {
+ regulator-name = "vdd_core";
+ regulator-boot-on;
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <1200000>;
+ };
+ vdd: buck3 {
+ regulator-name = "vdd";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-pull-down;
+ };
+ };
diff --git a/include/dt-bindings/mfd/st,stpmic1.h b/include/dt-bindings/mfd/st,stpmic1.h
new file mode 100644
index 0000000..e32ac8f
--- /dev/null
+++ b/include/dt-bindings/mfd/st,stpmic1.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Philippe Peurichard <[email protected]>,
+ * Pascal Paillet <[email protected]> for STMicroelectronics.
+ */
+
+#ifndef __DT_BINDINGS_STPMIC1_H__
+#define __DT_BINDINGS_STPMIC1_H__
+
+/* IRQ definitions */
+#define IT_PONKEY_F 0
+#define IT_PONKEY_R 1
+#define IT_WAKEUP_F 2
+#define IT_WAKEUP_R 3
+#define IT_VBUS_OTG_F 4
+#define IT_VBUS_OTG_R 5
+#define IT_SWOUT_F 6
+#define IT_SWOUT_R 7
+
+#define IT_CURLIM_BUCK1 8
+#define IT_CURLIM_BUCK2 9
+#define IT_CURLIM_BUCK3 10
+#define IT_CURLIM_BUCK4 11
+#define IT_OCP_OTG 12
+#define IT_OCP_SWOUT 13
+#define IT_OCP_BOOST 14
+#define IT_OVP_BOOST 15
+
+#define IT_CURLIM_LDO1 16
+#define IT_CURLIM_LDO2 17
+#define IT_CURLIM_LDO3 18
+#define IT_CURLIM_LDO4 19
+#define IT_CURLIM_LDO5 20
+#define IT_CURLIM_LDO6 21
+#define IT_SHORT_SWOTG 22
+#define IT_SHORT_SWOUT 23
+
+#define IT_TWARN_F 24
+#define IT_TWARN_R 25
+#define IT_VINLOW_F 26
+#define IT_VINLOW_R 27
+#define IT_SWIN_F 30
+#define IT_SWIN_R 31
+
+#endif /* __DT_BINDINGS_STPMIC1_H__ */
--
1.9.1
From: pascal paillet <[email protected]>
The stpmic1 PMIC embeds several regulators and witches with
different capabilities.
Signed-off-by: pascal paillet <[email protected]>
---
changes in v2:
* the hardware component has been renamed from stpmu1 to stpmic1 !
* change headers
* rework map_mode function
* remove proprietary get voltage functions
* do not override constraints
* register all the regulators even if not described in the dt
* stop resetting registers during probe
* use standard pull down setting
Mark, I did not change the mfd to use mfd_add_devices() yet because during our
last upstream (ST), we had to use devm_of_platform_populate(). I have asked for
confirmation in the mfd review message.
drivers/regulator/Kconfig | 12 +
drivers/regulator/Makefile | 1 +
drivers/regulator/stpmic1_regulator.c | 692 ++++++++++++++++++++++++++++++++++
3 files changed, 705 insertions(+)
create mode 100644 drivers/regulator/stpmic1_regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 5dbccf5..96f0748 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -804,6 +804,18 @@ config REGULATOR_TI_ABB
on TI SoCs may be unstable without enabling this as it provides
device specific optimized bias to allow/optimize functionality.
+config REGULATOR_STPMIC1
+ tristate "STMicroelectronics STPMIC1 PMIC Regulators"
+ depends on MFD_STPMIC1
+ help
+ This driver supports STMicroelectronics STPMIC1 PMIC voltage
+ regulators and switches. The STPMIC1 regulators supply power to
+ an application processor as well as to external system
+ peripherals such as DDR, Flash memories and system devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stpmic1_regulator.
+
config REGULATOR_STW481X_VMMC
bool "STMicroelectronics STW481X VMMC regulator"
depends on MFD_STW481X || COMPILE_TEST
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index bd818ce..716d9ff 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o
obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
+obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o
obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o
obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c
new file mode 100644
index 0000000..3b32d8f
--- /dev/null
+++ b/drivers/regulator/stpmic1_regulator.c
@@ -0,0 +1,692 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <[email protected]> for STMicroelectronics.
+
+#include <linux/interrupt.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/of_irq.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>
+
+/**
+ * stpmic1 regulator description
+ * @desc: regulator framework description
+ * @mask_reset_reg: mask reset register address
+ * @mask_reset_mask: mask rank and mask reset register mask
+ * @icc_reg: icc register address
+ * @icc_mask: icc register mask
+ */
+struct stpmic1_regulator_cfg {
+ struct regulator_desc desc;
+ u8 mask_reset_reg;
+ u8 mask_reset_mask;
+ u8 icc_reg;
+ u8 icc_mask;
+};
+
+/**
+ * stpmic1 regulator data: this structure is used as driver data
+ * @regul_id: regulator id
+ * @reg_node: DT node of regulator (unused on non-DT platforms)
+ * @cfg: stpmic specific regulator description
+ * @mask_reset: mask_reset bit value
+ * @irq_curlim: current limit interrupt number
+ * @regmap: point to parent regmap structure
+ */
+struct stpmic1_regulator {
+ unsigned int regul_id;
+ struct device_node *reg_node;
+ struct stpmic1_regulator_cfg *cfg;
+ u8 mask_reset;
+ int irq_curlim;
+ struct regmap *regmap;
+};
+
+/**
+ * struct stpmic1_device_data - contains all regulators data
+ * @regulator_table : contains all the regulators
+ */
+struct stpmic1_device_data {
+ struct stpmic1_regulator *regulator_table;
+};
+
+static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode);
+static unsigned int stpmic1_get_mode(struct regulator_dev *rdev);
+static int stpmic1_set_icc(struct regulator_dev *rdev);
+static int stpmic1_regulator_parse_dt(void *driver_data);
+static unsigned int stpmic1_map_mode(unsigned int mode);
+
+enum {
+ STPMIC1_BUCK1 = 0,
+ STPMIC1_BUCK2 = 1,
+ STPMIC1_BUCK3 = 2,
+ STPMIC1_BUCK4 = 3,
+ STPMIC1_LDO1 = 4,
+ STPMIC1_LDO2 = 5,
+ STPMIC1_LDO3 = 6,
+ STPMIC1_LDO4 = 7,
+ STPMIC1_LDO5 = 8,
+ STPMIC1_LDO6 = 9,
+ STPMIC1_VREF_DDR = 10,
+ STPMIC1_BOOST = 11,
+ STPMIC1_VBUS_OTG = 12,
+ STPMIC1_SW_OUT = 13,
+};
+
+/* Enable time worst case is 5000mV/(2250uV/uS) */
+#define PMIC_ENABLE_TIME_US 2200
+
+#define STPMIC1_BUCK_MODE_NORMAL 0
+#define STPMIC1_BUCK_MODE_LP BUCK_HPLP_ENABLE_MASK
+
+struct regulator_linear_range buck1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0, 30, 25000),
+ REGULATOR_LINEAR_RANGE(1350000, 31, 63, 0),
+};
+
+struct regulator_linear_range buck2_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1000000, 0, 17, 0),
+ REGULATOR_LINEAR_RANGE(1050000, 18, 19, 0),
+ REGULATOR_LINEAR_RANGE(1100000, 20, 21, 0),
+ REGULATOR_LINEAR_RANGE(1150000, 22, 23, 0),
+ REGULATOR_LINEAR_RANGE(1200000, 24, 25, 0),
+ REGULATOR_LINEAR_RANGE(1250000, 26, 27, 0),
+ REGULATOR_LINEAR_RANGE(1300000, 28, 29, 0),
+ REGULATOR_LINEAR_RANGE(1350000, 30, 31, 0),
+ REGULATOR_LINEAR_RANGE(1400000, 32, 33, 0),
+ REGULATOR_LINEAR_RANGE(1450000, 34, 35, 0),
+ REGULATOR_LINEAR_RANGE(1500000, 36, 63, 0),
+};
+
+struct regulator_linear_range buck3_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1000000, 0, 19, 0),
+ REGULATOR_LINEAR_RANGE(1100000, 20, 23, 0),
+ REGULATOR_LINEAR_RANGE(1200000, 24, 27, 0),
+ REGULATOR_LINEAR_RANGE(1300000, 28, 31, 0),
+ REGULATOR_LINEAR_RANGE(1400000, 32, 35, 0),
+ REGULATOR_LINEAR_RANGE(1500000, 36, 55, 100000),
+ REGULATOR_LINEAR_RANGE(3400000, 56, 63, 0),
+
+};
+
+struct regulator_linear_range buck4_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0, 27, 25000),
+ REGULATOR_LINEAR_RANGE(1300000, 28, 29, 0),
+ REGULATOR_LINEAR_RANGE(1350000, 30, 31, 0),
+ REGULATOR_LINEAR_RANGE(1400000, 32, 33, 0),
+ REGULATOR_LINEAR_RANGE(1450000, 34, 35, 0),
+ REGULATOR_LINEAR_RANGE(1500000, 36, 60, 100000),
+ REGULATOR_LINEAR_RANGE(3900000, 61, 63, 0),
+
+};
+
+struct regulator_linear_range ldo1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
+ REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000),
+ REGULATOR_LINEAR_RANGE(3300000, 25, 31, 0),
+
+};
+
+struct regulator_linear_range ldo2_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
+ REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000),
+ REGULATOR_LINEAR_RANGE(3300000, 25, 30, 0),
+
+};
+
+struct regulator_linear_range ldo3_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
+ REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000),
+ REGULATOR_LINEAR_RANGE(3300000, 25, 30, 0),
+ /* with index 31 LDO3 is in DDR mode */
+ REGULATOR_LINEAR_RANGE(500000, 31, 31, 0),
+};
+
+struct regulator_linear_range ldo5_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
+ REGULATOR_LINEAR_RANGE(1700000, 8, 30, 100000),
+ REGULATOR_LINEAR_RANGE(3900000, 31, 31, 0),
+};
+
+struct regulator_linear_range ldo6_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0, 24, 100000),
+ REGULATOR_LINEAR_RANGE(3300000, 25, 31, 0),
+};
+
+static struct regulator_ops stpmic1_ldo_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_pull_down = regulator_set_pull_down_regmap,
+ .set_over_current_protection = stpmic1_set_icc,
+};
+
+static struct regulator_ops stpmic1_ldo3_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_iterate,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_pull_down = regulator_set_pull_down_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
+ .set_over_current_protection = stpmic1_set_icc,
+};
+
+static struct regulator_ops stpmic1_ldo4_fixed_regul_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .set_pull_down = regulator_set_pull_down_regmap,
+ .set_over_current_protection = stpmic1_set_icc,
+};
+
+static struct regulator_ops stpmic1_buck_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_pull_down = regulator_set_pull_down_regmap,
+ .set_mode = stpmic1_set_mode,
+ .get_mode = stpmic1_get_mode,
+ .set_over_current_protection = stpmic1_set_icc,
+};
+
+static struct regulator_ops stpmic1_vref_ddr_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .set_pull_down = regulator_set_pull_down_regmap,
+};
+
+static struct regulator_ops stpmic1_switch_regul_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .set_over_current_protection = stpmic1_set_icc,
+};
+
+#define REG_LDO(ids, base) { \
+ .name = #ids, \
+ .id = STPMIC1_##ids, \
+ .n_voltages = 32, \
+ .ops = &stpmic1_ldo_ops, \
+ .linear_ranges = base ## _ranges, \
+ .n_linear_ranges = ARRAY_SIZE(base ## _ranges), \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = ids##_ACTIVE_CR, \
+ .vsel_mask = LDO_VOLTAGE_MASK, \
+ .enable_reg = ids##_ACTIVE_CR, \
+ .enable_mask = LDO_ENABLE_MASK, \
+ .enable_val = 1, \
+ .disable_val = 0, \
+ .enable_time = PMIC_ENABLE_TIME_US, \
+ .pull_down_reg = ids##_PULL_DOWN_REG, \
+ .pull_down_mask = ids##_PULL_DOWN_MASK, \
+ .supply_name = #base, \
+}
+
+#define REG_LDO3(ids, base) { \
+ .name = #ids, \
+ .id = STPMIC1_##ids, \
+ .n_voltages = 32, \
+ .ops = &stpmic1_ldo3_ops, \
+ .linear_ranges = ldo3_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(ldo3_ranges), \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = LDO3_ACTIVE_CR, \
+ .vsel_mask = LDO_VOLTAGE_MASK, \
+ .enable_reg = LDO3_ACTIVE_CR, \
+ .enable_mask = LDO_ENABLE_MASK, \
+ .enable_val = 1, \
+ .disable_val = 0, \
+ .enable_time = PMIC_ENABLE_TIME_US, \
+ .bypass_reg = LDO3_ACTIVE_CR, \
+ .bypass_mask = LDO_BYPASS_MASK, \
+ .bypass_val_on = LDO_BYPASS_MASK, \
+ .bypass_val_off = 0, \
+ .pull_down_reg = ids##_PULL_DOWN_REG, \
+ .pull_down_mask = ids##_PULL_DOWN_MASK, \
+ .supply_name = #base, \
+}
+
+#define REG_LDO4(ids, base) { \
+ .name = #ids, \
+ .id = STPMIC1_##ids, \
+ .n_voltages = 1, \
+ .ops = &stpmic1_ldo4_fixed_regul_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 3300000, \
+ .fixed_uV = 3300000, \
+ .enable_reg = LDO4_ACTIVE_CR, \
+ .enable_mask = LDO_ENABLE_MASK, \
+ .enable_val = 1, \
+ .disable_val = 0, \
+ .enable_time = PMIC_ENABLE_TIME_US, \
+ .pull_down_reg = ids##_PULL_DOWN_REG, \
+ .pull_down_mask = ids##_PULL_DOWN_MASK, \
+ .supply_name = #base, \
+}
+
+#define REG_BUCK(ids, base) { \
+ .name = #ids, \
+ .id = STPMIC1_##ids, \
+ .ops = &stpmic1_buck_ops, \
+ .n_voltages = 64, \
+ .linear_ranges = base ## _ranges, \
+ .n_linear_ranges = ARRAY_SIZE(base ## _ranges), \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = ids##_ACTIVE_CR, \
+ .vsel_mask = BUCK_VOLTAGE_MASK, \
+ .enable_reg = ids##_ACTIVE_CR, \
+ .enable_mask = BUCK_ENABLE_MASK, \
+ .enable_val = 1, \
+ .disable_val = 0, \
+ .enable_time = PMIC_ENABLE_TIME_US, \
+ .of_map_mode = stpmic1_map_mode, \
+ .pull_down_reg = ids##_PULL_DOWN_REG, \
+ .pull_down_mask = ids##_PULL_DOWN_MASK, \
+ .supply_name = #base, \
+}
+
+#define REG_VREF_DDR(ids, base) { \
+ .name = #ids, \
+ .id = STPMIC1_##ids, \
+ .n_voltages = 1, \
+ .ops = &stpmic1_vref_ddr_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 500000, \
+ .fixed_uV = 500000, \
+ .enable_reg = VREF_DDR_ACTIVE_CR, \
+ .enable_mask = BUCK_ENABLE_MASK, \
+ .enable_val = 1, \
+ .disable_val = 0, \
+ .enable_time = PMIC_ENABLE_TIME_US, \
+ .pull_down_reg = ids##_PULL_DOWN_REG, \
+ .pull_down_mask = ids##_PULL_DOWN_MASK, \
+ .supply_name = #base, \
+}
+
+#define REG_SWITCH(ids, base, reg, mask, val) { \
+ .name = #ids, \
+ .id = STPMIC1_##ids, \
+ .n_voltages = 1, \
+ .ops = &stpmic1_switch_regul_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 0, \
+ .fixed_uV = 5000000, \
+ .enable_reg = (reg), \
+ .enable_mask = (mask), \
+ .enable_val = (val), \
+ .disable_val = 0, \
+ .enable_time = PMIC_ENABLE_TIME_US, \
+ .supply_name = #base, \
+}
+
+struct stpmic1_regulator_cfg stpmic1_regulator_cfgs[] = {
+ [STPMIC1_BUCK1] = {
+ .desc = REG_BUCK(BUCK1, buck1),
+ .icc_reg = BUCKS_ICCTO_CR,
+ .icc_mask = BIT(0),
+ .mask_reset_reg = BUCKS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(0),
+ },
+ [STPMIC1_BUCK2] = {
+ .desc = REG_BUCK(BUCK2, buck2),
+ .icc_reg = BUCKS_ICCTO_CR,
+ .icc_mask = BIT(1),
+ .mask_reset_reg = BUCKS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(1),
+ },
+ [STPMIC1_BUCK3] = {
+ .desc = REG_BUCK(BUCK3, buck3),
+ .icc_reg = BUCKS_ICCTO_CR,
+ .icc_mask = BIT(2),
+ .mask_reset_reg = BUCKS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(2),
+ },
+ [STPMIC1_BUCK4] = {
+ .desc = REG_BUCK(BUCK4, buck4),
+ .icc_reg = BUCKS_ICCTO_CR,
+ .icc_mask = BIT(3),
+ .mask_reset_reg = BUCKS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(3),
+ },
+ [STPMIC1_LDO1] = {
+ .desc = REG_LDO(LDO1, ldo1),
+ .icc_reg = LDOS_ICCTO_CR,
+ .icc_mask = BIT(0),
+ .mask_reset_reg = LDOS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(0),
+ },
+ [STPMIC1_LDO2] = {
+ .desc = REG_LDO(LDO2, ldo2),
+ .icc_reg = LDOS_ICCTO_CR,
+ .icc_mask = BIT(1),
+ .mask_reset_reg = LDOS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(1),
+ },
+ [STPMIC1_LDO3] = {
+ .desc = REG_LDO3(LDO3, ldo3),
+ .icc_reg = LDOS_ICCTO_CR,
+ .icc_mask = BIT(2),
+ .mask_reset_reg = LDOS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(2),
+ },
+ [STPMIC1_LDO4] = {
+ .desc = REG_LDO4(LDO4, ldo4),
+ .icc_reg = LDOS_ICCTO_CR,
+ .icc_mask = BIT(3),
+ .mask_reset_reg = LDOS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(3),
+ },
+ [STPMIC1_LDO5] = {
+ .desc = REG_LDO(LDO5, ldo5),
+ .icc_reg = LDOS_ICCTO_CR,
+ .icc_mask = BIT(4),
+ .mask_reset_reg = LDOS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(4),
+ },
+ [STPMIC1_LDO6] = {
+ .desc = REG_LDO(LDO6, ldo6),
+ .icc_reg = LDOS_ICCTO_CR,
+ .icc_mask = BIT(5),
+ .mask_reset_reg = LDOS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(5),
+ },
+ [STPMIC1_VREF_DDR] = {
+ .desc = REG_VREF_DDR(VREF_DDR, vref_ddr),
+ .mask_reset_reg = LDOS_MASK_RESET_CR,
+ .mask_reset_mask = BIT(6),
+ },
+ [STPMIC1_BOOST] = {
+ .desc = REG_SWITCH(BOOST, boost, BST_SW_CR,
+ BOOST_ENABLED,
+ BOOST_ENABLED),
+ .icc_reg = BUCKS_ICCTO_CR,
+ .icc_mask = BIT(6),
+ },
+ [STPMIC1_VBUS_OTG] = {
+ .desc = REG_SWITCH(VBUS_OTG, pwr_sw1, BST_SW_CR,
+ USBSW_OTG_SWITCH_ENABLED,
+ USBSW_OTG_SWITCH_ENABLED),
+ .icc_reg = BUCKS_ICCTO_CR,
+ .icc_mask = BIT(4),
+ },
+ [STPMIC1_SW_OUT] = {
+ .desc = REG_SWITCH(SW_OUT, pwr_sw2, BST_SW_CR,
+ SWIN_SWOUT_ENABLED,
+ SWIN_SWOUT_ENABLED),
+ .icc_reg = BUCKS_ICCTO_CR,
+ .icc_mask = BIT(5),
+ },
+};
+
+static unsigned int stpmic1_map_mode(unsigned int mode)
+{
+ switch (mode) {
+ case STPMIC1_BUCK_MODE_NORMAL:
+ return REGULATOR_MODE_NORMAL;
+ case STPMIC1_BUCK_MODE_LP:
+ return REGULATOR_MODE_STANDBY;
+ default:
+ return -EINVAL;
+ }
+}
+
+static unsigned int stpmic1_get_mode(struct regulator_dev *rdev)
+{
+ int value;
+
+ regmap_read(rdev->regmap, rdev->desc->enable_reg, &value);
+
+ if (value & STPMIC1_BUCK_MODE_LP)
+ return REGULATOR_MODE_STANDBY;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ int value;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ value = STPMIC1_BUCK_MODE_NORMAL;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ value = STPMIC1_BUCK_MODE_LP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ STPMIC1_BUCK_MODE_LP, value);
+}
+
+static int stpmic1_set_icc(struct regulator_dev *rdev)
+{
+ struct stpmic1_regulator *regul = rdev_get_drvdata(rdev);
+
+ /* enable switch off in case of over current */
+ return regmap_update_bits(regul->regmap, regul->cfg->icc_reg,
+ regul->cfg->icc_mask, regul->cfg->icc_mask);
+}
+
+static irqreturn_t stpmic1_curlim_irq_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = (struct regulator_dev *)data;
+
+ mutex_lock(&rdev->mutex);
+
+ /* Send an overcurrent notification */
+ regulator_notifier_call_chain(rdev,
+ REGULATOR_EVENT_OVER_CURRENT,
+ NULL);
+
+ mutex_unlock(&rdev->mutex);
+
+ return IRQ_HANDLED;
+}
+
+static int stpmic1_regulator_init(struct platform_device *pdev,
+ struct regulator_dev *rdev)
+{
+ struct stpmic1_regulator *regul = rdev_get_drvdata(rdev);
+ int ret = 0;
+
+ /* set mask reset */
+ if (regul->mask_reset && regul->cfg->mask_reset_reg != 0) {
+ ret = regmap_update_bits(regul->regmap,
+ regul->cfg->mask_reset_reg,
+ regul->cfg->mask_reset_mask,
+ regul->cfg->mask_reset_mask);
+ if (ret) {
+ dev_err(&pdev->dev, "set mask reset failed\n");
+ return ret;
+ }
+ }
+
+ /* setup an irq handler for over-current detection */
+ if (regul->irq_curlim > 0) {
+ ret = devm_request_threaded_irq(&pdev->dev,
+ regul->irq_curlim, NULL,
+ stpmic1_curlim_irq_handler,
+ IRQF_ONESHOT | IRQF_SHARED,
+ pdev->name, rdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Request IRQ failed\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+#define MATCH(_name, _id) \
+ [STPMIC1_##_id] = { \
+ .name = #_name, \
+ .desc = &stpmic1_regulator_cfgs[STPMIC1_##_id].desc, \
+ }
+
+static struct of_regulator_match stpmic1_regulators_matches[] = {
+ MATCH(buck1, BUCK1),
+ MATCH(buck2, BUCK2),
+ MATCH(buck3, BUCK3),
+ MATCH(buck4, BUCK4),
+ MATCH(ldo1, LDO1),
+ MATCH(ldo2, LDO2),
+ MATCH(ldo3, LDO3),
+ MATCH(ldo4, LDO4),
+ MATCH(ldo5, LDO5),
+ MATCH(ldo6, LDO6),
+ MATCH(vref_ddr, VREF_DDR),
+ MATCH(boost, BOOST),
+ MATCH(pwr_sw1, VBUS_OTG),
+ MATCH(pwr_sw2, SW_OUT),
+};
+
+static int stpmic1_regulator_parse_dt(void *driver_data)
+{
+ struct stpmic1_regulator *regul =
+ (struct stpmic1_regulator *)driver_data;
+
+ if (!regul)
+ return -EINVAL;
+
+ if (of_get_property(regul->reg_node, "st,mask-reset", NULL))
+ regul->mask_reset = 1;
+
+ regul->irq_curlim = of_irq_get(regul->reg_node, 0);
+
+ return 0;
+}
+
+static struct
+regulator_dev *stpmic1_regulator_register(struct platform_device *pdev, int id,
+ struct regulator_init_data *init_data,
+ struct stpmic1_regulator *regul)
+{
+ struct stpmic1_dev *pmic_dev = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_dev *rdev;
+ struct regulator_config config = {};
+
+ config.dev = &pdev->dev;
+ config.init_data = init_data;
+ config.of_node = stpmic1_regulators_matches[id].of_node;
+ config.regmap = pmic_dev->regmap;
+ config.driver_data = regul;
+
+ regul->regul_id = id;
+ regul->reg_node = config.of_node;
+ regul->cfg = &stpmic1_regulator_cfgs[id];
+ regul->regmap = pmic_dev->regmap;
+
+ rdev = devm_regulator_register(&pdev->dev, ®ul->cfg->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s regulator\n",
+ regul->cfg->desc.name);
+ }
+
+ return rdev;
+}
+
+static int stpmic1_regulator_probe(struct platform_device *pdev)
+{
+ struct stpmic1_device_data *ddata;
+ struct regulator_dev *rdev;
+ struct stpmic1_regulator *regul;
+ struct regulator_init_data *init_data;
+ struct device_node *np;
+ int i, ret;
+
+ ddata = devm_kzalloc(&pdev->dev, sizeof(struct stpmic1_device_data),
+ GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ddata);
+
+ np = pdev->dev.of_node;
+
+ ret = of_regulator_match(&pdev->dev, np,
+ stpmic1_regulators_matches,
+ ARRAY_SIZE(stpmic1_regulators_matches));
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Error in PMIC regulator device tree node");
+ return ret;
+ }
+
+ regul = devm_kzalloc(&pdev->dev, ARRAY_SIZE(stpmic1_regulator_cfgs) *
+ sizeof(struct stpmic1_regulator),
+ GFP_KERNEL);
+ if (!regul)
+ return -ENOMEM;
+
+ ddata->regulator_table = regul;
+
+ for (i = 0; i < ARRAY_SIZE(stpmic1_regulator_cfgs); i++) {
+ /* Parse DT & find regulators to register */
+ init_data = stpmic1_regulators_matches[i].init_data;
+ if (init_data)
+ init_data->regulator_init = &stpmic1_regulator_parse_dt;
+
+ rdev = stpmic1_regulator_register(pdev, i, init_data, regul);
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
+
+ ret = stpmic1_regulator_init(pdev, rdev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to initialize regulator %d\n", ret);
+ return ret;
+ }
+
+ regul++;
+ }
+
+ dev_dbg(&pdev->dev, "stpmic1_regulator driver probed\n");
+
+ return 0;
+}
+
+static const struct of_device_id of_pmic_regulator_match[] = {
+ { .compatible = "st,stpmic1-regulators" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, of_pmic_regulator_match);
+
+static struct platform_driver stpmic1_regulator_driver = {
+ .driver = {
+ .name = "stpmic1-regulator",
+ .of_match_table = of_match_ptr(of_pmic_regulator_match),
+ },
+ .probe = stpmic1_regulator_probe,
+};
+
+module_platform_driver(stpmic1_regulator_driver);
+
+MODULE_DESCRIPTION("STPMIC1 PMIC voltage regulator driver");
+MODULE_AUTHOR("Pascal Paillet");
+MODULE_LICENSE("GPL v2");
--
1.9.1
On 09/07/2018 05:59 AM, Pascal PAILLET-LME wrote:
> From: pascal paillet <[email protected]>
>
> The stpmic1 PMIC embeds a watchdog which is disabled by default. As soon
> as the watchdog is started, it must be refreshed periodically otherwise
> the PMIC goes off.
>
> Signed-off-by: pascal paillet <[email protected]>
> ---
> changes in v2:
> * the hardware component has been renamed from stpmu1 to stpmic1 !
> * change headers
> * handle remarks from Guenter
Hmm ... as if I would remember what those were.
> * fix set timeout
>
> drivers/watchdog/Kconfig | 12 ++++
> drivers/watchdog/Makefile | 1 +
> drivers/watchdog/stpmic1_wdt.c | 130 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 143 insertions(+)
> create mode 100644 drivers/watchdog/stpmic1_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 9af07fd..11c5937 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -796,6 +796,18 @@ config STM32_WATCHDOG
> To compile this driver as a module, choose M here: the
> module will be called stm32_iwdg.
>
> +config STPMIC1_WATCHDOG
> + tristate "STPMIC1 PMIC watchdog support"
> + depends on MFD_STPMIC1
> + select WATCHDOG_CORE
> + help
> + Say Y here to include watchdog support embedded into STPMIC1 PMIC.
> + If the watchdog timer expires, stpmic1 shut-down all its power
> + supplies.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called spmic1_wdt.
> +
> config UNIPHIER_WATCHDOG
> tristate "UniPhier watchdog support"
> depends on ARCH_UNIPHIER || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 1d3c6b0..a898d8f 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -216,3 +216,4 @@ obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o
> obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
> obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
> obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
> +obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
> diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c
> new file mode 100644
> index 0000000..391e544
> --- /dev/null
> +++ b/drivers/watchdog/stpmic1_wdt.c
> @@ -0,0 +1,130 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (C) STMicroelectronics 2018
> +// Author: Pascal Paillet <[email protected]> for STMicroelectronics.
> + > +#include <linux/kernel.h>
> +#include <linux/mfd/stpmic1.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/regmap.h>
> +#include <linux/reboot.h>
> +#include <linux/slab.h>
> +#include <linux/watchdog.h>
> +
> +/* WATCHDOG CONTROL REGISTER bit */
> +#define WDT_START BIT(0)
> +#define WDT_PING BIT(1)
> +#define WDT_START_MASK BIT(0)
> +#define WDT_PING_MASK BIT(1)
> +
> +#define PMIC_WDT_MIN_TIMEOUT 1
> +#define PMIC_WDT_MAX_TIMEOUT 256
> +
> +struct stpmic1_wdt {
> + struct stpmic1_dev *pmic;
> + struct watchdog_device wdtdev;
> +};
> +
> +static int pmic_wdt_start(struct watchdog_device *wdd)
> +{
> + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> + return regmap_update_bits(wdt->pmic->regmap,
> + WCHDG_CR, WDT_START_MASK, WDT_START);
> +}
> +
> +static int pmic_wdt_stop(struct watchdog_device *wdd)
> +{
> + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> + return regmap_update_bits(wdt->pmic->regmap,
> + WCHDG_CR, WDT_START_MASK, ~WDT_START);
> +}
> +
> +static int pmic_wdt_ping(struct watchdog_device *wdd)
> +{
> + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> + return regmap_update_bits(wdt->pmic->regmap,
> + WCHDG_CR, WDT_PING_MASK, WDT_PING);
> +}
> +
> +static int pmic_wdt_set_timeout(struct watchdog_device *wdd,
> + unsigned int timeout)
> +{
> + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> + return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1);
What happens if the configured timeout is 1 ? Does that translate to a 1-second
timeout ?
This function also has to set wdd->timeout.
> +}
> +
> +static const struct watchdog_info pmic_watchdog_info = {
> + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
No WDIOF_MAGICCLOSE ?
> + .identity = "STPMIC1 PMIC Watchdog",
> +};
> +
> +static const struct watchdog_ops pmic_watchdog_ops = {
> + .owner = THIS_MODULE,
> + .start = pmic_wdt_start,
> + .stop = pmic_wdt_stop,
> + .ping = pmic_wdt_ping,
> + .set_timeout = pmic_wdt_set_timeout,
> +};
> +
> +static int pmic_wdt_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct stpmic1_dev *pmic;
> + struct stpmic1_wdt *wdt;
> +
> + if (!pdev->dev.parent)
> + return -EINVAL;
> +
> + pmic = dev_get_drvdata(pdev->dev.parent);
> + if (!pmic)
> + return -EINVAL;
> +
> + wdt = devm_kzalloc(&pdev->dev, sizeof(struct stpmic1_wdt), GFP_KERNEL);
> + if (!wdt)
> + return -ENOMEM;
> +
> + wdt->pmic = pmic;
> +
> + wdt->wdtdev.info = &pmic_watchdog_info;
> + wdt->wdtdev.ops = &pmic_watchdog_ops;
> + wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT;
> + wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT;
> + wdt->wdtdev.timeout = PMIC_WDT_MIN_TIMEOUT;
Are you sure about that ? A timeout of 1 second may be a cause for unexpected
reboots if user space is busy. Personally I find it better to set a larger
default and let userspace configure a tighter value if so desired.
Also,
watchdog_init_timeout(&wdt->wdtdev, 0, &pdev->dev);
might be desirable if you want to be able to specify the timeout in the
devicetree file.
> +
> + wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
> +
> + watchdog_set_drvdata(&wdt->wdtdev, wdt);
> + dev_set_drvdata(&pdev->dev, wdt);
Unless I am missing something this should not be needed.
> +
> + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
> + if (ret)
> + return ret;
> +
> + dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n");
> + return 0;
> +}
> +
> +static const struct of_device_id of_pmic_wdt_match[] = {
> + { .compatible = "st,stpmic1-wdt" },
> + { },
> +};
> +
> +MODULE_DEVICE_TABLE(of, of_pmic_wdt_match);
> +
> +static struct platform_driver stpmic1_wdt_driver = {
> + .probe = pmic_wdt_probe,
> + .driver = {
> + .name = "stpmic1-wdt",
> + .of_match_table = of_pmic_wdt_match,
> + },
> +};
> +module_platform_driver(stpmic1_wdt_driver);
> +
> +MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device");
> +MODULE_AUTHOR("Pascal Paillet");
> +MODULE_LICENSE("GPL v2");
>
On Fri, Sep 07, 2018 at 12:59:44PM +0000, Pascal PAILLET-LME wrote:
> @@ -804,6 +804,18 @@ config REGULATOR_TI_ABB
> on TI SoCs may be unstable without enabling this as it provides
> device specific optimized bias to allow/optimize functionality.
>
> +config REGULATOR_STPMIC1
This looks good to me except the Kconfig isn't alphabetically sorted and
has a conflict. If the series doesn't otherwise need a resend I should
be able to fix by hand, let me know.
From: pascal paillet <[email protected]>
The stpmic1 pmic is able to manage an onkey button. This driver exposes
the stpmic1 onkey as an input device. It can also be configured to
shut-down the power supplies on a long key-press with an adjustable
duration.
Signed-off-by: pascal paillet <[email protected]>
---
changes in v2:
* the hardware component has been renamed from stpmu1 to stpmic1 !
* change headers
* handle remarks from Dmitry
* the irq is threaded because is is nested in a thread; I have added a comment.
Dmitry, I'm sorry, but I did not catch your comment regarding usage of
"generic device property API.". could you tell more ?
drivers/input/misc/Kconfig | 11 ++
drivers/input/misc/Makefile | 2 +
drivers/input/misc/stpmic1_onkey.c | 257 +++++++++++++++++++++++++++++++++++++
3 files changed, 270 insertions(+)
create mode 100644 drivers/input/misc/stpmic1_onkey.c
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index c25606e..cc82dad 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -841,4 +841,15 @@ config INPUT_RAVE_SP_PWRBUTTON
To compile this driver as a module, choose M here: the
module will be called rave-sp-pwrbutton.
+config INPUT_STPMIC1_ONKEY
+ tristate "STPMIC1 PMIC Onkey support"
+ depends on MFD_STPMIC1
+ help
+ Say Y to enable support of onkey embedded into STPMIC1 PMIC. onkey
+ can be used to wakeup from low power modes and force a shut-down on
+ long press.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stpmic1_onkey.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 72cde28..f0e11b0 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
+obj-$(CONFIG_INPUT_STPMIC1_ONKEY) += stpmic1_onkey.o
obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
@@ -80,3 +81,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
+
diff --git a/drivers/input/misc/stpmic1_onkey.c b/drivers/input/misc/stpmic1_onkey.c
new file mode 100644
index 0000000..170d879
--- /dev/null
+++ b/drivers/input/misc/stpmic1_onkey.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <[email protected]> for STMicroelectronics.
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/**
+ * struct stpmic1_onkey - OnKey data
+ * @pmic: pointer to STPMIC1 PMIC device
+ * @input_dev: pointer to input device
+ * @irq_falling: irq that we are hooked on to
+ * @irq_rising: irq that we are hooked on to
+ */
+struct stpmic1_onkey {
+ struct stpmic1_dev *pmic;
+ struct input_dev *input_dev;
+ int irq_falling;
+ int irq_rising;
+};
+
+/**
+ * struct pmic_onkey_config - configuration of pmic PONKEYn
+ * @turnoff_enabled: value to enable turnoff condition
+ * @cc_flag_clear: value to clear CC flag in case of PowerOff
+ * trigger by longkey press
+ * @onkey_pullup_val: value of PONKEY PullUp (active or inactive)
+ * @long_press_time_val: value for long press h/w shutdown event
+ */
+struct pmic_onkey_config {
+ bool turnoff_enabled;
+ bool cc_flag_clear;
+ u8 onkey_pullup_val;
+ u8 long_press_time_val;
+};
+
+static irqreturn_t onkey_falling_irq(int irq, void *ponkey)
+{
+ struct stpmic1_onkey *onkey = ponkey;
+ struct input_dev *input_dev = onkey->input_dev;
+
+ input_report_key(input_dev, KEY_POWER, 1);
+ pm_wakeup_event(input_dev->dev.parent, 0);
+ input_sync(input_dev);
+
+ dev_dbg(&input_dev->dev, "Pwr Onkey Falling Interrupt received\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t onkey_rising_irq(int irq, void *ponkey)
+{
+ struct stpmic1_onkey *onkey = ponkey;
+ struct input_dev *input_dev = onkey->input_dev;
+
+ input_report_key(input_dev, KEY_POWER, 0);
+ pm_wakeup_event(input_dev->dev.parent, 0);
+ input_sync(input_dev);
+
+ dev_dbg(&input_dev->dev, "Pwr Onkey Rising Interrupt received\n");
+
+ return IRQ_HANDLED;
+}
+
+static int stpmic1_onkey_dt_params(struct platform_device *pdev,
+ struct stpmic1_onkey *onkey,
+ struct pmic_onkey_config *config)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np;
+ u32 val;
+
+ np = dev->of_node;
+
+ onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling");
+ if (onkey->irq_falling < 0) {
+ dev_err(dev, "failed: request IRQ onkey-falling %d\n",
+ onkey->irq_falling);
+ return onkey->irq_falling;
+ }
+
+ onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising");
+ if (onkey->irq_rising < 0) {
+ dev_err(dev, "failed: request IRQ onkey-rising %d\n",
+ onkey->irq_rising);
+ return onkey->irq_rising;
+ }
+
+ if (!of_property_read_u32(np, "st,onkey-long-press-seconds", &val)) {
+ if (val >= 1 && val <= 16) {
+ config->long_press_time_val = (16 - val);
+ } else {
+ dev_warn(dev,
+ "Invalid range of long key pressed timer %d (<16)\n",
+ val);
+ }
+ }
+ if (of_get_property(np, "st,onkey-pwroff-enabled", NULL))
+ config->turnoff_enabled = true;
+
+ if (of_get_property(np, "st,onkey-clear-cc-flag", NULL))
+ config->cc_flag_clear = true;
+
+ if (of_get_property(np, "st,onkey-pu-inactive", NULL))
+ config->onkey_pullup_val = PONKEY_PU_ACTIVE;
+
+ dev_dbg(dev, "onkey-switch-off duration=%d seconds\n",
+ config->long_press_time_val);
+
+ return 0;
+}
+
+static int stpmic1_onkey_probe(struct platform_device *pdev)
+{
+ struct stpmic1_dev *pmic = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct input_dev *input_dev;
+ struct stpmic1_onkey *onkey;
+ struct pmic_onkey_config config;
+ unsigned int val = 0;
+ int error;
+
+ onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
+ if (!onkey)
+ return -ENOMEM;
+
+ memset(&config, 0, sizeof(struct pmic_onkey_config));
+ error = stpmic1_onkey_dt_params(pdev, onkey, &config);
+ if (error)
+ return error;
+
+ input_dev = devm_input_allocate_device(dev);
+ if (!input_dev) {
+ dev_err(dev, "Can't allocate Pwr Onkey Input Device\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = "pmic_onkey";
+ input_dev->phys = "pmic_onkey/input0";
+
+ input_set_capability(input_dev, EV_KEY, KEY_POWER);
+
+ /* Setup Power Onkey Hardware parameters (long key press)*/
+ val = (config.long_press_time_val & PONKEY_TURNOFF_TIMER_MASK);
+ if (config.turnoff_enabled)
+ val |= PONKEY_PWR_OFF;
+ if (config.cc_flag_clear)
+ val |= PONKEY_CC_FLAG_CLEAR;
+ error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR,
+ PONKEY_TURNOFF_MASK, val);
+ if (error) {
+ dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", error);
+ return error;
+ }
+
+ error = regmap_update_bits(pmic->regmap, PADS_PULL_CR,
+ PONKEY_PU_ACTIVE,
+ config.onkey_pullup_val);
+ if (error) {
+ dev_err(dev, "ONKEY Pads configuration failed: %d\n", error);
+ return error;
+ }
+
+ onkey->pmic = pmic;
+ onkey->input_dev = input_dev;
+
+ /* interrupt is nested in a thread */
+ error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL,
+ onkey_falling_irq, IRQF_ONESHOT,
+ dev_name(dev), onkey);
+ if (error) {
+ dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error);
+ return error;
+ }
+
+ error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL,
+ onkey_rising_irq, IRQF_ONESHOT,
+ dev_name(dev), onkey);
+ if (error) {
+ dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error);
+ return error;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(dev, "Can't register power button: %d\n", error);
+ return error;
+ }
+
+ platform_set_drvdata(pdev, onkey);
+ device_init_wakeup(dev, true);
+
+ return 0;
+}
+
+static int stpmic1_onkey_remove(struct platform_device *pdev)
+{
+ struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
+
+ input_unregister_device(onkey->input_dev);
+ return 0;
+}
+
+static int __maybe_unused stpmic1_onkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(onkey->irq_falling);
+ enable_irq_wake(onkey->irq_rising);
+ }
+ return 0;
+}
+
+static int __maybe_unused stpmic1_onkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(onkey->irq_falling);
+ disable_irq_wake(onkey->irq_rising);
+ }
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm,
+ stpmic1_onkey_suspend,
+ stpmic1_onkey_resume);
+
+static const struct of_device_id of_stpmic1_onkey_match[] = {
+ { .compatible = "st,stpmic1-onkey" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match);
+
+static struct platform_driver stpmic1_onkey_driver = {
+ .probe = stpmic1_onkey_probe,
+ .remove = stpmic1_onkey_remove,
+ .driver = {
+ .name = "stpmic1_onkey",
+ .of_match_table = of_match_ptr(of_stpmic1_onkey_match),
+ .pm = &stpmic1_onkey_pm,
+ },
+};
+module_platform_driver(stpmic1_onkey_driver);
+
+MODULE_DESCRIPTION("Onkey driver for STPMIC1");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Pascal Paillet");
--
1.9.1
From: pascal paillet <[email protected]>
The stpmic1 pmic is able to manage an onkey button. It can be configured
to shut-down the power supplies on a long key-press with an adjustable
duration.
Signed-off-by: pascal paillet <[email protected]>
---
.../devicetree/bindings/input/st,stpmic1-onkey.txt | 31 ++++++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt
diff --git a/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt b/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt
new file mode 100644
index 0000000..b0db90d
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt
@@ -0,0 +1,31 @@
+STMicroelectronics STPMIC1 Onkey
+
+Required properties:
+
+- compatible = "st,stpmic1-onkey";
+- interrupt-parent: phandle to the parent interrupt controller
+- interrupts: interrupt line to use
+- interrupt-names = "onkey-falling", "onkey-rising"
+ onkey-falling: happens when onkey is pressed; IT_PONKEY_F of pmic
+ onkey-rising: happens when onkey is released; IT_PONKEY_R of pmic
+
+Optional properties:
+
+- st,onkey-pwroff-enabled: power off on long key-press
+- st,onkey-long-press-seconds: long key-press duration from 1 to 16s
+ (default 16s)
+- st,onkey-clear-cc-flag: onkey is able power on after an
+ over-current shutdown event.
+- st,onkey-pu-inactive: onkey pull up is not active
+
+Example:
+
+onkey {
+ compatible = "st,stpmic1-onkey";
+ interrupt-parent = <&pmic>;
+ interrupts = <IT_PONKEY_F 0>,<IT_PONKEY_R 1>;
+ interrupt-names = "onkey-falling", "onkey-rising";
+ status = "okay";
+ st,onkey-pwroff-enabled;
+ st,onkey-long-press-seconds = <10>;
+};
--
1.9.1
From: pascal paillet <[email protected]>
stpmic1 is a pmic from STMicroelectronics. The stpmic1 integrates 10
regulators and 3 switches with various capabilities.
Signed-off-by: pascal paillet <[email protected]>
---
changes in v2:
* the hardware component has been renamed from stpmu1 to stpmic1 !
* Handle remarks from Enric
* change headers
* split binding description on another patch
On other mfd upstreamed by us (ST) we always get the remark to use
devm_of_platform_populate and not mfd_add_devices().
MFD maintainers could you please clarify wich API I should here, thanks ?
drivers/mfd/Kconfig | 13 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/stpmic1.c | 457 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/stpmic1.h | 220 +++++++++++++++++++++
4 files changed, 691 insertions(+)
create mode 100644 drivers/mfd/stpmic1.c
create mode 100644 include/linux/mfd/stpmic1.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index b860eb5..7984803 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1812,6 +1812,19 @@ config MFD_STM32_TIMERS
for PWM and IIO Timer. This driver allow to share the
registers between the others drivers.
+config MFD_STPMIC1
+ tristate "Support for STPMIC1 PMIC"
+ depends on (I2C=y && OF)
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ select MFD_CORE
+ help
+ Support for STMicroelectronics STPMIC1 PMIC. Stpmic1 mfd driver is
+ the core driver for stpmic1 component that mainly handles interrupts.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stpmic1.
+
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e9fd20d..b194929 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -220,6 +220,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o
obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
+obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o
obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o
diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c
new file mode 100644
index 0000000..ea0bff2
--- /dev/null
+++ b/drivers/mfd/stpmic1.c
@@ -0,0 +1,457 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <[email protected]> for STMicroelectronics.
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/regmap.h>
+#include <dt-bindings/mfd/st,stpmic1.h>
+
+static bool stpmic1_reg_readable(struct device *dev, unsigned int reg);
+static bool stpmic1_reg_writeable(struct device *dev, unsigned int reg);
+static bool stpmic1_reg_volatile(struct device *dev, unsigned int reg);
+
+const struct regmap_config stpmic1_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = PMIC_MAX_REGISTER_ADDRESS,
+ .readable_reg = stpmic1_reg_readable,
+ .writeable_reg = stpmic1_reg_writeable,
+ .volatile_reg = stpmic1_reg_volatile,
+};
+
+#define FILL_IRQS(_index) \
+ [(_index)] = { \
+ .reg_offset = ((_index) >> 3), \
+ .mask = (1 << (_index % 8)), \
+ }
+
+static const struct regmap_irq stpmic1_irqs[] = {
+ FILL_IRQS(IT_PONKEY_F),
+ FILL_IRQS(IT_PONKEY_R),
+ FILL_IRQS(IT_WAKEUP_F),
+ FILL_IRQS(IT_WAKEUP_R),
+ FILL_IRQS(IT_VBUS_OTG_F),
+ FILL_IRQS(IT_VBUS_OTG_R),
+ FILL_IRQS(IT_SWOUT_F),
+ FILL_IRQS(IT_SWOUT_R),
+
+ FILL_IRQS(IT_CURLIM_BUCK1),
+ FILL_IRQS(IT_CURLIM_BUCK2),
+ FILL_IRQS(IT_CURLIM_BUCK3),
+ FILL_IRQS(IT_CURLIM_BUCK4),
+ FILL_IRQS(IT_OCP_OTG),
+ FILL_IRQS(IT_OCP_SWOUT),
+ FILL_IRQS(IT_OCP_BOOST),
+ FILL_IRQS(IT_OVP_BOOST),
+
+ FILL_IRQS(IT_CURLIM_LDO1),
+ FILL_IRQS(IT_CURLIM_LDO2),
+ FILL_IRQS(IT_CURLIM_LDO3),
+ FILL_IRQS(IT_CURLIM_LDO4),
+ FILL_IRQS(IT_CURLIM_LDO5),
+ FILL_IRQS(IT_CURLIM_LDO6),
+ FILL_IRQS(IT_SHORT_SWOTG),
+ FILL_IRQS(IT_SHORT_SWOUT),
+
+ FILL_IRQS(IT_TWARN_F),
+ FILL_IRQS(IT_TWARN_R),
+ FILL_IRQS(IT_VINLOW_F),
+ FILL_IRQS(IT_VINLOW_R),
+ FILL_IRQS(IT_SWIN_F),
+ FILL_IRQS(IT_SWIN_R),
+};
+
+static const struct regmap_irq_chip stpmic1_regmap_irq_chip = {
+ .name = "pmic_irq",
+ .status_base = INT_PENDING_R1,
+ .mask_base = INT_CLEAR_MASK_R1,
+ .unmask_base = INT_SET_MASK_R1,
+ .ack_base = INT_CLEAR_R1,
+ .num_regs = STPMIC1_PMIC_NUM_IRQ_REGS,
+ .irqs = stpmic1_irqs,
+ .num_irqs = ARRAY_SIZE(stpmic1_irqs),
+};
+
+static bool stpmic1_reg_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TURN_ON_SR:
+ case TURN_OFF_SR:
+ case ICC_LDO_TURN_OFF_SR:
+ case ICC_BUCK_TURN_OFF_SR:
+ case RREQ_STATE_SR:
+ case VERSION_SR:
+ case SWOFF_PWRCTRL_CR:
+ case PADS_PULL_CR:
+ case BUCKS_PD_CR:
+ case LDO14_PD_CR:
+ case LDO56_VREF_PD_CR:
+ case VBUS_DET_VIN_CR:
+ case PKEY_TURNOFF_CR:
+ case BUCKS_MASK_RANK_CR:
+ case BUCKS_MASK_RESET_CR:
+ case LDOS_MASK_RANK_CR:
+ case LDOS_MASK_RESET_CR:
+ case WCHDG_CR:
+ case WCHDG_TIMER_CR:
+ case BUCKS_ICCTO_CR:
+ case LDOS_ICCTO_CR:
+ case BUCK1_ACTIVE_CR:
+ case BUCK2_ACTIVE_CR:
+ case BUCK3_ACTIVE_CR:
+ case BUCK4_ACTIVE_CR:
+ case VREF_DDR_ACTIVE_CR:
+ case LDO1_ACTIVE_CR:
+ case LDO2_ACTIVE_CR:
+ case LDO3_ACTIVE_CR:
+ case LDO4_ACTIVE_CR:
+ case LDO5_ACTIVE_CR:
+ case LDO6_ACTIVE_CR:
+ case BUCK1_STDBY_CR:
+ case BUCK2_STDBY_CR:
+ case BUCK3_STDBY_CR:
+ case BUCK4_STDBY_CR:
+ case VREF_DDR_STDBY_CR:
+ case LDO1_STDBY_CR:
+ case LDO2_STDBY_CR:
+ case LDO3_STDBY_CR:
+ case LDO4_STDBY_CR:
+ case LDO5_STDBY_CR:
+ case LDO6_STDBY_CR:
+ case BST_SW_CR:
+ case INT_PENDING_R1:
+ case INT_PENDING_R2:
+ case INT_PENDING_R3:
+ case INT_PENDING_R4:
+ case INT_DBG_LATCH_R1:
+ case INT_DBG_LATCH_R2:
+ case INT_DBG_LATCH_R3:
+ case INT_DBG_LATCH_R4:
+ case INT_CLEAR_R1:
+ case INT_CLEAR_R2:
+ case INT_CLEAR_R3:
+ case INT_CLEAR_R4:
+ case INT_MASK_R1:
+ case INT_MASK_R2:
+ case INT_MASK_R3:
+ case INT_MASK_R4:
+ case INT_SET_MASK_R1:
+ case INT_SET_MASK_R2:
+ case INT_SET_MASK_R3:
+ case INT_SET_MASK_R4:
+ case INT_CLEAR_MASK_R1:
+ case INT_CLEAR_MASK_R2:
+ case INT_CLEAR_MASK_R3:
+ case INT_CLEAR_MASK_R4:
+ case INT_SRC_R1:
+ case INT_SRC_R2:
+ case INT_SRC_R3:
+ case INT_SRC_R4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool stpmic1_reg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SWOFF_PWRCTRL_CR:
+ case PADS_PULL_CR:
+ case BUCKS_PD_CR:
+ case LDO14_PD_CR:
+ case LDO56_VREF_PD_CR:
+ case VBUS_DET_VIN_CR:
+ case PKEY_TURNOFF_CR:
+ case BUCKS_MASK_RANK_CR:
+ case BUCKS_MASK_RESET_CR:
+ case LDOS_MASK_RANK_CR:
+ case LDOS_MASK_RESET_CR:
+ case WCHDG_CR:
+ case WCHDG_TIMER_CR:
+ case BUCKS_ICCTO_CR:
+ case LDOS_ICCTO_CR:
+ case BUCK1_ACTIVE_CR:
+ case BUCK2_ACTIVE_CR:
+ case BUCK3_ACTIVE_CR:
+ case BUCK4_ACTIVE_CR:
+ case VREF_DDR_ACTIVE_CR:
+ case LDO1_ACTIVE_CR:
+ case LDO2_ACTIVE_CR:
+ case LDO3_ACTIVE_CR:
+ case LDO4_ACTIVE_CR:
+ case LDO5_ACTIVE_CR:
+ case LDO6_ACTIVE_CR:
+ case BUCK1_STDBY_CR:
+ case BUCK2_STDBY_CR:
+ case BUCK3_STDBY_CR:
+ case BUCK4_STDBY_CR:
+ case VREF_DDR_STDBY_CR:
+ case LDO1_STDBY_CR:
+ case LDO2_STDBY_CR:
+ case LDO3_STDBY_CR:
+ case LDO4_STDBY_CR:
+ case LDO5_STDBY_CR:
+ case LDO6_STDBY_CR:
+ case BST_SW_CR:
+ case INT_DBG_LATCH_R1:
+ case INT_DBG_LATCH_R2:
+ case INT_DBG_LATCH_R3:
+ case INT_DBG_LATCH_R4:
+ case INT_CLEAR_R1:
+ case INT_CLEAR_R2:
+ case INT_CLEAR_R3:
+ case INT_CLEAR_R4:
+ case INT_SET_MASK_R1:
+ case INT_SET_MASK_R2:
+ case INT_SET_MASK_R3:
+ case INT_SET_MASK_R4:
+ case INT_CLEAR_MASK_R1:
+ case INT_CLEAR_MASK_R2:
+ case INT_CLEAR_MASK_R3:
+ case INT_CLEAR_MASK_R4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool stpmic1_reg_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TURN_ON_SR:
+ case TURN_OFF_SR:
+ case ICC_LDO_TURN_OFF_SR:
+ case ICC_BUCK_TURN_OFF_SR:
+ case RREQ_STATE_SR:
+ case INT_PENDING_R1:
+ case INT_PENDING_R2:
+ case INT_PENDING_R3:
+ case INT_PENDING_R4:
+ case INT_SRC_R1:
+ case INT_SRC_R2:
+ case INT_SRC_R3:
+ case INT_SRC_R4:
+ case WCHDG_CR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int stpmic1_configure_from_dt(struct stpmic1_dev *pmic_dev)
+{
+ struct device_node *np = pmic_dev->np;
+ u32 reg;
+ int ret, irq;
+
+ irq = of_irq_get(np, 0);
+ if (irq <= 0) {
+ dev_err(pmic_dev->dev,
+ "Failed to get irq config: %d\n", irq);
+ return irq ?: -ENODEV;
+ }
+ pmic_dev->irq = irq;
+
+ irq = of_irq_get(np, 1);
+ if (irq > 0)
+ pmic_dev->irq_wake = irq;
+ else
+ pmic_dev->irq_wake = pmic_dev->irq;
+
+ device_init_wakeup(pmic_dev->dev, true);
+ ret = dev_pm_set_dedicated_wake_irq(pmic_dev->dev, pmic_dev->irq_wake);
+ if (ret)
+ dev_warn(pmic_dev->dev, "failed to set up wakeup irq");
+
+ if (!of_property_read_u32(np, "st,main-control-register", ®)) {
+ ret = regmap_update_bits(pmic_dev->regmap,
+ SWOFF_PWRCTRL_CR,
+ PWRCTRL_POLARITY_HIGH |
+ PWRCTRL_PIN_VALID |
+ RESTART_REQUEST_ENABLED,
+ reg);
+ if (ret) {
+ dev_err(pmic_dev->dev,
+ "Failed to update main control register: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ if (!of_property_read_u32(np, "st,pads-pull-register", ®)) {
+ ret = regmap_update_bits(pmic_dev->regmap,
+ PADS_PULL_CR,
+ WAKEUP_DETECTOR_DISABLED |
+ PWRCTRL_PD_ACTIVE |
+ PWRCTRL_PU_ACTIVE |
+ WAKEUP_PD_ACTIVE,
+ reg);
+ if (ret) {
+ dev_err(pmic_dev->dev,
+ "Failed to update pads control register: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ if (!of_property_read_u32(np, "st,vin-control-register", ®)) {
+ ret = regmap_update_bits(pmic_dev->regmap,
+ VBUS_DET_VIN_CR,
+ VINLOW_CTRL_REG_MASK,
+ reg);
+ if (ret) {
+ dev_err(pmic_dev->dev,
+ "Failed to update vin control register: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ if (!of_property_read_u32(np, "st,usb-control-register", ®)) {
+ ret = regmap_update_bits(pmic_dev->regmap, BST_SW_CR,
+ BOOST_OVP_DISABLED |
+ VBUS_OTG_DETECTION_DISABLED |
+ SW_OUT_DISCHARGE |
+ VBUS_OTG_DISCHARGE |
+ OCP_LIMIT_HIGH,
+ reg);
+ if (ret) {
+ dev_err(pmic_dev->dev,
+ "Failed to update usb control register: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int stpmic1_device_init(struct stpmic1_dev *pmic_dev)
+{
+ int ret;
+ unsigned int val;
+
+ pmic_dev->regmap =
+ devm_regmap_init_i2c(pmic_dev->i2c, &stpmic1_regmap_config);
+
+ if (IS_ERR(pmic_dev->regmap))
+ return PTR_ERR(pmic_dev->regmap);
+
+ ret = stpmic1_configure_from_dt(pmic_dev);
+ if (ret) {
+ dev_err(pmic_dev->dev,
+ "Unable to configure PMIC from Device Tree: %d\n", ret);
+ return ret;
+ }
+
+ /* Read Version ID */
+ ret = regmap_read(pmic_dev->regmap, VERSION_SR, &val);
+ if (ret) {
+ dev_err(pmic_dev->dev, "Unable to read pmic version\n");
+ return ret;
+ }
+ dev_info(pmic_dev->dev, "PMIC Chip Version: 0x%x\n", val);
+
+ /* Initialize PMIC IRQ Chip & IRQ domains associated */
+ ret = devm_regmap_add_irq_chip(pmic_dev->dev, pmic_dev->regmap,
+ pmic_dev->irq,
+ IRQF_ONESHOT | IRQF_SHARED,
+ 0, &stpmic1_regmap_irq_chip,
+ &pmic_dev->irq_data);
+ if (ret) {
+ dev_err(pmic_dev->dev, "IRQ Chip registration failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stpmic1_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct stpmic1_dev *pmic;
+ struct device *dev = &i2c->dev;
+ int ret;
+
+ pmic = devm_kzalloc(dev, sizeof(struct stpmic1_dev), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ pmic->np = dev->of_node;
+
+ dev_set_drvdata(dev, pmic);
+ pmic->dev = dev;
+ pmic->i2c = i2c;
+
+ ret = stpmic1_device_init(pmic);
+ if (ret)
+ return ret;
+
+ return devm_of_platform_populate(pmic->dev);
+}
+
+static const struct i2c_device_id stpmic1_id[] = {
+ { "stpmic1", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, stpmic1_id);
+
+#ifdef CONFIG_PM_SLEEP
+static int stpmic1_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct stpmic1_dev *pmic_dev = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(pmic_dev->irq_wake);
+
+ disable_irq(pmic_dev->irq);
+ return 0;
+}
+
+static int stpmic1_resume(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct stpmic1_dev *pmic_dev = i2c_get_clientdata(i2c);
+ int ret;
+
+ ret = regcache_sync(pmic_dev->regmap);
+ if (ret)
+ return ret;
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(pmic_dev->irq_wake);
+
+ enable_irq(pmic_dev->irq);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stpmic1_pm, stpmic1_suspend, stpmic1_resume);
+
+static struct i2c_driver stpmic1_driver = {
+ .driver = {
+ .name = "stpmic1",
+ .pm = &stpmic1_pm,
+ },
+ .probe = stpmic1_probe,
+ .id_table = stpmic1_id,
+};
+
+module_i2c_driver(stpmic1_driver);
+
+MODULE_DESCRIPTION("STPMIC1 PMIC Driver");
+MODULE_AUTHOR("Pascal Paillet");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/stpmic1.h b/include/linux/mfd/stpmic1.h
new file mode 100644
index 0000000..024769d
--- /dev/null
+++ b/include/linux/mfd/stpmic1.h
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Philippe Peurichard <[email protected]>,
+ * Pascal Paillet <[email protected]> for STMicroelectronics.
+ */
+
+#ifndef __LINUX_MFD_STPMIC1_H
+#define __LINUX_MFD_STPMIC1_H
+
+#define TURN_ON_SR 0x1
+#define TURN_OFF_SR 0x2
+#define ICC_LDO_TURN_OFF_SR 0x3
+#define ICC_BUCK_TURN_OFF_SR 0x4
+#define RREQ_STATE_SR 0x5
+#define VERSION_SR 0x6
+
+#define SWOFF_PWRCTRL_CR 0x10
+#define PADS_PULL_CR 0x11
+#define BUCKS_PD_CR 0x12
+#define LDO14_PD_CR 0x13
+#define LDO56_VREF_PD_CR 0x14
+#define VBUS_DET_VIN_CR 0x15
+#define PKEY_TURNOFF_CR 0x16
+#define BUCKS_MASK_RANK_CR 0x17
+#define BUCKS_MASK_RESET_CR 0x18
+#define LDOS_MASK_RANK_CR 0x19
+#define LDOS_MASK_RESET_CR 0x1A
+#define WCHDG_CR 0x1B
+#define WCHDG_TIMER_CR 0x1C
+#define BUCKS_ICCTO_CR 0x1D
+#define LDOS_ICCTO_CR 0x1E
+
+#define BUCK1_ACTIVE_CR 0x20
+#define BUCK2_ACTIVE_CR 0x21
+#define BUCK3_ACTIVE_CR 0x22
+#define BUCK4_ACTIVE_CR 0x23
+#define VREF_DDR_ACTIVE_CR 0x24
+#define LDO1_ACTIVE_CR 0x25
+#define LDO2_ACTIVE_CR 0x26
+#define LDO3_ACTIVE_CR 0x27
+#define LDO4_ACTIVE_CR 0x28
+#define LDO5_ACTIVE_CR 0x29
+#define LDO6_ACTIVE_CR 0x2A
+
+#define BUCK1_STDBY_CR 0x30
+#define BUCK2_STDBY_CR 0x31
+#define BUCK3_STDBY_CR 0x32
+#define BUCK4_STDBY_CR 0x33
+#define VREF_DDR_STDBY_CR 0x34
+#define LDO1_STDBY_CR 0x35
+#define LDO2_STDBY_CR 0x36
+#define LDO3_STDBY_CR 0x37
+#define LDO4_STDBY_CR 0x38
+#define LDO5_STDBY_CR 0x39
+#define LDO6_STDBY_CR 0x3A
+
+#define BST_SW_CR 0x40
+
+#define INT_PENDING_R1 0x50
+#define INT_PENDING_R2 0x51
+#define INT_PENDING_R3 0x52
+#define INT_PENDING_R4 0x53
+
+#define INT_DBG_LATCH_R1 0x60
+#define INT_DBG_LATCH_R2 0x61
+#define INT_DBG_LATCH_R3 0x62
+#define INT_DBG_LATCH_R4 0x63
+
+#define INT_CLEAR_R1 0x70
+#define INT_CLEAR_R2 0x71
+#define INT_CLEAR_R3 0x72
+#define INT_CLEAR_R4 0x73
+
+#define INT_MASK_R1 0x80
+#define INT_MASK_R2 0x81
+#define INT_MASK_R3 0x82
+#define INT_MASK_R4 0x83
+
+#define INT_SET_MASK_R1 0x90
+#define INT_SET_MASK_R2 0x91
+#define INT_SET_MASK_R3 0x92
+#define INT_SET_MASK_R4 0x93
+
+#define INT_CLEAR_MASK_R1 0xA0
+#define INT_CLEAR_MASK_R2 0xA1
+#define INT_CLEAR_MASK_R3 0xA2
+#define INT_CLEAR_MASK_R4 0xA3
+
+#define INT_SRC_R1 0xB0
+#define INT_SRC_R2 0xB1
+#define INT_SRC_R3 0xB2
+#define INT_SRC_R4 0xB3
+
+#define PMIC_MAX_REGISTER_ADDRESS INT_SRC_R4
+
+#define STPMIC1_PMIC_NUM_IRQ_REGS 4
+
+#define TURN_OFF_SR_ICC_EVENT 0x08
+
+#define LDO_VOLTAGE_MASK GENMASK(6, 2)
+#define BUCK_VOLTAGE_MASK GENMASK(7, 2)
+#define LDO_BUCK_VOLTAGE_SHIFT 2
+
+#define LDO_ENABLE_MASK BIT(0)
+#define BUCK_ENABLE_MASK BIT(0)
+
+#define BUCK_HPLP_ENABLE_MASK BIT(1)
+#define BUCK_HPLP_SHIFT 1
+
+#define STDBY_ENABLE_MASK BIT(0)
+
+#define BUCKS_PD_CR_REG_MASK GENMASK(7, 0)
+#define BUCK_MASK_RANK_REGISTER_MASK GENMASK(3, 0)
+#define BUCK_MASK_RESET_REGISTER_MASK GENMASK(3, 0)
+#define LDO1234_PULL_DOWN_REGISTER_MASK GENMASK(7, 0)
+#define LDO56_VREF_PD_CR_REG_MASK GENMASK(5, 0)
+#define LDO_MASK_RANK_REGISTER_MASK GENMASK(5, 0)
+#define LDO_MASK_RESET_REGISTER_MASK GENMASK(5, 0)
+
+#define BUCK1_PULL_DOWN_REG BUCKS_PD_CR
+#define BUCK1_PULL_DOWN_MASK BIT(0)
+#define BUCK2_PULL_DOWN_REG BUCKS_PD_CR
+#define BUCK2_PULL_DOWN_MASK BIT(2)
+#define BUCK3_PULL_DOWN_REG BUCKS_PD_CR
+#define BUCK3_PULL_DOWN_MASK BIT(4)
+#define BUCK4_PULL_DOWN_REG BUCKS_PD_CR
+#define BUCK4_PULL_DOWN_MASK BIT(6)
+
+#define LDO1_PULL_DOWN_REG LDO14_PD_CR
+#define LDO1_PULL_DOWN_MASK BIT(0)
+#define LDO2_PULL_DOWN_REG LDO14_PD_CR
+#define LDO2_PULL_DOWN_MASK BIT(2)
+#define LDO3_PULL_DOWN_REG LDO14_PD_CR
+#define LDO3_PULL_DOWN_MASK BIT(4)
+#define LDO4_PULL_DOWN_REG LDO14_PD_CR
+#define LDO4_PULL_DOWN_MASK BIT(6)
+#define LDO5_PULL_DOWN_REG LDO56_VREF_PD_CR
+#define LDO5_PULL_DOWN_MASK BIT(0)
+#define LDO6_PULL_DOWN_REG LDO56_VREF_PD_CR
+#define LDO6_PULL_DOWN_MASK BIT(2)
+#define VREF_DDR_PULL_DOWN_REG LDO56_VREF_PD_CR
+#define VREF_DDR_PULL_DOWN_MASK BIT(4)
+
+#define BUCKS_ICCTO_CR_REG_MASK GENMASK(6, 0)
+#define LDOS_ICCTO_CR_REG_MASK GENMASK(5, 0)
+
+#define LDO_BYPASS_MASK BIT(7)
+
+/* Main PMIC Control Register
+ * SWOFF_PWRCTRL_CR
+ * Address : 0x10
+ */
+#define ICC_EVENT_ENABLED BIT(4)
+#define PWRCTRL_POLARITY_HIGH BIT(3)
+#define PWRCTRL_PIN_VALID BIT(2)
+#define RESTART_REQUEST_ENABLED BIT(1)
+#define SOFTWARE_SWITCH_OFF_ENABLED BIT(0)
+
+/* Main PMIC PADS Control Register
+ * PADS_PULL_CR
+ * Address : 0x11
+ */
+#define WAKEUP_DETECTOR_DISABLED BIT(4)
+#define PWRCTRL_PD_ACTIVE BIT(3)
+#define PWRCTRL_PU_ACTIVE BIT(2)
+#define WAKEUP_PD_ACTIVE BIT(1)
+#define PONKEY_PU_ACTIVE BIT(0)
+
+/* Main PMIC VINLOW Control Register
+ * VBUS_DET_VIN_CRC DMSC
+ * Address : 0x15
+ */
+#define SWIN_DETECTOR_ENABLED BIT(7)
+#define SWOUT_DETECTOR_ENABLED BIT(6)
+#define VINLOW_ENABLED BIT(0)
+#define VINLOW_CTRL_REG_MASK GENMASK(7, 0)
+
+/* USB Control Register
+ * Address : 0x40
+ */
+#define BOOST_OVP_DISABLED BIT(7)
+#define VBUS_OTG_DETECTION_DISABLED BIT(6)
+#define SW_OUT_DISCHARGE BIT(5)
+#define VBUS_OTG_DISCHARGE BIT(4)
+#define OCP_LIMIT_HIGH BIT(3)
+#define SWIN_SWOUT_ENABLED BIT(2)
+#define USBSW_OTG_SWITCH_ENABLED BIT(1)
+#define BOOST_ENABLED BIT(0)
+
+/* PKEY_TURNOFF_CR
+ * Address : 0x16
+ */
+#define PONKEY_PWR_OFF BIT(7)
+#define PONKEY_CC_FLAG_CLEAR BIT(6)
+#define PONKEY_TURNOFF_TIMER_MASK GENMASK(3, 0)
+#define PONKEY_TURNOFF_MASK GENMASK(7, 0)
+
+/*
+ * struct stpmic1_dev - stpmic1 master device for sub-drivers
+ * @dev: master device of the chip (can be used to access platform data)
+ * @i2c: i2c client private data for regulator
+ * @np: device DT node pointer
+ * @irq_base: base IRQ numbers
+ * @irq: generic IRQ number
+ * @irq_wake: wakeup IRQ number
+ * @regmap_irq_chip_data: irq chip data
+ */
+struct stpmic1_dev {
+ struct device *dev;
+ struct i2c_client *i2c;
+ struct regmap *regmap;
+ struct device_node *np;
+ unsigned int irq_base;
+ int irq;
+ int irq_wake;
+ struct regmap_irq_chip_data *irq_data;
+};
+
+#endif /* __LINUX_MFD_STPMIC1_H */
--
1.9.1
Hi Pascal,
On Fri, Sep 07, 2018 at 12:59:45PM +0000, Pascal PAILLET-LME wrote:
> From: pascal paillet <[email protected]>
>
> The stpmic1 pmic is able to manage an onkey button. This driver exposes
> the stpmic1 onkey as an input device. It can also be configured to
> shut-down the power supplies on a long key-press with an adjustable
> duration.
>
> Signed-off-by: pascal paillet <[email protected]>
> ---
> changes in v2:
> * the hardware component has been renamed from stpmu1 to stpmic1 !
> * change headers
> * handle remarks from Dmitry
> * the irq is threaded because is is nested in a thread; I have added a comment.
> Dmitry, I'm sorry, but I did not catch your comment regarding usage of
> "generic device property API.". could you tell more ?
You basically do
s/of_property_/device_property_/
and that's it.
>
> drivers/input/misc/Kconfig | 11 ++
> drivers/input/misc/Makefile | 2 +
> drivers/input/misc/stpmic1_onkey.c | 257 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 270 insertions(+)
> create mode 100644 drivers/input/misc/stpmic1_onkey.c
>
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index c25606e..cc82dad 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -841,4 +841,15 @@ config INPUT_RAVE_SP_PWRBUTTON
> To compile this driver as a module, choose M here: the
> module will be called rave-sp-pwrbutton.
>
> +config INPUT_STPMIC1_ONKEY
> + tristate "STPMIC1 PMIC Onkey support"
> + depends on MFD_STPMIC1
> + help
> + Say Y to enable support of onkey embedded into STPMIC1 PMIC. onkey
> + can be used to wakeup from low power modes and force a shut-down on
> + long press.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called stpmic1_onkey.
> +
> endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 72cde28..f0e11b0 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -70,6 +70,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
> obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
> obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o
> obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
> +obj-$(CONFIG_INPUT_STPMIC1_ONKEY) += stpmic1_onkey.o
> obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o
> obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
> obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
> @@ -80,3 +81,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
> obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
> obj-$(CONFIG_INPUT_YEALINK) += yealink.o
> obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
> +
> diff --git a/drivers/input/misc/stpmic1_onkey.c b/drivers/input/misc/stpmic1_onkey.c
> new file mode 100644
> index 0000000..170d879
> --- /dev/null
> +++ b/drivers/input/misc/stpmic1_onkey.c
> @@ -0,0 +1,257 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (C) STMicroelectronics 2018
> +// Author: Pascal Paillet <[email protected]> for STMicroelectronics.
> +
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/stpmic1.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +/**
> + * struct stpmic1_onkey - OnKey data
> + * @pmic: pointer to STPMIC1 PMIC device
> + * @input_dev: pointer to input device
> + * @irq_falling: irq that we are hooked on to
> + * @irq_rising: irq that we are hooked on to
> + */
> +struct stpmic1_onkey {
> + struct stpmic1_dev *pmic;
> + struct input_dev *input_dev;
> + int irq_falling;
> + int irq_rising;
> +};
> +
> +/**
> + * struct pmic_onkey_config - configuration of pmic PONKEYn
> + * @turnoff_enabled: value to enable turnoff condition
> + * @cc_flag_clear: value to clear CC flag in case of PowerOff
> + * trigger by longkey press
> + * @onkey_pullup_val: value of PONKEY PullUp (active or inactive)
> + * @long_press_time_val: value for long press h/w shutdown event
> + */
> +struct pmic_onkey_config {
> + bool turnoff_enabled;
> + bool cc_flag_clear;
> + u8 onkey_pullup_val;
> + u8 long_press_time_val;
> +};
> +
> +static irqreturn_t onkey_falling_irq(int irq, void *ponkey)
> +{
> + struct stpmic1_onkey *onkey = ponkey;
> + struct input_dev *input_dev = onkey->input_dev;
> +
> + input_report_key(input_dev, KEY_POWER, 1);
> + pm_wakeup_event(input_dev->dev.parent, 0);
> + input_sync(input_dev);
> +
> + dev_dbg(&input_dev->dev, "Pwr Onkey Falling Interrupt received\n");
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t onkey_rising_irq(int irq, void *ponkey)
> +{
> + struct stpmic1_onkey *onkey = ponkey;
> + struct input_dev *input_dev = onkey->input_dev;
> +
> + input_report_key(input_dev, KEY_POWER, 0);
> + pm_wakeup_event(input_dev->dev.parent, 0);
> + input_sync(input_dev);
> +
> + dev_dbg(&input_dev->dev, "Pwr Onkey Rising Interrupt received\n");
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int stpmic1_onkey_dt_params(struct platform_device *pdev,
> + struct stpmic1_onkey *onkey,
> + struct pmic_onkey_config *config)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np;
> + u32 val;
> +
> + np = dev->of_node;
> +
> + onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling");
> + if (onkey->irq_falling < 0) {
> + dev_err(dev, "failed: request IRQ onkey-falling %d\n",
> + onkey->irq_falling);
> + return onkey->irq_falling;
> + }
> +
> + onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising");
> + if (onkey->irq_rising < 0) {
> + dev_err(dev, "failed: request IRQ onkey-rising %d\n",
> + onkey->irq_rising);
> + return onkey->irq_rising;
> + }
> +
> + if (!of_property_read_u32(np, "st,onkey-long-press-seconds", &val)) {
> + if (val >= 1 && val <= 16) {
> + config->long_press_time_val = (16 - val);
> + } else {
> + dev_warn(dev,
> + "Invalid range of long key pressed timer %d (<16)\n",
> + val);
> + }
> + }
> + if (of_get_property(np, "st,onkey-pwroff-enabled", NULL))
> + config->turnoff_enabled = true;
> +
> + if (of_get_property(np, "st,onkey-clear-cc-flag", NULL))
> + config->cc_flag_clear = true;
> +
> + if (of_get_property(np, "st,onkey-pu-inactive", NULL))
> + config->onkey_pullup_val = PONKEY_PU_ACTIVE;
> +
> + dev_dbg(dev, "onkey-switch-off duration=%d seconds\n",
> + config->long_press_time_val);
> +
> + return 0;
> +}
> +
> +static int stpmic1_onkey_probe(struct platform_device *pdev)
> +{
> + struct stpmic1_dev *pmic = dev_get_drvdata(pdev->dev.parent);
> + struct device *dev = &pdev->dev;
> + struct input_dev *input_dev;
> + struct stpmic1_onkey *onkey;
> + struct pmic_onkey_config config;
> + unsigned int val = 0;
> + int error;
> +
> + onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
> + if (!onkey)
> + return -ENOMEM;
> +
> + memset(&config, 0, sizeof(struct pmic_onkey_config));
> + error = stpmic1_onkey_dt_params(pdev, onkey, &config);
> + if (error)
> + return error;
> +
> + input_dev = devm_input_allocate_device(dev);
> + if (!input_dev) {
> + dev_err(dev, "Can't allocate Pwr Onkey Input Device\n");
> + return -ENOMEM;
> + }
> +
> + input_dev->name = "pmic_onkey";
> + input_dev->phys = "pmic_onkey/input0";
> +
> + input_set_capability(input_dev, EV_KEY, KEY_POWER);
> +
> + /* Setup Power Onkey Hardware parameters (long key press)*/
> + val = (config.long_press_time_val & PONKEY_TURNOFF_TIMER_MASK);
> + if (config.turnoff_enabled)
> + val |= PONKEY_PWR_OFF;
> + if (config.cc_flag_clear)
> + val |= PONKEY_CC_FLAG_CLEAR;
> + error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR,
> + PONKEY_TURNOFF_MASK, val);
> + if (error) {
> + dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", error);
> + return error;
> + }
> +
> + error = regmap_update_bits(pmic->regmap, PADS_PULL_CR,
> + PONKEY_PU_ACTIVE,
> + config.onkey_pullup_val);
> + if (error) {
> + dev_err(dev, "ONKEY Pads configuration failed: %d\n", error);
> + return error;
> + }
> +
> + onkey->pmic = pmic;
> + onkey->input_dev = input_dev;
> +
> + /* interrupt is nested in a thread */
> + error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL,
> + onkey_falling_irq, IRQF_ONESHOT,
> + dev_name(dev), onkey);
> + if (error) {
> + dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error);
> + return error;
> + }
> +
> + error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL,
> + onkey_rising_irq, IRQF_ONESHOT,
> + dev_name(dev), onkey);
> + if (error) {
> + dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error);
> + return error;
> + }
> +
> + error = input_register_device(input_dev);
> + if (error) {
> + dev_err(dev, "Can't register power button: %d\n", error);
> + return error;
> + }
> +
> + platform_set_drvdata(pdev, onkey);
> + device_init_wakeup(dev, true);
> +
> + return 0;
> +}
> +
> +static int stpmic1_onkey_remove(struct platform_device *pdev)
> +{
> + struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
> +
> + input_unregister_device(onkey->input_dev);
> + return 0;
> +}
> +
> +static int __maybe_unused stpmic1_onkey_suspend(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
> +
> + if (device_may_wakeup(dev)) {
> + enable_irq_wake(onkey->irq_falling);
> + enable_irq_wake(onkey->irq_rising);
> + }
> + return 0;
> +}
> +
> +static int __maybe_unused stpmic1_onkey_resume(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
> +
> + if (device_may_wakeup(dev)) {
> + disable_irq_wake(onkey->irq_falling);
> + disable_irq_wake(onkey->irq_rising);
> + }
> + return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm,
> + stpmic1_onkey_suspend,
> + stpmic1_onkey_resume);
> +
> +static const struct of_device_id of_stpmic1_onkey_match[] = {
> + { .compatible = "st,stpmic1-onkey" },
> + { },
> +};
> +
> +MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match);
> +
> +static struct platform_driver stpmic1_onkey_driver = {
> + .probe = stpmic1_onkey_probe,
> + .remove = stpmic1_onkey_remove,
> + .driver = {
> + .name = "stpmic1_onkey",
> + .of_match_table = of_match_ptr(of_stpmic1_onkey_match),
> + .pm = &stpmic1_onkey_pm,
> + },
> +};
> +module_platform_driver(stpmic1_onkey_driver);
> +
> +MODULE_DESCRIPTION("Onkey driver for STPMIC1");
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Pascal Paillet");
> --
> 1.9.1
--
Dmitry
On Fri, Sep 7, 2018 at 3:15 PM Pascal PAILLET-LME <[email protected]> wrote:
>
> From: pascal paillet <[email protected]>
>
> The stpmic1 PMIC embeds several regulators and witches with
> different capabilities.
While fixing Kconfig, as Mark suggest, would you mind to fix also the
typo s/witches/switches/ in the commit message?
Antonio
On Fri, 07 Sep 2018, Pascal PAILLET-LME wrote:
> From: pascal paillet <[email protected]>
>
> stpmic1 is a pmic from STMicroelectronics. The stpmic1 integrates 10
> regulators and 3 switches with various capabilities.
>
> Signed-off-by: pascal paillet <[email protected]>
> ---
> changes in v2:
> * the hardware component has been renamed from stpmu1 to stpmic1 !
> * replace _ with - in properties name
> * fix node names in example
> * remove regulator compatibles in example
> * add st,stpmic1.h to the patch
>
> Rob, I did not change the usage of the properties because it would lead to a lot
> of st properties; for example st,main-control-register would be replaced by:
> st,power_cycling_on_turn_off
> st,pwrctrl_enabled
> st,pwrctrl_active_high
> should I go this way ?
>
> Rob, I did not found the standard property for st,onkey-press-seconds = <10>;
>
> .../devicetree/bindings/mfd/st,stpmic1.txt | 137 +++++++++++++++++++++
> include/dt-bindings/mfd/st,stpmic1.h | 46 +++++++
> 2 files changed, 183 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmic1.txt
> create mode 100644 include/dt-bindings/mfd/st,stpmic1.h
>
> diff --git a/Documentation/devicetree/bindings/mfd/st,stpmic1.txt b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt
> new file mode 100644
> index 0000000..9f2c516
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt
> @@ -0,0 +1,137 @@
> +* STMicroelectronics STPMIC1 Power Management IC
> +
> +Required parent device properties:
> +- compatible: "st,stpmic1"
> +- reg: the I2C slave address for the stpmic1 chip
> +- interrupts-extended: interrupt lines to use: second irq is for wakeup.
> +- #interrupt-cells: should be 2.
> +- interrupt-controller: describes the STPMIC1 as an interrupt
I always find these are easier to read when they are tab aligned.
While we're at it, let's throw some capital letters in there and
remove the '.', as it's only used on one of the lines:
- compatible: "st,stpmic1"
- reg: The I2C slave address for the STPIC1 chip
- interrupts-extended: Interrupt lines to use: second IRQ is for wakeup.
- #interrupt-cells: Should be 2
- interrupt-controller: Describes the STPMIC1 as an interrupt
Better, no?
> + controller (has its own domain). interrupt number are the following:
Capital letters usually follow a '.'
> + /* Interrupt Register 1 (0x50 for latch) */
> + IT_SWOUT_R=0
> + IT_SWOUT_F=1
> + IT_VBUS_OTG_R=2
> + IT_VBUS_OTG_F=3
> + IT_WAKEUP_R=4
> + IT_WAKEUP_F=5
> + IT_PONKEY_R=6
> + IT_PONKEY_F=7
> + /* Interrupt Register 2 (0x51 for latch) */
> + IT_OVP_BOOST=8
> + IT_OCP_BOOST=9
> + IT_OCP_SWOUT=10
> + IT_OCP_OTG=11
> + IT_CURLIM_BUCK4=12
> + IT_CURLIM_BUCK3=13
> + IT_CURLIM_BUCK2=14
> + IT_CURLIM_BUCK1=15
> + /* Interrupt Register 3 (0x52 for latch) */
> + IT_SHORT_SWOUT=16
> + IT_SHORT_SWOTG=17
> + IT_CURLIM_LDO6=18
> + IT_CURLIM_LDO5=19
> + IT_CURLIM_LDO4=20
> + IT_CURLIM_LDO3=21
> + IT_CURLIM_LDO2=22
> + IT_CURLIM_LDO1=23
> + /* Interrupt Register 3 (0x52 for latch) */
> + IT_SWIN_R=24
> + IT_SWIN_F=25
> + IT_RESERVED_1=26
> + IT_RESERVED_2=27
> + IT_VINLOW_R=28
> + IT_VINLOW_F=29
> + IT_TWARN_R=30
> + IT_TWARN_F=31
> +
> +Optional parent device properties:
> +- st,main-control-register:
> + -bit 1: Power cycling will be performed on turn OFF condition
> + -bit 2: PWRCTRL is functional
> + -bit 3: PWRCTRL active high
> +- st,pads-pull-register:
> + -bit 1: WAKEUP pull down is not active
> + -bit 2: PWRCTRL pull up is active
> + -bit 3: PWRCTRL pull down is active
> + -bit 4: WAKEUP detector is disabled
> +- st,vin-control-register:
> + -bit 0: VINLOW monitoring is enabled
> + -bit [1...3]: VINLOW rising threshold
> + 000 VINOK_f + 50mV
> + 001 VINOK_f + 100mV
> + 010 VINOK_f + 150mV
> + 011 VINOK_f + 200mV
> + 100 VINOK_f + 250mV
> + 101 VINOK_f + 300mV
> + 110 VINOK_f + 350mV
> + 111 VINOK_f + 400mV
> + -bit [4...5]: VINLOW hyst
> + 00 100mV
> + 01 200mV
> + 10 300mV
> + 11 400mV
> + -bit 6: SW_OUT detector is disabled
> + -bit 7: SW_IN detector is enabled.
> +- st,usb-control-register:
> + -bit 3: SW_OUT current limit
> + 0: 600mA
> + 1: 1.1A
> + -bit 4: VBUS_OTG discharge is enabled
> + -bit 5: SW_OUT discharge is enabled
> + -bit 6: VBUS_OTG detection is enabled
> + -bit 7: BOOST_OVP is disabled
> +
> +
Did you mean to add a double line space here?
> +stpmic1 consists is a varied group of sub-devices:
Should be STPMIC1, no?
> +Device Description
> +------ ------------
> +st,stpmic1-onkey : On key
Please describe this a "Power on key"
> +st,stpmic1-regulators : Regulators
> +st,stpmic1-wdt : Watchdog
> +
> +each sub-device bindings is be described in associated driver
"Each sub-device binding is described in own documentation file"
> +documentation section.
Please list them using relative paths e.g:
../../bindings/watchdog/st,stm32-iwdg.txt
Or
../watchdog/st,stm32-iwdg.txt
> +Example:
> +
> +pmic: stpmic1@33 {
> + compatible = "st,stpmic1";
> + reg = <0x33>;
> + interrupts = <0 2>;
> + interrupts-extended = <&intc GIC_SPI 149 IRQ_TYPE_NONE>,
> + <&exti 55 1>;
> + st,version_status = <0x10>;
> + st,main-control-register=<0x0c>;
> + interrupt-controller;
> + #interrupt-cells = <2>;
'\n' here.
> + onkey {
> + compatible = "st,stpmic1-onkey";
> + interrupt-parent = <&pmic>;
> + interrupts = <IT_PONKEY_F 0>,<IT_PONKEY_R 1>;
> + interrupt-names = "onkey-falling", "onkey-rising";
> + st,onkey-pwroff-enabled;
> + st,onkey-long-press-seconds = <10>;
> + };
> +
> + watchdog {
> + compatible = "st,stpmic1-wdt";
> + };
> +
> + regulators {
> + compatible = "st,stpmic1-regulators";
> +
> + vdd_core: buck1 {
> + regulator-name = "vdd_core";
> + regulator-boot-on;
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <1200000>;
> + };
> + vdd: buck3 {
> + regulator-name = "vdd";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-boot-on;
> + regulator-pull-down;
> + };
> + };
> diff --git a/include/dt-bindings/mfd/st,stpmic1.h b/include/dt-bindings/mfd/st,stpmic1.h
> new file mode 100644
> index 0000000..e32ac8f
> --- /dev/null
> +++ b/include/dt-bindings/mfd/st,stpmic1.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
> + * Author: Philippe Peurichard <[email protected]>,
> + * Pascal Paillet <[email protected]> for STMicroelectronics.
* Author(s): Philippe Peurichard <[email protected]>,
* Pascal Paillet <[email protected]> for STMicroelectronics.
> + */
> +
> +#ifndef __DT_BINDINGS_STPMIC1_H__
> +#define __DT_BINDINGS_STPMIC1_H__
> +
> +/* IRQ definitions */
> +#define IT_PONKEY_F 0
> +#define IT_PONKEY_R 1
> +#define IT_WAKEUP_F 2
> +#define IT_WAKEUP_R 3
> +#define IT_VBUS_OTG_F 4
> +#define IT_VBUS_OTG_R 5
> +#define IT_SWOUT_F 6
> +#define IT_SWOUT_R 7
Please ensure these are all tab aligned for readability.
> +#define IT_CURLIM_BUCK1 8
> +#define IT_CURLIM_BUCK2 9
> +#define IT_CURLIM_BUCK3 10
> +#define IT_CURLIM_BUCK4 11
> +#define IT_OCP_OTG 12
> +#define IT_OCP_SWOUT 13
> +#define IT_OCP_BOOST 14
> +#define IT_OVP_BOOST 15
> +
> +#define IT_CURLIM_LDO1 16
> +#define IT_CURLIM_LDO2 17
> +#define IT_CURLIM_LDO3 18
> +#define IT_CURLIM_LDO4 19
> +#define IT_CURLIM_LDO5 20
> +#define IT_CURLIM_LDO6 21
> +#define IT_SHORT_SWOTG 22
> +#define IT_SHORT_SWOUT 23
> +
> +#define IT_TWARN_F 24
> +#define IT_TWARN_R 25
> +#define IT_VINLOW_F 26
> +#define IT_VINLOW_R 27
> +#define IT_SWIN_F 30
> +#define IT_SWIN_R 31
> +
> +#endif /* __DT_BINDINGS_STPMIC1_H__ */
--
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
On Fri, 07 Sep 2018, Pascal PAILLET-LME wrote:
> From: pascal paillet <[email protected]>
This is odd. What is your reason for not using `git send-email`?
Also your name should really be capitalised.
Pascal Paillet
> stpmic1 is a pmic from STMicroelectronics. The stpmic1 integrates 10
"STPMIC1" and "PMIC"
> regulators and 3 switches with various capabilities.
What about the Watchdog that I saw in the bindings?
> Signed-off-by: pascal paillet <[email protected]>
> ---
> changes in v2:
> * the hardware component has been renamed from stpmu1 to stpmic1 !
> * Handle remarks from Enric
> * change headers
> * split binding description on another patch
Please use proper English grammar.
Capital letters at the start of sentences and for names of things, etc.
> On other mfd upstreamed by us (ST) we always get the remark to use
MFD
> devm_of_platform_populate and not mfd_add_devices().
> MFD maintainers could you please clarify wich API I should here, thanks ?
You can use either depending on the use-case.
Here I think the former (first - devm_of_platform_populate) is better.
> drivers/mfd/Kconfig | 13 ++
> drivers/mfd/Makefile | 1 +
> drivers/mfd/stpmic1.c | 457 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/stpmic1.h | 220 +++++++++++++++++++++
> 4 files changed, 691 insertions(+)
> create mode 100644 drivers/mfd/stpmic1.c
> create mode 100644 include/linux/mfd/stpmic1.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index b860eb5..7984803 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1812,6 +1812,19 @@ config MFD_STM32_TIMERS
> for PWM and IIO Timer. This driver allow to share the
> registers between the others drivers.
>
> +config MFD_STPMIC1
> + tristate "Support for STPMIC1 PMIC"
> + depends on (I2C=y && OF)
> + select REGMAP_I2C
> + select REGMAP_IRQ
> + select MFD_CORE
> + help
> + Support for STMicroelectronics STPMIC1 PMIC. Stpmic1 mfd driver is
"STPMIC MFD"
> + the core driver for stpmic1 component that mainly handles interrupts.
Same here.
> + To compile this driver as a module, choose M here: the
> + module will be called stpmic1.
> +
> menu "Multimedia Capabilities Port drivers"
> depends on ARCH_SA1100
>
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index e9fd20d..b194929 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -220,6 +220,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o
> obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
>
> obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
> +obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o
> obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
>
> obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o
> diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c
> new file mode 100644
> index 0000000..ea0bff2
> --- /dev/null
> +++ b/drivers/mfd/stpmic1.c
> @@ -0,0 +1,457 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (C) STMicroelectronics 2018
> +// Author: Pascal Paillet <[email protected]> for STMicroelectronics.
You don't need to put "for STMicroelectronics", since you are an ST
employee. This is something we agreed to use when upstreaming ST code
with our Linaro addresses.
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/stpmic1.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_wakeirq.h>
> +#include <linux/regmap.h>
'\n' here.
> +#include <dt-bindings/mfd/st,stpmic1.h>
> +
> +static bool stpmic1_reg_readable(struct device *dev, unsigned int reg);
> +static bool stpmic1_reg_writeable(struct device *dev, unsigned int reg);
> +static bool stpmic1_reg_volatile(struct device *dev, unsigned int reg);
This is a bad sign. Why you need these forward declarations?
Best to reorder the functions instead.
> +const struct regmap_config stpmic1_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .cache_type = REGCACHE_RBTREE,
> + .max_register = PMIC_MAX_REGISTER_ADDRESS,
> + .readable_reg = stpmic1_reg_readable,
> + .writeable_reg = stpmic1_reg_writeable,
> + .volatile_reg = stpmic1_reg_volatile,
> +};
> +
> +#define FILL_IRQS(_index) \
> + [(_index)] = { \
> + .reg_offset = ((_index) >> 3), \
> + .mask = (1 << (_index % 8)), \
> + }
If you need this, then it is likely that others need it too. Instead
of hand-rolling your own MACROs, please push it into the Regmap
subsystem.
> +static const struct regmap_irq stpmic1_irqs[] = {
> + FILL_IRQS(IT_PONKEY_F),
> + FILL_IRQS(IT_PONKEY_R),
> + FILL_IRQS(IT_WAKEUP_F),
> + FILL_IRQS(IT_WAKEUP_R),
> + FILL_IRQS(IT_VBUS_OTG_F),
> + FILL_IRQS(IT_VBUS_OTG_R),
> + FILL_IRQS(IT_SWOUT_F),
> + FILL_IRQS(IT_SWOUT_R),
> +
> + FILL_IRQS(IT_CURLIM_BUCK1),
> + FILL_IRQS(IT_CURLIM_BUCK2),
> + FILL_IRQS(IT_CURLIM_BUCK3),
> + FILL_IRQS(IT_CURLIM_BUCK4),
> + FILL_IRQS(IT_OCP_OTG),
> + FILL_IRQS(IT_OCP_SWOUT),
> + FILL_IRQS(IT_OCP_BOOST),
> + FILL_IRQS(IT_OVP_BOOST),
> +
> + FILL_IRQS(IT_CURLIM_LDO1),
> + FILL_IRQS(IT_CURLIM_LDO2),
> + FILL_IRQS(IT_CURLIM_LDO3),
> + FILL_IRQS(IT_CURLIM_LDO4),
> + FILL_IRQS(IT_CURLIM_LDO5),
> + FILL_IRQS(IT_CURLIM_LDO6),
> + FILL_IRQS(IT_SHORT_SWOTG),
> + FILL_IRQS(IT_SHORT_SWOUT),
> +
> + FILL_IRQS(IT_TWARN_F),
> + FILL_IRQS(IT_TWARN_R),
> + FILL_IRQS(IT_VINLOW_F),
> + FILL_IRQS(IT_VINLOW_R),
> + FILL_IRQS(IT_SWIN_F),
> + FILL_IRQS(IT_SWIN_R),
> +};
> +
> +static const struct regmap_irq_chip stpmic1_regmap_irq_chip = {
> + .name = "pmic_irq",
> + .status_base = INT_PENDING_R1,
> + .mask_base = INT_CLEAR_MASK_R1,
> + .unmask_base = INT_SET_MASK_R1,
> + .ack_base = INT_CLEAR_R1,
> + .num_regs = STPMIC1_PMIC_NUM_IRQ_REGS,
> + .irqs = stpmic1_irqs,
> + .num_irqs = ARRAY_SIZE(stpmic1_irqs),
> +};
> +
> +static bool stpmic1_reg_readable(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case TURN_ON_SR:
> + case TURN_OFF_SR:
> + case ICC_LDO_TURN_OFF_SR:
> + case ICC_BUCK_TURN_OFF_SR:
> + case RREQ_STATE_SR:
> + case VERSION_SR:
> + case SWOFF_PWRCTRL_CR:
> + case PADS_PULL_CR:
> + case BUCKS_PD_CR:
> + case LDO14_PD_CR:
> + case LDO56_VREF_PD_CR:
> + case VBUS_DET_VIN_CR:
> + case PKEY_TURNOFF_CR:
> + case BUCKS_MASK_RANK_CR:
> + case BUCKS_MASK_RESET_CR:
> + case LDOS_MASK_RANK_CR:
> + case LDOS_MASK_RESET_CR:
> + case WCHDG_CR:
> + case WCHDG_TIMER_CR:
> + case BUCKS_ICCTO_CR:
> + case LDOS_ICCTO_CR:
> + case BUCK1_ACTIVE_CR:
> + case BUCK2_ACTIVE_CR:
> + case BUCK3_ACTIVE_CR:
> + case BUCK4_ACTIVE_CR:
> + case VREF_DDR_ACTIVE_CR:
> + case LDO1_ACTIVE_CR:
> + case LDO2_ACTIVE_CR:
> + case LDO3_ACTIVE_CR:
> + case LDO4_ACTIVE_CR:
> + case LDO5_ACTIVE_CR:
> + case LDO6_ACTIVE_CR:
> + case BUCK1_STDBY_CR:
> + case BUCK2_STDBY_CR:
> + case BUCK3_STDBY_CR:
> + case BUCK4_STDBY_CR:
> + case VREF_DDR_STDBY_CR:
> + case LDO1_STDBY_CR:
> + case LDO2_STDBY_CR:
> + case LDO3_STDBY_CR:
> + case LDO4_STDBY_CR:
> + case LDO5_STDBY_CR:
> + case LDO6_STDBY_CR:
> + case BST_SW_CR:
> + case INT_PENDING_R1:
> + case INT_PENDING_R2:
> + case INT_PENDING_R3:
> + case INT_PENDING_R4:
> + case INT_DBG_LATCH_R1:
> + case INT_DBG_LATCH_R2:
> + case INT_DBG_LATCH_R3:
> + case INT_DBG_LATCH_R4:
> + case INT_CLEAR_R1:
> + case INT_CLEAR_R2:
> + case INT_CLEAR_R3:
> + case INT_CLEAR_R4:
> + case INT_MASK_R1:
> + case INT_MASK_R2:
> + case INT_MASK_R3:
> + case INT_MASK_R4:
> + case INT_SET_MASK_R1:
> + case INT_SET_MASK_R2:
> + case INT_SET_MASK_R3:
> + case INT_SET_MASK_R4:
> + case INT_CLEAR_MASK_R1:
> + case INT_CLEAR_MASK_R2:
> + case INT_CLEAR_MASK_R3:
> + case INT_CLEAR_MASK_R4:
> + case INT_SRC_R1:
> + case INT_SRC_R2:
> + case INT_SRC_R3:
> + case INT_SRC_R4:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static bool stpmic1_reg_writeable(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case SWOFF_PWRCTRL_CR:
> + case PADS_PULL_CR:
> + case BUCKS_PD_CR:
> + case LDO14_PD_CR:
> + case LDO56_VREF_PD_CR:
> + case VBUS_DET_VIN_CR:
> + case PKEY_TURNOFF_CR:
> + case BUCKS_MASK_RANK_CR:
> + case BUCKS_MASK_RESET_CR:
> + case LDOS_MASK_RANK_CR:
> + case LDOS_MASK_RESET_CR:
> + case WCHDG_CR:
> + case WCHDG_TIMER_CR:
> + case BUCKS_ICCTO_CR:
> + case LDOS_ICCTO_CR:
> + case BUCK1_ACTIVE_CR:
> + case BUCK2_ACTIVE_CR:
> + case BUCK3_ACTIVE_CR:
> + case BUCK4_ACTIVE_CR:
> + case VREF_DDR_ACTIVE_CR:
> + case LDO1_ACTIVE_CR:
> + case LDO2_ACTIVE_CR:
> + case LDO3_ACTIVE_CR:
> + case LDO4_ACTIVE_CR:
> + case LDO5_ACTIVE_CR:
> + case LDO6_ACTIVE_CR:
> + case BUCK1_STDBY_CR:
> + case BUCK2_STDBY_CR:
> + case BUCK3_STDBY_CR:
> + case BUCK4_STDBY_CR:
> + case VREF_DDR_STDBY_CR:
> + case LDO1_STDBY_CR:
> + case LDO2_STDBY_CR:
> + case LDO3_STDBY_CR:
> + case LDO4_STDBY_CR:
> + case LDO5_STDBY_CR:
> + case LDO6_STDBY_CR:
> + case BST_SW_CR:
> + case INT_DBG_LATCH_R1:
> + case INT_DBG_LATCH_R2:
> + case INT_DBG_LATCH_R3:
> + case INT_DBG_LATCH_R4:
> + case INT_CLEAR_R1:
> + case INT_CLEAR_R2:
> + case INT_CLEAR_R3:
> + case INT_CLEAR_R4:
> + case INT_SET_MASK_R1:
> + case INT_SET_MASK_R2:
> + case INT_SET_MASK_R3:
> + case INT_SET_MASK_R4:
> + case INT_CLEAR_MASK_R1:
> + case INT_CLEAR_MASK_R2:
> + case INT_CLEAR_MASK_R3:
> + case INT_CLEAR_MASK_R4:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static bool stpmic1_reg_volatile(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case TURN_ON_SR:
> + case TURN_OFF_SR:
> + case ICC_LDO_TURN_OFF_SR:
> + case ICC_BUCK_TURN_OFF_SR:
> + case RREQ_STATE_SR:
> + case INT_PENDING_R1:
> + case INT_PENDING_R2:
> + case INT_PENDING_R3:
> + case INT_PENDING_R4:
> + case INT_SRC_R1:
> + case INT_SRC_R2:
> + case INT_SRC_R3:
> + case INT_SRC_R4:
> + case WCHDG_CR:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static int stpmic1_configure_from_dt(struct stpmic1_dev *pmic_dev)
> +{
> + struct device_node *np = pmic_dev->np;
> + u32 reg;
> + int ret, irq;
> +
> + irq = of_irq_get(np, 0);
> + if (irq <= 0) {
> + dev_err(pmic_dev->dev,
If you do:
struct device *dev = pmic_dev->dev
... above you can save yourself a line break.
> + "Failed to get irq config: %d\n", irq);
Failed to get the config, or the IRQ?
Also should be "IRQ".
> + return irq ?: -ENODEV;
Is 0 not a valid IRQ? If not, what does it mean in this use-case?
> + }
> + pmic_dev->irq = irq;
> +
> + irq = of_irq_get(np, 1);
Please define what '0' and '1' mean here.
> + if (irq > 0)
> + pmic_dev->irq_wake = irq;
> + else
> + pmic_dev->irq_wake = pmic_dev->irq;
> +
> + device_init_wakeup(pmic_dev->dev, true);
'\n' here.
> + ret = dev_pm_set_dedicated_wake_irq(pmic_dev->dev, pmic_dev->irq_wake);
> + if (ret)
> + dev_warn(pmic_dev->dev, "failed to set up wakeup irq");
"IRQ"
> + if (!of_property_read_u32(np, "st,main-control-register", ®)) {
> + ret = regmap_update_bits(pmic_dev->regmap,
> + SWOFF_PWRCTRL_CR,
> + PWRCTRL_POLARITY_HIGH |
> + PWRCTRL_PIN_VALID |
> + RESTART_REQUEST_ENABLED,
> + reg);
> + if (ret) {
> + dev_err(pmic_dev->dev,
> + "Failed to update main control register: %d\n",
> + ret);
> + return ret;
> + }
> + }
> +
> + if (!of_property_read_u32(np, "st,pads-pull-register", ®)) {
> + ret = regmap_update_bits(pmic_dev->regmap,
> + PADS_PULL_CR,
> + WAKEUP_DETECTOR_DISABLED |
> + PWRCTRL_PD_ACTIVE |
> + PWRCTRL_PU_ACTIVE |
> + WAKEUP_PD_ACTIVE,
> + reg);
> + if (ret) {
> + dev_err(pmic_dev->dev,
> + "Failed to update pads control register: %d\n",
> + ret);
> + return ret;
> + }
> + }
> +
> + if (!of_property_read_u32(np, "st,vin-control-register", ®)) {
> + ret = regmap_update_bits(pmic_dev->regmap,
> + VBUS_DET_VIN_CR,
> + VINLOW_CTRL_REG_MASK,
> + reg);
> + if (ret) {
> + dev_err(pmic_dev->dev,
> + "Failed to update vin control register: %d\n",
> + ret);
> + return ret;
> + }
> + }
> +
> + if (!of_property_read_u32(np, "st,usb-control-register", ®)) {
> + ret = regmap_update_bits(pmic_dev->regmap, BST_SW_CR,
> + BOOST_OVP_DISABLED |
> + VBUS_OTG_DETECTION_DISABLED |
> + SW_OUT_DISCHARGE |
> + VBUS_OTG_DISCHARGE |
> + OCP_LIMIT_HIGH,
> + reg);
> + if (ret) {
> + dev_err(pmic_dev->dev,
> + "Failed to update usb control register: %d\n",
> + ret);
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +int stpmic1_device_init(struct stpmic1_dev *pmic_dev)
> +{
> + int ret;
> + unsigned int val;
> +
> + pmic_dev->regmap =
> + devm_regmap_init_i2c(pmic_dev->i2c, &stpmic1_regmap_config);
> +
Remove this line.
> + if (IS_ERR(pmic_dev->regmap))
> + return PTR_ERR(pmic_dev->regmap);
> +
> + ret = stpmic1_configure_from_dt(pmic_dev);
You don't need all of these separate probe/init/setup functions if you;
a) always call them and b) call them only once from a single call-site.
If you *really* want to break-up probe() just pull out the DT stuff,
but in all honesty, I wouldn't worry about it.
> + if (ret) {
> + dev_err(pmic_dev->dev,
> + "Unable to configure PMIC from Device Tree: %d\n", ret);
> + return ret;
> + }
> +
> + /* Read Version ID */
> + ret = regmap_read(pmic_dev->regmap, VERSION_SR, &val);
> + if (ret) {
> + dev_err(pmic_dev->dev, "Unable to read pmic version\n");
> + return ret;
> + }
> + dev_info(pmic_dev->dev, "PMIC Chip Version: 0x%x\n", val);
> +
> + /* Initialize PMIC IRQ Chip & IRQ domains associated */
> + ret = devm_regmap_add_irq_chip(pmic_dev->dev, pmic_dev->regmap,
> + pmic_dev->irq,
> + IRQF_ONESHOT | IRQF_SHARED,
> + 0, &stpmic1_regmap_irq_chip,
> + &pmic_dev->irq_data);
> + if (ret) {
> + dev_err(pmic_dev->dev, "IRQ Chip registration failed: %d\n",
> + ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int stpmic1_probe(struct i2c_client *i2c,
> + const struct i2c_device_id *id)
> +{
> + struct stpmic1_dev *pmic;
Remove the "_dev" and instead of 'pmic' please use 'ddata'.
Although I don't see the point in having device data at all. What do
you use it for besides passing to functions called from probe()?
> + struct device *dev = &i2c->dev;
> + int ret;
> +
> + pmic = devm_kzalloc(dev, sizeof(struct stpmic1_dev), GFP_KERNEL);
> + if (!pmic)
> + return -ENOMEM;
> +
> + pmic->np = dev->of_node;
> +
> + dev_set_drvdata(dev, pmic);
Looks like either this or the i2c_get_clientdata() call is not
correct. Have you tested suspend/resume? I suggest you do not use
i2c_get_clientdata().
> + pmic->dev = dev;
> + pmic->i2c = i2c;
> +
> + ret = stpmic1_device_init(pmic);
> + if (ret)
> + return ret;
> +
> + return devm_of_platform_populate(pmic->dev);
> +}
> +
> +static const struct i2c_device_id stpmic1_id[] = {
> + { "stpmic1", 0 },
Why 0?
> + {}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, stpmic1_id);
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int stpmic1_suspend(struct device *dev)
> +{
> + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
Should use to_i2c_client().
> + struct stpmic1_dev *pmic_dev = i2c_get_clientdata(i2c);
How does this 'struct stpmic1_dev *' get into there?
Also, if you put it into dev->driver_data instead, it will save a few
lines.
> + if (device_may_wakeup(dev))
> + enable_irq_wake(pmic_dev->irq_wake);
> +
> + disable_irq(pmic_dev->irq);
'\n' here.
> + return 0;
> +}
> +
> +static int stpmic1_resume(struct device *dev)
> +{
> + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
> + struct stpmic1_dev *pmic_dev = i2c_get_clientdata(i2c);
As above.
> + int ret;
> +
> + ret = regcache_sync(pmic_dev->regmap);
> + if (ret)
> + return ret;
> +
> + if (device_may_wakeup(dev))
> + disable_irq_wake(pmic_dev->irq_wake);
> +
> + enable_irq(pmic_dev->irq);
'\n' here.
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(stpmic1_pm, stpmic1_suspend, stpmic1_resume);
> +
> +static struct i2c_driver stpmic1_driver = {
> + .driver = {
> + .name = "stpmic1",
> + .pm = &stpmic1_pm,
> + },
Odd tabbing.
> + .probe = stpmic1_probe,
> + .id_table = stpmic1_id,
> +};
> +
> +module_i2c_driver(stpmic1_driver);
> +
> +MODULE_DESCRIPTION("STPMIC1 PMIC Driver");
> +MODULE_AUTHOR("Pascal Paillet");
This normally contains an email address.
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/stpmic1.h b/include/linux/mfd/stpmic1.h
> new file mode 100644
> index 0000000..024769d
> --- /dev/null
> +++ b/include/linux/mfd/stpmic1.h
> @@ -0,0 +1,220 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
> + * Author: Philippe Peurichard <[email protected]>,
> + * Pascal Paillet <[email protected]> for STMicroelectronics.
> + */
> +
> +#ifndef __LINUX_MFD_STPMIC1_H
> +#define __LINUX_MFD_STPMIC1_H
> +
> +#define TURN_ON_SR 0x1
> +#define TURN_OFF_SR 0x2
> +#define ICC_LDO_TURN_OFF_SR 0x3
> +#define ICC_BUCK_TURN_OFF_SR 0x4
> +#define RREQ_STATE_SR 0x5
> +#define VERSION_SR 0x6
> +
> +#define SWOFF_PWRCTRL_CR 0x10
> +#define PADS_PULL_CR 0x11
> +#define BUCKS_PD_CR 0x12
> +#define LDO14_PD_CR 0x13
> +#define LDO56_VREF_PD_CR 0x14
> +#define VBUS_DET_VIN_CR 0x15
> +#define PKEY_TURNOFF_CR 0x16
> +#define BUCKS_MASK_RANK_CR 0x17
> +#define BUCKS_MASK_RESET_CR 0x18
> +#define LDOS_MASK_RANK_CR 0x19
> +#define LDOS_MASK_RESET_CR 0x1A
> +#define WCHDG_CR 0x1B
> +#define WCHDG_TIMER_CR 0x1C
> +#define BUCKS_ICCTO_CR 0x1D
> +#define LDOS_ICCTO_CR 0x1E
> +
> +#define BUCK1_ACTIVE_CR 0x20
> +#define BUCK2_ACTIVE_CR 0x21
> +#define BUCK3_ACTIVE_CR 0x22
> +#define BUCK4_ACTIVE_CR 0x23
> +#define VREF_DDR_ACTIVE_CR 0x24
> +#define LDO1_ACTIVE_CR 0x25
> +#define LDO2_ACTIVE_CR 0x26
> +#define LDO3_ACTIVE_CR 0x27
> +#define LDO4_ACTIVE_CR 0x28
> +#define LDO5_ACTIVE_CR 0x29
> +#define LDO6_ACTIVE_CR 0x2A
> +
> +#define BUCK1_STDBY_CR 0x30
> +#define BUCK2_STDBY_CR 0x31
> +#define BUCK3_STDBY_CR 0x32
> +#define BUCK4_STDBY_CR 0x33
> +#define VREF_DDR_STDBY_CR 0x34
> +#define LDO1_STDBY_CR 0x35
> +#define LDO2_STDBY_CR 0x36
> +#define LDO3_STDBY_CR 0x37
> +#define LDO4_STDBY_CR 0x38
> +#define LDO5_STDBY_CR 0x39
> +#define LDO6_STDBY_CR 0x3A
> +
> +#define BST_SW_CR 0x40
> +
> +#define INT_PENDING_R1 0x50
> +#define INT_PENDING_R2 0x51
> +#define INT_PENDING_R3 0x52
> +#define INT_PENDING_R4 0x53
> +
> +#define INT_DBG_LATCH_R1 0x60
> +#define INT_DBG_LATCH_R2 0x61
> +#define INT_DBG_LATCH_R3 0x62
> +#define INT_DBG_LATCH_R4 0x63
> +
> +#define INT_CLEAR_R1 0x70
> +#define INT_CLEAR_R2 0x71
> +#define INT_CLEAR_R3 0x72
> +#define INT_CLEAR_R4 0x73
> +
> +#define INT_MASK_R1 0x80
> +#define INT_MASK_R2 0x81
> +#define INT_MASK_R3 0x82
> +#define INT_MASK_R4 0x83
> +
> +#define INT_SET_MASK_R1 0x90
> +#define INT_SET_MASK_R2 0x91
> +#define INT_SET_MASK_R3 0x92
> +#define INT_SET_MASK_R4 0x93
> +
> +#define INT_CLEAR_MASK_R1 0xA0
> +#define INT_CLEAR_MASK_R2 0xA1
> +#define INT_CLEAR_MASK_R3 0xA2
> +#define INT_CLEAR_MASK_R4 0xA3
> +
> +#define INT_SRC_R1 0xB0
> +#define INT_SRC_R2 0xB1
> +#define INT_SRC_R3 0xB2
> +#define INT_SRC_R4 0xB3
> +
> +#define PMIC_MAX_REGISTER_ADDRESS INT_SRC_R4
> +
> +#define STPMIC1_PMIC_NUM_IRQ_REGS 4
> +
> +#define TURN_OFF_SR_ICC_EVENT 0x08
> +
> +#define LDO_VOLTAGE_MASK GENMASK(6, 2)
> +#define BUCK_VOLTAGE_MASK GENMASK(7, 2)
> +#define LDO_BUCK_VOLTAGE_SHIFT 2
> +
> +#define LDO_ENABLE_MASK BIT(0)
> +#define BUCK_ENABLE_MASK BIT(0)
> +
> +#define BUCK_HPLP_ENABLE_MASK BIT(1)
> +#define BUCK_HPLP_SHIFT 1
> +
> +#define STDBY_ENABLE_MASK BIT(0)
> +
> +#define BUCKS_PD_CR_REG_MASK GENMASK(7, 0)
> +#define BUCK_MASK_RANK_REGISTER_MASK GENMASK(3, 0)
> +#define BUCK_MASK_RESET_REGISTER_MASK GENMASK(3, 0)
> +#define LDO1234_PULL_DOWN_REGISTER_MASK GENMASK(7, 0)
> +#define LDO56_VREF_PD_CR_REG_MASK GENMASK(5, 0)
> +#define LDO_MASK_RANK_REGISTER_MASK GENMASK(5, 0)
> +#define LDO_MASK_RESET_REGISTER_MASK GENMASK(5, 0)
> +
> +#define BUCK1_PULL_DOWN_REG BUCKS_PD_CR
> +#define BUCK1_PULL_DOWN_MASK BIT(0)
> +#define BUCK2_PULL_DOWN_REG BUCKS_PD_CR
> +#define BUCK2_PULL_DOWN_MASK BIT(2)
> +#define BUCK3_PULL_DOWN_REG BUCKS_PD_CR
> +#define BUCK3_PULL_DOWN_MASK BIT(4)
> +#define BUCK4_PULL_DOWN_REG BUCKS_PD_CR
> +#define BUCK4_PULL_DOWN_MASK BIT(6)
> +
> +#define LDO1_PULL_DOWN_REG LDO14_PD_CR
> +#define LDO1_PULL_DOWN_MASK BIT(0)
> +#define LDO2_PULL_DOWN_REG LDO14_PD_CR
> +#define LDO2_PULL_DOWN_MASK BIT(2)
> +#define LDO3_PULL_DOWN_REG LDO14_PD_CR
> +#define LDO3_PULL_DOWN_MASK BIT(4)
> +#define LDO4_PULL_DOWN_REG LDO14_PD_CR
> +#define LDO4_PULL_DOWN_MASK BIT(6)
> +#define LDO5_PULL_DOWN_REG LDO56_VREF_PD_CR
> +#define LDO5_PULL_DOWN_MASK BIT(0)
> +#define LDO6_PULL_DOWN_REG LDO56_VREF_PD_CR
> +#define LDO6_PULL_DOWN_MASK BIT(2)
> +#define VREF_DDR_PULL_DOWN_REG LDO56_VREF_PD_CR
> +#define VREF_DDR_PULL_DOWN_MASK BIT(4)
> +
> +#define BUCKS_ICCTO_CR_REG_MASK GENMASK(6, 0)
> +#define LDOS_ICCTO_CR_REG_MASK GENMASK(5, 0)
> +
> +#define LDO_BYPASS_MASK BIT(7)
> +
> +/* Main PMIC Control Register
> + * SWOFF_PWRCTRL_CR
> + * Address : 0x10
> + */
> +#define ICC_EVENT_ENABLED BIT(4)
> +#define PWRCTRL_POLARITY_HIGH BIT(3)
> +#define PWRCTRL_PIN_VALID BIT(2)
> +#define RESTART_REQUEST_ENABLED BIT(1)
> +#define SOFTWARE_SWITCH_OFF_ENABLED BIT(0)
> +
> +/* Main PMIC PADS Control Register
> + * PADS_PULL_CR
> + * Address : 0x11
> + */
> +#define WAKEUP_DETECTOR_DISABLED BIT(4)
> +#define PWRCTRL_PD_ACTIVE BIT(3)
> +#define PWRCTRL_PU_ACTIVE BIT(2)
> +#define WAKEUP_PD_ACTIVE BIT(1)
> +#define PONKEY_PU_ACTIVE BIT(0)
> +
> +/* Main PMIC VINLOW Control Register
> + * VBUS_DET_VIN_CRC DMSC
> + * Address : 0x15
> + */
> +#define SWIN_DETECTOR_ENABLED BIT(7)
> +#define SWOUT_DETECTOR_ENABLED BIT(6)
> +#define VINLOW_ENABLED BIT(0)
> +#define VINLOW_CTRL_REG_MASK GENMASK(7, 0)
> +
> +/* USB Control Register
> + * Address : 0x40
> + */
> +#define BOOST_OVP_DISABLED BIT(7)
> +#define VBUS_OTG_DETECTION_DISABLED BIT(6)
> +#define SW_OUT_DISCHARGE BIT(5)
> +#define VBUS_OTG_DISCHARGE BIT(4)
> +#define OCP_LIMIT_HIGH BIT(3)
> +#define SWIN_SWOUT_ENABLED BIT(2)
> +#define USBSW_OTG_SWITCH_ENABLED BIT(1)
> +#define BOOST_ENABLED BIT(0)
> +
> +/* PKEY_TURNOFF_CR
> + * Address : 0x16
> + */
> +#define PONKEY_PWR_OFF BIT(7)
> +#define PONKEY_CC_FLAG_CLEAR BIT(6)
> +#define PONKEY_TURNOFF_TIMER_MASK GENMASK(3, 0)
> +#define PONKEY_TURNOFF_MASK GENMASK(7, 0)
> +
> +/*
> + * struct stpmic1_dev - stpmic1 master device for sub-drivers
> + * @dev: master device of the chip (can be used to access platform data)
> + * @i2c: i2c client private data for regulator
> + * @np: device DT node pointer
> + * @irq_base: base IRQ numbers
> + * @irq: generic IRQ number
> + * @irq_wake: wakeup IRQ number
> + * @regmap_irq_chip_data: irq chip data
> + */
What do you do with this?
I'm fairly sure not all of these attributes are required.
> +struct stpmic1_dev {
> + struct device *dev;
> + struct i2c_client *i2c;
> + struct regmap *regmap;
> + struct device_node *np;
> + unsigned int irq_base;
> + int irq;
> + int irq_wake;
> + struct regmap_irq_chip_data *irq_data;
> +};
> +
> +#endif /* __LINUX_MFD_STPMIC1_H */
--
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
On Fri, Sep 07, 2018 at 12:59:43PM +0000, Pascal PAILLET-LME wrote:
> From: pascal paillet <[email protected]>
>
> stpmic1 is a pmic from STMicroelectronics. The stpmic1 integrates 10
> regulators and 3 switches with various capabilities.
>
> Signed-off-by: pascal paillet <[email protected]>
> ---
> changes in v2:
> * the hardware component has been renamed from stpmu1 to stpmic1 !
> * replace _ with - in properties name
> * fix node names in example
> * remove regulator compatibles in example
> * add st,stpmic1.h to the patch
>
> Rob, I did not change the usage of the properties because it would lead to a lot
> of st properties; for example st,main-control-register would be replaced by:
> st,power_cycling_on_turn_off
> st,pwrctrl_enabled
> st,pwrctrl_active_high
> should I go this way ?
I guess it is fine as-is.
> Rob, I did not found the standard property for st,onkey-press-seconds = <10>;
From input/keys.txt:
Optional properties for Keys:
- power-off-time-sec: Duration in seconds which the key should be kept
pressed for device to power off automatically. Device with key
pressed
shutdown feature can specify this property.
>
> .../devicetree/bindings/mfd/st,stpmic1.txt | 137 +++++++++++++++++++++
> include/dt-bindings/mfd/st,stpmic1.h | 46 +++++++
> 2 files changed, 183 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmic1.txt
> create mode 100644 include/dt-bindings/mfd/st,stpmic1.h
>
> diff --git a/Documentation/devicetree/bindings/mfd/st,stpmic1.txt b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt
> new file mode 100644
> index 0000000..9f2c516
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt
> @@ -0,0 +1,137 @@
> +* STMicroelectronics STPMIC1 Power Management IC
> +
> +Required parent device properties:
> +- compatible: "st,stpmic1"
> +- reg: the I2C slave address for the stpmic1 chip
> +- interrupts-extended: interrupt lines to use: second irq is for wakeup.
Just use 'interrupts' here. Support for interrupts-extended is implied.
> +- #interrupt-cells: should be 2.
> +- interrupt-controller: describes the STPMIC1 as an interrupt
> + controller (has its own domain). interrupt number are the following:
> + /* Interrupt Register 1 (0x50 for latch) */
> + IT_SWOUT_R=0
> + IT_SWOUT_F=1
> + IT_VBUS_OTG_R=2
> + IT_VBUS_OTG_F=3
> + IT_WAKEUP_R=4
> + IT_WAKEUP_F=5
> + IT_PONKEY_R=6
> + IT_PONKEY_F=7
> + /* Interrupt Register 2 (0x51 for latch) */
> + IT_OVP_BOOST=8
> + IT_OCP_BOOST=9
> + IT_OCP_SWOUT=10
> + IT_OCP_OTG=11
> + IT_CURLIM_BUCK4=12
> + IT_CURLIM_BUCK3=13
> + IT_CURLIM_BUCK2=14
> + IT_CURLIM_BUCK1=15
> + /* Interrupt Register 3 (0x52 for latch) */
> + IT_SHORT_SWOUT=16
> + IT_SHORT_SWOTG=17
> + IT_CURLIM_LDO6=18
> + IT_CURLIM_LDO5=19
> + IT_CURLIM_LDO4=20
> + IT_CURLIM_LDO3=21
> + IT_CURLIM_LDO2=22
> + IT_CURLIM_LDO1=23
> + /* Interrupt Register 3 (0x52 for latch) */
> + IT_SWIN_R=24
> + IT_SWIN_F=25
> + IT_RESERVED_1=26
> + IT_RESERVED_2=27
> + IT_VINLOW_R=28
> + IT_VINLOW_F=29
> + IT_TWARN_R=30
> + IT_TWARN_F=31
> +
> +Optional parent device properties:
> +- st,main-control-register:
> + -bit 1: Power cycling will be performed on turn OFF condition
> + -bit 2: PWRCTRL is functional
> + -bit 3: PWRCTRL active high
> +- st,pads-pull-register:
> + -bit 1: WAKEUP pull down is not active
> + -bit 2: PWRCTRL pull up is active
> + -bit 3: PWRCTRL pull down is active
> + -bit 4: WAKEUP detector is disabled
> +- st,vin-control-register:
> + -bit 0: VINLOW monitoring is enabled
> + -bit [1...3]: VINLOW rising threshold
> + 000 VINOK_f + 50mV
> + 001 VINOK_f + 100mV
> + 010 VINOK_f + 150mV
> + 011 VINOK_f + 200mV
> + 100 VINOK_f + 250mV
> + 101 VINOK_f + 300mV
> + 110 VINOK_f + 350mV
> + 111 VINOK_f + 400mV
> + -bit [4...5]: VINLOW hyst
> + 00 100mV
> + 01 200mV
> + 10 300mV
> + 11 400mV
> + -bit 6: SW_OUT detector is disabled
> + -bit 7: SW_IN detector is enabled.
> +- st,usb-control-register:
> + -bit 3: SW_OUT current limit
> + 0: 600mA
> + 1: 1.1A
> + -bit 4: VBUS_OTG discharge is enabled
> + -bit 5: SW_OUT discharge is enabled
> + -bit 6: VBUS_OTG detection is enabled
> + -bit 7: BOOST_OVP is disabled
> +
> +
> +stpmic1 consists is a varied group of sub-devices:
> +
> +Device Description
> +------ ------------
> +st,stpmic1-onkey : On key
> +st,stpmic1-regulators : Regulators
> +st,stpmic1-wdt : Watchdog
> +
> +each sub-device bindings is be described in associated driver
> +documentation section.
> +
> +Example:
> +
> +pmic: stpmic1@33 {
pmic@33
> + compatible = "st,stpmic1";
> + reg = <0x33>;
> + interrupts = <0 2>;
> + interrupts-extended = <&intc GIC_SPI 149 IRQ_TYPE_NONE>,
> + <&exti 55 1>;
> + st,version_status = <0x10>;
> + st,main-control-register=<0x0c>;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> + onkey {
> + compatible = "st,stpmic1-onkey";
> + interrupt-parent = <&pmic>;
> + interrupts = <IT_PONKEY_F 0>,<IT_PONKEY_R 1>;
> + interrupt-names = "onkey-falling", "onkey-rising";
> + st,onkey-pwroff-enabled;
> + st,onkey-long-press-seconds = <10>;
> + };
> +
> + watchdog {
> + compatible = "st,stpmic1-wdt";
> + };
> +
> + regulators {
> + compatible = "st,stpmic1-regulators";
> +
> + vdd_core: buck1 {
> + regulator-name = "vdd_core";
> + regulator-boot-on;
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <1200000>;
> + };
> + vdd: buck3 {
> + regulator-name = "vdd";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-boot-on;
> + regulator-pull-down;
> + };
> + };
> diff --git a/include/dt-bindings/mfd/st,stpmic1.h b/include/dt-bindings/mfd/st,stpmic1.h
> new file mode 100644
> index 0000000..e32ac8f
> --- /dev/null
> +++ b/include/dt-bindings/mfd/st,stpmic1.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
> + * Author: Philippe Peurichard <[email protected]>,
> + * Pascal Paillet <[email protected]> for STMicroelectronics.
> + */
> +
> +#ifndef __DT_BINDINGS_STPMIC1_H__
> +#define __DT_BINDINGS_STPMIC1_H__
> +
> +/* IRQ definitions */
> +#define IT_PONKEY_F 0
> +#define IT_PONKEY_R 1
> +#define IT_WAKEUP_F 2
> +#define IT_WAKEUP_R 3
> +#define IT_VBUS_OTG_F 4
> +#define IT_VBUS_OTG_R 5
> +#define IT_SWOUT_F 6
> +#define IT_SWOUT_R 7
> +
> +#define IT_CURLIM_BUCK1 8
> +#define IT_CURLIM_BUCK2 9
> +#define IT_CURLIM_BUCK3 10
> +#define IT_CURLIM_BUCK4 11
> +#define IT_OCP_OTG 12
> +#define IT_OCP_SWOUT 13
> +#define IT_OCP_BOOST 14
> +#define IT_OVP_BOOST 15
> +
> +#define IT_CURLIM_LDO1 16
> +#define IT_CURLIM_LDO2 17
> +#define IT_CURLIM_LDO3 18
> +#define IT_CURLIM_LDO4 19
> +#define IT_CURLIM_LDO5 20
> +#define IT_CURLIM_LDO6 21
> +#define IT_SHORT_SWOTG 22
> +#define IT_SHORT_SWOUT 23
> +
> +#define IT_TWARN_F 24
> +#define IT_TWARN_R 25
> +#define IT_VINLOW_F 26
> +#define IT_VINLOW_R 27
> +#define IT_SWIN_F 30
> +#define IT_SWIN_R 31
> +
> +#endif /* __DT_BINDINGS_STPMIC1_H__ */
> --
> 1.9.1
On Fri, Sep 07, 2018 at 12:59:44PM +0000, Pascal PAILLET-LME wrote:
> From: pascal paillet <[email protected]>
>
> The STPMIC1 regulators supply power to the application processor as well as
> to the external system peripherals such as DDR, Flash memories and system
> devices.
>
> Signed-off-by: pascal paillet <[email protected]>
> ---
> changes in v2:
> * the hardware component has been renamed from stpmu1 to stpmic1 !
> * replace _ with - in properties name
> * remove deprecated regulator-compatible from the example
> Rob, the st,mask_reset name is taken from the data-sheet. I have modified the
> description. I hope there is no confusion with bit masking know.
>
> .../bindings/regulator/st,stpmic1-regulator.txt | 68 ++++++++++++++++++++++
> 1 file changed, 68 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt
>
> diff --git a/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt b/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt
> new file mode 100644
> index 0000000..a3f4762
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt
> @@ -0,0 +1,68 @@
> +STMicroelectronics STPMIC1 Voltage regulators
> +
> +Regulator Nodes are optional depending on needs.
> +
> +Available Regulators in STPMIC1 device are:
> + - buck1 for Buck BUCK1
> + - buck2 for Buck BUCK2
> + - buck3 for Buck BUCK3
> + - buck4 for Buck BUCK4
> + - ldo1 for LDO LDO1
> + - ldo2 for LDO LDO2
> + - ldo3 for LDO LDO3
> + - ldo4 for LDO LDO4
> + - ldo5 for LDO LDO5
> + - ldo6 for LDO LDO6
> + - vref_ddr for LDO Vref DDR
> + - boost for Buck BOOST
> + - pwr_sw1 for VBUS_OTG switch
> + - pwr_sw2 for SW_OUT switch
> +
> +Switches are fixed voltage regulators with only enable/disable capability.
> +
> +Optional properties:
> +- st,mask-reset: mask reset for this regulator: the regulator configuration
> + is maintained during pmic reset.
State this is boolean.
> +- regulator-pull-down: enable high pull down
> + if not specified light pull down is used
> +- regulator-over-current-protection:
> + if set, all regulators are switched off in case of over-current detection
> + on this regulator,
> + if not set, the driver only sends an over-current event.
> +- interrupt-parent: phandle to the parent interrupt controller
This is implied. And actually not needed as the parent node is an
'interrupt-controller'.
> +- interrupts: index of current limit detection interrupt
> +- <regulator>-supply: phandle to the parent supply/regulator node
> + each regulator supply can be described except vref_ddr.
> +
> +Example:
> +regulators {
> + compatible = "st,stpmic1-regulators";
> +
> + ldo6-supply = <&v3v3>;
> +
> + vdd_core: buck1 {
> + regulator-name = "vdd_core";
> + interrupts = <IT_CURLIM_BUCK1 0>;
> + interrupt-parent = <&pmic>;
> + st,mask-reset;
> + regulator-pull-down;
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <1200000>;
> + };
> +
> + v3v3: buck4 {
> + regulator-name = "v3v3";
> + interrupts = <IT_CURLIM_BUCK4 0>;
> + interrupt-parent = <&mypmic>;
Different parent from buck1?
> +
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> + };
> +
> + v1v8: ldo6 {
> + regulator-name = "v1v8";
> + regulator-min-microvolt = <1800000>;
> + regulator-max-microvolt = <1800000>;
> + regulator-over-current-protection;
> + };
> +};
> --
> 1.9.1
On Fri, Sep 07, 2018 at 12:59:45PM +0000, Pascal PAILLET-LME wrote:
> From: pascal paillet <[email protected]>
>
> The stpmic1 pmic is able to manage an onkey button. It can be configured
> to shut-down the power supplies on a long key-press with an adjustable
> duration.
>
> Signed-off-by: pascal paillet <[email protected]>
> ---
> .../devicetree/bindings/input/st,stpmic1-onkey.txt | 31 ++++++++++++++++++++++
> 1 file changed, 31 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt
>
> diff --git a/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt b/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt
> new file mode 100644
> index 0000000..b0db90d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt
> @@ -0,0 +1,31 @@
> +STMicroelectronics STPMIC1 Onkey
> +
> +Required properties:
> +
> +- compatible = "st,stpmic1-onkey";
> +- interrupt-parent: phandle to the parent interrupt controller
> +- interrupts: interrupt line to use
> +- interrupt-names = "onkey-falling", "onkey-rising"
> + onkey-falling: happens when onkey is pressed; IT_PONKEY_F of pmic
> + onkey-rising: happens when onkey is released; IT_PONKEY_R of pmic
> +
> +Optional properties:
> +
> +- st,onkey-pwroff-enabled: power off on long key-press
This can be implied by the presence of 'power-off-time-sec' or not.
> +- st,onkey-long-press-seconds: long key-press duration from 1 to 16s
> + (default 16s)
> +- st,onkey-clear-cc-flag: onkey is able power on after an
> + over-current shutdown event.
> +- st,onkey-pu-inactive: onkey pull up is not active
> +
> +Example:
> +
> +onkey {
> + compatible = "st,stpmic1-onkey";
> + interrupt-parent = <&pmic>;
> + interrupts = <IT_PONKEY_F 0>,<IT_PONKEY_R 1>;
> + interrupt-names = "onkey-falling", "onkey-rising";
> + status = "okay";
Don't show status in examples.
> + st,onkey-pwroff-enabled;
> + st,onkey-long-press-seconds = <10>;
> +};
> --
> 1.9.1
On Fri, 7 Sep 2018 12:59:45 +0000, Pascal PAILLET-LME wrote:
> From: pascal paillet <[email protected]>
>
> The stpmic1 PMIC embeds a watchdog which is disabled by default.
> In case of watchdog, the PMIC goes off.
>
> Signed-off-by: pascal paillet <[email protected]>
> ---
> Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt | 11 +++++++++++
> 1 file changed, 11 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt
>
Reviewed-by: Rob Herring <[email protected]>