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 166d3ef8d8SPaul Gortmaker #include <linux/export.h> 17b1eea857SAaron Lu #include <linux/acpi.h> 187b5618f4SHans de Goede #include <linux/mfd/intel_soc_pmic.h> 19b1eea857SAaron Lu #include <linux/regmap.h> 20ac586e2dSSrinivas Pandruvada #include <acpi/acpi_lpat.h> 21b1eea857SAaron Lu #include "intel_pmic.h" 22b1eea857SAaron Lu 23b1eea857SAaron Lu #define PMIC_POWER_OPREGION_ID 0x8d 24b1eea857SAaron Lu #define PMIC_THERMAL_OPREGION_ID 0x8c 250afa877aSFelipe Balbi #define PMIC_REGS_OPREGION_ID 0x8f 260afa877aSFelipe Balbi 270afa877aSFelipe Balbi struct intel_pmic_regs_handler_ctx { 280afa877aSFelipe Balbi unsigned int val; 290afa877aSFelipe Balbi u16 addr; 300afa877aSFelipe Balbi }; 31b1eea857SAaron Lu 32b1eea857SAaron Lu struct intel_pmic_opregion { 33b1eea857SAaron Lu struct mutex lock; 34ac586e2dSSrinivas Pandruvada struct acpi_lpat_conversion_table *lpat_table; 35b1eea857SAaron Lu struct regmap *regmap; 36b1eea857SAaron Lu struct intel_pmic_opregion_data *data; 370afa877aSFelipe Balbi struct intel_pmic_regs_handler_ctx ctx; 38b1eea857SAaron Lu }; 39b1eea857SAaron Lu 407b5618f4SHans de Goede static struct intel_pmic_opregion *intel_pmic_opregion; 417b5618f4SHans de Goede 42b1eea857SAaron Lu static int pmic_get_reg_bit(int address, struct pmic_table *table, 43b1eea857SAaron Lu int count, int *reg, int *bit) 44b1eea857SAaron Lu { 45b1eea857SAaron Lu int i; 46b1eea857SAaron Lu 47b1eea857SAaron Lu for (i = 0; i < count; i++) { 48b1eea857SAaron Lu if (table[i].address == address) { 49b1eea857SAaron Lu *reg = table[i].reg; 50b1eea857SAaron Lu if (bit) 51b1eea857SAaron Lu *bit = table[i].bit; 52b1eea857SAaron Lu return 0; 53b1eea857SAaron Lu } 54b1eea857SAaron Lu } 55b1eea857SAaron Lu return -ENOENT; 56b1eea857SAaron Lu } 57b1eea857SAaron Lu 58b1eea857SAaron Lu static acpi_status intel_pmic_power_handler(u32 function, 59b1eea857SAaron Lu acpi_physical_address address, u32 bits, u64 *value64, 60b1eea857SAaron Lu void *handler_context, void *region_context) 61b1eea857SAaron Lu { 62b1eea857SAaron Lu struct intel_pmic_opregion *opregion = region_context; 63b1eea857SAaron Lu struct regmap *regmap = opregion->regmap; 64b1eea857SAaron Lu struct intel_pmic_opregion_data *d = opregion->data; 65b1eea857SAaron Lu int reg, bit, result; 66b1eea857SAaron Lu 67b1eea857SAaron Lu if (bits != 32 || !value64) 68b1eea857SAaron Lu return AE_BAD_PARAMETER; 69b1eea857SAaron Lu 70b1eea857SAaron Lu if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1)) 71b1eea857SAaron Lu return AE_BAD_PARAMETER; 72b1eea857SAaron Lu 73b1eea857SAaron Lu result = pmic_get_reg_bit(address, d->power_table, 74b1eea857SAaron Lu d->power_table_count, ®, &bit); 75b1eea857SAaron Lu if (result == -ENOENT) 76b1eea857SAaron Lu return AE_BAD_PARAMETER; 77b1eea857SAaron Lu 78b1eea857SAaron Lu mutex_lock(&opregion->lock); 79b1eea857SAaron Lu 80b1eea857SAaron Lu result = function == ACPI_READ ? 81b1eea857SAaron Lu d->get_power(regmap, reg, bit, value64) : 82b1eea857SAaron Lu d->update_power(regmap, reg, bit, *value64 == 1); 83b1eea857SAaron Lu 84b1eea857SAaron Lu mutex_unlock(&opregion->lock); 85b1eea857SAaron Lu 86b1eea857SAaron Lu return result ? AE_ERROR : AE_OK; 87b1eea857SAaron Lu } 88b1eea857SAaron Lu 89b1eea857SAaron Lu static int pmic_read_temp(struct intel_pmic_opregion *opregion, 90b1eea857SAaron Lu int reg, u64 *value) 91b1eea857SAaron Lu { 92b1eea857SAaron Lu int raw_temp, temp; 93b1eea857SAaron Lu 94b1eea857SAaron Lu if (!opregion->data->get_raw_temp) 95b1eea857SAaron Lu return -ENXIO; 96b1eea857SAaron Lu 97b1eea857SAaron Lu raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg); 98b1eea857SAaron Lu if (raw_temp < 0) 99b1eea857SAaron Lu return raw_temp; 100b1eea857SAaron Lu 101ac586e2dSSrinivas Pandruvada if (!opregion->lpat_table) { 102b1eea857SAaron Lu *value = raw_temp; 103b1eea857SAaron Lu return 0; 104b1eea857SAaron Lu } 105b1eea857SAaron Lu 106ac586e2dSSrinivas Pandruvada temp = acpi_lpat_raw_to_temp(opregion->lpat_table, raw_temp); 107b1eea857SAaron Lu if (temp < 0) 108b1eea857SAaron Lu return temp; 109b1eea857SAaron Lu 110b1eea857SAaron Lu *value = temp; 111b1eea857SAaron Lu return 0; 112b1eea857SAaron Lu } 113b1eea857SAaron Lu 114b1eea857SAaron Lu static int pmic_thermal_temp(struct intel_pmic_opregion *opregion, int reg, 115b1eea857SAaron Lu u32 function, u64 *value) 116b1eea857SAaron Lu { 117b1eea857SAaron Lu return function == ACPI_READ ? 118b1eea857SAaron Lu pmic_read_temp(opregion, reg, value) : -EINVAL; 119b1eea857SAaron Lu } 120b1eea857SAaron Lu 121b1eea857SAaron Lu static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg, 122b1eea857SAaron Lu u32 function, u64 *value) 123b1eea857SAaron Lu { 124b1eea857SAaron Lu int raw_temp; 125b1eea857SAaron Lu 126b1eea857SAaron Lu if (function == ACPI_READ) 127b1eea857SAaron Lu return pmic_read_temp(opregion, reg, value); 128b1eea857SAaron Lu 129b1eea857SAaron Lu if (!opregion->data->update_aux) 130b1eea857SAaron Lu return -ENXIO; 131b1eea857SAaron Lu 132ac586e2dSSrinivas Pandruvada if (opregion->lpat_table) { 133ac586e2dSSrinivas Pandruvada raw_temp = acpi_lpat_temp_to_raw(opregion->lpat_table, *value); 134b1eea857SAaron Lu if (raw_temp < 0) 135b1eea857SAaron Lu return raw_temp; 136b1eea857SAaron Lu } else { 137b1eea857SAaron Lu raw_temp = *value; 138b1eea857SAaron Lu } 139b1eea857SAaron Lu 140b1eea857SAaron Lu return opregion->data->update_aux(opregion->regmap, reg, raw_temp); 141b1eea857SAaron Lu } 142b1eea857SAaron Lu 143b1eea857SAaron Lu static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, 144d8ba8191SBin Gao int bit, u32 function, u64 *value) 145b1eea857SAaron Lu { 146b1eea857SAaron Lu struct intel_pmic_opregion_data *d = opregion->data; 147b1eea857SAaron Lu struct regmap *regmap = opregion->regmap; 148b1eea857SAaron Lu 149b1eea857SAaron Lu if (!d->get_policy || !d->update_policy) 150b1eea857SAaron Lu return -ENXIO; 151b1eea857SAaron Lu 152b1eea857SAaron Lu if (function == ACPI_READ) 153d8ba8191SBin Gao return d->get_policy(regmap, reg, bit, value); 154b1eea857SAaron Lu 155b1eea857SAaron Lu if (*value != 0 && *value != 1) 156b1eea857SAaron Lu return -EINVAL; 157b1eea857SAaron Lu 158d8ba8191SBin Gao return d->update_policy(regmap, reg, bit, *value); 159b1eea857SAaron Lu } 160b1eea857SAaron Lu 161b1eea857SAaron Lu static bool pmic_thermal_is_temp(int address) 162b1eea857SAaron Lu { 163b1eea857SAaron Lu return (address <= 0x3c) && !(address % 12); 164b1eea857SAaron Lu } 165b1eea857SAaron Lu 166b1eea857SAaron Lu static bool pmic_thermal_is_aux(int address) 167b1eea857SAaron Lu { 168b1eea857SAaron Lu return (address >= 4 && address <= 0x40 && !((address - 4) % 12)) || 169b1eea857SAaron Lu (address >= 8 && address <= 0x44 && !((address - 8) % 12)); 170b1eea857SAaron Lu } 171b1eea857SAaron Lu 172b1eea857SAaron Lu static bool pmic_thermal_is_pen(int address) 173b1eea857SAaron Lu { 174b1eea857SAaron Lu return address >= 0x48 && address <= 0x5c; 175b1eea857SAaron Lu } 176b1eea857SAaron Lu 177b1eea857SAaron Lu static acpi_status intel_pmic_thermal_handler(u32 function, 178b1eea857SAaron Lu acpi_physical_address address, u32 bits, u64 *value64, 179b1eea857SAaron Lu void *handler_context, void *region_context) 180b1eea857SAaron Lu { 181b1eea857SAaron Lu struct intel_pmic_opregion *opregion = region_context; 182b1eea857SAaron Lu struct intel_pmic_opregion_data *d = opregion->data; 183d8ba8191SBin Gao int reg, bit, result; 184b1eea857SAaron Lu 185b1eea857SAaron Lu if (bits != 32 || !value64) 186b1eea857SAaron Lu return AE_BAD_PARAMETER; 187b1eea857SAaron Lu 188b1eea857SAaron Lu result = pmic_get_reg_bit(address, d->thermal_table, 189d8ba8191SBin Gao d->thermal_table_count, ®, &bit); 190b1eea857SAaron Lu if (result == -ENOENT) 191b1eea857SAaron Lu return AE_BAD_PARAMETER; 192b1eea857SAaron Lu 193b1eea857SAaron Lu mutex_lock(&opregion->lock); 194b1eea857SAaron Lu 195b1eea857SAaron Lu if (pmic_thermal_is_temp(address)) 196b1eea857SAaron Lu result = pmic_thermal_temp(opregion, reg, function, value64); 197b1eea857SAaron Lu else if (pmic_thermal_is_aux(address)) 198b1eea857SAaron Lu result = pmic_thermal_aux(opregion, reg, function, value64); 199b1eea857SAaron Lu else if (pmic_thermal_is_pen(address)) 200d8ba8191SBin Gao result = pmic_thermal_pen(opregion, reg, bit, 201d8ba8191SBin Gao function, value64); 202b1eea857SAaron Lu else 203b1eea857SAaron Lu result = -EINVAL; 204b1eea857SAaron Lu 205b1eea857SAaron Lu mutex_unlock(&opregion->lock); 206b1eea857SAaron Lu 207b1eea857SAaron Lu if (result < 0) { 208b1eea857SAaron Lu if (result == -EINVAL) 209b1eea857SAaron Lu return AE_BAD_PARAMETER; 210b1eea857SAaron Lu else 211b1eea857SAaron Lu return AE_ERROR; 212b1eea857SAaron Lu } 213b1eea857SAaron Lu 214b1eea857SAaron Lu return AE_OK; 215b1eea857SAaron Lu } 216b1eea857SAaron Lu 2170afa877aSFelipe Balbi static acpi_status intel_pmic_regs_handler(u32 function, 2180afa877aSFelipe Balbi acpi_physical_address address, u32 bits, u64 *value64, 2190afa877aSFelipe Balbi void *handler_context, void *region_context) 2200afa877aSFelipe Balbi { 2210afa877aSFelipe Balbi struct intel_pmic_opregion *opregion = region_context; 222730de199SFelipe Balbi int result = 0; 2230afa877aSFelipe Balbi 2240afa877aSFelipe Balbi switch (address) { 2250afa877aSFelipe Balbi case 0: 2260afa877aSFelipe Balbi return AE_OK; 2270afa877aSFelipe Balbi case 1: 2280afa877aSFelipe Balbi opregion->ctx.addr |= (*value64 & 0xff) << 8; 2290afa877aSFelipe Balbi return AE_OK; 2300afa877aSFelipe Balbi case 2: 2310afa877aSFelipe Balbi opregion->ctx.addr |= *value64 & 0xff; 2320afa877aSFelipe Balbi return AE_OK; 2330afa877aSFelipe Balbi case 3: 2340afa877aSFelipe Balbi opregion->ctx.val = *value64 & 0xff; 2350afa877aSFelipe Balbi return AE_OK; 2360afa877aSFelipe Balbi case 4: 2370afa877aSFelipe Balbi if (*value64) { 2380afa877aSFelipe Balbi result = regmap_write(opregion->regmap, opregion->ctx.addr, 2390afa877aSFelipe Balbi opregion->ctx.val); 2400afa877aSFelipe Balbi } else { 2410afa877aSFelipe Balbi result = regmap_read(opregion->regmap, opregion->ctx.addr, 2420afa877aSFelipe Balbi &opregion->ctx.val); 2430afa877aSFelipe Balbi if (result == 0) 2440afa877aSFelipe Balbi *value64 = opregion->ctx.val; 2450afa877aSFelipe Balbi } 2460afa877aSFelipe Balbi memset(&opregion->ctx, 0x00, sizeof(opregion->ctx)); 2470afa877aSFelipe Balbi } 2480afa877aSFelipe Balbi 2490afa877aSFelipe Balbi if (result < 0) { 2500afa877aSFelipe Balbi if (result == -EINVAL) 2510afa877aSFelipe Balbi return AE_BAD_PARAMETER; 2520afa877aSFelipe Balbi else 2530afa877aSFelipe Balbi return AE_ERROR; 2540afa877aSFelipe Balbi } 2550afa877aSFelipe Balbi 2560afa877aSFelipe Balbi return AE_OK; 2570afa877aSFelipe Balbi } 2580afa877aSFelipe Balbi 259b1eea857SAaron Lu int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, 260b1eea857SAaron Lu struct regmap *regmap, 261b1eea857SAaron Lu struct intel_pmic_opregion_data *d) 262b1eea857SAaron Lu { 263b1eea857SAaron Lu acpi_status status; 264b1eea857SAaron Lu struct intel_pmic_opregion *opregion; 265ac586e2dSSrinivas Pandruvada int ret; 266b1eea857SAaron Lu 267b1eea857SAaron Lu if (!dev || !regmap || !d) 268b1eea857SAaron Lu return -EINVAL; 269b1eea857SAaron Lu 270b1eea857SAaron Lu if (!handle) 271b1eea857SAaron Lu return -ENODEV; 272b1eea857SAaron Lu 273b1eea857SAaron Lu opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL); 274b1eea857SAaron Lu if (!opregion) 275b1eea857SAaron Lu return -ENOMEM; 276b1eea857SAaron Lu 277b1eea857SAaron Lu mutex_init(&opregion->lock); 278b1eea857SAaron Lu opregion->regmap = regmap; 279ac586e2dSSrinivas Pandruvada opregion->lpat_table = acpi_lpat_get_conversion_table(handle); 280b1eea857SAaron Lu 281b1eea857SAaron Lu status = acpi_install_address_space_handler(handle, 282b1eea857SAaron Lu PMIC_POWER_OPREGION_ID, 283b1eea857SAaron Lu intel_pmic_power_handler, 284b1eea857SAaron Lu NULL, opregion); 285ac586e2dSSrinivas Pandruvada if (ACPI_FAILURE(status)) { 286ac586e2dSSrinivas Pandruvada ret = -ENODEV; 287ac586e2dSSrinivas Pandruvada goto out_error; 288ac586e2dSSrinivas Pandruvada } 289b1eea857SAaron Lu 290b1eea857SAaron Lu status = acpi_install_address_space_handler(handle, 291b1eea857SAaron Lu PMIC_THERMAL_OPREGION_ID, 292b1eea857SAaron Lu intel_pmic_thermal_handler, 293b1eea857SAaron Lu NULL, opregion); 294b1eea857SAaron Lu if (ACPI_FAILURE(status)) { 295b1eea857SAaron Lu acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, 296b1eea857SAaron Lu intel_pmic_power_handler); 297ac586e2dSSrinivas Pandruvada ret = -ENODEV; 2980afa877aSFelipe Balbi goto out_remove_power_handler; 2990afa877aSFelipe Balbi } 3000afa877aSFelipe Balbi 3010afa877aSFelipe Balbi status = acpi_install_address_space_handler(handle, 3020afa877aSFelipe Balbi PMIC_REGS_OPREGION_ID, intel_pmic_regs_handler, NULL, 3030afa877aSFelipe Balbi opregion); 3040afa877aSFelipe Balbi if (ACPI_FAILURE(status)) { 3050afa877aSFelipe Balbi ret = -ENODEV; 3060afa877aSFelipe Balbi goto out_remove_thermal_handler; 307b1eea857SAaron Lu } 308b1eea857SAaron Lu 309b1eea857SAaron Lu opregion->data = d; 3107b5618f4SHans de Goede intel_pmic_opregion = opregion; 311b1eea857SAaron Lu return 0; 312ac586e2dSSrinivas Pandruvada 3130afa877aSFelipe Balbi out_remove_thermal_handler: 3140afa877aSFelipe Balbi acpi_remove_address_space_handler(handle, PMIC_THERMAL_OPREGION_ID, 3150afa877aSFelipe Balbi intel_pmic_thermal_handler); 3160afa877aSFelipe Balbi 3170afa877aSFelipe Balbi out_remove_power_handler: 3180afa877aSFelipe Balbi acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, 3190afa877aSFelipe Balbi intel_pmic_power_handler); 3200afa877aSFelipe Balbi 321ac586e2dSSrinivas Pandruvada out_error: 322ac586e2dSSrinivas Pandruvada acpi_lpat_free_conversion_table(opregion->lpat_table); 323ac586e2dSSrinivas Pandruvada return ret; 324b1eea857SAaron Lu } 325b1eea857SAaron Lu EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); 3267b5618f4SHans de Goede 3277b5618f4SHans de Goede /** 3287b5618f4SHans de Goede * intel_soc_pmic_exec_mipi_pmic_seq_element - Execute PMIC MIPI sequence 3297b5618f4SHans de Goede * @i2c_address: I2C client address for the PMIC 3307b5618f4SHans de Goede * @reg_address: PMIC register address 3317b5618f4SHans de Goede * @value: New value for the register bits to change 3327b5618f4SHans de Goede * @mask: Mask indicating which register bits to change 3337b5618f4SHans de Goede * 3347b5618f4SHans de Goede * DSI LCD panels describe an initialization sequence in the i915 VBT (Video 3357b5618f4SHans de Goede * BIOS Tables) using so called MIPI sequences. One possible element in these 3367b5618f4SHans de Goede * sequences is a PMIC specific element of 15 bytes. 3377b5618f4SHans de Goede * 3387b5618f4SHans de Goede * This function executes these PMIC specific elements sending the embedded 3397b5618f4SHans de Goede * commands to the PMIC. 3407b5618f4SHans de Goede * 3417b5618f4SHans de Goede * Return 0 on success, < 0 on failure. 3427b5618f4SHans de Goede */ 3437b5618f4SHans de Goede int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address, 3447b5618f4SHans de Goede u32 value, u32 mask) 3457b5618f4SHans de Goede { 3467b5618f4SHans de Goede struct intel_pmic_opregion_data *d; 3477b5618f4SHans de Goede int ret; 3487b5618f4SHans de Goede 3497b5618f4SHans de Goede if (!intel_pmic_opregion) { 3507b5618f4SHans de Goede pr_warn("%s: No PMIC registered\n", __func__); 3517b5618f4SHans de Goede return -ENXIO; 3527b5618f4SHans de Goede } 3537b5618f4SHans de Goede 3547b5618f4SHans de Goede d = intel_pmic_opregion->data; 3557b5618f4SHans de Goede 3567b5618f4SHans de Goede mutex_lock(&intel_pmic_opregion->lock); 3577b5618f4SHans de Goede 3587b5618f4SHans de Goede if (d->exec_mipi_pmic_seq_element) { 3597b5618f4SHans de Goede ret = d->exec_mipi_pmic_seq_element(intel_pmic_opregion->regmap, 3607b5618f4SHans de Goede i2c_address, reg_address, 3617b5618f4SHans de Goede value, mask); 3627b5618f4SHans de Goede } else { 3637b5618f4SHans de Goede pr_warn("%s: Not implemented\n", __func__); 3647b5618f4SHans de Goede pr_warn("%s: i2c-addr: 0x%x reg-addr 0x%x value 0x%x mask 0x%x\n", 3657b5618f4SHans de Goede __func__, i2c_address, reg_address, value, mask); 3667b5618f4SHans de Goede ret = -EOPNOTSUPP; 3677b5618f4SHans de Goede } 3687b5618f4SHans de Goede 3697b5618f4SHans de Goede mutex_unlock(&intel_pmic_opregion->lock); 3707b5618f4SHans de Goede 3717b5618f4SHans de Goede return ret; 3727b5618f4SHans de Goede } 3737b5618f4SHans de Goede EXPORT_SYMBOL_GPL(intel_soc_pmic_exec_mipi_pmic_seq_element); 374