aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorAaron Lu <aaron.lu@intel.com>2014-11-24 04:21:54 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-11-26 17:32:05 -0500
commitb1eea857d8c70dc3789cc2231e3c0a273a67ba06 (patch)
tree4aa99b39d0b8aa244471dc2e8300dabc398c5194 /drivers/acpi
parenta26033a1f56b7b1f8a56050c0a9095694aecae11 (diff)
ACPI / PMIC: support PMIC operation region for CrystalCove
The Baytrail-T platform firmware has defined two customized operation regions for PMIC chip Crystal Cove - one is for power resource handling and one is for thermal: sensor temperature reporting, trip point setting, etc. This patch adds support for them on top of the existing Crystal Cove PMIC driver. The reason to split code into a separate file intel_pmic.c is that there are more PMIC drivers with ACPI operation region support coming and we can re-use those code. The intel_pmic_opregion_data structure is created also for this purpose: when we need to support a new PMIC's operation region, we just need to fill those callbacks and the two register mapping tables. Signed-off-by: Aaron Lu <aaron.lu@intel.com> Acked-by: Lee Jones <lee.jones@linaro.org> for the MFD part Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/Kconfig17
-rw-r--r--drivers/acpi/Makefile3
-rw-r--r--drivers/acpi/pmic/intel_pmic.c354
-rw-r--r--drivers/acpi/pmic/intel_pmic.h25
-rw-r--r--drivers/acpi/pmic/intel_pmic_crc.c211
5 files changed, 610 insertions, 0 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index b23fe37f67c0..df816456dc06 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -394,4 +394,21 @@ config ACPI_EXTLOG
394 driver adds support for that functionality with corresponding 394 driver adds support for that functionality with corresponding
395 tracepoint which carries that information to userspace. 395 tracepoint which carries that information to userspace.
396 396
397menuconfig PMIC_OPREGION
398 bool "PMIC (Power Management Integrated Circuit) operation region support"
399 help
400 Select this option to enable support for ACPI operation
401 region of the PMIC chip. The operation region can be used
402 to control power rails and sensor reading/writing on the
403 PMIC chip.
404
405if PMIC_OPREGION
406config CRC_PMIC_OPREGION
407 bool "ACPI operation region support for CrystalCove PMIC"
408 depends on INTEL_SOC_PMIC
409 help
410 This config adds ACPI operation region support for CrystalCove PMIC.
411
412endif
413
397endif # ACPI 414endif # ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index c3b2fcb729f3..38ea2a8245ed 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -87,3 +87,6 @@ obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
87obj-$(CONFIG_ACPI_APEI) += apei/ 87obj-$(CONFIG_ACPI_APEI) += apei/
88 88
89obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o 89obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o
90
91obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o
92obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c
new file mode 100644
index 000000000000..a732e5d7e322
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic.c
@@ -0,0 +1,354 @@
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
24struct acpi_lpat {
25 int temp;
26 int raw;
27};
28
29struct 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
37static 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 */
64static 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 */
95static 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
114static 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
148out:
149 kfree(buffer.pointer);
150}
151
152static 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
183static 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
208static 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
215static 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
238static 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
256static bool pmic_thermal_is_temp(int address)
257{
258 return (address <= 0x3c) && !(address % 12);
259}
260
261static 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
267static bool pmic_thermal_is_pen(int address)
268{
269 return address >= 0x48 && address <= 0x5c;
270}
271
272static 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
311int 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}
352EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler);
353
354MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/pmic/intel_pmic.h b/drivers/acpi/pmic/intel_pmic.h
new file mode 100644
index 000000000000..d4e90af8f0dd
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic.h
@@ -0,0 +1,25 @@
1#ifndef __INTEL_PMIC_H
2#define __INTEL_PMIC_H
3
4struct pmic_table {
5 int address; /* operation region address */
6 int reg; /* corresponding thermal register */
7 int bit; /* control bit for power */
8};
9
10struct intel_pmic_opregion_data {
11 int (*get_power)(struct regmap *r, int reg, int bit, u64 *value);
12 int (*update_power)(struct regmap *r, int reg, int bit, bool on);
13 int (*get_raw_temp)(struct regmap *r, int reg);
14 int (*update_aux)(struct regmap *r, int reg, int raw_temp);
15 int (*get_policy)(struct regmap *r, int reg, u64 *value);
16 int (*update_policy)(struct regmap *r, int reg, int enable);
17 struct pmic_table *power_table;
18 int power_table_count;
19 struct pmic_table *thermal_table;
20 int thermal_table_count;
21};
22
23int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, struct regmap *regmap, struct intel_pmic_opregion_data *d);
24
25#endif
diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
new file mode 100644
index 000000000000..ef7d8ff95abe
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_crc.c
@@ -0,0 +1,211 @@
1/*
2 * intel_pmic_crc.c - Intel CrystalCove 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/mfd/intel_soc_pmic.h>
19#include <linux/regmap.h>
20#include <linux/platform_device.h>
21#include "intel_pmic.h"
22
23#define PWR_SOURCE_SELECT BIT(1)
24
25#define PMIC_A0LOCK_REG 0xc5
26
27static struct pmic_table power_table[] = {
28 {
29 .address = 0x24,
30 .reg = 0x66,
31 .bit = 0x00,
32 },
33 {
34 .address = 0x48,
35 .reg = 0x5d,
36 .bit = 0x00,
37 },
38};
39
40static struct pmic_table thermal_table[] = {
41 {
42 .address = 0x00,
43 .reg = 0x75
44 },
45 {
46 .address = 0x04,
47 .reg = 0x95
48 },
49 {
50 .address = 0x08,
51 .reg = 0x97
52 },
53 {
54 .address = 0x0c,
55 .reg = 0x77
56 },
57 {
58 .address = 0x10,
59 .reg = 0x9a
60 },
61 {
62 .address = 0x14,
63 .reg = 0x9c
64 },
65 {
66 .address = 0x18,
67 .reg = 0x79
68 },
69 {
70 .address = 0x1c,
71 .reg = 0x9f
72 },
73 {
74 .address = 0x20,
75 .reg = 0xa1
76 },
77 {
78 .address = 0x48,
79 .reg = 0x94
80 },
81 {
82 .address = 0x4c,
83 .reg = 0x99
84 },
85 {
86 .address = 0x50,
87 .reg = 0x9e
88 },
89};
90
91static int intel_crc_pmic_get_power(struct regmap *regmap, int reg,
92 int bit, u64 *value)
93{
94 int data;
95
96 if (regmap_read(regmap, reg, &data))
97 return -EIO;
98
99 *value = (data & PWR_SOURCE_SELECT) && (data & BIT(bit)) ? 1 : 0;
100 return 0;
101}
102
103static int intel_crc_pmic_update_power(struct regmap *regmap, int reg,
104 int bit, bool on)
105{
106 int data;
107
108 if (regmap_read(regmap, reg, &data))
109 return -EIO;
110
111 if (on) {
112 data |= PWR_SOURCE_SELECT | BIT(bit);
113 } else {
114 data &= ~BIT(bit);
115 data |= PWR_SOURCE_SELECT;
116 }
117
118 if (regmap_write(regmap, reg, data))
119 return -EIO;
120 return 0;
121}
122
123static int intel_crc_pmic_get_raw_temp(struct regmap *regmap, int reg)
124{
125 int temp_l, temp_h;
126
127 /*
128 * Raw temperature value is 10bits: 8bits in reg
129 * and 2bits in reg-1: bit0,1
130 */
131 if (regmap_read(regmap, reg, &temp_l) ||
132 regmap_read(regmap, reg - 1, &temp_h))
133 return -EIO;
134
135 return temp_l | (temp_h & 0x3) << 8;
136}
137
138static int intel_crc_pmic_update_aux(struct regmap *regmap, int reg, int raw)
139{
140 return regmap_write(regmap, reg, raw) ||
141 regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8) ? -EIO : 0;
142}
143
144static int intel_crc_pmic_get_policy(struct regmap *regmap, int reg, u64 *value)
145{
146 int pen;
147
148 if (regmap_read(regmap, reg, &pen))
149 return -EIO;
150 *value = pen >> 7;
151 return 0;
152}
153
154static int intel_crc_pmic_update_policy(struct regmap *regmap,
155 int reg, int enable)
156{
157 int alert0;
158
159 /* Update to policy enable bit requires unlocking a0lock */
160 if (regmap_read(regmap, PMIC_A0LOCK_REG, &alert0))
161 return -EIO;
162
163 if (regmap_update_bits(regmap, PMIC_A0LOCK_REG, 0x01, 0))
164 return -EIO;
165
166 if (regmap_update_bits(regmap, reg, 0x80, enable << 7))
167 return -EIO;
168
169 /* restore alert0 */
170 if (regmap_write(regmap, PMIC_A0LOCK_REG, alert0))
171 return -EIO;
172
173 return 0;
174}
175
176static struct intel_pmic_opregion_data intel_crc_pmic_opregion_data = {
177 .get_power = intel_crc_pmic_get_power,
178 .update_power = intel_crc_pmic_update_power,
179 .get_raw_temp = intel_crc_pmic_get_raw_temp,
180 .update_aux = intel_crc_pmic_update_aux,
181 .get_policy = intel_crc_pmic_get_policy,
182 .update_policy = intel_crc_pmic_update_policy,
183 .power_table = power_table,
184 .power_table_count= ARRAY_SIZE(power_table),
185 .thermal_table = thermal_table,
186 .thermal_table_count = ARRAY_SIZE(thermal_table),
187};
188
189static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
190{
191 struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
192 return intel_pmic_install_opregion_handler(&pdev->dev,
193 ACPI_HANDLE(pdev->dev.parent), pmic->regmap,
194 &intel_crc_pmic_opregion_data);
195}
196
197static struct platform_driver intel_crc_pmic_opregion_driver = {
198 .probe = intel_crc_pmic_opregion_probe,
199 .driver = {
200 .name = "crystal_cove_pmic",
201 },
202};
203
204static int __init intel_crc_pmic_opregion_driver_init(void)
205{
206 return platform_driver_register(&intel_crc_pmic_opregion_driver);
207}
208module_init(intel_crc_pmic_opregion_driver_init);
209
210MODULE_DESCRIPTION("CrystalCove ACPI opration region driver");
211MODULE_LICENSE("GPL");