[PATCH 1/2] regulator: pca9450: enable restart handler for I2C operation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The NXP PCA9450 can perform various kinds of power cycles when triggered
by I2C-command.
We therefore make this functionality accessible by introducing a
respective restart handler. It will be used after a priority has been
defined within the devicetree.

Signed-off-by: Holger Assmann <h.assmann@xxxxxxxxxxxxxx>
---
 drivers/regulator/pca9450-regulator.c | 54 +++++++++++++++++++++++++++
 include/linux/regulator/pca9450.h     |  7 ++++
 2 files changed, 61 insertions(+)

diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c
index 2ab365d2749f9..e623323599964 100644
--- a/drivers/regulator/pca9450-regulator.c
+++ b/drivers/regulator/pca9450-regulator.c
@@ -16,6 +16,7 @@
 #include <linux/regulator/machine.h>
 #include <linux/regulator/of_regulator.h>
 #include <linux/regulator/pca9450.h>
+#include <linux/reboot.h>
 
 struct pc9450_dvs_config {
 	unsigned int run_reg; /* dvs0 */
@@ -33,6 +34,7 @@ struct pca9450 {
 	struct device *dev;
 	struct regmap *regmap;
 	struct gpio_desc *sd_vsel_gpio;
+	struct notifier_block restart_handler;
 	enum pca9450_chip_type type;
 	unsigned int rcnt;
 	int irq;
@@ -700,6 +702,34 @@ static irqreturn_t pca9450_irq_handler(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static int pca9450_restart(struct notifier_block *nb,
+			   unsigned long mode, void *cmd)
+{
+	struct pca9450 *pmic = container_of(nb, struct pca9450,
+					    restart_handler);
+	struct i2c_client *client = container_of(pmic->dev,
+						 struct i2c_client, dev);
+	int ret = 0;
+	u32 rtype;
+
+	switch (mode) {
+	case REBOOT_WARM:
+		/* Warm reset (Toggle POR_B for 20 ms) */
+		rtype = 0x35;
+		break;
+	default:
+		/* Cold reset (Power recycle all regulators) */
+		rtype = 0x64;
+	}
+
+	ret = i2c_smbus_write_byte_data(client, PCA9450_REG_SWRST, rtype);
+	if (ret < 0)
+		dev_alert(pmic->dev, "Failed to shutdown (err = %d)\n", ret);
+
+	mdelay(500);
+	return NOTIFY_DONE;
+}
+
 static int pca9450_i2c_probe(struct i2c_client *i2c)
 {
 	enum pca9450_chip_type type = (unsigned int)(uintptr_t)
@@ -845,12 +875,35 @@ static int pca9450_i2c_probe(struct i2c_client *i2c)
 		return PTR_ERR(pca9450->sd_vsel_gpio);
 	}
 
+	/* Register I2C restart handler if one is defined by device tree */
+	if (!of_property_read_u32(i2c->dev.of_node, "priority",
+				  &pca9450->restart_handler.priority)) {
+		pca9450->restart_handler.notifier_call = pca9450_restart;
+
+		ret = register_restart_handler(&pca9450->restart_handler);
+		if (ret)
+			return dev_err_probe(&i2c->dev, ret,
+					     "cannot register restart handler.\n");
+		else
+			dev_info(&i2c->dev,
+				 "registered restart handler with priority %d.\n",
+				 pca9450->restart_handler.priority);
+	}
+
 	dev_info(&i2c->dev, "%s probed.\n",
 		type == PCA9450_TYPE_PCA9450A ? "pca9450a" : "pca9450bc");
 
 	return 0;
 }
 
+static void pca9450_i2c_remove(struct i2c_client *i2c)
+{
+	struct pca9450 *pca9450 = dev_get_drvdata(&i2c->dev);
+
+	if (pca9450->restart_handler.notifier_call)
+		unregister_restart_handler(&pca9450->restart_handler);
+}
+
 static const struct of_device_id pca9450_of_match[] = {
 	{
 		.compatible = "nxp,pca9450a",
@@ -875,6 +928,7 @@ static struct i2c_driver pca9450_i2c_driver = {
 		.of_match_table = pca9450_of_match,
 	},
 	.probe = pca9450_i2c_probe,
+	.remove = pca9450_i2c_remove,
 };
 
 module_i2c_driver(pca9450_i2c_driver);
diff --git a/include/linux/regulator/pca9450.h b/include/linux/regulator/pca9450.h
index 505c908dbb817..2ee38bab23402 100644
--- a/include/linux/regulator/pca9450.h
+++ b/include/linux/regulator/pca9450.h
@@ -226,6 +226,13 @@ enum {
 #define WDOG_B_CFG_COLD_LDO12		0x80
 #define WDOG_B_CFG_COLD			0xC0
 
+/* PCA9450_REG_SWRST bits */
+#define SW_RST_NONE			0x00
+#define SW_RST_DEFAULTS			0x05
+#define SW_RST_COLD_LDO12_CLK32		0x14
+#define SW_RST_WARM			0x35
+#define SW_RST_COLD			0x64
+
 /* PCA9450_REG_CONFIG2 bits */
 #define I2C_LT_MASK			0x03
 #define I2C_LT_FORCE_DISABLE		0x00
-- 
2.39.2





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux