brintos

brintos / linux-shallow public Read only

0
0
Text · 9.4 KiB · 134e9ca Raw
384 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * intel_pmic.c - Intel PMIC operation region driver4 *5 * Copyright (C) 2014 Intel Corporation. All rights reserved.6 */7 8#include <linux/export.h>9#include <linux/acpi.h>10#include <linux/mfd/intel_soc_pmic.h>11#include <linux/regmap.h>12#include <acpi/acpi_lpat.h>13#include "intel_pmic.h"14 15#define PMIC_POWER_OPREGION_ID		0x8d16#define PMIC_THERMAL_OPREGION_ID	0x8c17#define PMIC_REGS_OPREGION_ID		0x8f18 19struct intel_pmic_regs_handler_ctx {20	unsigned int val;21	u16 addr;22};23 24struct intel_pmic_opregion {25	struct mutex lock;26	struct acpi_lpat_conversion_table *lpat_table;27	struct regmap *regmap;28	const struct intel_pmic_opregion_data *data;29	struct intel_pmic_regs_handler_ctx ctx;30};31 32static struct intel_pmic_opregion *intel_pmic_opregion;33 34static int pmic_get_reg_bit(int address, const struct pmic_table *table,35			    int count, int *reg, int *bit)36{37	int i;38 39	for (i = 0; i < count; i++) {40		if (table[i].address == address) {41			*reg = table[i].reg;42			if (bit)43				*bit = table[i].bit;44			return 0;45		}46	}47	return -ENOENT;48}49 50static acpi_status intel_pmic_power_handler(u32 function,51		acpi_physical_address address, u32 bits, u64 *value64,52		void *handler_context, void *region_context)53{54	struct intel_pmic_opregion *opregion = region_context;55	struct regmap *regmap = opregion->regmap;56	const struct intel_pmic_opregion_data *d = opregion->data;57	int reg, bit, result;58 59	if (bits != 32 || !value64)60		return AE_BAD_PARAMETER;61 62	if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1))63		return AE_BAD_PARAMETER;64 65	result = pmic_get_reg_bit(address, d->power_table,66				  d->power_table_count, &reg, &bit);67	if (result == -ENOENT)68		return AE_BAD_PARAMETER;69 70	mutex_lock(&opregion->lock);71 72	result = function == ACPI_READ ?73		d->get_power(regmap, reg, bit, value64) :74		d->update_power(regmap, reg, bit, *value64 == 1);75 76	mutex_unlock(&opregion->lock);77 78	return result ? AE_ERROR : AE_OK;79}80 81static int pmic_read_temp(struct intel_pmic_opregion *opregion,82			  int reg, u64 *value)83{84	int raw_temp, temp;85 86	if (!opregion->data->get_raw_temp)87		return -ENXIO;88 89	raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg);90	if (raw_temp < 0)91		return raw_temp;92 93	if (!opregion->lpat_table) {94		*value = raw_temp;95		return 0;96	}97 98	temp = opregion->data->lpat_raw_to_temp(opregion->lpat_table, raw_temp);99	if (temp < 0)100		return temp;101 102	*value = temp;103	return 0;104}105 106static int pmic_thermal_temp(struct intel_pmic_opregion *opregion, int reg,107			     u32 function, u64 *value)108{109	return function == ACPI_READ ?110		pmic_read_temp(opregion, reg, value) : -EINVAL;111}112 113static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg,114			    u32 function, u64 *value)115{116	int raw_temp;117 118	if (function == ACPI_READ)119		return pmic_read_temp(opregion, reg, value);120 121	if (!opregion->data->update_aux)122		return -ENXIO;123 124	if (opregion->lpat_table) {125		raw_temp = acpi_lpat_temp_to_raw(opregion->lpat_table, *value);126		if (raw_temp < 0)127			return raw_temp;128	} else {129		raw_temp = *value;130	}131 132	return opregion->data->update_aux(opregion->regmap, reg, raw_temp);133}134 135static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg,136			    int bit, u32 function, u64 *value)137{138	const struct intel_pmic_opregion_data *d = opregion->data;139	struct regmap *regmap = opregion->regmap;140 141	if (!d->get_policy || !d->update_policy)142		return -ENXIO;143 144	if (function == ACPI_READ)145		return d->get_policy(regmap, reg, bit, value);146 147	if (*value != 0 && *value != 1)148		return -EINVAL;149 150	return d->update_policy(regmap, reg, bit, *value);151}152 153static bool pmic_thermal_is_temp(int address)154{155	return (address <= 0x3c) && !(address % 12);156}157 158static bool pmic_thermal_is_aux(int address)159{160	return (address >= 4 && address <= 0x40 && !((address - 4) % 12)) ||161	       (address >= 8 && address <= 0x44 && !((address - 8) % 12));162}163 164static bool pmic_thermal_is_pen(int address)165{166	return address >= 0x48 && address <= 0x5c;167}168 169static acpi_status intel_pmic_thermal_handler(u32 function,170		acpi_physical_address address, u32 bits, u64 *value64,171		void *handler_context, void *region_context)172{173	struct intel_pmic_opregion *opregion = region_context;174	const struct intel_pmic_opregion_data *d = opregion->data;175	int reg, bit, result;176 177	if (bits != 32 || !value64)178		return AE_BAD_PARAMETER;179 180	result = pmic_get_reg_bit(address, d->thermal_table,181				  d->thermal_table_count, &reg, &bit);182	if (result == -ENOENT)183		return AE_BAD_PARAMETER;184 185	mutex_lock(&opregion->lock);186 187	if (pmic_thermal_is_temp(address))188		result = pmic_thermal_temp(opregion, reg, function, value64);189	else if (pmic_thermal_is_aux(address))190		result = pmic_thermal_aux(opregion, reg, function, value64);191	else if (pmic_thermal_is_pen(address))192		result = pmic_thermal_pen(opregion, reg, bit,193						function, value64);194	else195		result = -EINVAL;196 197	mutex_unlock(&opregion->lock);198 199	if (result < 0) {200		if (result == -EINVAL)201			return AE_BAD_PARAMETER;202		else203			return AE_ERROR;204	}205 206	return AE_OK;207}208 209static acpi_status intel_pmic_regs_handler(u32 function,210		acpi_physical_address address, u32 bits, u64 *value64,211		void *handler_context, void *region_context)212{213	struct intel_pmic_opregion *opregion = region_context;214	int result = -EINVAL;215 216	if (function == ACPI_WRITE) {217		switch (address) {218		case 0:219			return AE_OK;220		case 1:221			opregion->ctx.addr |= (*value64 & 0xff) << 8;222			return AE_OK;223		case 2:224			opregion->ctx.addr |= *value64 & 0xff;225			return AE_OK;226		case 3:227			opregion->ctx.val = *value64 & 0xff;228			return AE_OK;229		case 4:230			if (*value64) {231				result = regmap_write(opregion->regmap, opregion->ctx.addr,232						      opregion->ctx.val);233			} else {234				result = regmap_read(opregion->regmap, opregion->ctx.addr,235						     &opregion->ctx.val);236			}237			opregion->ctx.addr = 0;238		}239	}240 241	if (function == ACPI_READ && address == 3) {242		*value64 = opregion->ctx.val;243		return AE_OK;244	}245 246	if (result < 0) {247		if (result == -EINVAL)248			return AE_BAD_PARAMETER;249		else250			return AE_ERROR;251	}252 253	return AE_OK;254}255 256int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,257					struct regmap *regmap,258					const struct intel_pmic_opregion_data *d)259{260	acpi_status status = AE_OK;261	struct intel_pmic_opregion *opregion;262	int ret;263 264	if (!dev || !regmap || !d)265		return -EINVAL;266 267	if (!handle)268		return -ENODEV;269 270	opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL);271	if (!opregion)272		return -ENOMEM;273 274	mutex_init(&opregion->lock);275	opregion->regmap = regmap;276	opregion->lpat_table = acpi_lpat_get_conversion_table(handle);277 278	if (d->power_table_count)279		status = acpi_install_address_space_handler(handle,280						    PMIC_POWER_OPREGION_ID,281						    intel_pmic_power_handler,282						    NULL, opregion);283	if (ACPI_FAILURE(status)) {284		ret = -ENODEV;285		goto out_error;286	}287 288	if (d->thermal_table_count)289		status = acpi_install_address_space_handler(handle,290						    PMIC_THERMAL_OPREGION_ID,291						    intel_pmic_thermal_handler,292						    NULL, opregion);293	if (ACPI_FAILURE(status)) {294		ret = -ENODEV;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;304	}305 306	opregion->data = d;307	intel_pmic_opregion = opregion;308	return 0;309 310out_remove_thermal_handler:311	if (d->thermal_table_count)312		acpi_remove_address_space_handler(handle,313						  PMIC_THERMAL_OPREGION_ID,314						  intel_pmic_thermal_handler);315 316out_remove_power_handler:317	if (d->power_table_count)318		acpi_remove_address_space_handler(handle,319						  PMIC_POWER_OPREGION_ID,320						  intel_pmic_power_handler);321 322out_error:323	acpi_lpat_free_conversion_table(opregion->lpat_table);324	return ret;325}326EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler);327 328/**329 * intel_soc_pmic_exec_mipi_pmic_seq_element - Execute PMIC MIPI sequence330 * @i2c_address:  I2C client address for the PMIC331 * @reg_address:  PMIC register address332 * @value:        New value for the register bits to change333 * @mask:         Mask indicating which register bits to change334 *335 * DSI LCD panels describe an initialization sequence in the i915 VBT (Video336 * BIOS Tables) using so called MIPI sequences. One possible element in these337 * sequences is a PMIC specific element of 15 bytes.338 *339 * This function executes these PMIC specific elements sending the embedded340 * commands to the PMIC.341 *342 * Return 0 on success, < 0 on failure.343 */344int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address,345					      u32 value, u32 mask)346{347	const struct intel_pmic_opregion_data *d;348	int ret;349 350	if (!intel_pmic_opregion) {351		pr_warn("%s: No PMIC registered\n", __func__);352		return -ENXIO;353	}354 355	d = intel_pmic_opregion->data;356 357	mutex_lock(&intel_pmic_opregion->lock);358 359	if (d->exec_mipi_pmic_seq_element) {360		ret = d->exec_mipi_pmic_seq_element(intel_pmic_opregion->regmap,361						    i2c_address, reg_address,362						    value, mask);363	} else if (d->pmic_i2c_address) {364		if (i2c_address == d->pmic_i2c_address) {365			ret = regmap_update_bits(intel_pmic_opregion->regmap,366						 reg_address, mask, value);367		} else {368			pr_err("%s: Unexpected i2c-addr: 0x%02x (reg-addr 0x%x value 0x%x mask 0x%x)\n",369			       __func__, i2c_address, reg_address, value, mask);370			ret = -ENXIO;371		}372	} else {373		pr_warn("%s: Not implemented\n", __func__);374		pr_warn("%s: i2c-addr: 0x%x reg-addr 0x%x value 0x%x mask 0x%x\n",375			__func__, i2c_address, reg_address, value, mask);376		ret = -EOPNOTSUPP;377	}378 379	mutex_unlock(&intel_pmic_opregion->lock);380 381	return ret;382}383EXPORT_SYMBOL_GPL(intel_soc_pmic_exec_mipi_pmic_seq_element);384