1875fdd07SJulien Panis // SPDX-License-Identifier: GPL-2.0
2875fdd07SJulien Panis /*
3875fdd07SJulien Panis * ESM (Error Signal Monitor) driver for TI TPS6594/TPS6593/LP8764 PMICs
4875fdd07SJulien Panis *
5875fdd07SJulien Panis * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
6875fdd07SJulien Panis */
7875fdd07SJulien Panis
8875fdd07SJulien Panis #include <linux/interrupt.h>
9875fdd07SJulien Panis #include <linux/module.h>
10875fdd07SJulien Panis #include <linux/platform_device.h>
11875fdd07SJulien Panis #include <linux/pm_runtime.h>
12875fdd07SJulien Panis #include <linux/regmap.h>
13875fdd07SJulien Panis
14875fdd07SJulien Panis #include <linux/mfd/tps6594.h>
15875fdd07SJulien Panis
1677107b08SEsteban Blanc #define TPS6594_DEV_REV_1 0x08
1777107b08SEsteban Blanc
tps6594_esm_isr(int irq,void * dev_id)18875fdd07SJulien Panis static irqreturn_t tps6594_esm_isr(int irq, void *dev_id)
19875fdd07SJulien Panis {
20875fdd07SJulien Panis struct platform_device *pdev = dev_id;
21875fdd07SJulien Panis int i;
22875fdd07SJulien Panis
23875fdd07SJulien Panis for (i = 0 ; i < pdev->num_resources ; i++) {
24875fdd07SJulien Panis if (irq == platform_get_irq_byname(pdev, pdev->resource[i].name)) {
25875fdd07SJulien Panis dev_err(pdev->dev.parent, "%s error detected\n", pdev->resource[i].name);
26875fdd07SJulien Panis return IRQ_HANDLED;
27875fdd07SJulien Panis }
28875fdd07SJulien Panis }
29875fdd07SJulien Panis
30875fdd07SJulien Panis return IRQ_NONE;
31875fdd07SJulien Panis }
32875fdd07SJulien Panis
tps6594_esm_probe(struct platform_device * pdev)33875fdd07SJulien Panis static int tps6594_esm_probe(struct platform_device *pdev)
34875fdd07SJulien Panis {
35875fdd07SJulien Panis struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent);
36875fdd07SJulien Panis struct device *dev = &pdev->dev;
3777107b08SEsteban Blanc unsigned int rev;
38875fdd07SJulien Panis int irq;
39875fdd07SJulien Panis int ret;
40875fdd07SJulien Panis int i;
41875fdd07SJulien Panis
4277107b08SEsteban Blanc /*
4377107b08SEsteban Blanc * Due to a bug in revision 1 of the PMIC, the GPIO3 used for the
4477107b08SEsteban Blanc * SoC ESM function is used to power the load switch instead.
4577107b08SEsteban Blanc * As a consequence, ESM can not be used on those PMIC.
4677107b08SEsteban Blanc * Check the version and return an error in case of revision 1.
4777107b08SEsteban Blanc */
4877107b08SEsteban Blanc ret = regmap_read(tps->regmap, TPS6594_REG_DEV_REV, &rev);
4977107b08SEsteban Blanc if (ret)
5077107b08SEsteban Blanc return dev_err_probe(dev, ret,
5177107b08SEsteban Blanc "Failed to read PMIC revision\n");
5277107b08SEsteban Blanc if (rev == TPS6594_DEV_REV_1)
5377107b08SEsteban Blanc return dev_err_probe(dev, -ENODEV,
5477107b08SEsteban Blanc "ESM not supported for revision 1 PMIC\n");
5577107b08SEsteban Blanc
56875fdd07SJulien Panis for (i = 0; i < pdev->num_resources; i++) {
57875fdd07SJulien Panis irq = platform_get_irq_byname(pdev, pdev->resource[i].name);
58875fdd07SJulien Panis if (irq < 0)
59*1314e122SRuan Jinjie return irq;
60875fdd07SJulien Panis
61875fdd07SJulien Panis ret = devm_request_threaded_irq(dev, irq, NULL,
62875fdd07SJulien Panis tps6594_esm_isr, IRQF_ONESHOT,
63875fdd07SJulien Panis pdev->resource[i].name, pdev);
64875fdd07SJulien Panis if (ret)
65875fdd07SJulien Panis return dev_err_probe(dev, ret, "Failed to request irq\n");
66875fdd07SJulien Panis }
67875fdd07SJulien Panis
68875fdd07SJulien Panis ret = regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG,
69875fdd07SJulien Panis TPS6594_BIT_ESM_SOC_EN | TPS6594_BIT_ESM_SOC_ENDRV);
70875fdd07SJulien Panis if (ret)
71875fdd07SJulien Panis return dev_err_probe(dev, ret, "Failed to configure ESM\n");
72875fdd07SJulien Panis
73875fdd07SJulien Panis ret = regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_START_REG,
74875fdd07SJulien Panis TPS6594_BIT_ESM_SOC_START);
75875fdd07SJulien Panis if (ret)
76875fdd07SJulien Panis return dev_err_probe(dev, ret, "Failed to start ESM\n");
77875fdd07SJulien Panis
78875fdd07SJulien Panis pm_runtime_enable(dev);
79875fdd07SJulien Panis pm_runtime_get_sync(dev);
80875fdd07SJulien Panis
81875fdd07SJulien Panis return 0;
82875fdd07SJulien Panis }
83875fdd07SJulien Panis
tps6594_esm_remove(struct platform_device * pdev)8456730af7SUwe Kleine-König static void tps6594_esm_remove(struct platform_device *pdev)
85875fdd07SJulien Panis {
86875fdd07SJulien Panis struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent);
87875fdd07SJulien Panis struct device *dev = &pdev->dev;
88875fdd07SJulien Panis int ret;
89875fdd07SJulien Panis
90875fdd07SJulien Panis ret = regmap_clear_bits(tps->regmap, TPS6594_REG_ESM_SOC_START_REG,
91875fdd07SJulien Panis TPS6594_BIT_ESM_SOC_START);
92875fdd07SJulien Panis if (ret) {
93875fdd07SJulien Panis dev_err(dev, "Failed to stop ESM\n");
94875fdd07SJulien Panis goto out;
95875fdd07SJulien Panis }
96875fdd07SJulien Panis
97875fdd07SJulien Panis ret = regmap_clear_bits(tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG,
98875fdd07SJulien Panis TPS6594_BIT_ESM_SOC_EN | TPS6594_BIT_ESM_SOC_ENDRV);
99875fdd07SJulien Panis if (ret)
100875fdd07SJulien Panis dev_err(dev, "Failed to unconfigure ESM\n");
101875fdd07SJulien Panis
102875fdd07SJulien Panis out:
103875fdd07SJulien Panis pm_runtime_put_sync(dev);
104875fdd07SJulien Panis pm_runtime_disable(dev);
105875fdd07SJulien Panis }
106875fdd07SJulien Panis
tps6594_esm_suspend(struct device * dev)107875fdd07SJulien Panis static int tps6594_esm_suspend(struct device *dev)
108875fdd07SJulien Panis {
109875fdd07SJulien Panis struct tps6594 *tps = dev_get_drvdata(dev->parent);
110875fdd07SJulien Panis int ret;
111875fdd07SJulien Panis
112875fdd07SJulien Panis ret = regmap_clear_bits(tps->regmap, TPS6594_REG_ESM_SOC_START_REG,
113875fdd07SJulien Panis TPS6594_BIT_ESM_SOC_START);
114875fdd07SJulien Panis
115875fdd07SJulien Panis pm_runtime_put_sync(dev);
116875fdd07SJulien Panis
117875fdd07SJulien Panis return ret;
118875fdd07SJulien Panis }
119875fdd07SJulien Panis
tps6594_esm_resume(struct device * dev)120875fdd07SJulien Panis static int tps6594_esm_resume(struct device *dev)
121875fdd07SJulien Panis {
122875fdd07SJulien Panis struct tps6594 *tps = dev_get_drvdata(dev->parent);
123875fdd07SJulien Panis
124875fdd07SJulien Panis pm_runtime_get_sync(dev);
125875fdd07SJulien Panis
126875fdd07SJulien Panis return regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_START_REG,
127875fdd07SJulien Panis TPS6594_BIT_ESM_SOC_START);
128875fdd07SJulien Panis }
129875fdd07SJulien Panis
130875fdd07SJulien Panis static DEFINE_SIMPLE_DEV_PM_OPS(tps6594_esm_pm_ops, tps6594_esm_suspend, tps6594_esm_resume);
131875fdd07SJulien Panis
132875fdd07SJulien Panis static struct platform_driver tps6594_esm_driver = {
133875fdd07SJulien Panis .driver = {
134875fdd07SJulien Panis .name = "tps6594-esm",
135875fdd07SJulien Panis .pm = pm_sleep_ptr(&tps6594_esm_pm_ops),
136875fdd07SJulien Panis },
137875fdd07SJulien Panis .probe = tps6594_esm_probe,
13856730af7SUwe Kleine-König .remove_new = tps6594_esm_remove,
139875fdd07SJulien Panis };
140875fdd07SJulien Panis
141875fdd07SJulien Panis module_platform_driver(tps6594_esm_driver);
142875fdd07SJulien Panis
143875fdd07SJulien Panis MODULE_ALIAS("platform:tps6594-esm");
144875fdd07SJulien Panis MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
145875fdd07SJulien Panis MODULE_DESCRIPTION("TPS6594 Error Signal Monitor Driver");
146875fdd07SJulien Panis MODULE_LICENSE("GPL");
147