1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ESM (Error Signal Monitor) driver for TI TPS6594/TPS6593/LP8764 PMICs |
4 | * |
5 | * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ |
6 | */ |
7 | |
8 | #include <linux/interrupt.h> |
9 | #include <linux/module.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/pm_runtime.h> |
12 | #include <linux/regmap.h> |
13 | |
14 | #include <linux/mfd/tps6594.h> |
15 | |
16 | #define TPS6594_DEV_REV_1 0x08 |
17 | |
18 | static irqreturn_t tps6594_esm_isr(int irq, void *dev_id) |
19 | { |
20 | struct platform_device *pdev = dev_id; |
21 | int i; |
22 | |
23 | for (i = 0 ; i < pdev->num_resources ; i++) { |
24 | if (irq == platform_get_irq_byname(pdev, pdev->resource[i].name)) { |
25 | dev_err(pdev->dev.parent, "%s error detected\n" , pdev->resource[i].name); |
26 | return IRQ_HANDLED; |
27 | } |
28 | } |
29 | |
30 | return IRQ_NONE; |
31 | } |
32 | |
33 | static int tps6594_esm_probe(struct platform_device *pdev) |
34 | { |
35 | struct tps6594 *tps = dev_get_drvdata(dev: pdev->dev.parent); |
36 | struct device *dev = &pdev->dev; |
37 | unsigned int rev; |
38 | int irq; |
39 | int ret; |
40 | int i; |
41 | |
42 | /* |
43 | * Due to a bug in revision 1 of the PMIC, the GPIO3 used for the |
44 | * SoC ESM function is used to power the load switch instead. |
45 | * As a consequence, ESM can not be used on those PMIC. |
46 | * Check the version and return an error in case of revision 1. |
47 | */ |
48 | ret = regmap_read(map: tps->regmap, TPS6594_REG_DEV_REV, val: &rev); |
49 | if (ret) |
50 | return dev_err_probe(dev, err: ret, |
51 | fmt: "Failed to read PMIC revision\n" ); |
52 | if (rev == TPS6594_DEV_REV_1) |
53 | return dev_err_probe(dev, err: -ENODEV, |
54 | fmt: "ESM not supported for revision 1 PMIC\n" ); |
55 | |
56 | for (i = 0; i < pdev->num_resources; i++) { |
57 | irq = platform_get_irq_byname(pdev, pdev->resource[i].name); |
58 | if (irq < 0) |
59 | return irq; |
60 | |
61 | ret = devm_request_threaded_irq(dev, irq, NULL, |
62 | thread_fn: tps6594_esm_isr, IRQF_ONESHOT, |
63 | devname: pdev->resource[i].name, dev_id: pdev); |
64 | if (ret) |
65 | return dev_err_probe(dev, err: ret, fmt: "Failed to request irq\n" ); |
66 | } |
67 | |
68 | ret = regmap_set_bits(map: tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG, |
69 | TPS6594_BIT_ESM_SOC_EN | TPS6594_BIT_ESM_SOC_ENDRV); |
70 | if (ret) |
71 | return dev_err_probe(dev, err: ret, fmt: "Failed to configure ESM\n" ); |
72 | |
73 | ret = regmap_set_bits(map: tps->regmap, TPS6594_REG_ESM_SOC_START_REG, |
74 | TPS6594_BIT_ESM_SOC_START); |
75 | if (ret) |
76 | return dev_err_probe(dev, err: ret, fmt: "Failed to start ESM\n" ); |
77 | |
78 | pm_runtime_enable(dev); |
79 | pm_runtime_get_sync(dev); |
80 | |
81 | return 0; |
82 | } |
83 | |
84 | static void tps6594_esm_remove(struct platform_device *pdev) |
85 | { |
86 | struct tps6594 *tps = dev_get_drvdata(dev: pdev->dev.parent); |
87 | struct device *dev = &pdev->dev; |
88 | int ret; |
89 | |
90 | ret = regmap_clear_bits(map: tps->regmap, TPS6594_REG_ESM_SOC_START_REG, |
91 | TPS6594_BIT_ESM_SOC_START); |
92 | if (ret) { |
93 | dev_err(dev, "Failed to stop ESM\n" ); |
94 | goto out; |
95 | } |
96 | |
97 | ret = regmap_clear_bits(map: tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG, |
98 | TPS6594_BIT_ESM_SOC_EN | TPS6594_BIT_ESM_SOC_ENDRV); |
99 | if (ret) |
100 | dev_err(dev, "Failed to unconfigure ESM\n" ); |
101 | |
102 | out: |
103 | pm_runtime_put_sync(dev); |
104 | pm_runtime_disable(dev); |
105 | } |
106 | |
107 | static int tps6594_esm_suspend(struct device *dev) |
108 | { |
109 | struct tps6594 *tps = dev_get_drvdata(dev: dev->parent); |
110 | int ret; |
111 | |
112 | ret = regmap_clear_bits(map: tps->regmap, TPS6594_REG_ESM_SOC_START_REG, |
113 | TPS6594_BIT_ESM_SOC_START); |
114 | |
115 | pm_runtime_put_sync(dev); |
116 | |
117 | return ret; |
118 | } |
119 | |
120 | static int tps6594_esm_resume(struct device *dev) |
121 | { |
122 | struct tps6594 *tps = dev_get_drvdata(dev: dev->parent); |
123 | |
124 | pm_runtime_get_sync(dev); |
125 | |
126 | return regmap_set_bits(map: tps->regmap, TPS6594_REG_ESM_SOC_START_REG, |
127 | TPS6594_BIT_ESM_SOC_START); |
128 | } |
129 | |
130 | static DEFINE_SIMPLE_DEV_PM_OPS(tps6594_esm_pm_ops, tps6594_esm_suspend, tps6594_esm_resume); |
131 | |
132 | static struct platform_driver tps6594_esm_driver = { |
133 | .driver = { |
134 | .name = "tps6594-esm" , |
135 | .pm = pm_sleep_ptr(&tps6594_esm_pm_ops), |
136 | }, |
137 | .probe = tps6594_esm_probe, |
138 | .remove_new = tps6594_esm_remove, |
139 | }; |
140 | |
141 | module_platform_driver(tps6594_esm_driver); |
142 | |
143 | MODULE_ALIAS("platform:tps6594-esm" ); |
144 | MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>" ); |
145 | MODULE_DESCRIPTION("TPS6594 Error Signal Monitor Driver" ); |
146 | MODULE_LICENSE("GPL" ); |
147 | |