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@pengutronix.de>
---
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
On 19/04/2024 10:31, Holger Assmann wrote:
> 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.
>
...
> +
> 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)) {
Priority property does not define whether this is or is not restart
handler. In case of missing priority, you should use just default:
SYS_OFF_PRIO_DEFAULT. Not skip the registering.
Best regards,
Krzysztof
Hello Krzysztof, Am 19.04.24 um 15:41 schrieb Krzysztof Kozlowski: > > Priority property does not define whether this is or is not restart > handler. In case of missing priority, you should use just default: > SYS_OFF_PRIO_DEFAULT. Not skip the registering. > Thank you for your fast response and the feedback. I will work it in and send a v2 later. Kind regards, Holger -- Pengutronix e.K. | Holger Assmann | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
© 2016 - 2026 Red Hat, Inc.