1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. |
4 | * Copyright (c) 2014,2015, Linaro Ltd. |
5 | * |
6 | * SAW power controller driver |
7 | */ |
8 | |
9 | #include <linux/bitfield.h> |
10 | #include <linux/err.h> |
11 | #include <linux/init.h> |
12 | #include <linux/io.h> |
13 | #include <linux/iopoll.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/linear_range.h> |
16 | #include <linux/module.h> |
17 | #include <linux/of.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/smp.h> |
21 | |
22 | #include <linux/regulator/driver.h> |
23 | |
24 | #include <soc/qcom/spm.h> |
25 | |
26 | #define FIELD_SET(current, mask, val) \ |
27 | (((current) & ~(mask)) | FIELD_PREP((mask), (val))) |
28 | |
29 | #define SPM_CTL_INDEX 0x7f |
30 | #define SPM_CTL_INDEX_SHIFT 4 |
31 | #define SPM_CTL_EN BIT(0) |
32 | |
33 | /* These registers might be specific to SPM 1.1 */ |
34 | #define SPM_VCTL_VLVL GENMASK(7, 0) |
35 | #define SPM_PMIC_DATA_0_VLVL GENMASK(7, 0) |
36 | #define SPM_PMIC_DATA_1_MIN_VSEL GENMASK(5, 0) |
37 | #define SPM_PMIC_DATA_1_MAX_VSEL GENMASK(21, 16) |
38 | |
39 | #define SPM_1_1_AVS_CTL_AVS_ENABLED BIT(27) |
40 | #define SPM_AVS_CTL_MAX_VLVL GENMASK(22, 17) |
41 | #define SPM_AVS_CTL_MIN_VLVL GENMASK(15, 10) |
42 | |
43 | enum spm_reg { |
44 | SPM_REG_CFG, |
45 | SPM_REG_SPM_CTL, |
46 | SPM_REG_DLY, |
47 | SPM_REG_PMIC_DLY, |
48 | SPM_REG_PMIC_DATA_0, |
49 | SPM_REG_PMIC_DATA_1, |
50 | SPM_REG_VCTL, |
51 | SPM_REG_SEQ_ENTRY, |
52 | SPM_REG_STS0, |
53 | SPM_REG_STS1, |
54 | SPM_REG_PMIC_STS, |
55 | SPM_REG_AVS_CTL, |
56 | SPM_REG_AVS_LIMIT, |
57 | SPM_REG_RST, |
58 | SPM_REG_NR, |
59 | }; |
60 | |
61 | #define MAX_PMIC_DATA 2 |
62 | #define MAX_SEQ_DATA 64 |
63 | |
64 | struct spm_reg_data { |
65 | const u16 *reg_offset; |
66 | u32 spm_cfg; |
67 | u32 spm_dly; |
68 | u32 pmic_dly; |
69 | u32 pmic_data[MAX_PMIC_DATA]; |
70 | u32 avs_ctl; |
71 | u32 avs_limit; |
72 | u8 seq[MAX_SEQ_DATA]; |
73 | u8 start_index[PM_SLEEP_MODE_NR]; |
74 | |
75 | smp_call_func_t set_vdd; |
76 | /* for now we support only a single range */ |
77 | struct linear_range *range; |
78 | unsigned int ramp_delay; |
79 | unsigned int init_uV; |
80 | }; |
81 | |
82 | struct spm_driver_data { |
83 | void __iomem *reg_base; |
84 | const struct spm_reg_data *reg_data; |
85 | struct device *dev; |
86 | unsigned int volt_sel; |
87 | int reg_cpu; |
88 | }; |
89 | |
90 | static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = { |
91 | [SPM_REG_AVS_CTL] = 0x904, |
92 | [SPM_REG_AVS_LIMIT] = 0x908, |
93 | }; |
94 | |
95 | static const struct spm_reg_data spm_reg_660_gold_l2 = { |
96 | .reg_offset = spm_reg_offset_v4_1, |
97 | .avs_ctl = 0x1010031, |
98 | .avs_limit = 0x4580458, |
99 | }; |
100 | |
101 | static const struct spm_reg_data spm_reg_660_silver_l2 = { |
102 | .reg_offset = spm_reg_offset_v4_1, |
103 | .avs_ctl = 0x101c031, |
104 | .avs_limit = 0x4580458, |
105 | }; |
106 | |
107 | static const struct spm_reg_data spm_reg_8998_gold_l2 = { |
108 | .reg_offset = spm_reg_offset_v4_1, |
109 | .avs_ctl = 0x1010031, |
110 | .avs_limit = 0x4700470, |
111 | }; |
112 | |
113 | static const struct spm_reg_data spm_reg_8998_silver_l2 = { |
114 | .reg_offset = spm_reg_offset_v4_1, |
115 | .avs_ctl = 0x1010031, |
116 | .avs_limit = 0x4200420, |
117 | }; |
118 | |
119 | static const u16 spm_reg_offset_v3_0[SPM_REG_NR] = { |
120 | [SPM_REG_CFG] = 0x08, |
121 | [SPM_REG_SPM_CTL] = 0x30, |
122 | [SPM_REG_DLY] = 0x34, |
123 | [SPM_REG_SEQ_ENTRY] = 0x400, |
124 | }; |
125 | |
126 | /* SPM register data for 8909 */ |
127 | static const struct spm_reg_data spm_reg_8909_cpu = { |
128 | .reg_offset = spm_reg_offset_v3_0, |
129 | .spm_cfg = 0x1, |
130 | .spm_dly = 0x3C102800, |
131 | .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, |
132 | 0x5B, 0x60, 0x03, 0x60, 0x76, 0x76, 0x0B, 0x94, 0x5B, 0x80, |
133 | 0x10, 0x26, 0x30, 0x0F }, |
134 | .start_index[PM_SLEEP_MODE_STBY] = 0, |
135 | .start_index[PM_SLEEP_MODE_SPC] = 5, |
136 | }; |
137 | |
138 | /* SPM register data for 8916 */ |
139 | static const struct spm_reg_data spm_reg_8916_cpu = { |
140 | .reg_offset = spm_reg_offset_v3_0, |
141 | .spm_cfg = 0x1, |
142 | .spm_dly = 0x3C102800, |
143 | .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, |
144 | 0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B, |
145 | 0x80, 0x10, 0x26, 0x30, 0x0F }, |
146 | .start_index[PM_SLEEP_MODE_STBY] = 0, |
147 | .start_index[PM_SLEEP_MODE_SPC] = 5, |
148 | }; |
149 | |
150 | static const struct spm_reg_data spm_reg_8939_cpu = { |
151 | .reg_offset = spm_reg_offset_v3_0, |
152 | .spm_cfg = 0x1, |
153 | .spm_dly = 0x3C102800, |
154 | .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x50, 0x1B, 0x10, 0x80, |
155 | 0x30, 0x90, 0x5B, 0x60, 0x50, 0x03, 0x60, 0x76, 0x76, 0x0B, |
156 | 0x50, 0x1B, 0x94, 0x5B, 0x80, 0x10, 0x26, 0x30, 0x50, 0x0F }, |
157 | .start_index[PM_SLEEP_MODE_STBY] = 0, |
158 | .start_index[PM_SLEEP_MODE_SPC] = 5, |
159 | }; |
160 | |
161 | static const u16 spm_reg_offset_v2_3[SPM_REG_NR] = { |
162 | [SPM_REG_CFG] = 0x08, |
163 | [SPM_REG_SPM_CTL] = 0x30, |
164 | [SPM_REG_DLY] = 0x34, |
165 | [SPM_REG_PMIC_DATA_0] = 0x40, |
166 | [SPM_REG_PMIC_DATA_1] = 0x44, |
167 | }; |
168 | |
169 | /* SPM register data for 8976 */ |
170 | static const struct spm_reg_data spm_reg_8976_gold_l2 = { |
171 | .reg_offset = spm_reg_offset_v2_3, |
172 | .spm_cfg = 0x14, |
173 | .spm_dly = 0x3c11840a, |
174 | .pmic_data[0] = 0x03030080, |
175 | .pmic_data[1] = 0x00030000, |
176 | .start_index[PM_SLEEP_MODE_STBY] = 0, |
177 | .start_index[PM_SLEEP_MODE_SPC] = 3, |
178 | }; |
179 | |
180 | static const struct spm_reg_data spm_reg_8976_silver_l2 = { |
181 | .reg_offset = spm_reg_offset_v2_3, |
182 | .spm_cfg = 0x14, |
183 | .spm_dly = 0x3c102800, |
184 | .pmic_data[0] = 0x03030080, |
185 | .pmic_data[1] = 0x00030000, |
186 | .start_index[PM_SLEEP_MODE_STBY] = 0, |
187 | .start_index[PM_SLEEP_MODE_SPC] = 2, |
188 | }; |
189 | |
190 | static const u16 spm_reg_offset_v2_1[SPM_REG_NR] = { |
191 | [SPM_REG_CFG] = 0x08, |
192 | [SPM_REG_SPM_CTL] = 0x30, |
193 | [SPM_REG_DLY] = 0x34, |
194 | [SPM_REG_SEQ_ENTRY] = 0x80, |
195 | }; |
196 | |
197 | /* SPM register data for 8974, 8084 */ |
198 | static const struct spm_reg_data spm_reg_8974_8084_cpu = { |
199 | .reg_offset = spm_reg_offset_v2_1, |
200 | .spm_cfg = 0x1, |
201 | .spm_dly = 0x3C102800, |
202 | .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03, |
203 | 0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30, |
204 | 0x0F }, |
205 | .start_index[PM_SLEEP_MODE_STBY] = 0, |
206 | .start_index[PM_SLEEP_MODE_SPC] = 3, |
207 | }; |
208 | |
209 | /* SPM register data for 8226 */ |
210 | static const struct spm_reg_data spm_reg_8226_cpu = { |
211 | .reg_offset = spm_reg_offset_v2_1, |
212 | .spm_cfg = 0x0, |
213 | .spm_dly = 0x3C102800, |
214 | .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, |
215 | 0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B, |
216 | 0x80, 0x10, 0x26, 0x30, 0x0F }, |
217 | .start_index[PM_SLEEP_MODE_STBY] = 0, |
218 | .start_index[PM_SLEEP_MODE_SPC] = 5, |
219 | }; |
220 | |
221 | static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = { |
222 | [SPM_REG_CFG] = 0x08, |
223 | [SPM_REG_STS0] = 0x0c, |
224 | [SPM_REG_STS1] = 0x10, |
225 | [SPM_REG_VCTL] = 0x14, |
226 | [SPM_REG_AVS_CTL] = 0x18, |
227 | [SPM_REG_SPM_CTL] = 0x20, |
228 | [SPM_REG_PMIC_DLY] = 0x24, |
229 | [SPM_REG_PMIC_DATA_0] = 0x28, |
230 | [SPM_REG_PMIC_DATA_1] = 0x2C, |
231 | [SPM_REG_SEQ_ENTRY] = 0x80, |
232 | }; |
233 | |
234 | static void smp_set_vdd_v1_1(void *data); |
235 | |
236 | /* SPM register data for 8064 */ |
237 | static struct linear_range spm_v1_1_regulator_range = |
238 | REGULATOR_LINEAR_RANGE(700000, 0, 56, 12500); |
239 | |
240 | static const struct spm_reg_data spm_reg_8064_cpu = { |
241 | .reg_offset = spm_reg_offset_v1_1, |
242 | .spm_cfg = 0x1F, |
243 | .pmic_dly = 0x02020004, |
244 | .pmic_data[0] = 0x0084009C, |
245 | .pmic_data[1] = 0x00A4001C, |
246 | .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01, |
247 | 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F }, |
248 | .start_index[PM_SLEEP_MODE_STBY] = 0, |
249 | .start_index[PM_SLEEP_MODE_SPC] = 2, |
250 | .set_vdd = smp_set_vdd_v1_1, |
251 | .range = &spm_v1_1_regulator_range, |
252 | .init_uV = 1300000, |
253 | .ramp_delay = 1250, |
254 | }; |
255 | |
256 | static inline void spm_register_write(struct spm_driver_data *drv, |
257 | enum spm_reg reg, u32 val) |
258 | { |
259 | if (drv->reg_data->reg_offset[reg]) |
260 | writel_relaxed(val, drv->reg_base + |
261 | drv->reg_data->reg_offset[reg]); |
262 | } |
263 | |
264 | /* Ensure a guaranteed write, before return */ |
265 | static inline void spm_register_write_sync(struct spm_driver_data *drv, |
266 | enum spm_reg reg, u32 val) |
267 | { |
268 | u32 ret; |
269 | |
270 | if (!drv->reg_data->reg_offset[reg]) |
271 | return; |
272 | |
273 | do { |
274 | writel_relaxed(val, drv->reg_base + |
275 | drv->reg_data->reg_offset[reg]); |
276 | ret = readl_relaxed(drv->reg_base + |
277 | drv->reg_data->reg_offset[reg]); |
278 | if (ret == val) |
279 | break; |
280 | cpu_relax(); |
281 | } while (1); |
282 | } |
283 | |
284 | static inline u32 spm_register_read(struct spm_driver_data *drv, |
285 | enum spm_reg reg) |
286 | { |
287 | return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]); |
288 | } |
289 | |
290 | void spm_set_low_power_mode(struct spm_driver_data *drv, |
291 | enum pm_sleep_mode mode) |
292 | { |
293 | u32 start_index; |
294 | u32 ctl_val; |
295 | |
296 | start_index = drv->reg_data->start_index[mode]; |
297 | |
298 | ctl_val = spm_register_read(drv, reg: SPM_REG_SPM_CTL); |
299 | ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT); |
300 | ctl_val |= start_index << SPM_CTL_INDEX_SHIFT; |
301 | ctl_val |= SPM_CTL_EN; |
302 | spm_register_write_sync(drv, reg: SPM_REG_SPM_CTL, val: ctl_val); |
303 | } |
304 | |
305 | static int spm_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector) |
306 | { |
307 | struct spm_driver_data *drv = rdev_get_drvdata(rdev); |
308 | |
309 | drv->volt_sel = selector; |
310 | |
311 | /* Always do the SAW register writes on the corresponding CPU */ |
312 | return smp_call_function_single(cpuid: drv->reg_cpu, func: drv->reg_data->set_vdd, info: drv, wait: true); |
313 | } |
314 | |
315 | static int spm_get_voltage_sel(struct regulator_dev *rdev) |
316 | { |
317 | struct spm_driver_data *drv = rdev_get_drvdata(rdev); |
318 | |
319 | return drv->volt_sel; |
320 | } |
321 | |
322 | static const struct regulator_ops spm_reg_ops = { |
323 | .set_voltage_sel = spm_set_voltage_sel, |
324 | .get_voltage_sel = spm_get_voltage_sel, |
325 | .list_voltage = regulator_list_voltage_linear_range, |
326 | .set_voltage_time_sel = regulator_set_voltage_time_sel, |
327 | }; |
328 | |
329 | static void smp_set_vdd_v1_1(void *data) |
330 | { |
331 | struct spm_driver_data *drv = data; |
332 | unsigned int vctl, data0, data1, avs_ctl, sts; |
333 | unsigned int vlevel, volt_sel; |
334 | bool avs_enabled; |
335 | |
336 | volt_sel = drv->volt_sel; |
337 | vlevel = volt_sel | 0x80; /* band */ |
338 | |
339 | avs_ctl = spm_register_read(drv, reg: SPM_REG_AVS_CTL); |
340 | vctl = spm_register_read(drv, reg: SPM_REG_VCTL); |
341 | data0 = spm_register_read(drv, reg: SPM_REG_PMIC_DATA_0); |
342 | data1 = spm_register_read(drv, reg: SPM_REG_PMIC_DATA_1); |
343 | |
344 | avs_enabled = avs_ctl & SPM_1_1_AVS_CTL_AVS_ENABLED; |
345 | |
346 | /* If AVS is enabled, switch it off during the voltage change */ |
347 | if (avs_enabled) { |
348 | avs_ctl &= ~SPM_1_1_AVS_CTL_AVS_ENABLED; |
349 | spm_register_write(drv, reg: SPM_REG_AVS_CTL, val: avs_ctl); |
350 | } |
351 | |
352 | /* Kick the state machine back to idle */ |
353 | spm_register_write(drv, reg: SPM_REG_RST, val: 1); |
354 | |
355 | vctl = FIELD_SET(vctl, SPM_VCTL_VLVL, vlevel); |
356 | data0 = FIELD_SET(data0, SPM_PMIC_DATA_0_VLVL, vlevel); |
357 | data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MIN_VSEL, volt_sel); |
358 | data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MAX_VSEL, volt_sel); |
359 | |
360 | spm_register_write(drv, reg: SPM_REG_VCTL, val: vctl); |
361 | spm_register_write(drv, reg: SPM_REG_PMIC_DATA_0, val: data0); |
362 | spm_register_write(drv, reg: SPM_REG_PMIC_DATA_1, val: data1); |
363 | |
364 | if (read_poll_timeout_atomic(spm_register_read, |
365 | sts, sts == vlevel, |
366 | 1, 200, false, |
367 | drv, SPM_REG_STS1)) { |
368 | dev_err_ratelimited(drv->dev, "timeout setting the voltage (%x %x)!\n" , sts, vlevel); |
369 | goto enable_avs; |
370 | } |
371 | |
372 | if (avs_enabled) { |
373 | unsigned int max_avs = volt_sel; |
374 | unsigned int min_avs = max(max_avs, 4U) - 4; |
375 | |
376 | avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MIN_VLVL, min_avs); |
377 | avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MAX_VLVL, max_avs); |
378 | spm_register_write(drv, reg: SPM_REG_AVS_CTL, val: avs_ctl); |
379 | } |
380 | |
381 | enable_avs: |
382 | if (avs_enabled) { |
383 | avs_ctl |= SPM_1_1_AVS_CTL_AVS_ENABLED; |
384 | spm_register_write(drv, reg: SPM_REG_AVS_CTL, val: avs_ctl); |
385 | } |
386 | } |
387 | |
388 | static int spm_get_cpu(struct device *dev) |
389 | { |
390 | int cpu; |
391 | bool found; |
392 | |
393 | for_each_possible_cpu(cpu) { |
394 | struct device_node *cpu_node, *saw_node; |
395 | |
396 | cpu_node = of_cpu_device_node_get(cpu); |
397 | if (!cpu_node) |
398 | continue; |
399 | |
400 | saw_node = of_parse_phandle(np: cpu_node, phandle_name: "qcom,saw" , index: 0); |
401 | found = (saw_node == dev->of_node); |
402 | of_node_put(node: saw_node); |
403 | of_node_put(node: cpu_node); |
404 | |
405 | if (found) |
406 | return cpu; |
407 | } |
408 | |
409 | /* L2 SPM is not bound to any CPU, voltage setting is not supported */ |
410 | |
411 | return -EOPNOTSUPP; |
412 | } |
413 | |
414 | static int spm_register_regulator(struct device *dev, struct spm_driver_data *drv) |
415 | { |
416 | struct regulator_config config = { |
417 | .dev = dev, |
418 | .driver_data = drv, |
419 | }; |
420 | struct regulator_desc *rdesc; |
421 | struct regulator_dev *rdev; |
422 | int ret; |
423 | bool found; |
424 | |
425 | if (!drv->reg_data->set_vdd) |
426 | return 0; |
427 | |
428 | rdesc = devm_kzalloc(dev, size: sizeof(*rdesc), GFP_KERNEL); |
429 | if (!rdesc) |
430 | return -ENOMEM; |
431 | |
432 | rdesc->name = "spm" ; |
433 | rdesc->of_match = of_match_ptr("regulator" ); |
434 | rdesc->type = REGULATOR_VOLTAGE; |
435 | rdesc->owner = THIS_MODULE; |
436 | rdesc->ops = &spm_reg_ops; |
437 | |
438 | rdesc->linear_ranges = drv->reg_data->range; |
439 | rdesc->n_linear_ranges = 1; |
440 | rdesc->n_voltages = rdesc->linear_ranges[rdesc->n_linear_ranges - 1].max_sel + 1; |
441 | rdesc->ramp_delay = drv->reg_data->ramp_delay; |
442 | |
443 | ret = spm_get_cpu(dev); |
444 | if (ret < 0) |
445 | return ret; |
446 | |
447 | drv->reg_cpu = ret; |
448 | dev_dbg(dev, "SAW2 bound to CPU %d\n" , drv->reg_cpu); |
449 | |
450 | /* |
451 | * Program initial voltage, otherwise registration will also try |
452 | * setting the voltage, which might result in undervolting the CPU. |
453 | */ |
454 | drv->volt_sel = DIV_ROUND_UP(drv->reg_data->init_uV - rdesc->min_uV, |
455 | rdesc->uV_step); |
456 | ret = linear_range_get_selector_high(r: drv->reg_data->range, |
457 | val: drv->reg_data->init_uV, |
458 | selector: &drv->volt_sel, |
459 | found: &found); |
460 | if (ret) { |
461 | dev_err(dev, "Initial uV value out of bounds\n" ); |
462 | return ret; |
463 | } |
464 | |
465 | /* Always do the SAW register writes on the corresponding CPU */ |
466 | smp_call_function_single(cpuid: drv->reg_cpu, func: drv->reg_data->set_vdd, info: drv, wait: true); |
467 | |
468 | rdev = devm_regulator_register(dev, regulator_desc: rdesc, config: &config); |
469 | if (IS_ERR(ptr: rdev)) { |
470 | dev_err(dev, "failed to register regulator\n" ); |
471 | return PTR_ERR(ptr: rdev); |
472 | } |
473 | |
474 | return 0; |
475 | } |
476 | |
477 | static const struct of_device_id spm_match_table[] = { |
478 | { .compatible = "qcom,sdm660-gold-saw2-v4.1-l2" , |
479 | .data = &spm_reg_660_gold_l2 }, |
480 | { .compatible = "qcom,sdm660-silver-saw2-v4.1-l2" , |
481 | .data = &spm_reg_660_silver_l2 }, |
482 | { .compatible = "qcom,msm8226-saw2-v2.1-cpu" , |
483 | .data = &spm_reg_8226_cpu }, |
484 | { .compatible = "qcom,msm8909-saw2-v3.0-cpu" , |
485 | .data = &spm_reg_8909_cpu }, |
486 | { .compatible = "qcom,msm8916-saw2-v3.0-cpu" , |
487 | .data = &spm_reg_8916_cpu }, |
488 | { .compatible = "qcom,msm8939-saw2-v3.0-cpu" , |
489 | .data = &spm_reg_8939_cpu }, |
490 | { .compatible = "qcom,msm8974-saw2-v2.1-cpu" , |
491 | .data = &spm_reg_8974_8084_cpu }, |
492 | { .compatible = "qcom,msm8976-gold-saw2-v2.3-l2" , |
493 | .data = &spm_reg_8976_gold_l2 }, |
494 | { .compatible = "qcom,msm8976-silver-saw2-v2.3-l2" , |
495 | .data = &spm_reg_8976_silver_l2 }, |
496 | { .compatible = "qcom,msm8998-gold-saw2-v4.1-l2" , |
497 | .data = &spm_reg_8998_gold_l2 }, |
498 | { .compatible = "qcom,msm8998-silver-saw2-v4.1-l2" , |
499 | .data = &spm_reg_8998_silver_l2 }, |
500 | { .compatible = "qcom,apq8084-saw2-v2.1-cpu" , |
501 | .data = &spm_reg_8974_8084_cpu }, |
502 | { .compatible = "qcom,apq8064-saw2-v1.1-cpu" , |
503 | .data = &spm_reg_8064_cpu }, |
504 | { }, |
505 | }; |
506 | MODULE_DEVICE_TABLE(of, spm_match_table); |
507 | |
508 | static int spm_dev_probe(struct platform_device *pdev) |
509 | { |
510 | const struct of_device_id *match_id; |
511 | struct spm_driver_data *drv; |
512 | void __iomem *addr; |
513 | |
514 | drv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*drv), GFP_KERNEL); |
515 | if (!drv) |
516 | return -ENOMEM; |
517 | |
518 | drv->reg_base = devm_platform_ioremap_resource(pdev, index: 0); |
519 | if (IS_ERR(ptr: drv->reg_base)) |
520 | return PTR_ERR(ptr: drv->reg_base); |
521 | |
522 | match_id = of_match_node(matches: spm_match_table, node: pdev->dev.of_node); |
523 | if (!match_id) |
524 | return -ENODEV; |
525 | |
526 | drv->reg_data = match_id->data; |
527 | drv->dev = &pdev->dev; |
528 | platform_set_drvdata(pdev, data: drv); |
529 | |
530 | /* Write the SPM sequences first.. */ |
531 | addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY]; |
532 | __iowrite32_copy(to: addr, from: drv->reg_data->seq, |
533 | ARRAY_SIZE(drv->reg_data->seq) / 4); |
534 | |
535 | /* |
536 | * ..and then the control registers. |
537 | * On some SoC if the control registers are written first and if the |
538 | * CPU was held in reset, the reset signal could trigger the SPM state |
539 | * machine, before the sequences are completely written. |
540 | */ |
541 | spm_register_write(drv, reg: SPM_REG_AVS_CTL, val: drv->reg_data->avs_ctl); |
542 | spm_register_write(drv, reg: SPM_REG_AVS_LIMIT, val: drv->reg_data->avs_limit); |
543 | spm_register_write(drv, reg: SPM_REG_CFG, val: drv->reg_data->spm_cfg); |
544 | spm_register_write(drv, reg: SPM_REG_DLY, val: drv->reg_data->spm_dly); |
545 | spm_register_write(drv, reg: SPM_REG_PMIC_DLY, val: drv->reg_data->pmic_dly); |
546 | spm_register_write(drv, reg: SPM_REG_PMIC_DATA_0, |
547 | val: drv->reg_data->pmic_data[0]); |
548 | spm_register_write(drv, reg: SPM_REG_PMIC_DATA_1, |
549 | val: drv->reg_data->pmic_data[1]); |
550 | |
551 | /* Set up Standby as the default low power mode */ |
552 | if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL]) |
553 | spm_set_low_power_mode(drv, mode: PM_SLEEP_MODE_STBY); |
554 | |
555 | if (IS_ENABLED(CONFIG_REGULATOR)) |
556 | return spm_register_regulator(dev: &pdev->dev, drv); |
557 | |
558 | return 0; |
559 | } |
560 | |
561 | static struct platform_driver spm_driver = { |
562 | .probe = spm_dev_probe, |
563 | .driver = { |
564 | .name = "qcom_spm" , |
565 | .of_match_table = spm_match_table, |
566 | }, |
567 | }; |
568 | |
569 | static int __init qcom_spm_init(void) |
570 | { |
571 | return platform_driver_register(&spm_driver); |
572 | } |
573 | arch_initcall(qcom_spm_init); |
574 | |
575 | MODULE_LICENSE("GPL v2" ); |
576 | |