diff options
Diffstat (limited to 'drivers/acpi/pmic/intel_pmic.c')
-rw-r--r-- | drivers/acpi/pmic/intel_pmic.c | 84 |
1 files changed, 74 insertions, 10 deletions
diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c index bd772cd56494..ca18e0d23df9 100644 --- a/drivers/acpi/pmic/intel_pmic.c +++ b/drivers/acpi/pmic/intel_pmic.c | |||
@@ -13,7 +13,7 @@ | |||
13 | * GNU General Public License for more details. | 13 | * GNU General Public License for more details. |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include <linux/module.h> | 16 | #include <linux/export.h> |
17 | #include <linux/acpi.h> | 17 | #include <linux/acpi.h> |
18 | #include <linux/regmap.h> | 18 | #include <linux/regmap.h> |
19 | #include <acpi/acpi_lpat.h> | 19 | #include <acpi/acpi_lpat.h> |
@@ -21,12 +21,19 @@ | |||
21 | 21 | ||
22 | #define PMIC_POWER_OPREGION_ID 0x8d | 22 | #define PMIC_POWER_OPREGION_ID 0x8d |
23 | #define PMIC_THERMAL_OPREGION_ID 0x8c | 23 | #define PMIC_THERMAL_OPREGION_ID 0x8c |
24 | #define PMIC_REGS_OPREGION_ID 0x8f | ||
25 | |||
26 | struct intel_pmic_regs_handler_ctx { | ||
27 | unsigned int val; | ||
28 | u16 addr; | ||
29 | }; | ||
24 | 30 | ||
25 | struct intel_pmic_opregion { | 31 | struct intel_pmic_opregion { |
26 | struct mutex lock; | 32 | struct mutex lock; |
27 | struct acpi_lpat_conversion_table *lpat_table; | 33 | struct acpi_lpat_conversion_table *lpat_table; |
28 | struct regmap *regmap; | 34 | struct regmap *regmap; |
29 | struct intel_pmic_opregion_data *data; | 35 | struct intel_pmic_opregion_data *data; |
36 | struct intel_pmic_regs_handler_ctx ctx; | ||
30 | }; | 37 | }; |
31 | 38 | ||
32 | static int pmic_get_reg_bit(int address, struct pmic_table *table, | 39 | static int pmic_get_reg_bit(int address, struct pmic_table *table, |
@@ -131,7 +138,7 @@ static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg, | |||
131 | } | 138 | } |
132 | 139 | ||
133 | static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, | 140 | static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, |
134 | u32 function, u64 *value) | 141 | int bit, u32 function, u64 *value) |
135 | { | 142 | { |
136 | struct intel_pmic_opregion_data *d = opregion->data; | 143 | struct intel_pmic_opregion_data *d = opregion->data; |
137 | struct regmap *regmap = opregion->regmap; | 144 | struct regmap *regmap = opregion->regmap; |
@@ -140,12 +147,12 @@ static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, | |||
140 | return -ENXIO; | 147 | return -ENXIO; |
141 | 148 | ||
142 | if (function == ACPI_READ) | 149 | if (function == ACPI_READ) |
143 | return d->get_policy(regmap, reg, value); | 150 | return d->get_policy(regmap, reg, bit, value); |
144 | 151 | ||
145 | if (*value != 0 && *value != 1) | 152 | if (*value != 0 && *value != 1) |
146 | return -EINVAL; | 153 | return -EINVAL; |
147 | 154 | ||
148 | return d->update_policy(regmap, reg, *value); | 155 | return d->update_policy(regmap, reg, bit, *value); |
149 | } | 156 | } |
150 | 157 | ||
151 | static bool pmic_thermal_is_temp(int address) | 158 | static bool pmic_thermal_is_temp(int address) |
@@ -170,13 +177,13 @@ static acpi_status intel_pmic_thermal_handler(u32 function, | |||
170 | { | 177 | { |
171 | struct intel_pmic_opregion *opregion = region_context; | 178 | struct intel_pmic_opregion *opregion = region_context; |
172 | struct intel_pmic_opregion_data *d = opregion->data; | 179 | struct intel_pmic_opregion_data *d = opregion->data; |
173 | int reg, result; | 180 | int reg, bit, result; |
174 | 181 | ||
175 | if (bits != 32 || !value64) | 182 | if (bits != 32 || !value64) |
176 | return AE_BAD_PARAMETER; | 183 | return AE_BAD_PARAMETER; |
177 | 184 | ||
178 | result = pmic_get_reg_bit(address, d->thermal_table, | 185 | result = pmic_get_reg_bit(address, d->thermal_table, |
179 | d->thermal_table_count, ®, NULL); | 186 | d->thermal_table_count, ®, &bit); |
180 | if (result == -ENOENT) | 187 | if (result == -ENOENT) |
181 | return AE_BAD_PARAMETER; | 188 | return AE_BAD_PARAMETER; |
182 | 189 | ||
@@ -187,7 +194,8 @@ static acpi_status intel_pmic_thermal_handler(u32 function, | |||
187 | else if (pmic_thermal_is_aux(address)) | 194 | else if (pmic_thermal_is_aux(address)) |
188 | result = pmic_thermal_aux(opregion, reg, function, value64); | 195 | result = pmic_thermal_aux(opregion, reg, function, value64); |
189 | else if (pmic_thermal_is_pen(address)) | 196 | else if (pmic_thermal_is_pen(address)) |
190 | result = pmic_thermal_pen(opregion, reg, function, value64); | 197 | result = pmic_thermal_pen(opregion, reg, bit, |
198 | function, value64); | ||
191 | else | 199 | else |
192 | result = -EINVAL; | 200 | result = -EINVAL; |
193 | 201 | ||
@@ -203,6 +211,48 @@ static acpi_status intel_pmic_thermal_handler(u32 function, | |||
203 | return AE_OK; | 211 | return AE_OK; |
204 | } | 212 | } |
205 | 213 | ||
214 | static acpi_status intel_pmic_regs_handler(u32 function, | ||
215 | acpi_physical_address address, u32 bits, u64 *value64, | ||
216 | void *handler_context, void *region_context) | ||
217 | { | ||
218 | struct intel_pmic_opregion *opregion = region_context; | ||
219 | int result = 0; | ||
220 | |||
221 | switch (address) { | ||
222 | case 0: | ||
223 | return AE_OK; | ||
224 | case 1: | ||
225 | opregion->ctx.addr |= (*value64 & 0xff) << 8; | ||
226 | return AE_OK; | ||
227 | case 2: | ||
228 | opregion->ctx.addr |= *value64 & 0xff; | ||
229 | return AE_OK; | ||
230 | case 3: | ||
231 | opregion->ctx.val = *value64 & 0xff; | ||
232 | return AE_OK; | ||
233 | case 4: | ||
234 | if (*value64) { | ||
235 | result = regmap_write(opregion->regmap, opregion->ctx.addr, | ||
236 | opregion->ctx.val); | ||
237 | } else { | ||
238 | result = regmap_read(opregion->regmap, opregion->ctx.addr, | ||
239 | &opregion->ctx.val); | ||
240 | if (result == 0) | ||
241 | *value64 = opregion->ctx.val; | ||
242 | } | ||
243 | memset(&opregion->ctx, 0x00, sizeof(opregion->ctx)); | ||
244 | } | ||
245 | |||
246 | if (result < 0) { | ||
247 | if (result == -EINVAL) | ||
248 | return AE_BAD_PARAMETER; | ||
249 | else | ||
250 | return AE_ERROR; | ||
251 | } | ||
252 | |||
253 | return AE_OK; | ||
254 | } | ||
255 | |||
206 | int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, | 256 | int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, |
207 | struct regmap *regmap, | 257 | struct regmap *regmap, |
208 | struct intel_pmic_opregion_data *d) | 258 | struct intel_pmic_opregion_data *d) |
@@ -242,16 +292,30 @@ int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, | |||
242 | acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, | 292 | acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, |
243 | intel_pmic_power_handler); | 293 | intel_pmic_power_handler); |
244 | ret = -ENODEV; | 294 | ret = -ENODEV; |
245 | goto out_error; | 295 | goto out_remove_power_handler; |
296 | } | ||
297 | |||
298 | status = acpi_install_address_space_handler(handle, | ||
299 | PMIC_REGS_OPREGION_ID, intel_pmic_regs_handler, NULL, | ||
300 | opregion); | ||
301 | if (ACPI_FAILURE(status)) { | ||
302 | ret = -ENODEV; | ||
303 | goto out_remove_thermal_handler; | ||
246 | } | 304 | } |
247 | 305 | ||
248 | opregion->data = d; | 306 | opregion->data = d; |
249 | return 0; | 307 | return 0; |
250 | 308 | ||
309 | out_remove_thermal_handler: | ||
310 | acpi_remove_address_space_handler(handle, PMIC_THERMAL_OPREGION_ID, | ||
311 | intel_pmic_thermal_handler); | ||
312 | |||
313 | out_remove_power_handler: | ||
314 | acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, | ||
315 | intel_pmic_power_handler); | ||
316 | |||
251 | out_error: | 317 | out_error: |
252 | acpi_lpat_free_conversion_table(opregion->lpat_table); | 318 | acpi_lpat_free_conversion_table(opregion->lpat_table); |
253 | return ret; | 319 | return ret; |
254 | } | 320 | } |
255 | EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); | 321 | EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); |
256 | |||
257 | MODULE_LICENSE("GPL"); | ||