xref: /linux/drivers/mfd/sec-common.c (revision 3a17ba6557e28d5d99b7e3cad31f22ad28a36cc2)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2012 Samsung Electronics Co., Ltd
4  *                http://www.samsung.com
5  * Copyright 2025 Linaro Ltd.
6  *
7  * Samsung SxM core driver
8  */
9 
10 #include <linux/device.h>
11 #include <linux/err.h>
12 #include <linux/export.h>
13 #include <linux/interrupt.h>
14 #include <linux/mfd/core.h>
15 #include <linux/mfd/samsung/core.h>
16 #include <linux/mfd/samsung/irq.h>
17 #include <linux/mfd/samsung/s2mps11.h>
18 #include <linux/mfd/samsung/s2mps13.h>
19 #include <linux/module.h>
20 #include <linux/of.h>
21 #include <linux/pm.h>
22 #include <linux/pm_runtime.h>
23 #include <linux/regmap.h>
24 #include "sec-core.h"
25 
26 static const struct resource s5m8767_rtc_resources[] = {
27 	DEFINE_RES_IRQ_NAMED(S5M8767_IRQ_RTCA1, "alarm"),
28 };
29 
30 static const struct mfd_cell s5m8767_devs[] = {
31 	MFD_CELL_NAME("s5m8767-pmic"),
32 	MFD_CELL_RES("s5m-rtc", s5m8767_rtc_resources),
33 	MFD_CELL_OF("s5m8767-clk", NULL, NULL, 0, 0, "samsung,s5m8767-clk"),
34 };
35 
36 static const struct mfd_cell s2dos05_devs[] = {
37 	MFD_CELL_NAME("s2dos05-regulator"),
38 };
39 
40 static const struct resource s2mpg10_rtc_resources[] = {
41 	DEFINE_RES_IRQ_NAMED(S2MPG10_IRQ_RTCA0, "alarm"),
42 };
43 
44 static const struct mfd_cell s2mpg10_devs[] = {
45 	MFD_CELL_NAME("s2mpg10-meter"),
46 	MFD_CELL_NAME("s2mpg10-regulator"),
47 	MFD_CELL_RES("s2mpg10-rtc", s2mpg10_rtc_resources),
48 	MFD_CELL_OF("s2mpg10-clk", NULL, NULL, 0, 0, "samsung,s2mpg10-clk"),
49 	MFD_CELL_OF("s2mpg10-gpio", NULL, NULL, 0, 0, "samsung,s2mpg10-gpio"),
50 };
51 
52 static const struct mfd_cell s2mpg11_devs[] = {
53 	MFD_CELL_NAME("s2mpg11-meter"),
54 	MFD_CELL_NAME("s2mpg11-regulator"),
55 	MFD_CELL_OF("s2mpg11-gpio", NULL, NULL, 0, 0, "samsung,s2mpg11-gpio"),
56 };
57 
58 static const struct resource s2mps11_rtc_resources[] = {
59 	DEFINE_RES_IRQ_NAMED(S2MPS11_IRQ_RTCA0, "alarm"),
60 };
61 
62 static const struct mfd_cell s2mps11_devs[] = {
63 	MFD_CELL_NAME("s2mps11-regulator"),
64 	MFD_CELL_RES("s2mps14-rtc", s2mps11_rtc_resources),
65 	MFD_CELL_OF("s2mps11-clk", NULL, NULL, 0, 0, "samsung,s2mps11-clk"),
66 };
67 
68 static const struct resource s2mps14_rtc_resources[] = {
69 	DEFINE_RES_IRQ_NAMED(S2MPS14_IRQ_RTCA0, "alarm"),
70 };
71 
72 static const struct mfd_cell s2mps13_devs[] = {
73 	MFD_CELL_NAME("s2mps13-regulator"),
74 	MFD_CELL_RES("s2mps13-rtc", s2mps14_rtc_resources),
75 	MFD_CELL_OF("s2mps13-clk", NULL, NULL, 0, 0, "samsung,s2mps13-clk"),
76 };
77 
78 static const struct mfd_cell s2mps14_devs[] = {
79 	MFD_CELL_NAME("s2mps14-regulator"),
80 	MFD_CELL_RES("s2mps14-rtc", s2mps14_rtc_resources),
81 	MFD_CELL_OF("s2mps14-clk", NULL, NULL, 0, 0, "samsung,s2mps14-clk"),
82 };
83 
84 static const struct mfd_cell s2mps15_devs[] = {
85 	MFD_CELL_NAME("s2mps15-regulator"),
86 	MFD_CELL_RES("s2mps15-rtc", s2mps14_rtc_resources),
87 	MFD_CELL_OF("s2mps13-clk", NULL, NULL, 0, 0, "samsung,s2mps13-clk"),
88 };
89 
90 static const struct mfd_cell s2mpa01_devs[] = {
91 	MFD_CELL_NAME("s2mpa01-pmic"),
92 	MFD_CELL_RES("s2mps14-rtc", s2mps14_rtc_resources),
93 };
94 
95 static const struct mfd_cell s2mpu02_devs[] = {
96 	MFD_CELL_NAME("s2mpu02-regulator"),
97 };
98 
99 static const struct resource s2mpu05_rtc_resources[] = {
100 	DEFINE_RES_IRQ_NAMED(S2MPU05_IRQ_RTCA0, "alarm"),
101 };
102 
103 static const struct mfd_cell s2mpu05_devs[] = {
104 	MFD_CELL_NAME("s2mpu05-regulator"),
105 	MFD_CELL_RES("s2mps15-rtc", s2mpu05_rtc_resources),
106 };
107 
108 static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic)
109 {
110 	unsigned int val;
111 
112 	/* For s2mpg1x, the revision is in a different regmap */
113 	switch (sec_pmic->device_type) {
114 	case S2MPG10:
115 	case S2MPG11:
116 		return;
117 	default:
118 		break;
119 	}
120 
121 	/* For each device type, the REG_ID is always the first register */
122 	if (!regmap_read(sec_pmic->regmap_pmic, S2MPS11_REG_ID, &val))
123 		dev_dbg(sec_pmic->dev, "Revision: 0x%x\n", val);
124 }
125 
126 static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic)
127 {
128 	int err;
129 
130 	if (sec_pmic->device_type != S2MPS13X)
131 		return;
132 
133 	if (sec_pmic->pdata->disable_wrstbi) {
134 		/*
135 		 * If WRSTBI pin is pulled down this feature must be disabled
136 		 * because each Suspend to RAM will trigger buck voltage reset
137 		 * to default values.
138 		 */
139 		err = regmap_update_bits(sec_pmic->regmap_pmic,
140 					 S2MPS13_REG_WRSTBI,
141 					 S2MPS13_REG_WRSTBI_MASK, 0x0);
142 		if (err)
143 			dev_warn(sec_pmic->dev,
144 				 "Cannot initialize WRSTBI config: %d\n",
145 				 err);
146 	}
147 }
148 
149 /*
150  * Only the common platform data elements for s5m8767 are parsed here from the
151  * device tree. Other sub-modules of s5m8767 such as pmic, rtc , charger and
152  * others have to parse their own platform data elements from device tree.
153  *
154  * The s5m8767 platform data structure is instantiated here and the drivers for
155  * the sub-modules need not instantiate another instance while parsing their
156  * platform data.
157  */
158 static struct sec_platform_data *
159 sec_pmic_parse_dt_pdata(struct device *dev)
160 {
161 	struct sec_platform_data *pd;
162 
163 	pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
164 	if (!pd)
165 		return ERR_PTR(-ENOMEM);
166 
167 	pd->manual_poweroff = of_property_read_bool(dev->of_node,
168 						    "samsung,s2mps11-acokb-ground");
169 	pd->disable_wrstbi = of_property_read_bool(dev->of_node,
170 						   "samsung,s2mps11-wrstbi-ground");
171 	return pd;
172 }
173 
174 int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
175 		   struct regmap *regmap, struct i2c_client *client)
176 {
177 	struct regmap_irq_chip_data *irq_data;
178 	struct sec_platform_data *pdata;
179 	const struct mfd_cell *sec_devs;
180 	struct sec_pmic_dev *sec_pmic;
181 	int ret, num_sec_devs;
182 
183 	sec_pmic = devm_kzalloc(dev, sizeof(*sec_pmic), GFP_KERNEL);
184 	if (!sec_pmic)
185 		return -ENOMEM;
186 
187 	dev_set_drvdata(dev, sec_pmic);
188 	sec_pmic->dev = dev;
189 	sec_pmic->device_type = device_type;
190 	sec_pmic->i2c = client;
191 	sec_pmic->irq = irq;
192 	sec_pmic->regmap_pmic = regmap;
193 
194 	pdata = sec_pmic_parse_dt_pdata(sec_pmic->dev);
195 	if (IS_ERR(pdata)) {
196 		ret = PTR_ERR(pdata);
197 		return ret;
198 	}
199 
200 	sec_pmic->pdata = pdata;
201 
202 	irq_data = sec_irq_init(sec_pmic);
203 	if (IS_ERR(irq_data))
204 		return PTR_ERR(irq_data);
205 
206 	pm_runtime_set_active(sec_pmic->dev);
207 
208 	switch (sec_pmic->device_type) {
209 	case S5M8767X:
210 		sec_devs = s5m8767_devs;
211 		num_sec_devs = ARRAY_SIZE(s5m8767_devs);
212 		break;
213 	case S2DOS05:
214 		sec_devs = s2dos05_devs;
215 		num_sec_devs = ARRAY_SIZE(s2dos05_devs);
216 		break;
217 	case S2MPA01:
218 		sec_devs = s2mpa01_devs;
219 		num_sec_devs = ARRAY_SIZE(s2mpa01_devs);
220 		break;
221 	case S2MPG10:
222 		sec_devs = s2mpg10_devs;
223 		num_sec_devs = ARRAY_SIZE(s2mpg10_devs);
224 		break;
225 	case S2MPG11:
226 		sec_devs = s2mpg11_devs;
227 		num_sec_devs = ARRAY_SIZE(s2mpg11_devs);
228 		break;
229 	case S2MPS11X:
230 		sec_devs = s2mps11_devs;
231 		num_sec_devs = ARRAY_SIZE(s2mps11_devs);
232 		break;
233 	case S2MPS13X:
234 		sec_devs = s2mps13_devs;
235 		num_sec_devs = ARRAY_SIZE(s2mps13_devs);
236 		break;
237 	case S2MPS14X:
238 		sec_devs = s2mps14_devs;
239 		num_sec_devs = ARRAY_SIZE(s2mps14_devs);
240 		break;
241 	case S2MPS15X:
242 		sec_devs = s2mps15_devs;
243 		num_sec_devs = ARRAY_SIZE(s2mps15_devs);
244 		break;
245 	case S2MPU02:
246 		sec_devs = s2mpu02_devs;
247 		num_sec_devs = ARRAY_SIZE(s2mpu02_devs);
248 		break;
249 	case S2MPU05:
250 		sec_devs = s2mpu05_devs;
251 		num_sec_devs = ARRAY_SIZE(s2mpu05_devs);
252 		break;
253 	default:
254 		return dev_err_probe(sec_pmic->dev, -EINVAL,
255 				     "Unsupported device type %d\n",
256 				     sec_pmic->device_type);
257 	}
258 	ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs,
259 				   NULL, 0, regmap_irq_get_domain(irq_data));
260 	if (ret)
261 		return ret;
262 
263 	sec_pmic_configure(sec_pmic);
264 	sec_pmic_dump_rev(sec_pmic);
265 
266 	return ret;
267 }
268 EXPORT_SYMBOL_GPL(sec_pmic_probe);
269 
270 void sec_pmic_shutdown(struct device *dev)
271 {
272 	struct sec_pmic_dev *sec_pmic = dev_get_drvdata(dev);
273 	unsigned int reg, mask;
274 
275 	if (!sec_pmic->pdata->manual_poweroff)
276 		return;
277 
278 	switch (sec_pmic->device_type) {
279 	case S2MPS11X:
280 		reg = S2MPS11_REG_CTRL1;
281 		mask = S2MPS11_CTRL1_PWRHOLD_MASK;
282 		break;
283 	default:
284 		/*
285 		 * Currently only one board with S2MPS11 needs this, so just
286 		 * ignore the rest.
287 		 */
288 		dev_warn(sec_pmic->dev,
289 			 "Unsupported device %d for manual power off\n",
290 			 sec_pmic->device_type);
291 		return;
292 	}
293 
294 	regmap_update_bits(sec_pmic->regmap_pmic, reg, mask, 0);
295 }
296 EXPORT_SYMBOL_GPL(sec_pmic_shutdown);
297 
298 static int sec_pmic_suspend(struct device *dev)
299 {
300 	struct sec_pmic_dev *sec_pmic = dev_get_drvdata(dev);
301 
302 	if (device_may_wakeup(dev))
303 		enable_irq_wake(sec_pmic->irq);
304 	/*
305 	 * PMIC IRQ must be disabled during suspend for RTC alarm
306 	 * to work properly.
307 	 * When device is woken up from suspend, an
308 	 * interrupt occurs before resuming I2C bus controller.
309 	 * The interrupt is handled by regmap_irq_thread which tries
310 	 * to read RTC registers. This read fails (I2C is still
311 	 * suspended) and RTC Alarm interrupt is disabled.
312 	 */
313 	disable_irq(sec_pmic->irq);
314 
315 	return 0;
316 }
317 
318 static int sec_pmic_resume(struct device *dev)
319 {
320 	struct sec_pmic_dev *sec_pmic = dev_get_drvdata(dev);
321 
322 	if (device_may_wakeup(dev))
323 		disable_irq_wake(sec_pmic->irq);
324 	enable_irq(sec_pmic->irq);
325 
326 	return 0;
327 }
328 
329 DEFINE_SIMPLE_DEV_PM_OPS(sec_pmic_pm_ops, sec_pmic_suspend, sec_pmic_resume);
330 EXPORT_SYMBOL_GPL(sec_pmic_pm_ops);
331 
332 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
333 MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
334 MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
335 MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>");
336 MODULE_DESCRIPTION("Core driver for the Samsung S5M");
337 MODULE_LICENSE("GPL");
338