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