linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/8] Introduce STPMU1 PMIC Driver
@ 2018-07-05 15:14 Pascal PAILLET-LME
  2018-07-05 15:14 ` [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver Pascal PAILLET-LME
                   ` (7 more replies)
  0 siblings, 8 replies; 17+ messages in thread
From: Pascal PAILLET-LME @ 2018-07-05 15:14 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, lee.jones, lgirdwood,
	broonie, wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard
  Cc: Pascal PAILLET-LME

From: pascal paillet <p.paillet@st.com>

The goal of this patch-set is to propose a driver for the STPMU1 PMIC from 
ST Microelectronics. 
The STPMU1 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 STPMU1 is controlled via I2C. 

Main driver is drivers/mfd/stpmu1 that handle I2C regmap configuration and
irqchip. stpmu1_regulator, stpmu1_onkey and stpmu1_wdt need stpmu1 mfd
as parent.

stpmu1 mfd regulator drivers maybe mandatory at boot time.

*** BLURB HERE ***

pascal paillet (8):
  dt-bindings: mfd: document stpmu1 pmic
  mfd: stpmu1: add stpmu1 pmic driver
  dt-bindings: regulator: document stpmu1 pmic regulators
  regulator: stpmu1: add stpmu1 regulator driver
  dt-bindings: input: document stpmu1 pmic onkey
  input: stpmu1: add stpmu1 onkey driver
  dt-bindings: watchdog: document stpmu1 pmic watchdog
  watchdog: stpmu1: add stpmu1 watchdog driver

 .../devicetree/bindings/input/st,stpmu1-onkey.txt  |  31 +
 .../devicetree/bindings/mfd/st,stpmu1.txt          | 138 ++++
 .../bindings/regulator/st,stpmu1-regulator.txt     |  72 ++
 .../devicetree/bindings/watchdog/st,stpmu1-wdt.txt |  11 +
 drivers/input/misc/Kconfig                         |  11 +
 drivers/input/misc/Makefile                        |   2 +
 drivers/input/misc/stpmu1_onkey.c                  | 321 +++++++
 drivers/mfd/Kconfig                                |  14 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/stpmu1.c                               | 490 +++++++++++
 drivers/regulator/Kconfig                          |  12 +
 drivers/regulator/Makefile                         |   2 +
 drivers/regulator/stpmu1_regulator.c               | 919 +++++++++++++++++++++
 drivers/watchdog/Kconfig                           |  12 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/stpmu1_wdt.c                      | 177 ++++
 include/dt-bindings/mfd/st,stpmu1.h                |  46 ++
 include/linux/mfd/stpmu1.h                         | 220 +++++
 18 files changed, 2480 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/st,stpmu1-onkey.txt
 create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmu1.txt
 create mode 100644 Documentation/devicetree/bindings/regulator/st,stpmu1-regulator.txt
 create mode 100644 Documentation/devicetree/bindings/watchdog/st,stpmu1-wdt.txt
 create mode 100644 drivers/input/misc/stpmu1_onkey.c
 create mode 100644 drivers/mfd/stpmu1.c
 create mode 100644 drivers/regulator/stpmu1_regulator.c
 create mode 100644 drivers/watchdog/stpmu1_wdt.c
 create mode 100644 include/dt-bindings/mfd/st,stpmu1.h
 create mode 100644 include/linux/mfd/stpmu1.h

-- 
1.9.1

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

* [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver
  2018-07-05 15:14 [PATCH 0/8] Introduce STPMU1 PMIC Driver Pascal PAILLET-LME
@ 2018-07-05 15:14 ` Pascal PAILLET-LME
  2018-07-09 22:38   ` Enric Balletbo Serra
  2018-07-16 22:15   ` Rob Herring
  2018-07-05 15:14 ` [PATCH 1/8] dt-bindings: mfd: document stpmu1 pmic Pascal PAILLET-LME
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 17+ messages in thread
From: Pascal PAILLET-LME @ 2018-07-05 15:14 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, lee.jones, lgirdwood,
	broonie, wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard
  Cc: Pascal PAILLET-LME

From: pascal paillet <p.paillet@st.com>

stpmu1 is a pmic from STMicroelectronics. The stpmu1 integrates 10
regulators and 3 switches with various capabilities.

Signed-off-by: pascal paillet <p.paillet@st.com>
---
 drivers/mfd/Kconfig                 |  14 ++
 drivers/mfd/Makefile                |   1 +
 drivers/mfd/stpmu1.c                | 490 ++++++++++++++++++++++++++++++++++++
 include/dt-bindings/mfd/st,stpmu1.h |  46 ++++
 include/linux/mfd/stpmu1.h          | 220 ++++++++++++++++
 5 files changed, 771 insertions(+)
 create mode 100644 drivers/mfd/stpmu1.c
 create mode 100644 include/dt-bindings/mfd/st,stpmu1.h
 create mode 100644 include/linux/mfd/stpmu1.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index b860eb5..e15140b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1812,6 +1812,20 @@ config MFD_STM32_TIMERS
 	  for PWM and IIO Timer. This driver allow to share the
 	  registers between the others drivers.
 
+config MFD_STPMU1
+	tristate "Support for STPMU1 PMIC"
+	depends on (I2C=y && OF)
+	select REGMAP_I2C
+	select REGMAP_IRQ
+	select MFD_CORE
+	help
+	  Support for ST Microelectronics STPMU1 PMIC. Stpmu1 mfd driver is
+	  the core driver for stpmu1 component that mainly handles interrupts.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stpmu1.
+
+
 menu "Multimedia Capabilities Port drivers"
 	depends on ARCH_SA1100
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e9fd20d..f1c4be1 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_STPMU1)	+= stpmu1.o
 obj-$(CONFIG_MFD_SUN4I_GPADC)	+= sun4i-gpadc.o
 
 obj-$(CONFIG_MFD_STM32_LPTIMER)	+= stm32-lptimer.o
diff --git a/drivers/mfd/stpmu1.c b/drivers/mfd/stpmu1.c
new file mode 100644
index 0000000..a284a3e
--- /dev/null
+++ b/drivers/mfd/stpmu1.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Philippe Peurichard <philippe.peurichard@st.com>,
+ * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stpmu1.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/regmap.h>
+#include <dt-bindings/mfd/st,stpmu1.h>
+
+static bool stpmu1_reg_readable(struct device *dev, unsigned int reg);
+static bool stpmu1_reg_writeable(struct device *dev, unsigned int reg);
+static bool stpmu1_reg_volatile(struct device *dev, unsigned int reg);
+
+const struct regmap_config stpmu1_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_RBTREE,
+	.max_register = PMIC_MAX_REGISTER_ADDRESS,
+	.readable_reg = stpmu1_reg_readable,
+	.writeable_reg = stpmu1_reg_writeable,
+	.volatile_reg = stpmu1_reg_volatile,
+};
+
+#define FILL_IRQS(_index) \
+	[(_index)] = { \
+		.reg_offset = ((_index) >> 3), \
+		.mask = (1 << (_index % 8)), \
+	}
+
+static const struct regmap_irq stpmu1_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 stpmu1_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 = STPMU1_PMIC_NUM_IRQ_REGS,
+	.irqs = stpmu1_irqs,
+	.num_irqs = ARRAY_SIZE(stpmu1_irqs),
+};
+
+static bool stpmu1_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 stpmu1_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 stpmu1_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 stpmu1_configure_from_dt(struct stpmu1_dev *pmic_dev)
+{
+	struct device_node *np = pmic_dev->np;
+	u32 reg = 0;
+	int ret = 0;
+	int 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 ? irq : -ENODEV;
+	}
+	pmic_dev->irq = irq;
+
+	irq = of_irq_get(np, 1);
+	if (irq <= 0) {
+		dev_err(pmic_dev->dev,
+			"Failed to get irq_wake config: %d\n", irq);
+		return irq ? irq : -ENODEV;
+	}
+	pmic_dev->irq_wake = 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", &reg)) {
+		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", &reg)) {
+		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", &reg)) {
+		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", &reg)) {
+		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 stpmu1_device_init(struct stpmu1_dev *pmic_dev)
+{
+	int ret;
+	unsigned int val;
+
+	pmic_dev->regmap =
+	    devm_regmap_init_i2c(pmic_dev->i2c, &stpmu1_regmap_config);
+
+	if (IS_ERR(pmic_dev->regmap)) {
+		ret = PTR_ERR(pmic_dev->regmap);
+		dev_err(pmic_dev->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = stpmu1_configure_from_dt(pmic_dev);
+	if (ret < 0) {
+		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 < 0) {
+		dev_err(pmic_dev->dev, "Unable to read pmic version\n");
+		return ret;
+	}
+	dev_dbg(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, &stpmu1_regmap_irq_chip,
+				       &pmic_dev->irq_data);
+	if (ret < 0) {
+		dev_err(pmic_dev->dev, "IRQ Chip registration failed: %d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id stpmu1_dt_match[] = {
+	{.compatible = "st,stpmu1"},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, stpmu1_dt_match);
+
+static int stpmu1_remove(struct i2c_client *i2c)
+{
+	struct stpmu1_dev *pmic_dev = i2c_get_clientdata(i2c);
+
+	of_platform_depopulate(pmic_dev->dev);
+
+	return 0;
+}
+
+static int stpmu1_probe(struct i2c_client *i2c,
+			const struct i2c_device_id *id)
+{
+	struct stpmu1_dev *pmic;
+	struct device *dev = &i2c->dev;
+	int ret = 0;
+
+	pmic = devm_kzalloc(dev, sizeof(struct stpmu1_dev), GFP_KERNEL);
+	if (!pmic)
+		return -ENOMEM;
+
+	pmic->np = dev->of_node;
+
+	dev_set_drvdata(dev, pmic);
+	pmic->dev = dev;
+	pmic->i2c = i2c;
+
+	ret = stpmu1_device_init(pmic);
+	if (ret < 0)
+		goto err;
+
+	ret = of_platform_populate(pmic->np, NULL, NULL, pmic->dev);
+
+	dev_dbg(dev, "stpmu1 driver probed\n");
+err:
+	return ret;
+}
+
+static const struct i2c_device_id stpmu1_id[] = {
+	{"stpmu1", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, stpmu1_id);
+
+#ifdef CONFIG_PM_SLEEP
+static int stpmu1_suspend(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct stpmu1_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 stpmu1_resume(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct stpmu1_dev *pmic_dev = i2c_get_clientdata(i2c);
+
+	regcache_sync(pmic_dev->regmap);
+
+	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(stpmu1_pm, stpmu1_suspend, stpmu1_resume);
+
+static struct i2c_driver stpmu1_driver = {
+	.driver = {
+		   .name = "stpmu1",
+		   .owner = THIS_MODULE,
+		   .pm = &stpmu1_pm,
+		   .of_match_table = of_match_ptr(stpmu1_dt_match),
+		   },
+	.probe = stpmu1_probe,
+	.remove = stpmu1_remove,
+	.id_table = stpmu1_id,
+};
+
+module_i2c_driver(stpmu1_driver);
+
+MODULE_DESCRIPTION("STPMU1 PMIC I2C Client");
+MODULE_AUTHOR("<philippe.peurichard@st.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/dt-bindings/mfd/st,stpmu1.h b/include/dt-bindings/mfd/st,stpmu1.h
new file mode 100644
index 0000000..2d3bdf1
--- /dev/null
+++ b/include/dt-bindings/mfd/st,stpmu1.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Philippe Peurichard <philippe.peurichard@st.com>,
+ * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+ */
+
+#ifndef __DT_BINDINGS_STPMU1_H__
+#define __DT_BINDINGS_STPMU1_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_STPMU1_H__ */
diff --git a/include/linux/mfd/stpmu1.h b/include/linux/mfd/stpmu1.h
new file mode 100644
index 0000000..05fd029
--- /dev/null
+++ b/include/linux/mfd/stpmu1.h
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Philippe Peurichard <philippe.peurichard@st.com>,
+ * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+ */
+
+#ifndef __LINUX_MFD_STPMU1_H
+#define __LINUX_MFD_STPMU1_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 STPMU1_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 stpmu1_dev - stpmu1 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 stpmu1_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_STPMU1_H */
-- 
1.9.1

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

* [PATCH 1/8] dt-bindings: mfd: document stpmu1 pmic
  2018-07-05 15:14 [PATCH 0/8] Introduce STPMU1 PMIC Driver Pascal PAILLET-LME
  2018-07-05 15:14 ` [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver Pascal PAILLET-LME
@ 2018-07-05 15:14 ` Pascal PAILLET-LME
  2018-07-16 22:14   ` Rob Herring
  2018-07-05 15:14 ` [PATCH 3/8] dt-bindings: regulator: document stpmu1 pmic regulators Pascal PAILLET-LME
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Pascal PAILLET-LME @ 2018-07-05 15:14 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, lee.jones, lgirdwood,
	broonie, wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard
  Cc: Pascal PAILLET-LME

From: pascal paillet <p.paillet@st.com>

stpmu1 is a pmic from STMicroelectronics. The stpmu1 integrates 10
regulators and 3 switches with various capabilities.

Signed-off-by: pascal paillet <p.paillet@st.com>
---
 .../devicetree/bindings/mfd/st,stpmu1.txt          | 138 +++++++++++++++++++++
 1 file changed, 138 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmu1.txt

diff --git a/Documentation/devicetree/bindings/mfd/st,stpmu1.txt b/Documentation/devicetree/bindings/mfd/st,stpmu1.txt
new file mode 100644
index 0000000..53bdab4
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/st,stpmu1.txt
@@ -0,0 +1,138 @@
+* STMicroelectronics STPMU1 Power Management IC
+
+Required parent device properties:
+- compatible: "st,stpmu1"
+- reg: the I2C slave address for the stpmu1 chip
+- interrupts-extended: interrupt lines to use: second irq is for wakeup.
+- #interrupt-cells: should be 2.
+- interrupt-controller: describes the STPMU1 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
+
+
+stpmu1 consists is a varied group of sub-devices:
+
+Device			 Description
+------			------------
+stpmu1-onkey		: On key
+stpmu1-regulators	: Regulators
+stpmu1-wdt		: Watchdog
+
+each sub-device bindings is be described in associated driver
+documentation section.
+
+Example:
+
+pmic: stpmu1@33 {
+	compatible = "st,stpmu1";
+	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,stpmu1-onkey";
+		interrupt-parent = <&pmic>;
+		interrupts = <7 0>,<6 1>;
+		st,onkey-pwroff-enabled;
+		st,onkey-press-seconds = <10>;
+	};
+
+	watchdog {
+		compatible = "st,stpmu1-wdt";
+	};
+
+	regulators {
+		compatible = "st,stpmu1-regulators";
+
+		vdd_core: regulator@0 {
+			regulator-compatible = "buck1";
+			regulator-name = "vdd_core";
+			regulator-boot-on;
+			regulator-min-microvolt = <700000>;
+			regulator-max-microvolt = <1200000>;
+		};
+		vdd: regulator@1 {
+			regulator-compatible = "buck3";
+			regulator-name = "vdd";
+			regulator-min-microvolt = <3300000>;
+			regulator-max-microvolt = <3300000>;
+			regulator-boot-on;
+			regulator-pull-down;
+		};
+	};
-- 
1.9.1

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

* [PATCH 3/8] dt-bindings: regulator: document stpmu1 pmic regulators
  2018-07-05 15:14 [PATCH 0/8] Introduce STPMU1 PMIC Driver Pascal PAILLET-LME
  2018-07-05 15:14 ` [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver Pascal PAILLET-LME
  2018-07-05 15:14 ` [PATCH 1/8] dt-bindings: mfd: document stpmu1 pmic Pascal PAILLET-LME
@ 2018-07-05 15:14 ` Pascal PAILLET-LME
  2018-07-16 22:21   ` Rob Herring
  2018-07-05 15:14 ` [PATCH 4/8] regulator: stpmu1: add stpmu1 regulator driver Pascal PAILLET-LME
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Pascal PAILLET-LME @ 2018-07-05 15:14 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, lee.jones, lgirdwood,
	broonie, wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard
  Cc: Pascal PAILLET-LME

From: pascal paillet <p.paillet@st.com>

The STPMU1 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 <p.paillet@st.com>
---
 .../bindings/regulator/st,stpmu1-regulator.txt     | 72 ++++++++++++++++++++++
 1 file changed, 72 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/st,stpmu1-regulator.txt

diff --git a/Documentation/devicetree/bindings/regulator/st,stpmu1-regulator.txt b/Documentation/devicetree/bindings/regulator/st,stpmu1-regulator.txt
new file mode 100644
index 0000000..888e759
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/st,stpmu1-regulator.txt
@@ -0,0 +1,72 @@
+STMicroelectronics STPMU1 Voltage regulators
+
+Regulator Nodes are optional depending on needs.
+
+Available Regulators in STPMU1 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: stay on during Reset for particular regulator
+- 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 send 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,stpmu1-regulators";
+
+	ldo6-supply = <&v3v3>;
+
+	vdd_core: regulator@0 {
+		regulator-compatible = "buck1";
+		regulator-name = "vdd_core";
+		interrupts = <IT_CURLIM_BUCK1 0>;
+		interrupt-parent = <&pmic>;
+		st,mask_reset;
+		regulator-pull-down;
+		st,pulldown_config = <2>;
+		regulator-min-microvolt = <700000>;
+		regulator-max-microvolt = <1200000>;
+	};
+
+	v3v3: buck4 {
+		regulator-compatible = "buck4";
+		regulator-name = "v3v3";
+		interrupts = <IT_CURLIM_BUCK4 0>;
+		interrupt-parent = <&mypmic>;
+
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	v1v8: ldo6 {
+		regulator-compatible = "ldo6";
+		regulator-name = "v1v8";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-over-current-protection;
+	};
+
+};
-- 
1.9.1

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

* [PATCH 4/8] regulator: stpmu1: add stpmu1 regulator driver
  2018-07-05 15:14 [PATCH 0/8] Introduce STPMU1 PMIC Driver Pascal PAILLET-LME
                   ` (2 preceding siblings ...)
  2018-07-05 15:14 ` [PATCH 3/8] dt-bindings: regulator: document stpmu1 pmic regulators Pascal PAILLET-LME
@ 2018-07-05 15:14 ` Pascal PAILLET-LME
  2018-07-05 16:48   ` Mark Brown
  2018-07-05 15:14 ` [PATCH 6/8] input: stpmu1: add stpmu1 onkey driver Pascal PAILLET-LME
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Pascal PAILLET-LME @ 2018-07-05 15:14 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, lee.jones, lgirdwood,
	broonie, wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard
  Cc: Pascal PAILLET-LME

From: pascal paillet <p.paillet@st.com>

The stpmu1 PMIC embeds several regulators and witches with
different capabilities.

Signed-off-by: pascal paillet <p.paillet@st.com>
---
 drivers/regulator/Kconfig            |  12 +
 drivers/regulator/Makefile           |   2 +
 drivers/regulator/stpmu1_regulator.c | 919 +++++++++++++++++++++++++++++++++++
 3 files changed, 933 insertions(+)
 create mode 100644 drivers/regulator/stpmu1_regulator.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 5dbccf5..0b508f5 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_STPMU1
+	tristate "ST Microelectronics STPMU1 PMIC Regulators"
+	depends on MFD_STPMU1
+	help
+	  This driver supports ST Microelectronics STPMU1 PMIC voltage
+	  regulators and switches. The STPMU1 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 stpmu1_regulator.
+
 config REGULATOR_STW481X_VMMC
 	bool "ST Microelectronics STW481X VMMC regulator"
 	depends on MFD_STW481X || COMPILE_TEST
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index bd818ce..3d572ae 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_OF) += of_regulator.o
 obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
 obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
 obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
+obj-$(CONFIG_PROTECTION_CONSUMER) += protection-consumer.o
 
 obj-$(CONFIG_REGULATOR_88PG86X) += 88pg86x.o
 obj-$(CONFIG_REGULATOR_88PM800) += 88pm800.o
@@ -100,6 +101,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_STPMU1) += stpmu1_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/stpmu1_regulator.c b/drivers/regulator/stpmu1_regulator.c
new file mode 100644
index 0000000..aed778b
--- /dev/null
+++ b/drivers/regulator/stpmu1_regulator.c
@@ -0,0 +1,919 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Philippe Peurichard <philippe.peurichard@st.com>,
+ * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stpmu1.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.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>
+
+/**
+ * stpmu1 regulator description
+ * @desc: regulator framework description
+ * @valid_modes_mask: modes supported by the regulator
+ * @valid_ops_mask: ops supported by the regulator
+ * @pull_down_reg: pull down register address
+ * @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 stpmu1_regulator_cfg {
+	struct regulator_desc desc;
+
+	unsigned int valid_modes_mask;
+	unsigned int valid_ops_mask;
+	u8 mask_reset_reg;
+	u8 mask_reset_mask;
+	u8 icc_reg;
+	u8 icc_mask;
+};
+
+/**
+ * stpmu1 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: stpmu specific regulator description
+ * @voltage_ref_reg: Special case for Vref DDR & LDO3 for which voltage
+ * depends on Buck2
+ * @mask_reset: mask_reset bit value
+ * @irq_curlim: current limit interrupt number
+ * @regmap: point to parent regmap structure
+ */
+struct stpmu1_regulator {
+	unsigned int regul_id;
+	struct device_node *reg_node;
+	struct stpmu1_regulator_cfg *cfg;
+	struct regulator_dev *voltage_ref_reg;
+	u8 mask_reset;
+	int irq_curlim;
+	struct regmap *regmap;
+};
+
+/**
+ * struct stpmu1_device_data - contains all regulators data
+ * @regulator_table : contains all the regulators
+ * @num_regulators: number of regulators used
+ */
+struct stpmu1_device_data {
+	struct stpmu1_regulator *regulator_table;
+	int num_regulators;
+};
+
+static int stpmu1_set_mode(struct regulator_dev *rdev, unsigned int mode);
+static unsigned int stpmu1_get_mode(struct regulator_dev *rdev);
+static int stpmu1_set_icc(struct regulator_dev *rdev);
+static int stpmu1_set_bypass(struct regulator_dev *rdev, bool enable);
+static int stpmu1_regulator_parse_dt(void *driver_data);
+static unsigned int stpmu1_regulator_get_max_volt(struct regulator_desc *desc);
+static unsigned int stpmu1_map_mode(unsigned int mode);
+static int stpmu1_ldo3_list_voltage(struct regulator_dev *rdev,
+				    unsigned int sel);
+static int stpmu1_ldo3_get_voltage(struct regulator_dev *rdev);
+static int stpmu1_fixed_regul_get_voltage(struct regulator_dev *rdev);
+
+enum {
+	STPMU1_BUCK1 = 0,
+	STPMU1_BUCK2 = 1,
+	STPMU1_BUCK3 = 2,
+	STPMU1_BUCK4 = 3,
+	STPMU1_LDO1 = 4,
+	STPMU1_LDO2 = 5,
+	STPMU1_LDO3 = 6,
+	STPMU1_LDO4 = 7,
+	STPMU1_LDO5 = 8,
+	STPMU1_LDO6 = 9,
+	STPMU1_VREF_DDR = 10,
+	STPMU1_BOOST = 11,
+	STPMU1_VBUS_OTG = 12,
+	STPMU1_SW_OUT = 13,
+};
+
+/*
+ * PMIC Ramp_delay: Time to settle down after voltage change (unit: uV/us)
+ * or enable
+ * is 3.6mV/uS +/-60%  -> 2.25mV/uS worst case
+ */
+#define PMIC_RAMP_SLOPE_UV_PER_US 2250
+/* Enable time is 5000V/2250mV/uS */
+#define PMIC_ENABLE_TIME_US 2200
+
+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[] = {
+	/* Special case to allow range to cover lowest value of Buck2/2 */
+	REGULATOR_LINEAR_RANGE(500000, 0, 0, 0),
+	REGULATOR_LINEAR_RANGE(1700000, 1, 7, 0),
+	REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000),
+	/* index 31 is special case when LDO3 is in mode DDR */
+	REGULATOR_LINEAR_RANGE(3300000, 25, 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 stpmu1_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 = stpmu1_set_icc,
+};
+
+static struct regulator_ops stpmu1_ldo3_ops = {
+	.list_voltage = stpmu1_ldo3_list_voltage,
+	.map_voltage = regulator_map_voltage_iterate,
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.get_voltage = stpmu1_ldo3_get_voltage,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.set_pull_down = regulator_set_pull_down_regmap,
+	.get_bypass = regulator_get_bypass_regmap,
+	.set_bypass = stpmu1_set_bypass,
+	.set_over_current_protection = stpmu1_set_icc,
+};
+
+static struct regulator_ops stpmu1_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 = stpmu1_set_icc,
+};
+
+static struct regulator_ops stpmu1_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 = stpmu1_set_mode,
+	.get_mode = stpmu1_get_mode,
+	.set_over_current_protection = stpmu1_set_icc,
+};
+
+static struct regulator_ops stpmu1_fixed_regul_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.get_voltage = stpmu1_fixed_regul_get_voltage,
+	.set_pull_down = regulator_set_pull_down_regmap,
+};
+
+static struct regulator_ops stpmu1_switch_regul_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.get_voltage = stpmu1_fixed_regul_get_voltage,
+	.set_over_current_protection = stpmu1_set_icc,
+};
+
+#define REG_LDO(ids, base) { \
+	.name = #ids, \
+	.id = STPMU1_##ids, \
+	.n_voltages = 32, \
+	.ops = &stpmu1_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, \
+	.pull_down_reg = ids##_PULL_DOWN_REG, \
+	.pull_down_mask = ids##_PULL_DOWN_MASK, \
+	.supply_name = #base, \
+}
+
+#define REG_LDO3(ids) { \
+	.name = #ids, \
+	.id = STPMU1_##ids, \
+	.n_voltages = 32, \
+	.ops = &stpmu1_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, \
+	.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 = "ldo3", \
+}
+
+#define REG_LDO4(ids) { \
+	.name = #ids, \
+	.id = STPMU1_##ids, \
+	.n_voltages = 1, \
+	.ops = &stpmu1_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, \
+	.pull_down_reg = ids##_PULL_DOWN_REG, \
+	.pull_down_mask = ids##_PULL_DOWN_MASK, \
+	.supply_name = "ldo4", \
+}
+
+#define REG_BUCK(ids, base) { \
+	.name = #ids, \
+	.id = STPMU1_##ids, \
+	.ops = &stpmu1_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, \
+	.of_map_mode = stpmu1_map_mode, \
+	.pull_down_reg = ids##_PULL_DOWN_REG, \
+	.pull_down_mask = ids##_PULL_DOWN_MASK, \
+	.supply_name = #base, \
+}
+
+#define REG_VREF_DDR(ids, reg) { \
+	.name = #ids, \
+	.id = STPMU1_##ids, \
+	.n_voltages = 1, \
+	.ops = &stpmu1_fixed_regul_ops, \
+	.type = REGULATOR_VOLTAGE, \
+	.owner = THIS_MODULE, \
+	.min_uV = 0, \
+	.fixed_uV = 5000000, \
+	.enable_reg = (reg), \
+	.enable_mask = BUCK_ENABLE_MASK, \
+	.enable_val = 1, \
+	.disable_val = 0, \
+	.of_map_mode = stpmu1_map_mode, \
+	.pull_down_reg = ids##_PULL_DOWN_REG, \
+	.pull_down_mask = ids##_PULL_DOWN_MASK, \
+	.supply_name = NULL, \
+}
+
+#define REG_SWITCH(ids, base, reg, mask, val) { \
+	.name = #ids, \
+	.id = STPMU1_##ids, \
+	.n_voltages = 1, \
+	.ops = &stpmu1_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, \
+	.of_map_mode = stpmu1_map_mode, \
+	.supply_name = #base, \
+}
+
+struct stpmu1_regulator_cfg stpmu1_regulator_cfgs[] = {
+	[STPMU1_BUCK1] = {
+		.desc = REG_BUCK(BUCK1, buck1),
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+				  REGULATOR_CHANGE_MODE,
+		.valid_modes_mask = REGULATOR_MODE_NORMAL |
+				    REGULATOR_MODE_STANDBY,
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(0),
+		.mask_reset_reg = BUCKS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(0),
+	},
+	[STPMU1_BUCK2] = {
+		.desc = REG_BUCK(BUCK2, buck2),
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+				  REGULATOR_CHANGE_MODE,
+		.valid_modes_mask = REGULATOR_MODE_NORMAL |
+				    REGULATOR_MODE_STANDBY,
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(1),
+		.mask_reset_reg = BUCKS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(1),
+	},
+	[STPMU1_BUCK3] = {
+		.desc = REG_BUCK(BUCK3, buck3),
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+				  REGULATOR_CHANGE_MODE,
+		.valid_modes_mask = REGULATOR_MODE_NORMAL |
+				    REGULATOR_MODE_STANDBY,
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(2),
+		.mask_reset_reg = BUCKS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(2),
+	},
+	[STPMU1_BUCK4] = {
+		.desc = REG_BUCK(BUCK4, buck4),
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+				  REGULATOR_CHANGE_MODE,
+		.valid_modes_mask = REGULATOR_MODE_NORMAL |
+				    REGULATOR_MODE_STANDBY,
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(3),
+		.mask_reset_reg = BUCKS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(3),
+	},
+	[STPMU1_LDO1] = {
+		.desc = REG_LDO(LDO1, ldo1),
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(0),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(0),
+	},
+	[STPMU1_LDO2] = {
+		.desc = REG_LDO(LDO2, ldo2),
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(1),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(1),
+	},
+	[STPMU1_LDO3] = {
+		.desc = REG_LDO3(LDO3),
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+				  REGULATOR_CHANGE_BYPASS,
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(2),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(2),
+	},
+	[STPMU1_LDO4] = {
+		.desc = REG_LDO4(LDO4),
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(3),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(3),
+	},
+	[STPMU1_LDO5] = {
+		.desc = REG_LDO(LDO5, ldo5),
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(4),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(4),
+	},
+	[STPMU1_LDO6] = {
+		.desc = REG_LDO(LDO6, ldo6),
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+		.icc_reg = LDOS_ICCTO_CR,
+		.icc_mask = BIT(5),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(5),
+	},
+	[STPMU1_VREF_DDR] = {
+		.desc = REG_VREF_DDR(VREF_DDR, VREF_DDR_ACTIVE_CR),
+		.mask_reset_reg = LDOS_MASK_RESET_CR,
+		.mask_reset_mask = BIT(6),
+	},
+	[STPMU1_BOOST] = {
+		.desc = REG_SWITCH(BOOST, boost, BST_SW_CR,
+				   BOOST_ENABLED,
+				   BOOST_ENABLED),
+		.icc_reg = BUCKS_ICCTO_CR,
+		.icc_mask = BIT(6),
+	},
+	[STPMU1_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),
+	},
+	[STPMU1_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),
+	},
+};
+
+#define GET_MINIMUM_VOLTAGE(_regulator_desc_ptr) \
+	((_regulator_desc_ptr)->linear_ranges[0].min_uV)
+
+static unsigned int stpmu1_map_mode(unsigned int mode)
+{
+	return mode == REGULATOR_MODE_STANDBY ?
+		REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
+}
+
+static unsigned int stpmu1_regulator_get_max_volt(struct regulator_desc *desc)
+{
+	const struct regulator_linear_range *last_range =
+		&desc->linear_ranges[desc->n_linear_ranges - 1];
+
+	unsigned int max_voltage =
+		last_range->min_uV +
+		(last_range->max_sel - last_range->min_sel + 1)
+		* last_range->uV_step;
+
+	return max_voltage;
+}
+
+static int stpmu1_get_voltage_regmap(struct regulator_dev *rdev)
+{
+	int selector = 0;
+
+	if (!rdev)
+		return -EINVAL;
+	selector = regulator_get_voltage_sel_regmap(rdev);
+	return regulator_list_voltage_linear_range(rdev, selector);
+}
+
+static int stpmu1_ldo3_list_voltage(struct regulator_dev *rdev,
+				    unsigned int sel)
+{
+	struct stpmu1_regulator *regul = rdev_get_drvdata(rdev);
+
+	if (sel == 0)
+		return regulator_list_voltage_linear_range(rdev, 1);
+
+	if (sel < 31)
+		return regulator_list_voltage_linear_range(rdev, sel);
+
+	if (sel == 31)
+		return stpmu1_get_voltage_regmap(regul->voltage_ref_reg) / 2;
+
+	return -EINVAL;
+}
+
+static int stpmu1_ldo3_get_voltage(struct regulator_dev *rdev)
+{
+	int sel = regulator_get_voltage_sel_regmap(rdev);
+
+	if (sel < 0)
+		return -EINVAL;
+
+	return stpmu1_ldo3_list_voltage(rdev, (unsigned int)sel);
+}
+
+static int stpmu1_fixed_regul_get_voltage(struct regulator_dev *rdev)
+{
+	struct stpmu1_regulator *regul = rdev_get_drvdata(rdev);
+	int id = rdev_get_id(rdev);
+
+	/* VREF_DDR voltage is equal to Buck2/2 */
+	if (id == STPMU1_VREF_DDR)
+		return stpmu1_get_voltage_regmap(regul->voltage_ref_reg) / 2;
+
+	/* For all other case , rely on fixed value defined by Hw settings */
+	return regul->cfg->desc.fixed_uV;
+}
+
+static int stpmu1_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+	struct stpmu1_regulator *regul = rdev_get_drvdata(rdev);
+	unsigned int hplp;
+
+	/* The low power mode will be set for NORMAL/RUN registers */
+	switch (mode) {
+	case REGULATOR_MODE_NORMAL:
+		hplp = 0;
+		break;
+	case REGULATOR_MODE_STANDBY:
+		hplp = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return  regmap_update_bits(regul->regmap, regul->cfg->desc.enable_reg,
+				   BUCK_HPLP_ENABLE_MASK,
+				   hplp << BUCK_HPLP_SHIFT);
+}
+
+static unsigned int stpmu1_get_mode(struct regulator_dev *rdev)
+{
+	struct stpmu1_regulator *regul = rdev_get_drvdata(rdev);
+	int ret;
+	unsigned int val;
+
+	ret = regmap_read(regul->regmap, regul->cfg->desc.enable_reg, &val);
+	if (ret < 0)
+		return ret;
+
+	val &= BUCK_HPLP_ENABLE_MASK;
+	val >>= BUCK_HPLP_SHIFT;
+
+	return val ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
+}
+
+static int stpmu1_set_icc(struct regulator_dev *rdev)
+{
+	struct stpmu1_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 int stpmu1_set_bypass(struct regulator_dev *rdev, bool enable)
+{
+	struct stpmu1_regulator *regul = rdev_get_drvdata(rdev);
+	int ret;
+	unsigned int val;
+
+	if (enable)
+		val = regul->cfg->desc.bypass_val_on;
+	else
+		val = regul->cfg->desc.bypass_val_off;
+
+	ret = regmap_update_bits(regul->regmap, regul->cfg->desc.bypass_reg,
+				 regul->cfg->desc.bypass_mask, val);
+
+	return ret;
+}
+
+static irqreturn_t stpmu1_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 stpmu1_regulator_init(struct platform_device *pdev,
+				 struct regulator_dev *rdev)
+{
+	struct stpmu1_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,
+						stpmu1_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) \
+	[STPMU1_##_id] = { \
+		.name = #_name, \
+		.desc = &stpmu1_regulator_cfgs[STPMU1_##_id].desc, \
+	}
+
+static struct of_regulator_match stpmu1_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 inline struct device_node *match_of_node(struct platform_device *pdev,
+						int index)
+{
+	if (!stpmu1_regulators_matches[index].of_node)
+		dev_info(&pdev->dev,
+			 "DT node not found for regulator %i\n\r", index);
+
+	return stpmu1_regulators_matches[index].of_node;
+}
+
+static int stpmu1_regulator_parse_dt(void *driver_data)
+{
+	struct stpmu1_regulator *regul =
+		(struct stpmu1_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 void update_regulator_constraints(int index,
+					 struct regulator_init_data *init_data)
+{
+	struct stpmu1_regulator_cfg *cfg = &stpmu1_regulator_cfgs[index];
+	struct regulator_desc *desc = &cfg->desc;
+
+	init_data->constraints.valid_ops_mask |=
+		cfg->valid_ops_mask;
+	init_data->constraints.valid_modes_mask |=
+		cfg->valid_modes_mask;
+
+	/*
+	 * if all constraints are not specified in DT , ensure Hw
+	 * constraints are met
+	 */
+	if (desc->n_voltages > 1) {
+		if (!init_data->constraints.min_uV)
+			init_data->constraints.min_uV =
+				GET_MINIMUM_VOLTAGE(desc);
+		if (!init_data->constraints.max_uV)
+			init_data->constraints.max_uV =
+			stpmu1_regulator_get_max_volt(desc);
+	}
+
+	if (!init_data->constraints.ramp_delay)
+		init_data->constraints.ramp_delay = PMIC_RAMP_SLOPE_UV_PER_US;
+
+	if (!init_data->constraints.enable_time)
+		init_data->constraints.enable_time = PMIC_ENABLE_TIME_US;
+}
+
+static struct
+regulator_dev *stpmu1_regulator_register(struct platform_device *pdev, int id,
+					 struct regulator_init_data *init_data,
+					 struct stpmu1_regulator *regul,
+					 struct regulator_dev *buck2)
+{
+	struct stpmu1_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 = match_of_node(pdev, id);
+	config.regmap = pmic_dev->regmap;
+	config.driver_data = regul;
+
+	regul->regul_id = id;
+	regul->reg_node = config.of_node;
+	regul->cfg = &stpmu1_regulator_cfgs[id];
+	regul->regmap = pmic_dev->regmap;
+
+	/* LDO3 and VREF_DDR can use buck2 as reference voltage */
+	if (regul->regul_id == STPMU1_LDO3 ||
+	    regul->regul_id == STPMU1_VREF_DDR) {
+		if (!buck2) {
+			dev_err(&pdev->dev,
+				"Error in PMIC regulator settings: Buck2 is not defined prior to LDO3 or VREF_DDR regulators\n"
+				);
+			return ERR_PTR(-EINVAL);
+		}
+		regul->voltage_ref_reg = buck2;
+	}
+
+	rdev = devm_regulator_register(&pdev->dev, &regul->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 stpmu1_regulator_probe(struct platform_device *pdev)
+{
+	struct stpmu1_dev *pmic_dev = dev_get_drvdata(pdev->dev.parent);
+	struct stpmu1_device_data *ddata;
+	struct regulator_dev *rdev;
+	struct stpmu1_regulator *regul;
+	struct regulator_init_data *init_data;
+	struct device_node *np;
+	int i, ret;
+	struct regulator_dev *buck2_rdev = NULL;
+
+	ddata = devm_kzalloc(&pdev->dev, sizeof(struct stpmu1_device_data),
+			     GFP_KERNEL);
+	if (!ddata)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, ddata);
+
+	/* disable icc for all reguls */
+	ret = regmap_update_bits(pmic_dev->regmap, BUCKS_ICCTO_CR,
+				 BUCKS_ICCTO_CR_REG_MASK, 0);
+	if (ret == 0)
+		ret = regmap_update_bits(pmic_dev->regmap, LDOS_ICCTO_CR,
+					 LDOS_ICCTO_CR_REG_MASK, 0);
+	/* reset pulldown value */
+	if (ret == 0)
+		ret = regmap_update_bits(pmic_dev->regmap, BUCKS_PD_CR,
+					 BUCKS_PD_CR_REG_MASK, 0);
+	if (ret == 0)
+		ret = regmap_update_bits(pmic_dev->regmap, LDO14_PD_CR,
+					 LDO1234_PULL_DOWN_REGISTER_MASK, 0);
+	if (ret == 0)
+		ret = regmap_update_bits(pmic_dev->regmap, LDO56_VREF_PD_CR,
+					 LDO56_VREF_PD_CR_REG_MASK, 0);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"failed to update stpmu1 register %d\n", ret);
+		return ret;
+	}
+
+	np = pdev->dev.of_node;
+	if (!np) {
+		dev_err(&pdev->dev, "regulators node not found\n");
+		return -EINVAL;
+	}
+
+	ret = of_regulator_match(&pdev->dev, np,
+				 stpmu1_regulators_matches,
+				 ARRAY_SIZE(stpmu1_regulators_matches));
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Error in PMIC regulator device tree node");
+		return ret;
+	}
+	ddata->num_regulators = ret;
+
+	dev_dbg(&pdev->dev, "%d regulator(s) found in DT\n",
+		ddata->num_regulators);
+
+	regul = devm_kzalloc(&pdev->dev, ddata->num_regulators *
+			     sizeof(struct stpmu1_regulator),
+			     GFP_KERNEL);
+	if (!regul)
+		return -ENOMEM;
+
+	ddata->regulator_table = regul;
+
+	/* Register all defined (from DT) regulators to Regulator Framework */
+	for (i = 0; i < ARRAY_SIZE(stpmu1_regulator_cfgs); i++) {
+		/* Parse DT & find regulators to register */
+		init_data = stpmu1_regulators_matches[i].init_data;
+		if (init_data) {
+			init_data->regulator_init = &stpmu1_regulator_parse_dt;
+
+			update_regulator_constraints(i, init_data);
+
+			rdev = stpmu1_regulator_register(pdev, i, init_data,
+							 regul, buck2_rdev);
+			if (IS_ERR(rdev))
+				return PTR_ERR(rdev);
+
+			ret = stpmu1_regulator_init(pdev, rdev);
+			if (ret) {
+				dev_err(&pdev->dev,
+					"failed to initialize regulator %d\n",
+					ret);
+				return ret;
+			}
+
+			if (regul->regul_id == STPMU1_BUCK2)
+				buck2_rdev = rdev;
+
+			regul++;
+		}
+	}
+
+	dev_dbg(&pdev->dev, "stpmu1_regulator driver probed\n");
+
+	return 0;
+}
+
+static const struct of_device_id of_pmic_regulator_match[] = {
+	{ .compatible = "st,stpmu1-regulators" },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, of_pmic_regulator_match);
+
+static struct platform_driver stpmu1_regulator_driver = {
+	.driver = {
+		.name = "stpmu1-regulator",
+		.of_match_table = of_match_ptr(of_pmic_regulator_match),
+	},
+	.probe = stpmu1_regulator_probe,
+};
+
+module_platform_driver(stpmu1_regulator_driver);
+
+MODULE_DESCRIPTION("STPMU1 PMIC voltage regulator driver");
+MODULE_AUTHOR("<philippe.peurichard@st.com>");
+MODULE_LICENSE("GPL");
-- 
1.9.1

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

* [PATCH 6/8] input: stpmu1: add stpmu1 onkey driver
  2018-07-05 15:14 [PATCH 0/8] Introduce STPMU1 PMIC Driver Pascal PAILLET-LME
                   ` (3 preceding siblings ...)
  2018-07-05 15:14 ` [PATCH 4/8] regulator: stpmu1: add stpmu1 regulator driver Pascal PAILLET-LME
@ 2018-07-05 15:14 ` Pascal PAILLET-LME
  2018-08-06 22:47   ` Dmitry Torokhov
  2018-07-05 15:14 ` [PATCH 5/8] dt-bindings: input: document stpmu1 pmic onkey Pascal PAILLET-LME
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Pascal PAILLET-LME @ 2018-07-05 15:14 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, lee.jones, lgirdwood,
	broonie, wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard
  Cc: Pascal PAILLET-LME

From: pascal paillet <p.paillet@st.com>

The stpmu1 pmic is able to manage an onkey button. This driver exposes
the stpmu1 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 <p.paillet@st.com>
---
 drivers/input/misc/Kconfig        |  11 ++
 drivers/input/misc/Makefile       |   2 +
 drivers/input/misc/stpmu1_onkey.c | 321 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 334 insertions(+)
 create mode 100644 drivers/input/misc/stpmu1_onkey.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index c25606e..d020971 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_STPMU1_ONKEY
+	tristate "STPMU1 PMIC Onkey support"
+	depends on MFD_STPMU1
+	help
+	  Say Y to enable support of onkey embedded into STPMU1 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 stpmu1_onkey.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 72cde28..cc60dc1 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_STPMU1_ONKEY)  	+= stpmu1_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/stpmu1_onkey.c b/drivers/input/misc/stpmu1_onkey.c
new file mode 100644
index 0000000..084a31f
--- /dev/null
+++ b/drivers/input/misc/stpmu1_onkey.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Philippe Peurichard <philippe.peurichard@st.com>,
+ * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+ */
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stpmu1.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/**
+ * struct stpmu1_onkey - OnKey data
+ * @pmic:		pointer to STPMU1 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 stpmu1_onkey {
+	struct stpmu1_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;
+};
+
+/**
+ * onkey_falling_irq() - button press isr
+ * @irq:		irq
+ * @pmic_onkey:		onkey device
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t onkey_falling_irq(int irq, void *ponkey)
+{
+	struct stpmu1_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;
+}
+
+/**
+ * onkey_rising_irq() - button released isr
+ * @irq:		irq
+ * @pmic_onkey:		onkey device
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t onkey_rising_irq(int irq, void *ponkey)
+{
+	struct stpmu1_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;
+}
+
+/**
+ * stpmu1_onkey_dt_params() - device tree parameter parser
+ * @pdev:	platform device for the PONKEY
+ * @onkey:	pointer to onkey driver data
+ * @config:	configuration params to be filled up
+ */
+static int stpmu1_onkey_dt_params(struct platform_device *pdev,
+				  struct stpmu1_onkey *onkey,
+				  struct pmic_onkey_config *config)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np;
+	u32 val;
+
+	np = dev->of_node;
+	if (!np)
+		return -EINVAL;
+
+	onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling");
+	if (onkey->irq_falling < 0) {
+		dev_err(dev, "failed: request IRQ onkey-falling %d",
+			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",
+			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\r",
+				 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;
+}
+
+/**
+ * stpmu1_onkey_probe() - probe
+ * @pdev:	platform device for the PONKEY
+ *
+ * Return: 0 for successful probe else appropriate error
+ */
+static int stpmu1_onkey_probe(struct platform_device *pdev)
+{
+	struct stpmu1_dev *pmic = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct input_dev *input_dev;
+	struct stpmu1_onkey *onkey;
+	struct pmic_onkey_config config;
+	unsigned int val = 0;
+	int ret;
+
+	onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
+	if (!onkey)
+		return -ENOMEM;
+
+	memset(&config, 0, sizeof(struct pmic_onkey_config));
+	ret = stpmu1_onkey_dt_params(pdev, onkey, &config);
+	if (ret < 0)
+		goto err;
+
+	input_dev = devm_input_allocate_device(dev);
+	if (!input_dev) {
+		dev_err(dev, "Can't allocate Pwr Onkey Input Device\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	input_dev->name = "pmic_onkey";
+	input_dev->phys = "pmic_onkey/input0";
+	input_dev->dev.parent = dev;
+
+	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;
+	ret = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR,
+				 PONKEY_TURNOFF_MASK, val);
+	if (ret) {
+		dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", ret);
+		goto err;
+	}
+
+	ret = regmap_update_bits(pmic->regmap, PADS_PULL_CR,
+				 PONKEY_PU_ACTIVE,
+				 config.onkey_pullup_val);
+
+	if (ret) {
+		dev_err(dev, "ONKEY Pads configuration failed: %d\n", ret);
+		goto err;
+	}
+
+	onkey->pmic = pmic;
+	onkey->input_dev = input_dev;
+
+	ret = devm_request_threaded_irq(dev, onkey->irq_falling, NULL,
+					onkey_falling_irq,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					dev_name(dev), onkey);
+	if (ret) {
+		dev_err(dev, "Can't get IRQ for Onkey Falling edge: %d\n", ret);
+		goto err;
+	}
+
+	ret = devm_request_threaded_irq(dev, onkey->irq_rising, NULL,
+					onkey_rising_irq,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					dev_name(dev), onkey);
+	if (ret) {
+		dev_err(dev, "Can't get IRQ for Onkey Rising edge: %d\n", ret);
+		goto err;
+	}
+
+	ret = input_register_device(input_dev);
+	if (ret) {
+		dev_err(dev, "Can't register power button: %d\n", ret);
+		goto err;
+	}
+
+	platform_set_drvdata(pdev, onkey);
+	device_init_wakeup(dev, true);
+
+	dev_dbg(dev, "PMIC Pwr Onkey driver probed\n");
+
+err:
+	return ret;
+}
+
+/**
+ * stpmu1_onkey_remove() - Cleanup on removal
+ * @pdev:	platform device for the button
+ *
+ * Return: 0
+ */
+static int stpmu1_onkey_remove(struct platform_device *pdev)
+{
+	struct stpmu1_onkey *onkey = platform_get_drvdata(pdev);
+
+	input_unregister_device(onkey->input_dev);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pmic_onkey_suspend() - suspend handler
+ * @dev:	power button device
+ *
+ * Cancel all pending work items for the power button, setup irq for wakeup
+ *
+ * Return: 0
+ */
+static int __maybe_unused stpmu1_onkey_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct stpmu1_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;
+}
+
+/**
+ * pmic_onkey_resume() - resume handler
+ * @dev:	power button device
+ *
+ * Just disable the wakeup capability of irq here.
+ *
+ * Return: 0
+ */
+static int __maybe_unused stpmu1_onkey_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct stpmu1_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;
+}
+
+#endif
+
+static SIMPLE_DEV_PM_OPS(stpmu1_onkey_pm,
+			 stpmu1_onkey_suspend,
+			 stpmu1_onkey_resume);
+
+static const struct of_device_id of_stpmu1_onkey_match[] = {
+	{ .compatible = "st,stpmu1-onkey" },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, of_stpmu1_onkey_match);
+
+static struct platform_driver stpmu1_onkey_driver = {
+	.probe	= stpmu1_onkey_probe,
+	.remove	= stpmu1_onkey_remove,
+	.driver	= {
+		.name	= "stpmu1_onkey",
+		.of_match_table = of_match_ptr(of_stpmu1_onkey_match),
+		.pm	= &stpmu1_onkey_pm,
+	},
+};
+module_platform_driver(stpmu1_onkey_driver);
+
+MODULE_DESCRIPTION("Onkey driver for STPMU1");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("philippe.peurichard@st.com>");
-- 
1.9.1

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

* [PATCH 5/8] dt-bindings: input: document stpmu1 pmic onkey
  2018-07-05 15:14 [PATCH 0/8] Introduce STPMU1 PMIC Driver Pascal PAILLET-LME
                   ` (4 preceding siblings ...)
  2018-07-05 15:14 ` [PATCH 6/8] input: stpmu1: add stpmu1 onkey driver Pascal PAILLET-LME
@ 2018-07-05 15:14 ` Pascal PAILLET-LME
  2018-07-05 15:14 ` [PATCH 7/8] dt-bindings: watchdog: document stpmu1 pmic watchdog Pascal PAILLET-LME
  2018-07-05 15:14 ` [PATCH 8/8] watchdog: stpmu1: add stpmu1 watchdog driver Pascal PAILLET-LME
  7 siblings, 0 replies; 17+ messages in thread
From: Pascal PAILLET-LME @ 2018-07-05 15:14 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, lee.jones, lgirdwood,
	broonie, wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard
  Cc: Pascal PAILLET-LME

From: pascal paillet <p.paillet@st.com>

The stpmu1 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 <p.paillet@st.com>
---
 .../devicetree/bindings/input/st,stpmu1-onkey.txt  | 31 ++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/st,stpmu1-onkey.txt

diff --git a/Documentation/devicetree/bindings/input/st,stpmu1-onkey.txt b/Documentation/devicetree/bindings/input/st,stpmu1-onkey.txt
new file mode 100644
index 0000000..cc42b7f
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/st,stpmu1-onkey.txt
@@ -0,0 +1,31 @@
+STMicroelectronics STPMU1 Onkey
+
+Required properties:
+
+- compatible = "st,stpmu1-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,stpmu1-onkey";
+	interrupt-parent = <&pmic>;
+	interrupts = <7 0>,<6 1>;
+	interrupt-names = "onkey-falling", "onkey-rising";
+	status = "okay";
+	st,onkey-pwroff-enabled;
+	st,onkey-long-press-seconds = <10>;
+};
-- 
1.9.1

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

* [PATCH 8/8] watchdog: stpmu1: add stpmu1 watchdog driver
  2018-07-05 15:14 [PATCH 0/8] Introduce STPMU1 PMIC Driver Pascal PAILLET-LME
                   ` (6 preceding siblings ...)
  2018-07-05 15:14 ` [PATCH 7/8] dt-bindings: watchdog: document stpmu1 pmic watchdog Pascal PAILLET-LME
@ 2018-07-05 15:14 ` Pascal PAILLET-LME
  2018-07-05 18:48   ` Guenter Roeck
  7 siblings, 1 reply; 17+ messages in thread
From: Pascal PAILLET-LME @ 2018-07-05 15:14 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, lee.jones, lgirdwood,
	broonie, wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard
  Cc: Pascal PAILLET-LME

From: pascal paillet <p.paillet@st.com>

The stpmu1 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 <p.paillet@st.com>
---
 drivers/watchdog/Kconfig      |  12 +++
 drivers/watchdog/Makefile     |   1 +
 drivers/watchdog/stpmu1_wdt.c | 177 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 190 insertions(+)
 create mode 100644 drivers/watchdog/stpmu1_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9af07fd..2155f4d 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 STPMU1_WATCHDOG
+	tristate "STPMU1 PMIC watchdog support"
+	depends on MFD_STPMU1
+	select WATCHDOG_CORE
+	help
+	  Say Y here to include watchdog support embedded into STPMU1 PMIC.
+	  If the watchdog timer expires, stpmu1 shut-down all its power
+	  supplies.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called spmu1_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..c9eba94 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_STPMU1_WATCHDOG) += stpmu1_wdt.o
diff --git a/drivers/watchdog/stpmu1_wdt.c b/drivers/watchdog/stpmu1_wdt.c
new file mode 100644
index 0000000..57e0afa
--- /dev/null
+++ b/drivers/watchdog/stpmu1_wdt.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Philippe Peurichard <philippe.peurichard@st.com>,
+ * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mfd/stpmu1.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 stpmu1_wdt {
+	struct stpmu1_dev *pmic;
+	struct watchdog_device wdtdev;
+	struct notifier_block restart_handler;
+};
+
+static int pmic_wdt_start(struct watchdog_device *wdd)
+{
+	struct stpmu1_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 stpmu1_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 stpmu1_wdt *wdt = watchdog_get_drvdata(wdd);
+	int ret;
+
+	return regmap_update_bits(wdt->pmic->regmap,
+				  WCHDG_CR, WDT_PING_MASK, WDT_PING);
+	return ret;
+}
+
+static int pmic_wdt_set_timeout(struct watchdog_device *wdd,
+				unsigned int timeout)
+{
+	struct stpmu1_wdt *wdt = watchdog_get_drvdata(wdd);
+	int ret;
+
+	ret = regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout);
+	if (ret)
+		dev_err(wdt->pmic->dev,
+			"Failed to set watchdog timeout (err = %d)\n", ret);
+	else
+		wdd->timeout = PMIC_WDT_MAX_TIMEOUT;
+
+	return ret;
+}
+
+static int pmic_wdt_restart_handler(struct notifier_block *this,
+				    unsigned long mode, void *cmd)
+{
+	struct stpmu1_wdt *wdt = container_of(this,
+						   struct stpmu1_wdt,
+						   restart_handler);
+
+	dev_info(wdt->pmic->dev,
+		 "PMIC Watchdog Elapsed (timeout %d), shutdown of PMIC initiated\n",
+		 wdt->wdtdev.timeout);
+
+	return NOTIFY_DONE;
+}
+
+static const struct watchdog_info pmic_watchdog_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+	.identity = "STPMU1 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 stpmu1_dev *pmic;
+	struct stpmu1_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 stpmu1_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_MAX_TIMEOUT;
+
+	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
+
+	watchdog_set_drvdata(&wdt->wdtdev, wdt);
+	dev_set_drvdata(&pdev->dev, wdt);
+
+	ret = watchdog_register_device(&wdt->wdtdev);
+	if (ret)
+		return ret;
+
+	wdt->restart_handler.notifier_call = pmic_wdt_restart_handler;
+	wdt->restart_handler.priority = 128;
+	ret = register_restart_handler(&wdt->restart_handler);
+	if (ret) {
+		dev_err(wdt->pmic->dev, "failed to register restart handler\n");
+		return ret;
+	}
+
+	dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n");
+	return 0;
+}
+
+static int pmic_wdt_remove(struct platform_device *pdev)
+{
+	struct stpmu1_wdt *wdt = dev_get_drvdata(&pdev->dev);
+
+	unregister_restart_handler(&wdt->restart_handler);
+	watchdog_unregister_device(&wdt->wdtdev);
+
+	return 0;
+}
+
+static const struct of_device_id of_pmic_wdt_match[] = {
+	{ .compatible = "st,stpmu1-wdt" },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, of_pmic_wdt_match);
+
+static struct platform_driver stpmu1_wdt_driver = {
+	.probe = pmic_wdt_probe,
+	.remove = pmic_wdt_remove,
+	.driver = {
+		.name = "stpmu1-wdt",
+		.of_match_table = of_pmic_wdt_match,
+	},
+};
+module_platform_driver(stpmu1_wdt_driver);
+
+MODULE_AUTHOR("philippe.peurichard@st.com>");
+MODULE_DESCRIPTION("Watchdog driver for STPMU1 device");
+MODULE_LICENSE("GPL");
-- 
1.9.1

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

* [PATCH 7/8] dt-bindings: watchdog: document stpmu1 pmic watchdog
  2018-07-05 15:14 [PATCH 0/8] Introduce STPMU1 PMIC Driver Pascal PAILLET-LME
                   ` (5 preceding siblings ...)
  2018-07-05 15:14 ` [PATCH 5/8] dt-bindings: input: document stpmu1 pmic onkey Pascal PAILLET-LME
@ 2018-07-05 15:14 ` Pascal PAILLET-LME
  2018-07-05 15:14 ` [PATCH 8/8] watchdog: stpmu1: add stpmu1 watchdog driver Pascal PAILLET-LME
  7 siblings, 0 replies; 17+ messages in thread
From: Pascal PAILLET-LME @ 2018-07-05 15:14 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, lee.jones, lgirdwood,
	broonie, wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard
  Cc: Pascal PAILLET-LME

From: pascal paillet <p.paillet@st.com>

The stpmu1 PMIC embeds a watchdog which is disabled by default.
In case of watchdog, the PMIC goes off.

Signed-off-by: pascal paillet <p.paillet@st.com>
---
 Documentation/devicetree/bindings/watchdog/st,stpmu1-wdt.txt | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/st,stpmu1-wdt.txt

diff --git a/Documentation/devicetree/bindings/watchdog/st,stpmu1-wdt.txt b/Documentation/devicetree/bindings/watchdog/st,stpmu1-wdt.txt
new file mode 100644
index 0000000..b558b3c
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/st,stpmu1-wdt.txt
@@ -0,0 +1,11 @@
+STMicroelectronics STPMU1 Watchdog
+
+Required properties:
+
+- compatible : should be "st,stpmu1-wdt"
+
+Example:
+
+watchdog {
+	compatible = "st,stpmu1-wdt";
+};
-- 
1.9.1

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

* Re: [PATCH 4/8] regulator: stpmu1: add stpmu1 regulator driver
  2018-07-05 15:14 ` [PATCH 4/8] regulator: stpmu1: add stpmu1 regulator driver Pascal PAILLET-LME
@ 2018-07-05 16:48   ` Mark Brown
  0 siblings, 0 replies; 17+ messages in thread
From: Mark Brown @ 2018-07-05 16:48 UTC (permalink / raw)
  To: Pascal PAILLET-LME
  Cc: dmitry.torokhov, robh+dt, mark.rutland, lee.jones, lgirdwood,
	wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard

[-- Attachment #1: Type: text/plain, Size: 5543 bytes --]

On Thu, Jul 05, 2018 at 03:14:23PM +0000, Pascal PAILLET-LME wrote:

>  obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
>  obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
>  obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
> +obj-$(CONFIG_PROTECTION_CONSUMER) += protection-consumer.o
>  
>  obj-$(CONFIG_REGULATOR_88PG86X) += 88pg86x.o
>  obj-$(CONFIG_REGULATOR_88PM800) += 88pm800.o

Looks like this got included by mistake...

> --- /dev/null
> +++ b/drivers/regulator/stpmu1_regulator.c
> @@ -0,0 +1,919 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved

All rights reserved and GPL :)

Please also make the entire comment a C++ one so the block looks more
intentional.

> +static unsigned int stpmu1_map_mode(unsigned int mode)
> +{
> +	return mode == REGULATOR_MODE_STANDBY ?
> +		REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
> +}

This looks confused - what's going on here?  Normally a map_mode()
operation would be translating values in DT but this translates
everything that isn't standby into normal which suggests there's an
error checking problem.

> +static int stpmu1_ldo3_list_voltage(struct regulator_dev *rdev,
> +				    unsigned int sel)
> +{
> +	struct stpmu1_regulator *regul = rdev_get_drvdata(rdev);
> +
> +	if (sel == 0)
> +		return regulator_list_voltage_linear_range(rdev, 1);
> +
> +	if (sel < 31)
> +		return regulator_list_voltage_linear_range(rdev, sel);
> +
> +	if (sel == 31)
> +		return stpmu1_get_voltage_regmap(regul->voltage_ref_reg) / 2;
> +
> +	return -EINVAL;
> +}

The only thing that's going on here that's odd and that couldn't be
represented with a helper is selector 31 which looks to be some sort of
divided bypass mode - can you explain what this represents in hardware
terms please?

> +static int stpmu1_ldo3_get_voltage(struct regulator_dev *rdev)
> +{
> +	int sel = regulator_get_voltage_sel_regmap(rdev);
> +
> +	if (sel < 0)
> +		return -EINVAL;
> +
> +	return stpmu1_ldo3_list_voltage(rdev, (unsigned int)sel);
> +}

This is just the standard core behaviour, no need for this.

> +static int stpmu1_fixed_regul_get_voltage(struct regulator_dev *rdev)
> +{
> +	struct stpmu1_regulator *regul = rdev_get_drvdata(rdev);
> +	int id = rdev_get_id(rdev);
> +
> +	/* VREF_DDR voltage is equal to Buck2/2 */
> +	if (id == STPMU1_VREF_DDR)
> +		return stpmu1_get_voltage_regmap(regul->voltage_ref_reg) / 2;
> +
> +	/* For all other case , rely on fixed value defined by Hw settings */
> +	return regul->cfg->desc.fixed_uV;
> +}

It'd be better to just use a separate set of operations for the DDR
regulator rather than have a conditional here, less code and makes it
clearer that this one is a special case.

> +static void update_regulator_constraints(int index,
> +					 struct regulator_init_data *init_data)
> +{
> +	struct stpmu1_regulator_cfg *cfg = &stpmu1_regulator_cfgs[index];
> +	struct regulator_desc *desc = &cfg->desc;
> +
> +	init_data->constraints.valid_ops_mask |=
> +		cfg->valid_ops_mask;
> +	init_data->constraints.valid_modes_mask |=
> +		cfg->valid_modes_mask;

Drivers should never be modifying their constraints, this is a no no.

> +	/*
> +	 * if all constraints are not specified in DT , ensure Hw
> +	 * constraints are met
> +	 */
> +	if (desc->n_voltages > 1) {

Drivers shouldn't be doing this either.  The API will not allow any
modifications of hardware state without constraints so unless the
bootloader is leaving things in a broken state we should be fine.

> +	if (!init_data->constraints.ramp_delay)
> +		init_data->constraints.ramp_delay = PMIC_RAMP_SLOPE_UV_PER_US;
> +
> +	if (!init_data->constraints.enable_time)
> +		init_data->constraints.enable_time = PMIC_ENABLE_TIME_US;

If these are hard constraints we know from the chip design they should
be being set in the descriptor.  The constraints are there to override
if either they're board dependent or the board needs something longer
than the chip default.

> +	/* LDO3 and VREF_DDR can use buck2 as reference voltage */
> +	if (regul->regul_id == STPMU1_LDO3 ||
> +	    regul->regul_id == STPMU1_VREF_DDR) {
> +		if (!buck2) {
> +			dev_err(&pdev->dev,
> +				"Error in PMIC regulator settings: Buck2 is not defined prior to LDO3 or VREF_DDR regulators\n"
> +				);
> +			return ERR_PTR(-EINVAL);
> +		}
> +		regul->voltage_ref_reg = buck2;
> +	}

Can or do?  Usually this would be hooked up in the DT (with the
regulator specifying a supply name in the desc).

> +	np = pdev->dev.of_node;
> +	if (!np) {
> +		dev_err(&pdev->dev, "regulators node not found\n");
> +		return -EINVAL;
> +	}
> +

May as well let the driver probe?

> +	/* Register all defined (from DT) regulators to Regulator Framework */
> +	for (i = 0; i < ARRAY_SIZE(stpmu1_regulator_cfgs); i++) {
> +		/* Parse DT & find regulators to register */
> +		init_data = stpmu1_regulators_matches[i].init_data;

You should register everything that's physically there unconditionally,
there's no harm in having a regulator registered that's not used and it
might be useful for debugging purposes.

> +static const struct of_device_id of_pmic_regulator_match[] = {
> +	{ .compatible = "st,stpmu1-regulators" },
> +	{ },
> +};

This is part of a MFD for a single chip, why do we need a specific
compatible string here rather than just enumerating from the base
registration of the device?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH 8/8] watchdog: stpmu1: add stpmu1 watchdog driver
  2018-07-05 15:14 ` [PATCH 8/8] watchdog: stpmu1: add stpmu1 watchdog driver Pascal PAILLET-LME
@ 2018-07-05 18:48   ` Guenter Roeck
  0 siblings, 0 replies; 17+ messages in thread
From: Guenter Roeck @ 2018-07-05 18:48 UTC (permalink / raw)
  To: Pascal PAILLET-LME, dmitry.torokhov, robh+dt, mark.rutland,
	lee.jones, lgirdwood, broonie, wim, linux-input, devicetree,
	linux-kernel, linux-watchdog, benjamin.gaignard

On 07/05/2018 08:14 AM, Pascal PAILLET-LME wrote:
> From: pascal paillet <p.paillet@st.com>
> 
> The stpmu1 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 <p.paillet@st.com>
> ---
>   drivers/watchdog/Kconfig      |  12 +++
>   drivers/watchdog/Makefile     |   1 +
>   drivers/watchdog/stpmu1_wdt.c | 177 ++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 190 insertions(+)
>   create mode 100644 drivers/watchdog/stpmu1_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 9af07fd..2155f4d 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 STPMU1_WATCHDOG
> +	tristate "STPMU1 PMIC watchdog support"
> +	depends on MFD_STPMU1
> +	select WATCHDOG_CORE
> +	help
> +	  Say Y here to include watchdog support embedded into STPMU1 PMIC.
> +	  If the watchdog timer expires, stpmu1 shut-down all its power
> +	  supplies.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called spmu1_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..c9eba94 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_STPMU1_WATCHDOG) += stpmu1_wdt.o
> diff --git a/drivers/watchdog/stpmu1_wdt.c b/drivers/watchdog/stpmu1_wdt.c
> new file mode 100644
> index 0000000..57e0afa
> --- /dev/null
> +++ b/drivers/watchdog/stpmu1_wdt.c
> @@ -0,0 +1,177 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
> + * Author: Philippe Peurichard <philippe.peurichard@st.com>,
> + * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/mfd/stpmu1.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 stpmu1_wdt {
> +	struct stpmu1_dev *pmic;
> +	struct watchdog_device wdtdev;
> +	struct notifier_block restart_handler;
> +};
> +
> +static int pmic_wdt_start(struct watchdog_device *wdd)
> +{
> +	struct stpmu1_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 stpmu1_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 stpmu1_wdt *wdt = watchdog_get_drvdata(wdd);
> +	int ret;
> +
> +	return regmap_update_bits(wdt->pmic->regmap,
> +				  WCHDG_CR, WDT_PING_MASK, WDT_PING);
> +	return ret;
> +}
> +
> +static int pmic_wdt_set_timeout(struct watchdog_device *wdd,
> +				unsigned int timeout)
> +{
> +	struct stpmu1_wdt *wdt = watchdog_get_drvdata(wdd);
> +	int ret;
> +
> +	ret = regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout);
> +	if (ret)
> +		dev_err(wdt->pmic->dev,
> +			"Failed to set watchdog timeout (err = %d)\n", ret);
> +	else
> +		wdd->timeout = PMIC_WDT_MAX_TIMEOUT;

First the requested timeout is set, then the caller is notified
that the timeout was set to the maximum possible value ? That doesn't
make sense. If that is really intentional, I would expect a detailed
explanation, and I would expect that the value written into the chip
register matches the value reported back to the user.

> +
> +	return ret;
> +}
> +
> +static int pmic_wdt_restart_handler(struct notifier_block *this,
> +				    unsigned long mode, void *cmd)
> +{
> +	struct stpmu1_wdt *wdt = container_of(this,
> +						   struct stpmu1_wdt,
> +						   restart_handler);
> +
> +	dev_info(wdt->pmic->dev,
> +		 "PMIC Watchdog Elapsed (timeout %d), shutdown of PMIC initiated\n",
> +		 wdt->wdtdev.timeout);
> +

Register a restart handler just to issue a message ? That is quite pointless.
A restart handler is supposed to restart the system. Besides, the message
is highly misleading; there is no reason to believe that it will be called
after the watchdog expired.

This function should restart the system. If it doesn't, drop it.

> +	return NOTIFY_DONE;
> +}
> +
> +static const struct watchdog_info pmic_watchdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
> +	.identity = "STPMU1 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 stpmu1_dev *pmic;
> +	struct stpmu1_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 stpmu1_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_MAX_TIMEOUT;

256 seconds default timeout ? Unusual, just making sure that this is what you want.

> +
> +	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
> +
> +	watchdog_set_drvdata(&wdt->wdtdev, wdt);
> +	dev_set_drvdata(&pdev->dev, wdt);
> +
> +	ret = watchdog_register_device(&wdt->wdtdev);
> +	if (ret)
> +		return ret;
> +
> +	wdt->restart_handler.notifier_call = pmic_wdt_restart_handler;
> +	wdt->restart_handler.priority = 128;
> +	ret = register_restart_handler(&wdt->restart_handler);

Why is the restart handler provided by the watchdog core not sufficient ?


> +	if (ret) {
> +		dev_err(wdt->pmic->dev, "failed to register restart handler\n");
> +		return ret;
> +	}
> +
> +	dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n");

The only reasons not to use the devm_ function to register the watchdog
are the restart handler, which 1) doesn't do anything and 2) should use
the watchdog core, and this debug message. I would suggest to use the
devm_ function to register the watchdog instead.

> +	return 0;
> +}
> +
> +static int pmic_wdt_remove(struct platform_device *pdev)
> +{
> +	struct stpmu1_wdt *wdt = dev_get_drvdata(&pdev->dev);
> +
> +	unregister_restart_handler(&wdt->restart_handler);
> +	watchdog_unregister_device(&wdt->wdtdev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id of_pmic_wdt_match[] = {
> +	{ .compatible = "st,stpmu1-wdt" },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, of_pmic_wdt_match);
> +
> +static struct platform_driver stpmu1_wdt_driver = {
> +	.probe = pmic_wdt_probe,
> +	.remove = pmic_wdt_remove,
> +	.driver = {
> +		.name = "stpmu1-wdt",
> +		.of_match_table = of_pmic_wdt_match,
> +	},
> +};
> +module_platform_driver(stpmu1_wdt_driver);
> +
> +MODULE_AUTHOR("philippe.peurichard@st.com>");
> +MODULE_DESCRIPTION("Watchdog driver for STPMU1 device");
> +MODULE_LICENSE("GPL");
> 


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

* Re: [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver
  2018-07-05 15:14 ` [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver Pascal PAILLET-LME
@ 2018-07-09 22:38   ` Enric Balletbo Serra
  2018-08-21 12:23     ` Pascal PAILLET-LME
  2018-07-16 22:15   ` Rob Herring
  1 sibling, 1 reply; 17+ messages in thread
From: Enric Balletbo Serra @ 2018-07-09 22:38 UTC (permalink / raw)
  To: p.paillet
  Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Lee Jones,
	Liam Girdwood, Mark Brown, wim, Guenter Roeck, linux-input,
	devicetree, linux-kernel, linux-watchdog, benjamin.gaignard

Hi Pascal,

Thanks for the patch some comments below.

Missatge de Pascal PAILLET-LME <p.paillet@st.com> del dia dj., 5 de
jul. 2018 a les 17:17:
>
> From: pascal paillet <p.paillet@st.com>
>
> stpmu1 is a pmic from STMicroelectronics. The stpmu1 integrates 10
> regulators and 3 switches with various capabilities.
>
> Signed-off-by: pascal paillet <p.paillet@st.com>
> ---
>  drivers/mfd/Kconfig                 |  14 ++
>  drivers/mfd/Makefile                |   1 +
>  drivers/mfd/stpmu1.c                | 490 ++++++++++++++++++++++++++++++++++++
>  include/dt-bindings/mfd/st,stpmu1.h |  46 ++++
>  include/linux/mfd/stpmu1.h          | 220 ++++++++++++++++
>  5 files changed, 771 insertions(+)
>  create mode 100644 drivers/mfd/stpmu1.c
>  create mode 100644 include/dt-bindings/mfd/st,stpmu1.h
>  create mode 100644 include/linux/mfd/stpmu1.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index b860eb5..e15140b 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1812,6 +1812,20 @@ config MFD_STM32_TIMERS
>           for PWM and IIO Timer. This driver allow to share the
>           registers between the others drivers.
>
> +config MFD_STPMU1
> +       tristate "Support for STPMU1 PMIC"
> +       depends on (I2C=y && OF)
> +       select REGMAP_I2C
> +       select REGMAP_IRQ
> +       select MFD_CORE
> +       help
> +         Support for ST Microelectronics STPMU1 PMIC. Stpmu1 mfd driver is
> +         the core driver for stpmu1 component that mainly handles interrupts.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called stpmu1.
> +
> +
Extra line not needed.

>  menu "Multimedia Capabilities Port drivers"
>         depends on ARCH_SA1100
>
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index e9fd20d..f1c4be1 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_STPMU1)       += stpmu1.o
>  obj-$(CONFIG_MFD_SUN4I_GPADC)  += sun4i-gpadc.o
>
>  obj-$(CONFIG_MFD_STM32_LPTIMER)        += stm32-lptimer.o
> diff --git a/drivers/mfd/stpmu1.c b/drivers/mfd/stpmu1.c
> new file mode 100644
> index 0000000..a284a3e
> --- /dev/null
> +++ b/drivers/mfd/stpmu1.c
> @@ -0,0 +1,490 @@
> +// SPDX-License-Identifier: GPL-2.0

There is a license mismatch between SPDX and MODULE_LICENSE. Or SPDX
identifier should be GPL-2.0-or-later or MODULE_LICENSE should be
("GPL v2")

See https://elixir.bootlin.com/linux/latest/source/include/linux/module.h#L175

> +/*
> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
> + * Author: Philippe Peurichard <philippe.peurichard@st.com>,
> + * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
> + */
> +
I think that Lee, like Linus, prefers the C++ style here

> +#include <linux/err.h>
That this include is not needed.

> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/stpmu1.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
ditto
> +#include <linux/pm_runtime.h>
ditto

> +#include <linux/pm_wakeirq.h>
> +#include <linux/regmap.h>
> +#include <dt-bindings/mfd/st,stpmu1.h>
> +

[snip]

> +
> +static int stpmu1_configure_from_dt(struct stpmu1_dev *pmic_dev)
> +{
> +       struct device_node *np = pmic_dev->np;
> +       u32 reg = 0;
You don't need to initialize reg to 0, anyway will be overwriten.

> +       int ret = 0;
You don't need to initialize ret to 0, anyway will be overwritten.

> +       int irq;
> +
> +       irq = of_irq_get(np, 0);
> +       if (irq <= 0) {
> +               dev_err(pmic_dev->dev,
> +                       "Failed to get irq config: %d\n", irq);
This can be in one line.

> +               return irq ? irq : -ENODEV;
nit: return irq ?: -ENODEV;

> +       }
> +       pmic_dev->irq = irq;
> +
> +       irq = of_irq_get(np, 1);
> +       if (irq <= 0) {
> +               dev_err(pmic_dev->dev,
> +                       "Failed to get irq_wake config: %d\n", irq);
> +               return irq ? irq : -ENODEV;
nit: return irq ?: -ENODEV;

> +       }
> +       pmic_dev->irq_wake = 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", &reg)) {
> +               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", &reg)) {
> +               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", &reg)) {
> +               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", &reg)) {
> +               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 stpmu1_device_init(struct stpmu1_dev *pmic_dev)
> +{
> +       int ret;
> +       unsigned int val;
> +
> +       pmic_dev->regmap =
> +           devm_regmap_init_i2c(pmic_dev->i2c, &stpmu1_regmap_config);
> +
> +       if (IS_ERR(pmic_dev->regmap)) {
> +               ret = PTR_ERR(pmic_dev->regmap);
You can remove this ...

> +               dev_err(pmic_dev->dev, "Failed to allocate register map: %d\n",
> +                       ret);
> +               return ret;
and just return PTR_ERR(pmic_dev->regmap);

> +       }
> +
> +       ret = stpmu1_configure_from_dt(pmic_dev);
> +       if (ret < 0) {
Is ret >0 return valid?  If not, perhaps "if (ret)" would be better.

> +               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 < 0) {
Is ret >0 return valid?  If not, perhaps "if (ret)" would be better.

> +               dev_err(pmic_dev->dev, "Unable to read pmic version\n");
> +               return ret;
> +       }
> +       dev_dbg(pmic_dev->dev, "PMIC Chip Version: 0x%x\n", val);
nit: Maybe that should be dev_info instead of dev_dbg?

> +
> +       /* 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, &stpmu1_regmap_irq_chip,
> +                                      &pmic_dev->irq_data);
> +       if (ret < 0) {
Is ret >0 return valid?  If not, perhaps "if (ret)" would be better.

> +               dev_err(pmic_dev->dev, "IRQ Chip registration failed: %d\n",
> +                       ret);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id stpmu1_dt_match[] = {
> +       {.compatible = "st,stpmu1"},
> +       {},
I'd rewrite this as
 +       { .compatible = "st,stpmu1" },
 +       { }
Space after/before brackets and no comma at the end.  The sentinel
indicates the last item on structure/arrays so no need to add a comma
at the end.

> +};
> +
Remove this line

> +MODULE_DEVICE_TABLE(of, stpmu1_dt_match);
> +
> +static int stpmu1_remove(struct i2c_client *i2c)
> +{
> +       struct stpmu1_dev *pmic_dev = i2c_get_clientdata(i2c);
> +
> +       of_platform_depopulate(pmic_dev->dev);
> +
> +       return 0;
> +}
You can remove this function, see below ...

> +
> +static int stpmu1_probe(struct i2c_client *i2c,
> +                       const struct i2c_device_id *id)
> +{
> +       struct stpmu1_dev *pmic;
> +       struct device *dev = &i2c->dev;
> +       int ret = 0;
No need to initialize to 0 if ...

> +
> +       pmic = devm_kzalloc(dev, sizeof(struct stpmu1_dev), GFP_KERNEL);
> +       if (!pmic)
> +               return -ENOMEM;
> +
> +       pmic->np = dev->of_node;
> +
> +       dev_set_drvdata(dev, pmic);
> +       pmic->dev = dev;
> +       pmic->i2c = i2c;
> +
> +       ret = stpmu1_device_init(pmic);
> +       if (ret < 0)
Is ret >0 return valid?  If not, perhaps "if (ret)" would be better.
> +               goto err;
return ret;

> +
> +       ret = of_platform_populate(pmic->np, NULL, NULL, pmic->dev);
> +
ret = devm_of_platform_populate(pmic->dev);

or even better

return  devm_of_platform_populate(pmic->dev);

And remove the stpmu1_remove function.

> +       dev_dbg(dev, "stpmu1 driver probed\n");
That message looks redundant to me. I'd remove it.

> +err:
And you can remove this label.

> +       return ret;
And this

> +}
> +
> +static const struct i2c_device_id stpmu1_id[] = {
> +       {"stpmu1", 0},
> +       {}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, stpmu1_id);
The above code shouldn't be needed anymore for DT-only devices. See
da10c06a044b ("i2c: Make I2C ID tables non-mandatory for DT'ed
devices")

> +
> +#ifdef CONFIG_PM_SLEEP
> +static int stpmu1_suspend(struct device *dev)
> +{
> +       struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
> +       struct stpmu1_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 stpmu1_resume(struct device *dev)
> +{
> +       struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
> +       struct stpmu1_dev *pmic_dev = i2c_get_clientdata(i2c);
> +
> +       regcache_sync(pmic_dev->regmap);
Maybe you would like to check for an error here.

> +
> +       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(stpmu1_pm, stpmu1_suspend, stpmu1_resume);
> +
> +static struct i2c_driver stpmu1_driver = {
> +       .driver = {
> +                  .name = "stpmu1",
> +                  .owner = THIS_MODULE,
This is not needed, the core does it for you.

> +                  .pm = &stpmu1_pm,
> +                  .of_match_table = of_match_ptr(stpmu1_dt_match),
It is a DT-only device so no need the of_match_ptr.

> +                  },
> +       .probe = stpmu1_probe,
> +       .remove = stpmu1_remove,
Now you can remove this

> +       .id_table = stpmu1_id,
And you can remove this also.

> +};
> +
> +module_i2c_driver(stpmu1_driver);
> +
> +MODULE_DESCRIPTION("STPMU1 PMIC I2C Client");
nit: PMIC I2C Client sounds weird to me, "STPMU1 PMIC driver" ? Note
that I am not english native so I could be wrong.

> +MODULE_AUTHOR("<philippe.peurichard@st.com>");
Use "Name <email>" or just "Name"

> +MODULE_LICENSE("GPL");
As I told you there is a license mismatch with SPDX.

[snip]

Best regards,
 Enric

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

* Re: [PATCH 1/8] dt-bindings: mfd: document stpmu1 pmic
  2018-07-05 15:14 ` [PATCH 1/8] dt-bindings: mfd: document stpmu1 pmic Pascal PAILLET-LME
@ 2018-07-16 22:14   ` Rob Herring
  0 siblings, 0 replies; 17+ messages in thread
From: Rob Herring @ 2018-07-16 22:14 UTC (permalink / raw)
  To: Pascal PAILLET-LME
  Cc: dmitry.torokhov, mark.rutland, lee.jones, lgirdwood, broonie,
	wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard

On Thu, Jul 05, 2018 at 03:14:22PM +0000, Pascal PAILLET-LME wrote:
> From: pascal paillet <p.paillet@st.com>
> 
> stpmu1 is a pmic from STMicroelectronics. The stpmu1 integrates 10
> regulators and 3 switches with various capabilities.
> 
> Signed-off-by: pascal paillet <p.paillet@st.com>
> ---
>  .../devicetree/bindings/mfd/st,stpmu1.txt          | 138 +++++++++++++++++++++
>  1 file changed, 138 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmu1.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/st,stpmu1.txt b/Documentation/devicetree/bindings/mfd/st,stpmu1.txt
> new file mode 100644
> index 0000000..53bdab4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/st,stpmu1.txt
> @@ -0,0 +1,138 @@
> +* STMicroelectronics STPMU1 Power Management IC
> +
> +Required parent device properties:
> +- compatible: "st,stpmu1"
> +- reg: the I2C slave address for the stpmu1 chip
> +- interrupts-extended: interrupt lines to use: second irq is for wakeup.
> +- #interrupt-cells: should be 2.
> +- interrupt-controller: describes the STPMU1 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:

s/_/-/

And elsewhere...

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

Just dumping register values into DT is not the greatest design.

> +
> +
> +stpmu1 consists is a varied group of sub-devices:
> +
> +Device			 Description
> +------			------------
> +stpmu1-onkey		: On key
> +stpmu1-regulators	: Regulators
> +stpmu1-wdt		: Watchdog

These should match the node name below.

> +
> +each sub-device bindings is be described in associated driver
> +documentation section.
> +
> +Example:
> +
> +pmic: stpmu1@33 {
> +	compatible = "st,stpmu1";
> +	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,stpmu1-onkey";
> +		interrupt-parent = <&pmic>;
> +		interrupts = <7 0>,<6 1>;

> +		st,onkey-pwroff-enabled;
> +		st,onkey-press-seconds = <10>;

IIRC, we have some standard properties for these.

> +	};
> +
> +	watchdog {
> +		compatible = "st,stpmu1-wdt";
> +	};
> +
> +	regulators {
> +		compatible = "st,stpmu1-regulators";
> +
> +		vdd_core: regulator@0 {

unit-address without reg is not valid. regulator-buck1, etc. instead.

> +			regulator-compatible = "buck1";
> +			regulator-name = "vdd_core";
> +			regulator-boot-on;
> +			regulator-min-microvolt = <700000>;
> +			regulator-max-microvolt = <1200000>;
> +		};
> +		vdd: regulator@1 {
> +			regulator-compatible = "buck3";
> +			regulator-name = "vdd";
> +			regulator-min-microvolt = <3300000>;
> +			regulator-max-microvolt = <3300000>;
> +			regulator-boot-on;
> +			regulator-pull-down;
> +		};
> +	};
> -- 
> 1.9.1

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

* Re: [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver
  2018-07-05 15:14 ` [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver Pascal PAILLET-LME
  2018-07-09 22:38   ` Enric Balletbo Serra
@ 2018-07-16 22:15   ` Rob Herring
  1 sibling, 0 replies; 17+ messages in thread
From: Rob Herring @ 2018-07-16 22:15 UTC (permalink / raw)
  To: Pascal PAILLET-LME
  Cc: dmitry.torokhov, mark.rutland, lee.jones, lgirdwood, broonie,
	wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard

On Thu, Jul 05, 2018 at 03:14:22PM +0000, Pascal PAILLET-LME wrote:
> From: pascal paillet <p.paillet@st.com>
> 
> stpmu1 is a pmic from STMicroelectronics. The stpmu1 integrates 10
> regulators and 3 switches with various capabilities.
> 
> Signed-off-by: pascal paillet <p.paillet@st.com>
> ---
>  drivers/mfd/Kconfig                 |  14 ++
>  drivers/mfd/Makefile                |   1 +
>  drivers/mfd/stpmu1.c                | 490 ++++++++++++++++++++++++++++++++++++
>  include/dt-bindings/mfd/st,stpmu1.h |  46 ++++

This belongs with patch 1.

>  include/linux/mfd/stpmu1.h          | 220 ++++++++++++++++
>  5 files changed, 771 insertions(+)
>  create mode 100644 drivers/mfd/stpmu1.c
>  create mode 100644 include/dt-bindings/mfd/st,stpmu1.h
>  create mode 100644 include/linux/mfd/stpmu1.h

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

* Re: [PATCH 3/8] dt-bindings: regulator: document stpmu1 pmic regulators
  2018-07-05 15:14 ` [PATCH 3/8] dt-bindings: regulator: document stpmu1 pmic regulators Pascal PAILLET-LME
@ 2018-07-16 22:21   ` Rob Herring
  0 siblings, 0 replies; 17+ messages in thread
From: Rob Herring @ 2018-07-16 22:21 UTC (permalink / raw)
  To: Pascal PAILLET-LME
  Cc: dmitry.torokhov, mark.rutland, lee.jones, lgirdwood, broonie,
	wim, linux, linux-input, devicetree, linux-kernel,
	linux-watchdog, benjamin.gaignard

On Thu, Jul 05, 2018 at 03:14:23PM +0000, Pascal PAILLET-LME wrote:
> From: pascal paillet <p.paillet@st.com>
> 
> The STPMU1 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 <p.paillet@st.com>
> ---
>  .../bindings/regulator/st,stpmu1-regulator.txt     | 72 ++++++++++++++++++++++
>  1 file changed, 72 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/regulator/st,stpmu1-regulator.txt
> 
> diff --git a/Documentation/devicetree/bindings/regulator/st,stpmu1-regulator.txt b/Documentation/devicetree/bindings/regulator/st,stpmu1-regulator.txt
> new file mode 100644
> index 0000000..888e759
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/regulator/st,stpmu1-regulator.txt
> @@ -0,0 +1,72 @@
> +STMicroelectronics STPMU1 Voltage regulators
> +
> +Regulator Nodes are optional depending on needs.
> +
> +Available Regulators in STPMU1 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: stay on during Reset for particular regulator

s/_/-/

What's the type? 'mask' in the name makes it sound like a bitmask.

> +- 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 send 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,stpmu1-regulators";
> +
> +	ldo6-supply = <&v3v3>;
> +
> +	vdd_core: regulator@0 {

Drop the unit address.

> +		regulator-compatible = "buck1";
> +		regulator-name = "vdd_core";

I think you have the names backwards here. Plus, regulator-compatible is 
not generally used.

> +		interrupts = <IT_CURLIM_BUCK1 0>;
> +		interrupt-parent = <&pmic>;
> +		st,mask_reset;
> +		regulator-pull-down;
> +		st,pulldown_config = <2>;
> +		regulator-min-microvolt = <700000>;
> +		regulator-max-microvolt = <1200000>;
> +	};
> +
> +	v3v3: buck4 {
> +		regulator-compatible = "buck4";
> +		regulator-name = "v3v3";
> +		interrupts = <IT_CURLIM_BUCK4 0>;
> +		interrupt-parent = <&mypmic>;
> +
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +	};
> +
> +	v1v8: ldo6 {
> +		regulator-compatible = "ldo6";
> +		regulator-name = "v1v8";
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <1800000>;
> +		regulator-over-current-protection;
> +	};
> +
> +};
> -- 
> 1.9.1

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

* Re: [PATCH 6/8] input: stpmu1: add stpmu1 onkey driver
  2018-07-05 15:14 ` [PATCH 6/8] input: stpmu1: add stpmu1 onkey driver Pascal PAILLET-LME
@ 2018-08-06 22:47   ` Dmitry Torokhov
  0 siblings, 0 replies; 17+ messages in thread
From: Dmitry Torokhov @ 2018-08-06 22:47 UTC (permalink / raw)
  To: Pascal PAILLET-LME
  Cc: robh+dt, mark.rutland, lee.jones, lgirdwood, broonie, wim, linux,
	linux-input, devicetree, linux-kernel, linux-watchdog,
	benjamin.gaignard

Hi Pascal,

On Thu, Jul 05, 2018 at 03:14:24PM +0000, Pascal PAILLET-LME wrote:
> From: pascal paillet <p.paillet@st.com>
> 
> The stpmu1 pmic is able to manage an onkey button. This driver exposes
> the stpmu1 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 <p.paillet@st.com>
> ---
>  drivers/input/misc/Kconfig        |  11 ++
>  drivers/input/misc/Makefile       |   2 +
>  drivers/input/misc/stpmu1_onkey.c | 321 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 334 insertions(+)
>  create mode 100644 drivers/input/misc/stpmu1_onkey.c
> 
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index c25606e..d020971 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_STPMU1_ONKEY
> +	tristate "STPMU1 PMIC Onkey support"
> +	depends on MFD_STPMU1
> +	help
> +	  Say Y to enable support of onkey embedded into STPMU1 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 stpmu1_onkey.
> +
>  endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 72cde28..cc60dc1 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_STPMU1_ONKEY)  	+= stpmu1_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/stpmu1_onkey.c b/drivers/input/misc/stpmu1_onkey.c
> new file mode 100644
> index 0000000..084a31f
> --- /dev/null
> +++ b/drivers/input/misc/stpmu1_onkey.c
> @@ -0,0 +1,321 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
> + * Author: Philippe Peurichard <philippe.peurichard@st.com>,
> + * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
> + */
> +
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/stpmu1.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +/**
> + * struct stpmu1_onkey - OnKey data
> + * @pmic:		pointer to STPMU1 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 stpmu1_onkey {
> +	struct stpmu1_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;
> +};
> +
> +/**
> + * onkey_falling_irq() - button press isr
> + * @irq:		irq
> + * @pmic_onkey:		onkey device
> + *
> + * Return: IRQ_HANDLED
> + */

This is internal driver functions, I do not see the need for kernel-doc
style comments (or any comments for this one to be honest).

> +static irqreturn_t onkey_falling_irq(int irq, void *ponkey)
> +{
> +	struct stpmu1_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;
> +}
> +
> +/**
> + * onkey_rising_irq() - button released isr
> + * @irq:		irq
> + * @pmic_onkey:		onkey device
> + *
> + * Return: IRQ_HANDLED
> + */
> +static irqreturn_t onkey_rising_irq(int irq, void *ponkey)
> +{
> +	struct stpmu1_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;
> +}
> +
> +/**
> + * stpmu1_onkey_dt_params() - device tree parameter parser
> + * @pdev:	platform device for the PONKEY
> + * @onkey:	pointer to onkey driver data
> + * @config:	configuration params to be filled up
> + */
> +static int stpmu1_onkey_dt_params(struct platform_device *pdev,
> +				  struct stpmu1_onkey *onkey,
> +				  struct pmic_onkey_config *config)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np;
> +	u32 val;
> +
> +	np = dev->of_node;
> +	if (!np)
> +		return -EINVAL;

Is this possible?

> +
> +	onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling");
> +	if (onkey->irq_falling < 0) {
> +		dev_err(dev, "failed: request IRQ onkey-falling %d",

Some of your messages use \n, some don't. I'd rather they all did.

> +			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",
> +			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\r",

Why \n\r?

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

Even though the driver is only used in OF systems, I wonder if we should
not be using generic device property API.

> +
> +	dev_dbg(dev, "onkey-switch-off duration=%d seconds\n",
> +		config->long_press_time_val);
> +
> +	return 0;
> +}
> +
> +/**
> + * stpmu1_onkey_probe() - probe
> + * @pdev:	platform device for the PONKEY
> + *
> + * Return: 0 for successful probe else appropriate error
> + */
> +static int stpmu1_onkey_probe(struct platform_device *pdev)
> +{
> +	struct stpmu1_dev *pmic = dev_get_drvdata(pdev->dev.parent);
> +	struct device *dev = &pdev->dev;
> +	struct input_dev *input_dev;
> +	struct stpmu1_onkey *onkey;
> +	struct pmic_onkey_config config;
> +	unsigned int val = 0;
> +	int ret;

Call this variable "error" please.

> +
> +	onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
> +	if (!onkey)
> +		return -ENOMEM;
> +
> +	memset(&config, 0, sizeof(struct pmic_onkey_config));
> +	ret = stpmu1_onkey_dt_params(pdev, onkey, &config);
> +	if (ret < 0)

stpmu1_onkey_dt_params() does not return positives (good) so:

	if (error)
		return error;

> +		goto err;
> +
> +	input_dev = devm_input_allocate_device(dev);
> +	if (!input_dev) {
> +		dev_err(dev, "Can't allocate Pwr Onkey Input Device\n");
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	input_dev->name = "pmic_onkey";
> +	input_dev->phys = "pmic_onkey/input0";
> +	input_dev->dev.parent = dev;

This is already set by devm_input_allocate_device().

> +
> +	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;
> +	ret = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR,
> +				 PONKEY_TURNOFF_MASK, val);
> +	if (ret) {
> +		dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", ret);
> +		goto err;

You are using all managed resources, so "return error" directly, no need
to just to error unwinding path.

> +	}
> +
> +	ret = regmap_update_bits(pmic->regmap, PADS_PULL_CR,
> +				 PONKEY_PU_ACTIVE,
> +				 config.onkey_pullup_val);
> +
> +	if (ret) {
> +		dev_err(dev, "ONKEY Pads configuration failed: %d\n", ret);
> +		goto err;
> +	}
> +
> +	onkey->pmic = pmic;
> +	onkey->input_dev = input_dev;
> +
> +	ret = devm_request_threaded_irq(dev, onkey->irq_falling, NULL,

Why does this need to be threaded? The isr does not seem to be calling
any APIs that may wait.

> +					onkey_falling_irq,
> +					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +					dev_name(dev), onkey);
> +	if (ret) {
> +		dev_err(dev, "Can't get IRQ for Onkey Falling edge: %d\n", ret);
> +		goto err;
> +	}
> +
> +	ret = devm_request_threaded_irq(dev, onkey->irq_rising, NULL,
> +					onkey_rising_irq,
> +					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +					dev_name(dev), onkey);
> +	if (ret) {
> +		dev_err(dev, "Can't get IRQ for Onkey Rising edge: %d\n", ret);
> +		goto err;
> +	}
> +
> +	ret = input_register_device(input_dev);
> +	if (ret) {
> +		dev_err(dev, "Can't register power button: %d\n", ret);
> +		goto err;
> +	}
> +
> +	platform_set_drvdata(pdev, onkey);
> +	device_init_wakeup(dev, true);
> +
> +	dev_dbg(dev, "PMIC Pwr Onkey driver probed\n");

I'd rather dropped this. Input core will print when registering device
already.

> +
> +err:
> +	return ret;
> +}
> +
> +/**
> + * stpmu1_onkey_remove() - Cleanup on removal
> + * @pdev:	platform device for the button
> + *
> + * Return: 0
> + */
> +static int stpmu1_onkey_remove(struct platform_device *pdev)
> +{
> +	struct stpmu1_onkey *onkey = platform_get_drvdata(pdev);
> +
> +	input_unregister_device(onkey->input_dev);

You are using managed input device, so this call is not needed. You
should be able to remove entire stpmu1_onkey_remove().

> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP

You annotated suspend/resume methods with __maybe_unused, so this guard
is not needed.

> +
> +/**
> + * pmic_onkey_suspend() - suspend handler
> + * @dev:	power button device
> + *
> + * Cancel all pending work items for the power button, setup irq for wakeup
> + *
> + * Return: 0
> + */
> +static int __maybe_unused stpmu1_onkey_suspend(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct stpmu1_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;
> +}
> +
> +/**
> + * pmic_onkey_resume() - resume handler
> + * @dev:	power button device
> + *
> + * Just disable the wakeup capability of irq here.
> + *
> + * Return: 0
> + */
> +static int __maybe_unused stpmu1_onkey_resume(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct stpmu1_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;
> +}
> +
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(stpmu1_onkey_pm,
> +			 stpmu1_onkey_suspend,
> +			 stpmu1_onkey_resume);
> +
> +static const struct of_device_id of_stpmu1_onkey_match[] = {
> +	{ .compatible = "st,stpmu1-onkey" },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, of_stpmu1_onkey_match);
> +
> +static struct platform_driver stpmu1_onkey_driver = {
> +	.probe	= stpmu1_onkey_probe,
> +	.remove	= stpmu1_onkey_remove,
> +	.driver	= {
> +		.name	= "stpmu1_onkey",
> +		.of_match_table = of_match_ptr(of_stpmu1_onkey_match),
> +		.pm	= &stpmu1_onkey_pm,
> +	},
> +};
> +module_platform_driver(stpmu1_onkey_driver);
> +
> +MODULE_DESCRIPTION("Onkey driver for STPMU1");
> +MODULE_LICENSE("GPL");

To match your SPDX notice this should be MODULE_LICENSE("GPL v2").

> +MODULE_AUTHOR("philippe.peurichard@st.com>");
> -- 
> 1.9.1

Thanks.

-- 
Dmitry

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

* Re: [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver
  2018-07-09 22:38   ` Enric Balletbo Serra
@ 2018-08-21 12:23     ` Pascal PAILLET-LME
  0 siblings, 0 replies; 17+ messages in thread
From: Pascal PAILLET-LME @ 2018-08-21 12:23 UTC (permalink / raw)
  To: Enric Balletbo Serra
  Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Lee Jones,
	Liam Girdwood, Mark Brown, wim, Guenter Roeck, linux-input,
	devicetree, linux-kernel, linux-watchdog, benjamin.gaignard

Hi,

Thanks a lot for the review ! I just have a question below regarding 
populate method.


Le 07/10/2018 12:38 AM, Enric Balletbo Serra a écrit :
> Hi Pascal,
>
> Thanks for the patch some comments below.
>
> Missatge de Pascal PAILLET-LME <p.paillet@st.com> del dia dj., 5 de
> jul. 2018 a les 17:17:
>> From: pascal paillet <p.paillet@st.com>
>>
>> stpmu1 is a pmic from STMicroelectronics. The stpmu1 integrates 10
>> regulators and 3 switches with various capabilities.
>>
>> Signed-off-by: pascal paillet <p.paillet@st.com>
>> ---
>>   drivers/mfd/Kconfig                 |  14 ++
>>   drivers/mfd/Makefile                |   1 +
>>   drivers/mfd/stpmu1.c                | 490 ++++++++++++++++++++++++++++++++++++
>>   include/dt-bindings/mfd/st,stpmu1.h |  46 ++++
>>   include/linux/mfd/stpmu1.h          | 220 ++++++++++++++++
>>   5 files changed, 771 insertions(+)
>>   create mode 100644 drivers/mfd/stpmu1.c
>>   create mode 100644 include/dt-bindings/mfd/st,stpmu1.h
>>   create mode 100644 include/linux/mfd/stpmu1.h
>>
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index b860eb5..e15140b 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -1812,6 +1812,20 @@ config MFD_STM32_TIMERS
>>            for PWM and IIO Timer. This driver allow to share the
>>            registers between the others drivers.
>>
>> +config MFD_STPMU1
>> +       tristate "Support for STPMU1 PMIC"
>> +       depends on (I2C=y && OF)
>> +       select REGMAP_I2C
>> +       select REGMAP_IRQ
>> +       select MFD_CORE
>> +       help
>> +         Support for ST Microelectronics STPMU1 PMIC. Stpmu1 mfd driver is
>> +         the core driver for stpmu1 component that mainly handles interrupts.
>> +
>> +         To compile this driver as a module, choose M here: the
>> +         module will be called stpmu1.
>> +
>> +
> Extra line not needed.
>
>>   menu "Multimedia Capabilities Port drivers"
>>          depends on ARCH_SA1100
>>
>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>> index e9fd20d..f1c4be1 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_STPMU1)       += stpmu1.o
>>   obj-$(CONFIG_MFD_SUN4I_GPADC)  += sun4i-gpadc.o
>>
>>   obj-$(CONFIG_MFD_STM32_LPTIMER)        += stm32-lptimer.o
>> diff --git a/drivers/mfd/stpmu1.c b/drivers/mfd/stpmu1.c
>> new file mode 100644
>> index 0000000..a284a3e
>> --- /dev/null
>> +++ b/drivers/mfd/stpmu1.c
>> @@ -0,0 +1,490 @@
>> +// SPDX-License-Identifier: GPL-2.0
> There is a license mismatch between SPDX and MODULE_LICENSE. Or SPDX
> identifier should be GPL-2.0-or-later or MODULE_LICENSE should be
> ("GPL v2")
>
> See https://elixir.bootlin.com/linux/latest/source/include/linux/module.h#L175
>
>> +/*
>> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
>> + * Author: Philippe Peurichard <philippe.peurichard@st.com>,
>> + * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
>> + */
>> +
> I think that Lee, like Linus, prefers the C++ style here
>
>> +#include <linux/err.h>
> That this include is not needed.
>
>> +#include <linux/i2c.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/mfd/stpmu1.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
> ditto
>> +#include <linux/pm_runtime.h>
> ditto
>
>> +#include <linux/pm_wakeirq.h>
>> +#include <linux/regmap.h>
>> +#include <dt-bindings/mfd/st,stpmu1.h>
>> +
> [snip]
>
>> +
>> +static int stpmu1_configure_from_dt(struct stpmu1_dev *pmic_dev)
>> +{
>> +       struct device_node *np = pmic_dev->np;
>> +       u32 reg = 0;
> You don't need to initialize reg to 0, anyway will be overwriten.
>
>> +       int ret = 0;
> You don't need to initialize ret to 0, anyway will be overwritten.
>
>> +       int irq;
>> +
>> +       irq = of_irq_get(np, 0);
>> +       if (irq <= 0) {
>> +               dev_err(pmic_dev->dev,
>> +                       "Failed to get irq config: %d\n", irq);
> This can be in one line.
>
>> +               return irq ? irq : -ENODEV;
> nit: return irq ?: -ENODEV;
>
>> +       }
>> +       pmic_dev->irq = irq;
>> +
>> +       irq = of_irq_get(np, 1);
>> +       if (irq <= 0) {
>> +               dev_err(pmic_dev->dev,
>> +                       "Failed to get irq_wake config: %d\n", irq);
>> +               return irq ? irq : -ENODEV;
> nit: return irq ?: -ENODEV;
>
>> +       }
>> +       pmic_dev->irq_wake = 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", &reg)) {
>> +               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", &reg)) {
>> +               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", &reg)) {
>> +               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", &reg)) {
>> +               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 stpmu1_device_init(struct stpmu1_dev *pmic_dev)
>> +{
>> +       int ret;
>> +       unsigned int val;
>> +
>> +       pmic_dev->regmap =
>> +           devm_regmap_init_i2c(pmic_dev->i2c, &stpmu1_regmap_config);
>> +
>> +       if (IS_ERR(pmic_dev->regmap)) {
>> +               ret = PTR_ERR(pmic_dev->regmap);
> You can remove this ...
>
>> +               dev_err(pmic_dev->dev, "Failed to allocate register map: %d\n",
>> +                       ret);
>> +               return ret;
> and just return PTR_ERR(pmic_dev->regmap);
>
>> +       }
>> +
>> +       ret = stpmu1_configure_from_dt(pmic_dev);
>> +       if (ret < 0) {
> Is ret >0 return valid?  If not, perhaps "if (ret)" would be better.
>
>> +               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 < 0) {
> Is ret >0 return valid?  If not, perhaps "if (ret)" would be better.
>
>> +               dev_err(pmic_dev->dev, "Unable to read pmic version\n");
>> +               return ret;
>> +       }
>> +       dev_dbg(pmic_dev->dev, "PMIC Chip Version: 0x%x\n", val);
> nit: Maybe that should be dev_info instead of dev_dbg?
>
>> +
>> +       /* 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, &stpmu1_regmap_irq_chip,
>> +                                      &pmic_dev->irq_data);
>> +       if (ret < 0) {
> Is ret >0 return valid?  If not, perhaps "if (ret)" would be better.
>
>> +               dev_err(pmic_dev->dev, "IRQ Chip registration failed: %d\n",
>> +                       ret);
>> +               return ret;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id stpmu1_dt_match[] = {
>> +       {.compatible = "st,stpmu1"},
>> +       {},
> I'd rewrite this as
>   +       { .compatible = "st,stpmu1" },
>   +       { }
> Space after/before brackets and no comma at the end.  The sentinel
> indicates the last item on structure/arrays so no need to add a comma
> at the end.
>
>> +};
>> +
> Remove this line
>
>> +MODULE_DEVICE_TABLE(of, stpmu1_dt_match);
>> +
>> +static int stpmu1_remove(struct i2c_client *i2c)
>> +{
>> +       struct stpmu1_dev *pmic_dev = i2c_get_clientdata(i2c);
>> +
>> +       of_platform_depopulate(pmic_dev->dev);
>> +
>> +       return 0;
>> +}
> You can remove this function, see below ...
>
>> +
>> +static int stpmu1_probe(struct i2c_client *i2c,
>> +                       const struct i2c_device_id *id)
>> +{
>> +       struct stpmu1_dev *pmic;
>> +       struct device *dev = &i2c->dev;
>> +       int ret = 0;
> No need to initialize to 0 if ...
>
>> +
>> +       pmic = devm_kzalloc(dev, sizeof(struct stpmu1_dev), GFP_KERNEL);
>> +       if (!pmic)
>> +               return -ENOMEM;
>> +
>> +       pmic->np = dev->of_node;
>> +
>> +       dev_set_drvdata(dev, pmic);
>> +       pmic->dev = dev;
>> +       pmic->i2c = i2c;
>> +
>> +       ret = stpmu1_device_init(pmic);
>> +       if (ret < 0)
> Is ret >0 return valid?  If not, perhaps "if (ret)" would be better.
>> +               goto err;
> return ret;
>
>> +
>> +       ret = of_platform_populate(pmic->np, NULL, NULL, pmic->dev);
>> +
> ret = devm_of_platform_populate(pmic->dev);
>
> or even better
>
> return  devm_of_platform_populate(pmic->dev);
>
> And remove the stpmu1_remove function.
 From the regulator driver review, Mark Brown suggest to use 
mfd_add_devices() to remove the compatible strings in the child drivers.
This means adding struct mfd_cell with a list of devices to probe.
Is it ok if I switch to mfd_add_devices() ?


>> +       dev_dbg(dev, "stpmu1 driver probed\n");
> That message looks redundant to me. I'd remove it.
>
>> +err:
> And you can remove this label.
>
>> +       return ret;
> And this
>
>> +}
>> +
>> +static const struct i2c_device_id stpmu1_id[] = {
>> +       {"stpmu1", 0},
>> +       {}
>> +};
>> +
>> +MODULE_DEVICE_TABLE(i2c, stpmu1_id);
> The above code shouldn't be needed anymore for DT-only devices. See
> da10c06a044b ("i2c: Make I2C ID tables non-mandatory for DT'ed
> devices")
>
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int stpmu1_suspend(struct device *dev)
>> +{
>> +       struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
>> +       struct stpmu1_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 stpmu1_resume(struct device *dev)
>> +{
>> +       struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
>> +       struct stpmu1_dev *pmic_dev = i2c_get_clientdata(i2c);
>> +
>> +       regcache_sync(pmic_dev->regmap);
> Maybe you would like to check for an error here.
>
>> +
>> +       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(stpmu1_pm, stpmu1_suspend, stpmu1_resume);
>> +
>> +static struct i2c_driver stpmu1_driver = {
>> +       .driver = {
>> +                  .name = "stpmu1",
>> +                  .owner = THIS_MODULE,
> This is not needed, the core does it for you.
>
>> +                  .pm = &stpmu1_pm,
>> +                  .of_match_table = of_match_ptr(stpmu1_dt_match),
> It is a DT-only device so no need the of_match_ptr.
>
>> +                  },
>> +       .probe = stpmu1_probe,
>> +       .remove = stpmu1_remove,
> Now you can remove this
>
>> +       .id_table = stpmu1_id,
> And you can remove this also.
>
>> +};
>> +
>> +module_i2c_driver(stpmu1_driver);
>> +
>> +MODULE_DESCRIPTION("STPMU1 PMIC I2C Client");
> nit: PMIC I2C Client sounds weird to me, "STPMU1 PMIC driver" ? Note
> that I am not english native so I could be wrong.
>
>> +MODULE_AUTHOR("<philippe.peurichard@st.com>");
> Use "Name <email>" or just "Name"
>
>> +MODULE_LICENSE("GPL");
> As I told you there is a license mismatch with SPDX.
>
> [snip]
>
> Best regards,
>   Enric
>

Best Regards,
pascal

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

end of thread, other threads:[~2018-08-21 12:23 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-05 15:14 [PATCH 0/8] Introduce STPMU1 PMIC Driver Pascal PAILLET-LME
2018-07-05 15:14 ` [PATCH 2/8] mfd: stpmu1: add stpmu1 pmic driver Pascal PAILLET-LME
2018-07-09 22:38   ` Enric Balletbo Serra
2018-08-21 12:23     ` Pascal PAILLET-LME
2018-07-16 22:15   ` Rob Herring
2018-07-05 15:14 ` [PATCH 1/8] dt-bindings: mfd: document stpmu1 pmic Pascal PAILLET-LME
2018-07-16 22:14   ` Rob Herring
2018-07-05 15:14 ` [PATCH 3/8] dt-bindings: regulator: document stpmu1 pmic regulators Pascal PAILLET-LME
2018-07-16 22:21   ` Rob Herring
2018-07-05 15:14 ` [PATCH 4/8] regulator: stpmu1: add stpmu1 regulator driver Pascal PAILLET-LME
2018-07-05 16:48   ` Mark Brown
2018-07-05 15:14 ` [PATCH 6/8] input: stpmu1: add stpmu1 onkey driver Pascal PAILLET-LME
2018-08-06 22:47   ` Dmitry Torokhov
2018-07-05 15:14 ` [PATCH 5/8] dt-bindings: input: document stpmu1 pmic onkey Pascal PAILLET-LME
2018-07-05 15:14 ` [PATCH 7/8] dt-bindings: watchdog: document stpmu1 pmic watchdog Pascal PAILLET-LME
2018-07-05 15:14 ` [PATCH 8/8] watchdog: stpmu1: add stpmu1 watchdog driver Pascal PAILLET-LME
2018-07-05 18:48   ` Guenter Roeck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).