1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Intel BXT WhiskeyCove PMIC operation region driver |
4 | * |
5 | * Copyright (C) 2015 Intel Corporation. All rights reserved. |
6 | */ |
7 | |
8 | #include <linux/init.h> |
9 | #include <linux/acpi.h> |
10 | #include <linux/mfd/intel_soc_pmic.h> |
11 | #include <linux/regmap.h> |
12 | #include <linux/platform_device.h> |
13 | #include "intel_pmic.h" |
14 | |
15 | #define WHISKEY_COVE_ALRT_HIGH_BIT_MASK 0x0F |
16 | #define WHISKEY_COVE_ADC_HIGH_BIT(x) (((x & 0x0F) << 8)) |
17 | #define WHISKEY_COVE_ADC_CURSRC(x) (((x & 0xF0) >> 4)) |
18 | #define VR_MODE_DISABLED 0 |
19 | #define VR_MODE_AUTO BIT(0) |
20 | #define VR_MODE_NORMAL BIT(1) |
21 | #define VR_MODE_SWITCH BIT(2) |
22 | #define VR_MODE_ECO (BIT(0)|BIT(1)) |
23 | #define VSWITCH2_OUTPUT BIT(5) |
24 | #define VSWITCH1_OUTPUT BIT(4) |
25 | #define VUSBPHY_CHARGE BIT(1) |
26 | |
27 | static struct pmic_table power_table[] = { |
28 | { |
29 | .address = 0x0, |
30 | .reg = 0x63, |
31 | .bit = VR_MODE_AUTO, |
32 | }, /* VDD1 -> VDD1CNT */ |
33 | { |
34 | .address = 0x04, |
35 | .reg = 0x65, |
36 | .bit = VR_MODE_AUTO, |
37 | }, /* VDD2 -> VDD2CNT */ |
38 | { |
39 | .address = 0x08, |
40 | .reg = 0x67, |
41 | .bit = VR_MODE_AUTO, |
42 | }, /* VDD3 -> VDD3CNT */ |
43 | { |
44 | .address = 0x0c, |
45 | .reg = 0x6d, |
46 | .bit = VR_MODE_AUTO, |
47 | }, /* VLFX -> VFLEXCNT */ |
48 | { |
49 | .address = 0x10, |
50 | .reg = 0x6f, |
51 | .bit = VR_MODE_NORMAL, |
52 | }, /* VP1A -> VPROG1ACNT */ |
53 | { |
54 | .address = 0x14, |
55 | .reg = 0x70, |
56 | .bit = VR_MODE_NORMAL, |
57 | }, /* VP1B -> VPROG1BCNT */ |
58 | { |
59 | .address = 0x18, |
60 | .reg = 0x71, |
61 | .bit = VR_MODE_NORMAL, |
62 | }, /* VP1C -> VPROG1CCNT */ |
63 | { |
64 | .address = 0x1c, |
65 | .reg = 0x72, |
66 | .bit = VR_MODE_NORMAL, |
67 | }, /* VP1D -> VPROG1DCNT */ |
68 | { |
69 | .address = 0x20, |
70 | .reg = 0x73, |
71 | .bit = VR_MODE_NORMAL, |
72 | }, /* VP2A -> VPROG2ACNT */ |
73 | { |
74 | .address = 0x24, |
75 | .reg = 0x74, |
76 | .bit = VR_MODE_NORMAL, |
77 | }, /* VP2B -> VPROG2BCNT */ |
78 | { |
79 | .address = 0x28, |
80 | .reg = 0x75, |
81 | .bit = VR_MODE_NORMAL, |
82 | }, /* VP2C -> VPROG2CCNT */ |
83 | { |
84 | .address = 0x2c, |
85 | .reg = 0x76, |
86 | .bit = VR_MODE_NORMAL, |
87 | }, /* VP3A -> VPROG3ACNT */ |
88 | { |
89 | .address = 0x30, |
90 | .reg = 0x77, |
91 | .bit = VR_MODE_NORMAL, |
92 | }, /* VP3B -> VPROG3BCNT */ |
93 | { |
94 | .address = 0x34, |
95 | .reg = 0x78, |
96 | .bit = VSWITCH2_OUTPUT, |
97 | }, /* VSW2 -> VLD0CNT Bit 5*/ |
98 | { |
99 | .address = 0x38, |
100 | .reg = 0x78, |
101 | .bit = VSWITCH1_OUTPUT, |
102 | }, /* VSW1 -> VLD0CNT Bit 4 */ |
103 | { |
104 | .address = 0x3c, |
105 | .reg = 0x78, |
106 | .bit = VUSBPHY_CHARGE, |
107 | }, /* VUPY -> VLDOCNT Bit 1 */ |
108 | { |
109 | .address = 0x40, |
110 | .reg = 0x7b, |
111 | .bit = VR_MODE_NORMAL, |
112 | }, /* VRSO -> VREFSOCCNT*/ |
113 | { |
114 | .address = 0x44, |
115 | .reg = 0xA0, |
116 | .bit = VR_MODE_NORMAL, |
117 | }, /* VP1E -> VPROG1ECNT */ |
118 | { |
119 | .address = 0x48, |
120 | .reg = 0xA1, |
121 | .bit = VR_MODE_NORMAL, |
122 | }, /* VP1F -> VPROG1FCNT */ |
123 | { |
124 | .address = 0x4c, |
125 | .reg = 0xA2, |
126 | .bit = VR_MODE_NORMAL, |
127 | }, /* VP2D -> VPROG2DCNT */ |
128 | { |
129 | .address = 0x50, |
130 | .reg = 0xA3, |
131 | .bit = VR_MODE_NORMAL, |
132 | }, /* VP4A -> VPROG4ACNT */ |
133 | { |
134 | .address = 0x54, |
135 | .reg = 0xA4, |
136 | .bit = VR_MODE_NORMAL, |
137 | }, /* VP4B -> VPROG4BCNT */ |
138 | { |
139 | .address = 0x58, |
140 | .reg = 0xA5, |
141 | .bit = VR_MODE_NORMAL, |
142 | }, /* VP4C -> VPROG4CCNT */ |
143 | { |
144 | .address = 0x5c, |
145 | .reg = 0xA6, |
146 | .bit = VR_MODE_NORMAL, |
147 | }, /* VP4D -> VPROG4DCNT */ |
148 | { |
149 | .address = 0x60, |
150 | .reg = 0xA7, |
151 | .bit = VR_MODE_NORMAL, |
152 | }, /* VP5A -> VPROG5ACNT */ |
153 | { |
154 | .address = 0x64, |
155 | .reg = 0xA8, |
156 | .bit = VR_MODE_NORMAL, |
157 | }, /* VP5B -> VPROG5BCNT */ |
158 | { |
159 | .address = 0x68, |
160 | .reg = 0xA9, |
161 | .bit = VR_MODE_NORMAL, |
162 | }, /* VP6A -> VPROG6ACNT */ |
163 | { |
164 | .address = 0x6c, |
165 | .reg = 0xAA, |
166 | .bit = VR_MODE_NORMAL, |
167 | }, /* VP6B -> VPROG6BCNT */ |
168 | { |
169 | .address = 0x70, |
170 | .reg = 0x36, |
171 | .bit = BIT(2), |
172 | }, /* SDWN_N -> MODEMCTRL Bit 2 */ |
173 | { |
174 | .address = 0x74, |
175 | .reg = 0x36, |
176 | .bit = BIT(0), |
177 | } /* MOFF -> MODEMCTRL Bit 0 */ |
178 | }; |
179 | |
180 | static struct pmic_table thermal_table[] = { |
181 | { |
182 | .address = 0x00, |
183 | .reg = 0x4F39 |
184 | }, |
185 | { |
186 | .address = 0x04, |
187 | .reg = 0x4F24 |
188 | }, |
189 | { |
190 | .address = 0x08, |
191 | .reg = 0x4F26 |
192 | }, |
193 | { |
194 | .address = 0x0c, |
195 | .reg = 0x4F3B |
196 | }, |
197 | { |
198 | .address = 0x10, |
199 | .reg = 0x4F28 |
200 | }, |
201 | { |
202 | .address = 0x14, |
203 | .reg = 0x4F2A |
204 | }, |
205 | { |
206 | .address = 0x18, |
207 | .reg = 0x4F3D |
208 | }, |
209 | { |
210 | .address = 0x1c, |
211 | .reg = 0x4F2C |
212 | }, |
213 | { |
214 | .address = 0x20, |
215 | .reg = 0x4F2E |
216 | }, |
217 | { |
218 | .address = 0x24, |
219 | .reg = 0x4F3F |
220 | }, |
221 | { |
222 | .address = 0x28, |
223 | .reg = 0x4F30 |
224 | }, |
225 | { |
226 | .address = 0x30, |
227 | .reg = 0x4F41 |
228 | }, |
229 | { |
230 | .address = 0x34, |
231 | .reg = 0x4F32 |
232 | }, |
233 | { |
234 | .address = 0x3c, |
235 | .reg = 0x4F43 |
236 | }, |
237 | { |
238 | .address = 0x40, |
239 | .reg = 0x4F34 |
240 | }, |
241 | { |
242 | .address = 0x48, |
243 | .reg = 0x4F6A, |
244 | .bit = 0, |
245 | }, |
246 | { |
247 | .address = 0x4C, |
248 | .reg = 0x4F6A, |
249 | .bit = 1 |
250 | }, |
251 | { |
252 | .address = 0x50, |
253 | .reg = 0x4F6A, |
254 | .bit = 2 |
255 | }, |
256 | { |
257 | .address = 0x54, |
258 | .reg = 0x4F6A, |
259 | .bit = 4 |
260 | }, |
261 | { |
262 | .address = 0x58, |
263 | .reg = 0x4F6A, |
264 | .bit = 5 |
265 | }, |
266 | { |
267 | .address = 0x5C, |
268 | .reg = 0x4F6A, |
269 | .bit = 3 |
270 | }, |
271 | }; |
272 | |
273 | static int intel_bxtwc_pmic_get_power(struct regmap *regmap, int reg, |
274 | int bit, u64 *value) |
275 | { |
276 | int data; |
277 | |
278 | if (regmap_read(map: regmap, reg, val: &data)) |
279 | return -EIO; |
280 | |
281 | *value = (data & bit) ? 1 : 0; |
282 | return 0; |
283 | } |
284 | |
285 | static int intel_bxtwc_pmic_update_power(struct regmap *regmap, int reg, |
286 | int bit, bool on) |
287 | { |
288 | u8 val, mask = bit; |
289 | |
290 | if (on) |
291 | val = 0xFF; |
292 | else |
293 | val = 0x0; |
294 | |
295 | return regmap_update_bits(map: regmap, reg, mask, val); |
296 | } |
297 | |
298 | static int intel_bxtwc_pmic_get_raw_temp(struct regmap *regmap, int reg) |
299 | { |
300 | unsigned int val, adc_val, reg_val; |
301 | u8 temp_l, temp_h, cursrc; |
302 | unsigned long rlsb; |
303 | static const unsigned long rlsb_array[] = { |
304 | 0, 260420, 130210, 65100, 32550, 16280, |
305 | 8140, 4070, 2030, 0, 260420, 130210 }; |
306 | |
307 | if (regmap_read(map: regmap, reg, val: &val)) |
308 | return -EIO; |
309 | temp_l = (u8) val; |
310 | |
311 | if (regmap_read(map: regmap, reg: (reg - 1), val: &val)) |
312 | return -EIO; |
313 | temp_h = (u8) val; |
314 | |
315 | reg_val = temp_l | WHISKEY_COVE_ADC_HIGH_BIT(temp_h); |
316 | cursrc = WHISKEY_COVE_ADC_CURSRC(temp_h); |
317 | rlsb = rlsb_array[cursrc]; |
318 | adc_val = reg_val * rlsb / 1000; |
319 | |
320 | return adc_val; |
321 | } |
322 | |
323 | static int |
324 | intel_bxtwc_pmic_update_aux(struct regmap *regmap, int reg, int raw) |
325 | { |
326 | u32 bsr_num; |
327 | u16 resi_val, count = 0, thrsh = 0; |
328 | u8 alrt_h, alrt_l, cursel = 0; |
329 | |
330 | bsr_num = raw; |
331 | bsr_num /= (1 << 5); |
332 | |
333 | count = fls(x: bsr_num) - 1; |
334 | |
335 | cursel = clamp_t(s8, (count - 7), 0, 7); |
336 | thrsh = raw / (1 << (4 + cursel)); |
337 | |
338 | resi_val = (cursel << 9) | thrsh; |
339 | alrt_h = (resi_val >> 8) & WHISKEY_COVE_ALRT_HIGH_BIT_MASK; |
340 | if (regmap_update_bits(map: regmap, |
341 | reg: reg - 1, |
342 | WHISKEY_COVE_ALRT_HIGH_BIT_MASK, |
343 | val: alrt_h)) |
344 | return -EIO; |
345 | |
346 | alrt_l = (u8)resi_val; |
347 | return regmap_write(map: regmap, reg, val: alrt_l); |
348 | } |
349 | |
350 | static int |
351 | intel_bxtwc_pmic_get_policy(struct regmap *regmap, int reg, int bit, u64 *value) |
352 | { |
353 | u8 mask = BIT(bit); |
354 | unsigned int val; |
355 | |
356 | if (regmap_read(map: regmap, reg, val: &val)) |
357 | return -EIO; |
358 | |
359 | *value = (val & mask) >> bit; |
360 | return 0; |
361 | } |
362 | |
363 | static int |
364 | intel_bxtwc_pmic_update_policy(struct regmap *regmap, |
365 | int reg, int bit, int enable) |
366 | { |
367 | u8 mask = BIT(bit), val = enable << bit; |
368 | |
369 | return regmap_update_bits(map: regmap, reg, mask, val); |
370 | } |
371 | |
372 | static const struct intel_pmic_opregion_data intel_bxtwc_pmic_opregion_data = { |
373 | .get_power = intel_bxtwc_pmic_get_power, |
374 | .update_power = intel_bxtwc_pmic_update_power, |
375 | .get_raw_temp = intel_bxtwc_pmic_get_raw_temp, |
376 | .update_aux = intel_bxtwc_pmic_update_aux, |
377 | .get_policy = intel_bxtwc_pmic_get_policy, |
378 | .update_policy = intel_bxtwc_pmic_update_policy, |
379 | .lpat_raw_to_temp = acpi_lpat_raw_to_temp, |
380 | .power_table = power_table, |
381 | .power_table_count = ARRAY_SIZE(power_table), |
382 | .thermal_table = thermal_table, |
383 | .thermal_table_count = ARRAY_SIZE(thermal_table), |
384 | }; |
385 | |
386 | static int intel_bxtwc_pmic_opregion_probe(struct platform_device *pdev) |
387 | { |
388 | struct intel_soc_pmic *pmic = dev_get_drvdata(dev: pdev->dev.parent); |
389 | |
390 | return intel_pmic_install_opregion_handler(dev: &pdev->dev, |
391 | ACPI_HANDLE(pdev->dev.parent), |
392 | regmap: pmic->regmap, |
393 | d: &intel_bxtwc_pmic_opregion_data); |
394 | } |
395 | |
396 | static const struct platform_device_id bxt_wc_opregion_id_table[] = { |
397 | { .name = "bxt_wcove_region" }, |
398 | {}, |
399 | }; |
400 | |
401 | static struct platform_driver intel_bxtwc_pmic_opregion_driver = { |
402 | .probe = intel_bxtwc_pmic_opregion_probe, |
403 | .driver = { |
404 | .name = "bxt_whiskey_cove_pmic" , |
405 | }, |
406 | .id_table = bxt_wc_opregion_id_table, |
407 | }; |
408 | builtin_platform_driver(intel_bxtwc_pmic_opregion_driver); |
409 | |