Merge tag 'pinctrl-v3.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[cascardo/linux.git] / drivers / acpi / pmic / intel_pmic.c
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/module.h>
17 #include <linux/acpi.h>
18 #include <linux/regmap.h>
19 #include "intel_pmic.h"
20
21 #define PMIC_POWER_OPREGION_ID          0x8d
22 #define PMIC_THERMAL_OPREGION_ID        0x8c
23
24 struct acpi_lpat {
25         int temp;
26         int raw;
27 };
28
29 struct intel_pmic_opregion {
30         struct mutex lock;
31         struct acpi_lpat *lpat;
32         int lpat_count;
33         struct regmap *regmap;
34         struct intel_pmic_opregion_data *data;
35 };
36
37 static int pmic_get_reg_bit(int address, struct pmic_table *table,
38                             int count, int *reg, int *bit)
39 {
40         int i;
41
42         for (i = 0; i < count; i++) {
43                 if (table[i].address == address) {
44                         *reg = table[i].reg;
45                         if (bit)
46                                 *bit = table[i].bit;
47                         return 0;
48                 }
49         }
50         return -ENOENT;
51 }
52
53 /**
54  * raw_to_temp(): Return temperature from raw value through LPAT table
55  *
56  * @lpat: the temperature_raw mapping table
57  * @count: the count of the above mapping table
58  * @raw: the raw value, used as a key to get the temerature from the
59  *       above mapping table
60  *
61  * A positive value will be returned on success, a negative errno will
62  * be returned in error cases.
63  */
64 static int raw_to_temp(struct acpi_lpat *lpat, int count, int raw)
65 {
66         int i, delta_temp, delta_raw, temp;
67
68         for (i = 0; i < count - 1; i++) {
69                 if ((raw >= lpat[i].raw && raw <= lpat[i+1].raw) ||
70                     (raw <= lpat[i].raw && raw >= lpat[i+1].raw))
71                         break;
72         }
73
74         if (i == count - 1)
75                 return -ENOENT;
76
77         delta_temp = lpat[i+1].temp - lpat[i].temp;
78         delta_raw = lpat[i+1].raw - lpat[i].raw;
79         temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw;
80
81         return temp;
82 }
83
84 /**
85  * temp_to_raw(): Return raw value from temperature through LPAT table
86  *
87  * @lpat: the temperature_raw mapping table
88  * @count: the count of the above mapping table
89  * @temp: the temperature, used as a key to get the raw value from the
90  *        above mapping table
91  *
92  * A positive value will be returned on success, a negative errno will
93  * be returned in error cases.
94  */
95 static int temp_to_raw(struct acpi_lpat *lpat, int count, int temp)
96 {
97         int i, delta_temp, delta_raw, raw;
98
99         for (i = 0; i < count - 1; i++) {
100                 if (temp >= lpat[i].temp && temp <= lpat[i+1].temp)
101                         break;
102         }
103
104         if (i == count - 1)
105                 return -ENOENT;
106
107         delta_temp = lpat[i+1].temp - lpat[i].temp;
108         delta_raw = lpat[i+1].raw - lpat[i].raw;
109         raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp;
110
111         return raw;
112 }
113
114 static void pmic_thermal_lpat(struct intel_pmic_opregion *opregion,
115                               acpi_handle handle, struct device *dev)
116 {
117         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
118         union acpi_object *obj_p, *obj_e;
119         int *lpat, i;
120         acpi_status status;
121
122         status = acpi_evaluate_object(handle, "LPAT", NULL, &buffer);
123         if (ACPI_FAILURE(status))
124                 return;
125
126         obj_p = (union acpi_object *)buffer.pointer;
127         if (!obj_p || (obj_p->type != ACPI_TYPE_PACKAGE) ||
128             (obj_p->package.count % 2) || (obj_p->package.count < 4))
129                 goto out;
130
131         lpat = devm_kmalloc(dev, sizeof(int) * obj_p->package.count,
132                             GFP_KERNEL);
133         if (!lpat)
134                 goto out;
135
136         for (i = 0; i < obj_p->package.count; i++) {
137                 obj_e = &obj_p->package.elements[i];
138                 if (obj_e->type != ACPI_TYPE_INTEGER) {
139                         devm_kfree(dev, lpat);
140                         goto out;
141                 }
142                 lpat[i] = (s64)obj_e->integer.value;
143         }
144
145         opregion->lpat = (struct acpi_lpat *)lpat;
146         opregion->lpat_count = obj_p->package.count / 2;
147
148 out:
149         kfree(buffer.pointer);
150 }
151
152 static acpi_status intel_pmic_power_handler(u32 function,
153                 acpi_physical_address address, u32 bits, u64 *value64,
154                 void *handler_context, void *region_context)
155 {
156         struct intel_pmic_opregion *opregion = region_context;
157         struct regmap *regmap = opregion->regmap;
158         struct intel_pmic_opregion_data *d = opregion->data;
159         int reg, bit, result;
160
161         if (bits != 32 || !value64)
162                 return AE_BAD_PARAMETER;
163
164         if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1))
165                 return AE_BAD_PARAMETER;
166
167         result = pmic_get_reg_bit(address, d->power_table,
168                                   d->power_table_count, &reg, &bit);
169         if (result == -ENOENT)
170                 return AE_BAD_PARAMETER;
171
172         mutex_lock(&opregion->lock);
173
174         result = function == ACPI_READ ?
175                 d->get_power(regmap, reg, bit, value64) :
176                 d->update_power(regmap, reg, bit, *value64 == 1);
177
178         mutex_unlock(&opregion->lock);
179
180         return result ? AE_ERROR : AE_OK;
181 }
182
183 static int pmic_read_temp(struct intel_pmic_opregion *opregion,
184                           int reg, u64 *value)
185 {
186         int raw_temp, temp;
187
188         if (!opregion->data->get_raw_temp)
189                 return -ENXIO;
190
191         raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg);
192         if (raw_temp < 0)
193                 return raw_temp;
194
195         if (!opregion->lpat) {
196                 *value = raw_temp;
197                 return 0;
198         }
199
200         temp = raw_to_temp(opregion->lpat, opregion->lpat_count, raw_temp);
201         if (temp < 0)
202                 return temp;
203
204         *value = temp;
205         return 0;
206 }
207
208 static int pmic_thermal_temp(struct intel_pmic_opregion *opregion, int reg,
209                              u32 function, u64 *value)
210 {
211         return function == ACPI_READ ?
212                 pmic_read_temp(opregion, reg, value) : -EINVAL;
213 }
214
215 static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg,
216                             u32 function, u64 *value)
217 {
218         int raw_temp;
219
220         if (function == ACPI_READ)
221                 return pmic_read_temp(opregion, reg, value);
222
223         if (!opregion->data->update_aux)
224                 return -ENXIO;
225
226         if (opregion->lpat) {
227                 raw_temp = temp_to_raw(opregion->lpat, opregion->lpat_count,
228                                        *value);
229                 if (raw_temp < 0)
230                         return raw_temp;
231         } else {
232                 raw_temp = *value;
233         }
234
235         return opregion->data->update_aux(opregion->regmap, reg, raw_temp);
236 }
237
238 static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg,
239                             u32 function, u64 *value)
240 {
241         struct intel_pmic_opregion_data *d = opregion->data;
242         struct regmap *regmap = opregion->regmap;
243
244         if (!d->get_policy || !d->update_policy)
245                 return -ENXIO;
246
247         if (function == ACPI_READ)
248                 return d->get_policy(regmap, reg, value);
249
250         if (*value != 0 && *value != 1)
251                 return -EINVAL;
252
253         return d->update_policy(regmap, reg, *value);
254 }
255
256 static bool pmic_thermal_is_temp(int address)
257 {
258         return (address <= 0x3c) && !(address % 12);
259 }
260
261 static bool pmic_thermal_is_aux(int address)
262 {
263         return (address >= 4 && address <= 0x40 && !((address - 4) % 12)) ||
264                (address >= 8 && address <= 0x44 && !((address - 8) % 12));
265 }
266
267 static bool pmic_thermal_is_pen(int address)
268 {
269         return address >= 0x48 && address <= 0x5c;
270 }
271
272 static acpi_status intel_pmic_thermal_handler(u32 function,
273                 acpi_physical_address address, u32 bits, u64 *value64,
274                 void *handler_context, void *region_context)
275 {
276         struct intel_pmic_opregion *opregion = region_context;
277         struct intel_pmic_opregion_data *d = opregion->data;
278         int reg, result;
279
280         if (bits != 32 || !value64)
281                 return AE_BAD_PARAMETER;
282
283         result = pmic_get_reg_bit(address, d->thermal_table,
284                                   d->thermal_table_count, &reg, NULL);
285         if (result == -ENOENT)
286                 return AE_BAD_PARAMETER;
287
288         mutex_lock(&opregion->lock);
289
290         if (pmic_thermal_is_temp(address))
291                 result = pmic_thermal_temp(opregion, reg, function, value64);
292         else if (pmic_thermal_is_aux(address))
293                 result = pmic_thermal_aux(opregion, reg, function, value64);
294         else if (pmic_thermal_is_pen(address))
295                 result = pmic_thermal_pen(opregion, reg, function, value64);
296         else
297                 result = -EINVAL;
298
299         mutex_unlock(&opregion->lock);
300
301         if (result < 0) {
302                 if (result == -EINVAL)
303                         return AE_BAD_PARAMETER;
304                 else
305                         return AE_ERROR;
306         }
307
308         return AE_OK;
309 }
310
311 int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,
312                                         struct regmap *regmap,
313                                         struct intel_pmic_opregion_data *d)
314 {
315         acpi_status status;
316         struct intel_pmic_opregion *opregion;
317
318         if (!dev || !regmap || !d)
319                 return -EINVAL;
320
321         if (!handle)
322                 return -ENODEV;
323
324         opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL);
325         if (!opregion)
326                 return -ENOMEM;
327
328         mutex_init(&opregion->lock);
329         opregion->regmap = regmap;
330         pmic_thermal_lpat(opregion, handle, dev);
331
332         status = acpi_install_address_space_handler(handle,
333                                                     PMIC_POWER_OPREGION_ID,
334                                                     intel_pmic_power_handler,
335                                                     NULL, opregion);
336         if (ACPI_FAILURE(status))
337                 return -ENODEV;
338
339         status = acpi_install_address_space_handler(handle,
340                                                     PMIC_THERMAL_OPREGION_ID,
341                                                     intel_pmic_thermal_handler,
342                                                     NULL, opregion);
343         if (ACPI_FAILURE(status)) {
344                 acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID,
345                                                   intel_pmic_power_handler);
346                 return -ENODEV;
347         }
348
349         opregion->data = d;
350         return 0;
351 }
352 EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler);
353
354 MODULE_LICENSE("GPL");