On Fri, Jul 28, 2017 at 05:30:26PM -0700, Rajmohan Mani wrote: > The Kabylake platform coreboot (Chrome OS equivalent of > BIOS) has defined 4 operation regions for the TI TPS68470 PMIC. > These operation regions are to enable/disable voltage > regulators, configure voltage regulators, enable/disable > clocks and to configure clocks. > > This config adds ACPI operation region support for TI TPS68470 PMIC. > TPS68470 device is an advanced power management unit that powers > a Compact Camera Module (CCM), generates clocks for image sensors, > drives a dual LED for flash and incorporates two LED drivers for > general purpose indicators. > This driver enables ACPI operation region support to control voltage > regulators and clocks for the TPS68470 PMIC. > > Signed-off-by: Rajmohan Mani <rajmohan.mani@xxxxxxxxx> Acked-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx> > --- > drivers/acpi/Kconfig | 16 ++ > drivers/acpi/Makefile | 2 + > drivers/acpi/pmic/tps68470_pmic.c | 455 ++++++++++++++++++++++++++++++++++++++ > 3 files changed, 473 insertions(+) > create mode 100644 drivers/acpi/pmic/tps68470_pmic.c > > diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig > index 1ce52f8..e124a82d 100644 > --- a/drivers/acpi/Kconfig > +++ b/drivers/acpi/Kconfig > @@ -535,4 +535,20 @@ if ARM64 > source "drivers/acpi/arm64/Kconfig" > endif > > +config TPS68470_PMIC_OPREGION > + bool "ACPI operation region support for TPS68470 PMIC" > + depends on MFD_TPS68470 > + help > + This config adds ACPI operation region support for TI TPS68470 PMIC. > + TPS68470 device is an advanced power management unit that powers > + a Compact Camera Module (CCM), generates clocks for image sensors, > + drives a dual LED for flash and incorporates two LED drivers for > + general purpose indicators. > + This driver enables ACPI operation region support control voltage > + regulators and clocks. > + > + This option is a bool as it provides an ACPI operation > + region, which must be available before any of the devices > + using this, are probed. > + > endif # ACPI > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile > index b1aacfc..2d70a7f 100644 > --- a/drivers/acpi/Makefile > +++ b/drivers/acpi/Makefile > @@ -106,6 +106,8 @@ obj-$(CONFIG_CHT_WC_PMIC_OPREGION) += pmic/intel_pmic_chtwc.o > > obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o > > +obj-$(CONFIG_TPS68470_PMIC_OPREGION) += pmic/tps68470_pmic.o > + > video-objs += acpi_video.o video_detect.o > obj-y += dptf/ > > diff --git a/drivers/acpi/pmic/tps68470_pmic.c b/drivers/acpi/pmic/tps68470_pmic.c > new file mode 100644 > index 0000000..7f3c567 > --- /dev/null > +++ b/drivers/acpi/pmic/tps68470_pmic.c > @@ -0,0 +1,455 @@ > +/* > + * TI TPS68470 PMIC operation region driver > + * > + * Copyright (C) 2017 Intel Corporation. All rights reserved. > + * > + * Author: Rajmohan Mani <rajmohan.mani@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License version > + * 2 as published by the Free Software Foundation. > + * > + * This program is distributed "as is" WITHOUT ANY WARRANTY of any > + * kind, whether express or implied; without even the implied warranty > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * Based on drivers/acpi/pmic/intel_pmic* drivers > + */ > + > +#include <linux/acpi.h> > +#include <linux/mfd/tps68470.h> > +#include <linux/init.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > + > +struct tps68470_pmic_table { > + u32 address; /* operation region address */ > + u32 reg; /* corresponding register */ > + u32 bitmask; /* bit mask for power, clock */ > +}; > + > +#define TI_PMIC_POWER_OPREGION_ID 0xB0 > +#define TI_PMIC_VR_VAL_OPREGION_ID 0xB1 > +#define TI_PMIC_CLOCK_OPREGION_ID 0xB2 > +#define TI_PMIC_CLKFREQ_OPREGION_ID 0xB3 > + > +struct tps68470_pmic_opregion { > + struct mutex lock; > + struct regmap *regmap; > +}; > + > +#define S_IO_I2C_EN (BIT(0) | BIT(1)) > + > +static const struct tps68470_pmic_table power_table[] = { > + { > + .address = 0x00, > + .reg = TPS68470_REG_S_I2C_CTL, > + .bitmask = S_IO_I2C_EN, > + /* S_I2C_CTL */ > + }, > + { > + .address = 0x04, > + .reg = TPS68470_REG_VCMCTL, > + .bitmask = BIT(0), > + /* VCMCTL */ > + }, > + { > + .address = 0x08, > + .reg = TPS68470_REG_VAUX1CTL, > + .bitmask = BIT(0), > + /* VAUX1_CTL */ > + }, > + { > + .address = 0x0C, > + .reg = TPS68470_REG_VAUX2CTL, > + .bitmask = BIT(0), > + /* VAUX2CTL */ > + }, > + { > + .address = 0x10, > + .reg = TPS68470_REG_VACTL, > + .bitmask = BIT(0), > + /* VACTL */ > + }, > + { > + .address = 0x14, > + .reg = TPS68470_REG_VDCTL, > + .bitmask = BIT(0), > + /* VDCTL */ > + }, > +}; > + > +/* Table to set voltage regulator value */ > +static const struct tps68470_pmic_table vr_val_table[] = { > + { > + .address = 0x00, > + .reg = TPS68470_REG_VSIOVAL, > + .bitmask = TPS68470_VSIOVAL_IOVOLT_MASK, > + /* TPS68470_REG_VSIOVAL */ > + }, > + { > + .address = 0x04, > + .reg = TPS68470_REG_VIOVAL, > + .bitmask = TPS68470_VIOVAL_IOVOLT_MASK, > + /* TPS68470_REG_VIOVAL */ > + }, > + { > + .address = 0x08, > + .reg = TPS68470_REG_VCMVAL, > + .bitmask = TPS68470_VCMVAL_VCVOLT_MASK, > + /* TPS68470_REG_VCMVAL */ > + }, > + { > + .address = 0x0C, > + .reg = TPS68470_REG_VAUX1VAL, > + .bitmask = TPS68470_VAUX1VAL_AUX1VOLT_MASK, > + /* TPS68470_REG_VAUX1VAL */ > + }, > + { > + .address = 0x10, > + .reg = TPS68470_REG_VAUX2VAL, > + .bitmask = TPS68470_VAUX2VAL_AUX2VOLT_MASK, > + /* TPS68470_REG_VAUX2VAL */ > + }, > + { > + .address = 0x14, > + .reg = TPS68470_REG_VAVAL, > + .bitmask = TPS68470_VAVAL_AVOLT_MASK, > + /* TPS68470_REG_VAVAL */ > + }, > + { > + .address = 0x18, > + .reg = TPS68470_REG_VDVAL, > + .bitmask = TPS68470_VDVAL_DVOLT_MASK, > + /* TPS68470_REG_VDVAL */ > + }, > +}; > + > +/* Table to configure clock frequency */ > +static const struct tps68470_pmic_table clk_freq_table[] = { > + { > + .address = 0x00, > + .reg = TPS68470_REG_POSTDIV2, > + .bitmask = BIT(0) | BIT(1), > + /* TPS68470_REG_POSTDIV2 */ > + }, > + { > + .address = 0x04, > + .reg = TPS68470_REG_BOOSTDIV, > + .bitmask = 0x1F, > + /* TPS68470_REG_BOOSTDIV */ > + }, > + { > + .address = 0x08, > + .reg = TPS68470_REG_BUCKDIV, > + .bitmask = 0x0F, > + /* TPS68470_REG_BUCKDIV */ > + }, > + { > + .address = 0x0C, > + .reg = TPS68470_REG_PLLSWR, > + .bitmask = 0x13, > + /* TPS68470_REG_PLLSWR */ > + }, > + { > + .address = 0x10, > + .reg = TPS68470_REG_XTALDIV, > + .bitmask = 0xFF, > + /* TPS68470_REG_XTALDIV */ > + }, > + { > + .address = 0x14, > + .reg = TPS68470_REG_PLLDIV, > + .bitmask = 0xFF, > + /* TPS68470_REG_PLLDIV */ > + }, > + { > + .address = 0x18, > + .reg = TPS68470_REG_POSTDIV, > + .bitmask = 0x83, > + /* TPS68470_REG_POSTDIV */ > + }, > +}; > + > +/* Table to configure and enable clocks */ > +static const struct tps68470_pmic_table clk_table[] = { > + { > + .address = 0x00, > + .reg = TPS68470_REG_PLLCTL, > + .bitmask = 0xF5, > + /* TPS68470_REG_PLLCTL */ > + }, > + { > + .address = 0x04, > + .reg = TPS68470_REG_PLLCTL2, > + .bitmask = BIT(0), > + /* TPS68470_REG_PLLCTL2 */ > + }, > + { > + .address = 0x08, > + .reg = TPS68470_REG_CLKCFG1, > + .bitmask = TPS68470_CLKCFG1_MODE_A_MASK | > + TPS68470_CLKCFG1_MODE_B_MASK, > + /* TPS68470_REG_CLKCFG1 */ > + }, > + { > + .address = 0x0C, > + .reg = TPS68470_REG_CLKCFG2, > + .bitmask = TPS68470_CLKCFG1_MODE_A_MASK | > + TPS68470_CLKCFG1_MODE_B_MASK, > + /* TPS68470_REG_CLKCFG2 */ > + }, > +}; > + > +static int pmic_get_reg_bit(u64 address, > + const struct tps68470_pmic_table *table, > + const unsigned int table_size, int *reg, > + int *bitmask) > +{ > + u64 i; > + > + i = address / 4; > + if (i >= table_size) > + return -ENOENT; > + > + if (!reg || !bitmask) > + return -EINVAL; > + > + *reg = table[i].reg; > + *bitmask = table[i].bitmask; > + > + return 0; > +} > + > +static int tps68470_pmic_get_power(struct regmap *regmap, int reg, > + int bitmask, u64 *value) > +{ > + unsigned int data; > + > + if (regmap_read(regmap, reg, &data)) > + return -EIO; > + > + *value = (data & bitmask) ? 1 : 0; > + return 0; > +} > + > +static int tps68470_pmic_get_vr_val(struct regmap *regmap, int reg, > + int bitmask, u64 *value) > +{ > + unsigned int data; > + > + if (regmap_read(regmap, reg, &data)) > + return -EIO; > + > + *value = data & bitmask; > + return 0; > +} > + > +static int tps68470_pmic_get_clk(struct regmap *regmap, int reg, > + int bitmask, u64 *value) > +{ > + unsigned int data; > + > + if (regmap_read(regmap, reg, &data)) > + return -EIO; > + > + *value = (data & bitmask) ? 1 : 0; > + return 0; > +} > + > +static int tps68470_pmic_get_clk_freq(struct regmap *regmap, int reg, > + int bitmask, u64 *value) > +{ > + unsigned int data; > + > + if (regmap_read(regmap, reg, &data)) > + return -EIO; > + > + *value = data & bitmask; > + return 0; > +} > + > +static int ti_tps68470_regmap_update_bits(struct regmap *regmap, int reg, > + int bitmask, u64 value) > +{ > + return regmap_update_bits(regmap, reg, bitmask, value); > +} > + > +static acpi_status tps68470_pmic_common_handler(u32 function, > + acpi_physical_address address, > + u32 bits, u64 *value, > + void *region_context, > + int (*get)(struct regmap *, > + int, int, u64 *), > + int (*update)(struct regmap *, > + int, int, u64), > + const struct tps68470_pmic_table *tbl, > + unsigned int tbl_size) > +{ > + struct tps68470_pmic_opregion *opregion = region_context; > + struct regmap *regmap = opregion->regmap; > + int reg, ret, bitmask; > + > + if (bits != 32) > + return AE_BAD_PARAMETER; > + > + ret = pmic_get_reg_bit(address, tbl, tbl_size, ®, &bitmask); > + if (ret < 0) > + return AE_BAD_PARAMETER; > + > + if (function == ACPI_WRITE && *value > bitmask) > + return AE_BAD_PARAMETER; > + > + mutex_lock(&opregion->lock); > + > + ret = (function == ACPI_READ) ? > + get(regmap, reg, bitmask, value) : > + update(regmap, reg, bitmask, *value); > + > + mutex_unlock(&opregion->lock); > + > + return ret ? AE_ERROR : AE_OK; > +} > + > +static acpi_status tps68470_pmic_cfreq_handler(u32 function, > + acpi_physical_address address, > + u32 bits, u64 *value, > + void *handler_context, > + void *region_context) > +{ > + return tps68470_pmic_common_handler(function, address, bits, value, > + region_context, > + tps68470_pmic_get_clk_freq, > + ti_tps68470_regmap_update_bits, > + clk_freq_table, > + ARRAY_SIZE(clk_freq_table)); > +} > + > +static acpi_status tps68470_pmic_clk_handler(u32 function, > + acpi_physical_address address, u32 bits, > + u64 *value, void *handler_context, > + void *region_context) > +{ > + return tps68470_pmic_common_handler(function, address, bits, value, > + region_context, > + tps68470_pmic_get_clk, > + ti_tps68470_regmap_update_bits, > + clk_table, > + ARRAY_SIZE(clk_table)); > +} > + > +static acpi_status tps68470_pmic_vrval_handler(u32 function, > + acpi_physical_address address, > + u32 bits, u64 *value, > + void *handler_context, > + void *region_context) > +{ > + return tps68470_pmic_common_handler(function, address, bits, value, > + region_context, > + tps68470_pmic_get_vr_val, > + ti_tps68470_regmap_update_bits, > + vr_val_table, > + ARRAY_SIZE(vr_val_table)); > +} > + > +static acpi_status tps68470_pmic_pwr_handler(u32 function, > + acpi_physical_address address, > + u32 bits, u64 *value, > + void *handler_context, > + void *region_context) > +{ > + if (bits != 32) > + return AE_BAD_PARAMETER; > + > + /* set/clear for bit 0, bits 0 and 1 together */ > + if (function == ACPI_WRITE && > + !(*value == 0 || *value == 1 || *value == 3)) { > + return AE_BAD_PARAMETER; > + } > + > + return tps68470_pmic_common_handler(function, address, bits, value, > + region_context, > + tps68470_pmic_get_power, > + ti_tps68470_regmap_update_bits, > + power_table, > + ARRAY_SIZE(power_table)); > +} > + > +static int tps68470_pmic_opregion_probe(struct platform_device *pdev) > +{ > + struct regmap *tps68470_regmap = dev_get_drvdata(pdev->dev.parent); > + acpi_handle handle = ACPI_HANDLE(pdev->dev.parent); > + struct device *dev = &pdev->dev; > + struct tps68470_pmic_opregion *opregion; > + acpi_status status; > + > + if (!dev || !tps68470_regmap) { > + dev_warn(dev, "dev or regmap is NULL\n"); > + return -EINVAL; > + } > + > + if (!handle) { > + dev_warn(dev, "acpi handle is NULL\n"); > + return -ENODEV; > + } > + > + opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL); > + if (!opregion) > + return -ENOMEM; > + > + mutex_init(&opregion->lock); > + opregion->regmap = tps68470_regmap; > + > + status = acpi_install_address_space_handler(handle, > + TI_PMIC_POWER_OPREGION_ID, > + tps68470_pmic_pwr_handler, > + NULL, opregion); > + if (ACPI_FAILURE(status)) > + goto out_mutex_destroy; > + > + status = acpi_install_address_space_handler(handle, > + TI_PMIC_VR_VAL_OPREGION_ID, > + tps68470_pmic_vrval_handler, > + NULL, opregion); > + if (ACPI_FAILURE(status)) > + goto out_remove_power_handler; > + > + status = acpi_install_address_space_handler(handle, > + TI_PMIC_CLOCK_OPREGION_ID, > + tps68470_pmic_clk_handler, > + NULL, opregion); > + if (ACPI_FAILURE(status)) > + goto out_remove_vr_val_handler; > + > + status = acpi_install_address_space_handler(handle, > + TI_PMIC_CLKFREQ_OPREGION_ID, > + tps68470_pmic_cfreq_handler, > + NULL, opregion); > + if (ACPI_FAILURE(status)) > + goto out_remove_clk_handler; > + > + return 0; > + > +out_remove_clk_handler: > + acpi_remove_address_space_handler(handle, TI_PMIC_CLOCK_OPREGION_ID, > + tps68470_pmic_clk_handler); > +out_remove_vr_val_handler: > + acpi_remove_address_space_handler(handle, TI_PMIC_VR_VAL_OPREGION_ID, > + tps68470_pmic_vrval_handler); > +out_remove_power_handler: > + acpi_remove_address_space_handler(handle, TI_PMIC_POWER_OPREGION_ID, > + tps68470_pmic_pwr_handler); > +out_mutex_destroy: > + mutex_destroy(&opregion->lock); > + return -ENODEV; > +} > + > +static struct platform_driver tps68470_pmic_opregion_driver = { > + .probe = tps68470_pmic_opregion_probe, > + .driver = { > + .name = "tps68470_pmic_opregion", > + }, > +}; > + > +builtin_platform_driver(tps68470_pmic_opregion_driver) > -- > 1.9.1 > -- Sakari Ailus sakari.ailus@xxxxxxxxxxxxxxx -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html