1 | /* |
2 | * intel_pmic.c - Intel PMIC operation region driver |
3 | * |
4 | * Copyright (C) 2014 Intel Corporation. All rights reserved. |
5 | * |
6 | * This program is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public License version |
8 | * 2 as published by the Free Software Foundation. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | */ |
15 | |
16 | #include <linux/export.h> |
17 | #include <linux/acpi.h> |
18 | #include <linux/mfd/intel_soc_pmic.h> |
19 | #include <linux/regmap.h> |
20 | #include <acpi/acpi_lpat.h> |
21 | #include "intel_pmic.h" |
22 | |
23 | #define PMIC_POWER_OPREGION_ID 0x8d |
24 | #define PMIC_THERMAL_OPREGION_ID 0x8c |
25 | #define PMIC_REGS_OPREGION_ID 0x8f |
26 | |
27 | struct intel_pmic_regs_handler_ctx { |
28 | unsigned int val; |
29 | u16 addr; |
30 | }; |
31 | |
32 | struct intel_pmic_opregion { |
33 | struct mutex lock; |
34 | struct acpi_lpat_conversion_table *lpat_table; |
35 | struct regmap *regmap; |
36 | struct intel_pmic_opregion_data *data; |
37 | struct intel_pmic_regs_handler_ctx ctx; |
38 | }; |
39 | |
40 | static struct intel_pmic_opregion *intel_pmic_opregion; |
41 | |
42 | static int pmic_get_reg_bit(int address, struct pmic_table *table, |
43 | int count, int *reg, int *bit) |
44 | { |
45 | int i; |
46 | |
47 | for (i = 0; i < count; i++) { |
48 | if (table[i].address == address) { |
49 | *reg = table[i].reg; |
50 | if (bit) |
51 | *bit = table[i].bit; |
52 | return 0; |
53 | } |
54 | } |
55 | return -ENOENT; |
56 | } |
57 | |
58 | static acpi_status intel_pmic_power_handler(u32 function, |
59 | acpi_physical_address address, u32 bits, u64 *value64, |
60 | void *handler_context, void *region_context) |
61 | { |
62 | struct intel_pmic_opregion *opregion = region_context; |
63 | struct regmap *regmap = opregion->regmap; |
64 | struct intel_pmic_opregion_data *d = opregion->data; |
65 | int reg, bit, result; |
66 | |
67 | if (bits != 32 || !value64) |
68 | return AE_BAD_PARAMETER; |
69 | |
70 | if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1)) |
71 | return AE_BAD_PARAMETER; |
72 | |
73 | result = pmic_get_reg_bit(address, d->power_table, |
74 | d->power_table_count, ®, &bit); |
75 | if (result == -ENOENT) |
76 | return AE_BAD_PARAMETER; |
77 | |
78 | mutex_lock(&opregion->lock); |
79 | |
80 | result = function == ACPI_READ ? |
81 | d->get_power(regmap, reg, bit, value64) : |
82 | d->update_power(regmap, reg, bit, *value64 == 1); |
83 | |
84 | mutex_unlock(&opregion->lock); |
85 | |
86 | return result ? AE_ERROR : AE_OK; |
87 | } |
88 | |
89 | static int pmic_read_temp(struct intel_pmic_opregion *opregion, |
90 | int reg, u64 *value) |
91 | { |
92 | int raw_temp, temp; |
93 | |
94 | if (!opregion->data->get_raw_temp) |
95 | return -ENXIO; |
96 | |
97 | raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg); |
98 | if (raw_temp < 0) |
99 | return raw_temp; |
100 | |
101 | if (!opregion->lpat_table) { |
102 | *value = raw_temp; |
103 | return 0; |
104 | } |
105 | |
106 | temp = acpi_lpat_raw_to_temp(opregion->lpat_table, raw_temp); |
107 | if (temp < 0) |
108 | return temp; |
109 | |
110 | *value = temp; |
111 | return 0; |
112 | } |
113 | |
114 | static int pmic_thermal_temp(struct intel_pmic_opregion *opregion, int reg, |
115 | u32 function, u64 *value) |
116 | { |
117 | return function == ACPI_READ ? |
118 | pmic_read_temp(opregion, reg, value) : -EINVAL; |
119 | } |
120 | |
121 | static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg, |
122 | u32 function, u64 *value) |
123 | { |
124 | int raw_temp; |
125 | |
126 | if (function == ACPI_READ) |
127 | return pmic_read_temp(opregion, reg, value); |
128 | |
129 | if (!opregion->data->update_aux) |
130 | return -ENXIO; |
131 | |
132 | if (opregion->lpat_table) { |
133 | raw_temp = acpi_lpat_temp_to_raw(opregion->lpat_table, *value); |
134 | if (raw_temp < 0) |
135 | return raw_temp; |
136 | } else { |
137 | raw_temp = *value; |
138 | } |
139 | |
140 | return opregion->data->update_aux(opregion->regmap, reg, raw_temp); |
141 | } |
142 | |
143 | static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, |
144 | int bit, u32 function, u64 *value) |
145 | { |
146 | struct intel_pmic_opregion_data *d = opregion->data; |
147 | struct regmap *regmap = opregion->regmap; |
148 | |
149 | if (!d->get_policy || !d->update_policy) |
150 | return -ENXIO; |
151 | |
152 | if (function == ACPI_READ) |
153 | return d->get_policy(regmap, reg, bit, value); |
154 | |
155 | if (*value != 0 && *value != 1) |
156 | return -EINVAL; |
157 | |
158 | return d->update_policy(regmap, reg, bit, *value); |
159 | } |
160 | |
161 | static bool pmic_thermal_is_temp(int address) |
162 | { |
163 | return (address <= 0x3c) && !(address % 12); |
164 | } |
165 | |
166 | static bool pmic_thermal_is_aux(int address) |
167 | { |
168 | return (address >= 4 && address <= 0x40 && !((address - 4) % 12)) || |
169 | (address >= 8 && address <= 0x44 && !((address - 8) % 12)); |
170 | } |
171 | |
172 | static bool pmic_thermal_is_pen(int address) |
173 | { |
174 | return address >= 0x48 && address <= 0x5c; |
175 | } |
176 | |
177 | static acpi_status intel_pmic_thermal_handler(u32 function, |
178 | acpi_physical_address address, u32 bits, u64 *value64, |
179 | void *handler_context, void *region_context) |
180 | { |
181 | struct intel_pmic_opregion *opregion = region_context; |
182 | struct intel_pmic_opregion_data *d = opregion->data; |
183 | int reg, bit, result; |
184 | |
185 | if (bits != 32 || !value64) |
186 | return AE_BAD_PARAMETER; |
187 | |
188 | result = pmic_get_reg_bit(address, d->thermal_table, |
189 | d->thermal_table_count, ®, &bit); |
190 | if (result == -ENOENT) |
191 | return AE_BAD_PARAMETER; |
192 | |
193 | mutex_lock(&opregion->lock); |
194 | |
195 | if (pmic_thermal_is_temp(address)) |
196 | result = pmic_thermal_temp(opregion, reg, function, value64); |
197 | else if (pmic_thermal_is_aux(address)) |
198 | result = pmic_thermal_aux(opregion, reg, function, value64); |
199 | else if (pmic_thermal_is_pen(address)) |
200 | result = pmic_thermal_pen(opregion, reg, bit, |
201 | function, value64); |
202 | else |
203 | result = -EINVAL; |
204 | |
205 | mutex_unlock(&opregion->lock); |
206 | |
207 | if (result < 0) { |
208 | if (result == -EINVAL) |
209 | return AE_BAD_PARAMETER; |
210 | else |
211 | return AE_ERROR; |
212 | } |
213 | |
214 | return AE_OK; |
215 | } |
216 | |
217 | static acpi_status intel_pmic_regs_handler(u32 function, |
218 | acpi_physical_address address, u32 bits, u64 *value64, |
219 | void *handler_context, void *region_context) |
220 | { |
221 | struct intel_pmic_opregion *opregion = region_context; |
222 | int result = 0; |
223 | |
224 | switch (address) { |
225 | case 0: |
226 | return AE_OK; |
227 | case 1: |
228 | opregion->ctx.addr |= (*value64 & 0xff) << 8; |
229 | return AE_OK; |
230 | case 2: |
231 | opregion->ctx.addr |= *value64 & 0xff; |
232 | return AE_OK; |
233 | case 3: |
234 | opregion->ctx.val = *value64 & 0xff; |
235 | return AE_OK; |
236 | case 4: |
237 | if (*value64) { |
238 | result = regmap_write(opregion->regmap, opregion->ctx.addr, |
239 | opregion->ctx.val); |
240 | } else { |
241 | result = regmap_read(opregion->regmap, opregion->ctx.addr, |
242 | &opregion->ctx.val); |
243 | if (result == 0) |
244 | *value64 = opregion->ctx.val; |
245 | } |
246 | memset(&opregion->ctx, 0x00, sizeof(opregion->ctx)); |
247 | } |
248 | |
249 | if (result < 0) { |
250 | if (result == -EINVAL) |
251 | return AE_BAD_PARAMETER; |
252 | else |
253 | return AE_ERROR; |
254 | } |
255 | |
256 | return AE_OK; |
257 | } |
258 | |
259 | int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, |
260 | struct regmap *regmap, |
261 | struct intel_pmic_opregion_data *d) |
262 | { |
263 | acpi_status status; |
264 | struct intel_pmic_opregion *opregion; |
265 | int ret; |
266 | |
267 | if (!dev || !regmap || !d) |
268 | return -EINVAL; |
269 | |
270 | if (!handle) |
271 | return -ENODEV; |
272 | |
273 | opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL); |
274 | if (!opregion) |
275 | return -ENOMEM; |
276 | |
277 | mutex_init(&opregion->lock); |
278 | opregion->regmap = regmap; |
279 | opregion->lpat_table = acpi_lpat_get_conversion_table(handle); |
280 | |
281 | status = acpi_install_address_space_handler(handle, |
282 | PMIC_POWER_OPREGION_ID, |
283 | intel_pmic_power_handler, |
284 | NULL, opregion); |
285 | if (ACPI_FAILURE(status)) { |
286 | ret = -ENODEV; |
287 | goto out_error; |
288 | } |
289 | |
290 | status = acpi_install_address_space_handler(handle, |
291 | PMIC_THERMAL_OPREGION_ID, |
292 | intel_pmic_thermal_handler, |
293 | NULL, opregion); |
294 | if (ACPI_FAILURE(status)) { |
295 | acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, |
296 | intel_pmic_power_handler); |
297 | ret = -ENODEV; |
298 | goto out_remove_power_handler; |
299 | } |
300 | |
301 | status = acpi_install_address_space_handler(handle, |
302 | PMIC_REGS_OPREGION_ID, intel_pmic_regs_handler, NULL, |
303 | opregion); |
304 | if (ACPI_FAILURE(status)) { |
305 | ret = -ENODEV; |
306 | goto out_remove_thermal_handler; |
307 | } |
308 | |
309 | opregion->data = d; |
310 | intel_pmic_opregion = opregion; |
311 | return 0; |
312 | |
313 | out_remove_thermal_handler: |
314 | acpi_remove_address_space_handler(handle, PMIC_THERMAL_OPREGION_ID, |
315 | intel_pmic_thermal_handler); |
316 | |
317 | out_remove_power_handler: |
318 | acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, |
319 | intel_pmic_power_handler); |
320 | |
321 | out_error: |
322 | acpi_lpat_free_conversion_table(opregion->lpat_table); |
323 | return ret; |
324 | } |
325 | EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); |
326 | |
327 | /** |
328 | * intel_soc_pmic_exec_mipi_pmic_seq_element - Execute PMIC MIPI sequence |
329 | * @i2c_address: I2C client address for the PMIC |
330 | * @reg_address: PMIC register address |
331 | * @value: New value for the register bits to change |
332 | * @mask: Mask indicating which register bits to change |
333 | * |
334 | * DSI LCD panels describe an initialization sequence in the i915 VBT (Video |
335 | * BIOS Tables) using so called MIPI sequences. One possible element in these |
336 | * sequences is a PMIC specific element of 15 bytes. |
337 | * |
338 | * This function executes these PMIC specific elements sending the embedded |
339 | * commands to the PMIC. |
340 | * |
341 | * Return 0 on success, < 0 on failure. |
342 | */ |
343 | int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address, |
344 | u32 value, u32 mask) |
345 | { |
346 | struct intel_pmic_opregion_data *d; |
347 | int ret; |
348 | |
349 | if (!intel_pmic_opregion) { |
350 | pr_warn("%s: No PMIC registered\n" , __func__); |
351 | return -ENXIO; |
352 | } |
353 | |
354 | d = intel_pmic_opregion->data; |
355 | |
356 | mutex_lock(&intel_pmic_opregion->lock); |
357 | |
358 | if (d->exec_mipi_pmic_seq_element) { |
359 | ret = d->exec_mipi_pmic_seq_element(intel_pmic_opregion->regmap, |
360 | i2c_address, reg_address, |
361 | value, mask); |
362 | } else if (d->pmic_i2c_address) { |
363 | if (i2c_address == d->pmic_i2c_address) { |
364 | ret = regmap_update_bits(intel_pmic_opregion->regmap, |
365 | reg_address, mask, value); |
366 | } else { |
367 | pr_err("%s: Unexpected i2c-addr: 0x%02x (reg-addr 0x%x value 0x%x mask 0x%x)\n" , |
368 | __func__, i2c_address, reg_address, value, mask); |
369 | ret = -ENXIO; |
370 | } |
371 | } else { |
372 | pr_warn("%s: Not implemented\n" , __func__); |
373 | pr_warn("%s: i2c-addr: 0x%x reg-addr 0x%x value 0x%x mask 0x%x\n" , |
374 | __func__, i2c_address, reg_address, value, mask); |
375 | ret = -EOPNOTSUPP; |
376 | } |
377 | |
378 | mutex_unlock(&intel_pmic_opregion->lock); |
379 | |
380 | return ret; |
381 | } |
382 | EXPORT_SYMBOL_GPL(intel_soc_pmic_exec_mipi_pmic_seq_element); |
383 | |