1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * TI TPS68470 PMIC operation region driver |
4 | * |
5 | * Copyright (C) 2017 Intel Corporation. All rights reserved. |
6 | * |
7 | * Author: Rajmohan Mani <rajmohan.mani@intel.com> |
8 | * |
9 | * Based on drivers/acpi/pmic/intel_pmic* drivers |
10 | */ |
11 | |
12 | #include <linux/acpi.h> |
13 | #include <linux/init.h> |
14 | #include <linux/mfd/tps68470.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/regmap.h> |
17 | |
18 | struct tps68470_pmic_table { |
19 | u32 address; /* operation region address */ |
20 | u32 reg; /* corresponding register */ |
21 | u32 bitmask; /* bit mask for power, clock */ |
22 | }; |
23 | |
24 | #define TI_PMIC_POWER_OPREGION_ID 0xB0 |
25 | #define TI_PMIC_VR_VAL_OPREGION_ID 0xB1 |
26 | #define TI_PMIC_CLOCK_OPREGION_ID 0xB2 |
27 | #define TI_PMIC_CLKFREQ_OPREGION_ID 0xB3 |
28 | |
29 | struct tps68470_pmic_opregion { |
30 | struct mutex lock; |
31 | struct regmap *regmap; |
32 | }; |
33 | |
34 | #define S_IO_I2C_EN (BIT(0) | BIT(1)) |
35 | |
36 | static const struct tps68470_pmic_table power_table[] = { |
37 | { |
38 | .address = 0x00, |
39 | .reg = TPS68470_REG_S_I2C_CTL, |
40 | .bitmask = S_IO_I2C_EN, |
41 | /* S_I2C_CTL */ |
42 | }, |
43 | { |
44 | .address = 0x04, |
45 | .reg = TPS68470_REG_VCMCTL, |
46 | .bitmask = BIT(0), |
47 | /* VCMCTL */ |
48 | }, |
49 | { |
50 | .address = 0x08, |
51 | .reg = TPS68470_REG_VAUX1CTL, |
52 | .bitmask = BIT(0), |
53 | /* VAUX1_CTL */ |
54 | }, |
55 | { |
56 | .address = 0x0C, |
57 | .reg = TPS68470_REG_VAUX2CTL, |
58 | .bitmask = BIT(0), |
59 | /* VAUX2CTL */ |
60 | }, |
61 | { |
62 | .address = 0x10, |
63 | .reg = TPS68470_REG_VACTL, |
64 | .bitmask = BIT(0), |
65 | /* VACTL */ |
66 | }, |
67 | { |
68 | .address = 0x14, |
69 | .reg = TPS68470_REG_VDCTL, |
70 | .bitmask = BIT(0), |
71 | /* VDCTL */ |
72 | }, |
73 | }; |
74 | |
75 | /* Table to set voltage regulator value */ |
76 | static const struct tps68470_pmic_table vr_val_table[] = { |
77 | { |
78 | .address = 0x00, |
79 | .reg = TPS68470_REG_VSIOVAL, |
80 | .bitmask = TPS68470_VSIOVAL_IOVOLT_MASK, |
81 | /* TPS68470_REG_VSIOVAL */ |
82 | }, |
83 | { |
84 | .address = 0x04, |
85 | .reg = TPS68470_REG_VIOVAL, |
86 | .bitmask = TPS68470_VIOVAL_IOVOLT_MASK, |
87 | /* TPS68470_REG_VIOVAL */ |
88 | }, |
89 | { |
90 | .address = 0x08, |
91 | .reg = TPS68470_REG_VCMVAL, |
92 | .bitmask = TPS68470_VCMVAL_VCVOLT_MASK, |
93 | /* TPS68470_REG_VCMVAL */ |
94 | }, |
95 | { |
96 | .address = 0x0C, |
97 | .reg = TPS68470_REG_VAUX1VAL, |
98 | .bitmask = TPS68470_VAUX1VAL_AUX1VOLT_MASK, |
99 | /* TPS68470_REG_VAUX1VAL */ |
100 | }, |
101 | { |
102 | .address = 0x10, |
103 | .reg = TPS68470_REG_VAUX2VAL, |
104 | .bitmask = TPS68470_VAUX2VAL_AUX2VOLT_MASK, |
105 | /* TPS68470_REG_VAUX2VAL */ |
106 | }, |
107 | { |
108 | .address = 0x14, |
109 | .reg = TPS68470_REG_VAVAL, |
110 | .bitmask = TPS68470_VAVAL_AVOLT_MASK, |
111 | /* TPS68470_REG_VAVAL */ |
112 | }, |
113 | { |
114 | .address = 0x18, |
115 | .reg = TPS68470_REG_VDVAL, |
116 | .bitmask = TPS68470_VDVAL_DVOLT_MASK, |
117 | /* TPS68470_REG_VDVAL */ |
118 | }, |
119 | }; |
120 | |
121 | /* Table to configure clock frequency */ |
122 | static const struct tps68470_pmic_table clk_freq_table[] = { |
123 | { |
124 | .address = 0x00, |
125 | .reg = TPS68470_REG_POSTDIV2, |
126 | .bitmask = BIT(0) | BIT(1), |
127 | /* TPS68470_REG_POSTDIV2 */ |
128 | }, |
129 | { |
130 | .address = 0x04, |
131 | .reg = TPS68470_REG_BOOSTDIV, |
132 | .bitmask = 0x1F, |
133 | /* TPS68470_REG_BOOSTDIV */ |
134 | }, |
135 | { |
136 | .address = 0x08, |
137 | .reg = TPS68470_REG_BUCKDIV, |
138 | .bitmask = 0x0F, |
139 | /* TPS68470_REG_BUCKDIV */ |
140 | }, |
141 | { |
142 | .address = 0x0C, |
143 | .reg = TPS68470_REG_PLLSWR, |
144 | .bitmask = 0x13, |
145 | /* TPS68470_REG_PLLSWR */ |
146 | }, |
147 | { |
148 | .address = 0x10, |
149 | .reg = TPS68470_REG_XTALDIV, |
150 | .bitmask = 0xFF, |
151 | /* TPS68470_REG_XTALDIV */ |
152 | }, |
153 | { |
154 | .address = 0x14, |
155 | .reg = TPS68470_REG_PLLDIV, |
156 | .bitmask = 0xFF, |
157 | /* TPS68470_REG_PLLDIV */ |
158 | }, |
159 | { |
160 | .address = 0x18, |
161 | .reg = TPS68470_REG_POSTDIV, |
162 | .bitmask = 0x83, |
163 | /* TPS68470_REG_POSTDIV */ |
164 | }, |
165 | }; |
166 | |
167 | /* Table to configure and enable clocks */ |
168 | static const struct tps68470_pmic_table clk_table[] = { |
169 | { |
170 | .address = 0x00, |
171 | .reg = TPS68470_REG_PLLCTL, |
172 | .bitmask = 0xF5, |
173 | /* TPS68470_REG_PLLCTL */ |
174 | }, |
175 | { |
176 | .address = 0x04, |
177 | .reg = TPS68470_REG_PLLCTL2, |
178 | .bitmask = BIT(0), |
179 | /* TPS68470_REG_PLLCTL2 */ |
180 | }, |
181 | { |
182 | .address = 0x08, |
183 | .reg = TPS68470_REG_CLKCFG1, |
184 | .bitmask = TPS68470_CLKCFG1_MODE_A_MASK | |
185 | TPS68470_CLKCFG1_MODE_B_MASK, |
186 | /* TPS68470_REG_CLKCFG1 */ |
187 | }, |
188 | { |
189 | .address = 0x0C, |
190 | .reg = TPS68470_REG_CLKCFG2, |
191 | .bitmask = TPS68470_CLKCFG1_MODE_A_MASK | |
192 | TPS68470_CLKCFG1_MODE_B_MASK, |
193 | /* TPS68470_REG_CLKCFG2 */ |
194 | }, |
195 | }; |
196 | |
197 | static int pmic_get_reg_bit(u64 address, |
198 | const struct tps68470_pmic_table *table, |
199 | const unsigned int table_size, int *reg, |
200 | int *bitmask) |
201 | { |
202 | u64 i; |
203 | |
204 | i = address / 4; |
205 | if (i >= table_size) |
206 | return -ENOENT; |
207 | |
208 | if (!reg || !bitmask) |
209 | return -EINVAL; |
210 | |
211 | *reg = table[i].reg; |
212 | *bitmask = table[i].bitmask; |
213 | |
214 | return 0; |
215 | } |
216 | |
217 | static int tps68470_pmic_get_power(struct regmap *regmap, int reg, |
218 | int bitmask, u64 *value) |
219 | { |
220 | unsigned int data; |
221 | |
222 | if (regmap_read(map: regmap, reg, val: &data)) |
223 | return -EIO; |
224 | |
225 | *value = (data & bitmask) ? 1 : 0; |
226 | return 0; |
227 | } |
228 | |
229 | static int tps68470_pmic_get_vr_val(struct regmap *regmap, int reg, |
230 | int bitmask, u64 *value) |
231 | { |
232 | unsigned int data; |
233 | |
234 | if (regmap_read(map: regmap, reg, val: &data)) |
235 | return -EIO; |
236 | |
237 | *value = data & bitmask; |
238 | return 0; |
239 | } |
240 | |
241 | static int tps68470_pmic_get_clk(struct regmap *regmap, int reg, |
242 | int bitmask, u64 *value) |
243 | { |
244 | unsigned int data; |
245 | |
246 | if (regmap_read(map: regmap, reg, val: &data)) |
247 | return -EIO; |
248 | |
249 | *value = (data & bitmask) ? 1 : 0; |
250 | return 0; |
251 | } |
252 | |
253 | static int tps68470_pmic_get_clk_freq(struct regmap *regmap, int reg, |
254 | int bitmask, u64 *value) |
255 | { |
256 | unsigned int data; |
257 | |
258 | if (regmap_read(map: regmap, reg, val: &data)) |
259 | return -EIO; |
260 | |
261 | *value = data & bitmask; |
262 | return 0; |
263 | } |
264 | |
265 | static int ti_tps68470_regmap_update_bits(struct regmap *regmap, int reg, |
266 | int bitmask, u64 value) |
267 | { |
268 | return regmap_update_bits(map: regmap, reg, mask: bitmask, val: value); |
269 | } |
270 | |
271 | static acpi_status tps68470_pmic_common_handler(u32 function, |
272 | acpi_physical_address address, |
273 | u32 bits, u64 *value, |
274 | void *region_context, |
275 | int (*get)(struct regmap *, |
276 | int, int, u64 *), |
277 | int (*update)(struct regmap *, |
278 | int, int, u64), |
279 | const struct tps68470_pmic_table *tbl, |
280 | unsigned int tbl_size) |
281 | { |
282 | struct tps68470_pmic_opregion *opregion = region_context; |
283 | struct regmap *regmap = opregion->regmap; |
284 | int reg, ret, bitmask; |
285 | |
286 | if (bits != 32) |
287 | return AE_BAD_PARAMETER; |
288 | |
289 | ret = pmic_get_reg_bit(address, table: tbl, table_size: tbl_size, reg: ®, bitmask: &bitmask); |
290 | if (ret < 0) |
291 | return AE_BAD_PARAMETER; |
292 | |
293 | if (function == ACPI_WRITE && *value > bitmask) |
294 | return AE_BAD_PARAMETER; |
295 | |
296 | mutex_lock(&opregion->lock); |
297 | |
298 | ret = (function == ACPI_READ) ? |
299 | get(regmap, reg, bitmask, value) : |
300 | update(regmap, reg, bitmask, *value); |
301 | |
302 | mutex_unlock(lock: &opregion->lock); |
303 | |
304 | return ret ? AE_ERROR : AE_OK; |
305 | } |
306 | |
307 | static acpi_status tps68470_pmic_cfreq_handler(u32 function, |
308 | acpi_physical_address address, |
309 | u32 bits, u64 *value, |
310 | void *handler_context, |
311 | void *region_context) |
312 | { |
313 | return tps68470_pmic_common_handler(function, address, bits, value, |
314 | region_context, |
315 | get: tps68470_pmic_get_clk_freq, |
316 | update: ti_tps68470_regmap_update_bits, |
317 | tbl: clk_freq_table, |
318 | ARRAY_SIZE(clk_freq_table)); |
319 | } |
320 | |
321 | static acpi_status tps68470_pmic_clk_handler(u32 function, |
322 | acpi_physical_address address, u32 bits, |
323 | u64 *value, void *handler_context, |
324 | void *region_context) |
325 | { |
326 | return tps68470_pmic_common_handler(function, address, bits, value, |
327 | region_context, |
328 | get: tps68470_pmic_get_clk, |
329 | update: ti_tps68470_regmap_update_bits, |
330 | tbl: clk_table, |
331 | ARRAY_SIZE(clk_table)); |
332 | } |
333 | |
334 | static acpi_status tps68470_pmic_vrval_handler(u32 function, |
335 | acpi_physical_address address, |
336 | u32 bits, u64 *value, |
337 | void *handler_context, |
338 | void *region_context) |
339 | { |
340 | return tps68470_pmic_common_handler(function, address, bits, value, |
341 | region_context, |
342 | get: tps68470_pmic_get_vr_val, |
343 | update: ti_tps68470_regmap_update_bits, |
344 | tbl: vr_val_table, |
345 | ARRAY_SIZE(vr_val_table)); |
346 | } |
347 | |
348 | static acpi_status tps68470_pmic_pwr_handler(u32 function, |
349 | acpi_physical_address address, |
350 | u32 bits, u64 *value, |
351 | void *handler_context, |
352 | void *region_context) |
353 | { |
354 | if (bits != 32) |
355 | return AE_BAD_PARAMETER; |
356 | |
357 | /* set/clear for bit 0, bits 0 and 1 together */ |
358 | if (function == ACPI_WRITE && |
359 | !(*value == 0 || *value == 1 || *value == 3)) { |
360 | return AE_BAD_PARAMETER; |
361 | } |
362 | |
363 | return tps68470_pmic_common_handler(function, address, bits, value, |
364 | region_context, |
365 | get: tps68470_pmic_get_power, |
366 | update: ti_tps68470_regmap_update_bits, |
367 | tbl: power_table, |
368 | ARRAY_SIZE(power_table)); |
369 | } |
370 | |
371 | static int tps68470_pmic_opregion_probe(struct platform_device *pdev) |
372 | { |
373 | struct regmap *tps68470_regmap = dev_get_drvdata(dev: pdev->dev.parent); |
374 | acpi_handle handle = ACPI_HANDLE(pdev->dev.parent); |
375 | struct device *dev = &pdev->dev; |
376 | struct tps68470_pmic_opregion *opregion; |
377 | acpi_status status; |
378 | |
379 | if (!dev || !tps68470_regmap) { |
380 | dev_warn(dev, "dev or regmap is NULL\n" ); |
381 | return -EINVAL; |
382 | } |
383 | |
384 | if (!handle) { |
385 | dev_warn(dev, "acpi handle is NULL\n" ); |
386 | return -ENODEV; |
387 | } |
388 | |
389 | opregion = devm_kzalloc(dev, size: sizeof(*opregion), GFP_KERNEL); |
390 | if (!opregion) |
391 | return -ENOMEM; |
392 | |
393 | mutex_init(&opregion->lock); |
394 | opregion->regmap = tps68470_regmap; |
395 | |
396 | status = acpi_install_address_space_handler(device: handle, |
397 | TI_PMIC_POWER_OPREGION_ID, |
398 | handler: tps68470_pmic_pwr_handler, |
399 | NULL, context: opregion); |
400 | if (ACPI_FAILURE(status)) |
401 | goto out_mutex_destroy; |
402 | |
403 | status = acpi_install_address_space_handler(device: handle, |
404 | TI_PMIC_VR_VAL_OPREGION_ID, |
405 | handler: tps68470_pmic_vrval_handler, |
406 | NULL, context: opregion); |
407 | if (ACPI_FAILURE(status)) |
408 | goto out_remove_power_handler; |
409 | |
410 | status = acpi_install_address_space_handler(device: handle, |
411 | TI_PMIC_CLOCK_OPREGION_ID, |
412 | handler: tps68470_pmic_clk_handler, |
413 | NULL, context: opregion); |
414 | if (ACPI_FAILURE(status)) |
415 | goto out_remove_vr_val_handler; |
416 | |
417 | status = acpi_install_address_space_handler(device: handle, |
418 | TI_PMIC_CLKFREQ_OPREGION_ID, |
419 | handler: tps68470_pmic_cfreq_handler, |
420 | NULL, context: opregion); |
421 | if (ACPI_FAILURE(status)) |
422 | goto out_remove_clk_handler; |
423 | |
424 | return 0; |
425 | |
426 | out_remove_clk_handler: |
427 | acpi_remove_address_space_handler(device: handle, TI_PMIC_CLOCK_OPREGION_ID, |
428 | handler: tps68470_pmic_clk_handler); |
429 | out_remove_vr_val_handler: |
430 | acpi_remove_address_space_handler(device: handle, TI_PMIC_VR_VAL_OPREGION_ID, |
431 | handler: tps68470_pmic_vrval_handler); |
432 | out_remove_power_handler: |
433 | acpi_remove_address_space_handler(device: handle, TI_PMIC_POWER_OPREGION_ID, |
434 | handler: tps68470_pmic_pwr_handler); |
435 | out_mutex_destroy: |
436 | mutex_destroy(lock: &opregion->lock); |
437 | return -ENODEV; |
438 | } |
439 | |
440 | static struct platform_driver tps68470_pmic_opregion_driver = { |
441 | .probe = tps68470_pmic_opregion_probe, |
442 | .driver = { |
443 | .name = "tps68470_pmic_opregion" , |
444 | }, |
445 | }; |
446 | |
447 | builtin_platform_driver(tps68470_pmic_opregion_driver) |
448 | |