diff --git drivers/mfd/Kconfig drivers/mfd/Kconfig index 997a6172735e..e33bd218301b 100644 --- drivers/mfd/Kconfig +++ drivers/mfd/Kconfig @@ -1818,6 +1818,14 @@ config MFD_BD71837 if you say yes here you get support for the BD71837 Power Management chips. +config MFD_PCA9450 + bool "PCA9450 Power Management chip" + depends on I2C=y + select MFD_CORE + help + if you say yes here you get support for the PCA9450 + Power Management chips. + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git drivers/mfd/Makefile drivers/mfd/Makefile index c6755df735ba..962dcc88d99c 100644 --- drivers/mfd/Makefile +++ drivers/mfd/Makefile @@ -232,3 +232,4 @@ obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_MFD_BD71837) += bd71837.o +obj-$(CONFIG_MFD_PCA9450) += pca9450.o diff --git drivers/mfd/pca9450.c drivers/mfd/pca9450.c new file mode 100644 index 000000000000..85ce6e3eef68 --- /dev/null +++ drivers/mfd/pca9450.c @@ -0,0 +1,304 @@ +/* + * @file pca9450.c -- NXP PCA9450 mfd driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Copyright 2019 NXP. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* @brief pca9450 irq resource */ +static struct resource pmic_resources[] = { + { + .start = PCA9450_IRQ, + .end = PCA9450_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +/* @brief pca9450 multi function cells */ +static struct mfd_cell pca9450_mfd_cells[] = { + { + .name = "pca9450-pmic", + .num_resources = ARRAY_SIZE(pmic_resources), + .resources = &pmic_resources[0], + }, +}; + +/* @brief pca9450 irqs */ +static const struct regmap_irq pca9450_irqs[] = { + [PCA9450_IRQ] = { + .mask = PCA9450_INT_MASK, + .reg_offset = 0, + }, +}; + +/* @brief pca9450 irq chip definition */ +static struct regmap_irq_chip pca9450_irq_chip = { + .name = "pca9450", + .irqs = pca9450_irqs, + .num_irqs = ARRAY_SIZE(pca9450_irqs), + .num_regs = 1, + .irq_reg_stride = 1, + .status_base = PCA9450_INT1, + .mask_base = PCA9450_INT1_MSK, + .mask_invert = true, +}; + +/* + * @brief pca9450 irq initialize + * @param pca9450 pca9450 device to init + * @param bdinfo platform init data + * @retval 0 probe success + * @retval negative error number + */ +static int pca9450_irq_init(struct pca9450 *pca9450, + struct pca9450_board *bdinfo) +{ + int irq; + int ret = 0; + + if (!bdinfo) { + dev_warn(pca9450->dev, "No interrupt support, no pdata\n"); + return -EINVAL; + } + + dev_info(pca9450->dev, "gpio_intr = %d\n", bdinfo->gpio_intr); + irq = gpio_to_irq(bdinfo->gpio_intr); + + pca9450->chip_irq = irq; + dev_info(pca9450->dev, "chip_irq=%d\n", pca9450->chip_irq); + ret = regmap_add_irq_chip(pca9450->regmap, pca9450->chip_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, bdinfo->irq_base, + &pca9450_irq_chip, &pca9450->irq_data); + if (ret < 0) + dev_warn(pca9450->dev, "Failed to add irq_chip %d\n", ret); + + return ret; +} + +/* + * @brief pca9450 irq initialize + * @param pca9450 pca9450 device to init + * @retval 0 probe success + * @retval negative error number + */ +static int pca9450_irq_exit(struct pca9450 *pca9450) +{ + if (pca9450->chip_irq > 0) + regmap_del_irq_chip(pca9450->chip_irq, pca9450->irq_data); + return 0; +} + +/* + * @brief check whether volatile register + * @param dev kernel device pointer + * @param reg register index + */ +static bool is_volatile_reg(struct device *dev, unsigned int reg) +{ + /* + * Caching all regulator registers. + */ + return true; +} + +/* @brief regmap configures */ +static const struct regmap_config pca9450_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = is_volatile_reg, + .max_register = PCA9450_MAX_REGISTER - 1, + .cache_type = REGCACHE_RBTREE, +}; + +#ifdef CONFIG_OF +static const struct of_device_id pca9450_of_match[] = { + { .compatible = "nxp,pca9450", .data = (void *)0}, + { }, +}; +MODULE_DEVICE_TABLE(of, pca9450_of_match); + +/* + * @brief parse device tree data of pca9450 + * @param client client object provided by system + * @param chip_id return chip id back to caller + * @return board initialize data + */ +static struct pca9450_board *pca9450_parse_dt(struct i2c_client *client, + int *chip_id) +{ + struct device_node *np = client->dev.of_node; + struct pca9450_board *board_info; + unsigned int prop; + const struct of_device_id *match; + int r = 0; + + match = of_match_device(pca9450_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, "Failed to find matching dt id\n"); + return NULL; + } + + chip_id = (int *)match->data; + + board_info = devm_kzalloc(&client->dev, sizeof(*board_info), + GFP_KERNEL); + if (!board_info) + return NULL; + + board_info->gpio_intr = of_get_named_gpio(np, "gpio_intr", 0); + if (!gpio_is_valid(board_info->gpio_intr)) { + dev_err(&client->dev, "no pmic intr pin available\n"); + goto err_intr; + } + + r = of_property_read_u32(np, "irq_base", &prop); + if (!r) + board_info->irq_base = prop; + else + board_info->irq_base = -1; + + return board_info; + +err_intr: + devm_kfree(&client->dev, board_info); + return NULL; +} +#endif + +/* + * @brief probe pca9450 device + * @param i2c client object provided by system + * @param id chip id + * @retval 0 probe success + * @retval negative error number + */ +static int pca9450_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct pca9450 *pca9450; + struct pca9450_board *pmic_plat_data; + struct pca9450_board *of_pmic_plat_data = NULL; + int chip_id = id->driver_data; + int ret = 0; + + pmic_plat_data = dev_get_platdata(&i2c->dev); + + if (!pmic_plat_data && i2c->dev.of_node) { + pmic_plat_data = pca9450_parse_dt(i2c, &chip_id); + of_pmic_plat_data = pmic_plat_data; + } + + if (!pmic_plat_data) + return -EINVAL; + + pca9450 = kzalloc(sizeof(struct pca9450), GFP_KERNEL); + if (pca9450 == NULL) + return -ENOMEM; + + pca9450->of_plat_data = of_pmic_plat_data; + i2c_set_clientdata(i2c, pca9450); + pca9450->dev = &i2c->dev; + pca9450->i2c_client = i2c; + pca9450->id = chip_id; + mutex_init(&pca9450->io_mutex); + + pca9450->regmap = devm_regmap_init_i2c(i2c, &pca9450_regmap_config); + if (IS_ERR(pca9450->regmap)) { + ret = PTR_ERR(pca9450->regmap); + dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); + return ret; + } + + ret = pca9450_reg_read(pca9450, PCA9450_REG_DEV_ID); + if (ret < 0) { + dev_err(pca9450->dev, "%s(): Read PCA9450_REG_DEVICE failed!\n", + __func__); + goto err; + } + dev_info(pca9450->dev, "Device ID=0x%X\n", ret); + + pca9450_irq_init(pca9450, of_pmic_plat_data); + + ret = mfd_add_devices(pca9450->dev, -1, + pca9450_mfd_cells, ARRAY_SIZE(pca9450_mfd_cells), + NULL, 0, + regmap_irq_get_domain(pca9450->irq_data)); + if (ret < 0) + goto err; + + return ret; + +err: + mfd_remove_devices(pca9450->dev); + kfree(pca9450); + return ret; +} + +/* + * @brief remove pca9450 device + * @param i2c client object provided by system + * @return 0 + */ +static int pca9450_i2c_remove(struct i2c_client *i2c) +{ + struct pca9450 *pca9450 = i2c_get_clientdata(i2c); + + pca9450_irq_exit(pca9450); + mfd_remove_devices(pca9450->dev); + kfree(pca9450); + + return 0; +} + +static const struct i2c_device_id pca9450_i2c_id[] = { + { "pca9450", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca9450_i2c_id); + +static struct i2c_driver pca9450_i2c_driver = { + .driver = { + .name = "pca9450", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pca9450_of_match), + }, + .probe = pca9450_i2c_probe, + .remove = pca9450_i2c_remove, + .id_table = pca9450_i2c_id, +}; + +static int __init pca9450_i2c_init(void) +{ + return i2c_add_driver(&pca9450_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(pca9450_i2c_init); + +static void __exit pca9450_i2c_exit(void) +{ + i2c_del_driver(&pca9450_i2c_driver); +} +module_exit(pca9450_i2c_exit); + +MODULE_AUTHOR("John Lee "); +MODULE_DESCRIPTION("PCA9450 chip multi-function driver"); +MODULE_LICENSE("GPL"); diff --git drivers/regulator/Kconfig drivers/regulator/Kconfig index 5361947ea726..e5b3c9ffb982 100644 --- drivers/regulator/Kconfig +++ drivers/regulator/Kconfig @@ -983,5 +983,11 @@ config REGULATOR_BD71837 help This driver supports BD71837 voltage regulator chips. +config REGULATOR_PCA9450 + tristate "NXP PCA9450 Power Regulator" + depends on MFD_PCA9450 + help + This driver supports PCA9450 voltage regulator chips. + endif diff --git drivers/regulator/Makefile drivers/regulator/Makefile index 1bddbefbc8e7..0072ad5666f8 100644 --- drivers/regulator/Makefile +++ drivers/regulator/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o obj-$(CONFIG_REGULATOR_BD71837) += bd71837-regulator.o +obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git drivers/regulator/pca9450-regulator.c drivers/regulator/pca9450-regulator.c new file mode 100644 index 000000000000..6b9ce8f34e0e --- /dev/null +++ drivers/regulator/pca9450-regulator.c @@ -0,0 +1,783 @@ +/* + * @file pca9450-regulator.c ROHM PCA9450MWV regulator driver + * + * Copyright 2019 NXP. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCA9450_DVS_BUCK_NUM 3 /* Buck 1/2/3 support DVS */ +#define PCA9450_DVS0_1 2 +#define PCA9450_DVS0 1 + +struct pca9450_buck_dvs { + u32 voltage[PCA9450_DVS0_1]; +}; + +/* @brief pca9450 regulator type */ +struct pca9450_pmic { + struct regulator_desc descs[PCA9450_REGULATOR_CNT]; + struct pca9450 *mfd; + struct device *dev; + struct regulator_dev *rdev[PCA9450_REGULATOR_CNT]; + struct pca9450_buck_dvs buck_dvs[PCA9450_DVS_BUCK_NUM]; + int reg_index; +}; + +/* + * BUCK1/2/3 + * BUCK1RAM[1:0] BUCK1 DVS ramp rate setting + * 00: 25mV/1usec + * 01: 25mV/2usec + * 10: 25mV/4usec + * 11: 25mV/8usec + */ +static int pca9450_buck123_set_ramp_delay(struct regulator_dev *rdev, + int ramp_delay) +{ + struct pca9450_pmic *pmic = rdev_get_drvdata(rdev); + struct pca9450 *mfd = pmic->mfd; + int id = rdev->desc->id; + unsigned int ramp_value = BUCK1_RAMP_3P125MV; + unsigned int buckctrl[3] = {PCA9450_BUCK1CTRL, PCA9450_BUCK2CTRL, + PCA9450_BUCK3CTRL}; + + dev_dbg(pmic->dev, "Buck[%d] Set Ramp = %d\n", id + 1, ramp_delay); + switch (ramp_delay) { + case 1 ... 3125: + ramp_value = BUCK1_RAMP_3P125MV; + break; + case 3126 ... 6250: + ramp_value = BUCK1_RAMP_6P25MV; + break; + case 6251 ... 12500: + ramp_value = BUCK1_RAMP_12P5MV; + break; + case 12501 ... 25000: + ramp_value = BUCK1_RAMP_25MV; + break; + default: + ramp_value = BUCK1_RAMP_25MV; + } + + return regmap_update_bits(mfd->regmap, buckctrl[id], + BUCK1_RAMP_MASK, ramp_value << 6); +} + +static struct regulator_ops pca9450_ldo_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static struct regulator_ops pca9450_fixed_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +static struct regulator_ops pca9450_buck_regulator_ops = { + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +static struct regulator_ops pca9450_buck123_regulator_ops = { + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = pca9450_buck123_set_ramp_delay, +}; + +/* + * BUCK1/2/3 + * 0.60 to 2.1875V (12.5mV step) + */ +static const struct regulator_linear_range pca9450_buck123_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0x00, 0x7F, 12500), +}; + +/* + * BUCK4/5/6 + * 0.6V to 3.4V (25mV step) + */ +static const struct regulator_linear_range pca9450_buck456_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0x00, 0x70, 25000), + REGULATOR_LINEAR_RANGE(3400000, 0x71, 0x7F, 0), +}; + +/* + * LDO1 + * 1.6 to 3.3V () + */ +static const struct regulator_linear_range pca9450_ldo1_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(1600000, 0x00, 0x03, 100000), + REGULATOR_LINEAR_RANGE(3000000, 0x04, 0x07, 100000), +}; + +/* + * LDO2 + * 0.8 to 1.15V (50mV step) + */ +static const struct regulator_linear_range pca9450_ldo2_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(800000, 0x00, 0x07, 50000), +}; + +/* + * LDO3 + * 0.8 to 3.3V (100mV step) + */ +static const struct regulator_linear_range pca9450_ldo34_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(800000, 0x00, 0x19, 100000), + REGULATOR_LINEAR_RANGE(3300000, 0x1A, 0x1F, 0), +}; + +/* + * LDO5 + * 1.8 to 3.3V (100mV step) + */ +static const struct regulator_linear_range pca9450_ldo5_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000), +}; + +static const struct regulator_desc pca9450_regulators[] = { + { + .name = "BUCK1", + .id = PCA9450_BUCK1, + .ops = &pca9450_buck123_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_BUCK1_VOLTAGE_NUM, + .linear_ranges = pca9450_buck123_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(pca9450_buck123_voltage_ranges), + .vsel_reg = PCA9450_BUCK1OUT_DVS0, + .vsel_mask = BUCK1OUT_DVS0_MASK, + .enable_reg = PCA9450_BUCK1CTRL, + .enable_mask = BUCK1_ENMODE_MASK, + .owner = THIS_MODULE, + }, + { + .name = "BUCK2", + .id = PCA9450_BUCK2, + .ops = &pca9450_buck123_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_BUCK2_VOLTAGE_NUM, + .linear_ranges = pca9450_buck123_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(pca9450_buck123_voltage_ranges), + .vsel_reg = PCA9450_BUCK2OUT_DVS0, + .vsel_mask = BUCK2OUT_DVS0_MASK, + .enable_reg = PCA9450_BUCK2CTRL, + .enable_mask = BUCK2_ENMODE_MASK, + .owner = THIS_MODULE, + }, + { + .name = "BUCK3", + .id = PCA9450_BUCK3, + .ops = &pca9450_buck123_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_BUCK3_VOLTAGE_NUM, + .linear_ranges = pca9450_buck123_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(pca9450_buck123_voltage_ranges), + .vsel_reg = PCA9450_BUCK3OUT_DVS0, + .vsel_mask = BUCK3OUT_DVS0_MASK, + .enable_reg = PCA9450_BUCK3CTRL, + .enable_mask = BUCK3_ENMODE_MASK, + .owner = THIS_MODULE, + }, + { + .name = "BUCK4", + .id = PCA9450_BUCK4, + .ops = &pca9450_buck_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_BUCK4_VOLTAGE_NUM, + .linear_ranges = pca9450_buck456_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(pca9450_buck456_voltage_ranges), + .vsel_reg = PCA9450_BUCK4OUT, + .vsel_mask = BUCK4OUT_MASK, + .enable_reg = PCA9450_BUCK4CTRL, + .enable_mask = BUCK4_ENMODE_MASK, + .owner = THIS_MODULE, + }, + { + .name = "BUCK5", + .id = PCA9450_BUCK5, + .ops = &pca9450_buck_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_BUCK5_VOLTAGE_NUM, + .linear_ranges = pca9450_buck456_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(pca9450_buck456_voltage_ranges), + .vsel_reg = PCA9450_BUCK5OUT, + .vsel_mask = BUCK5OUT_MASK, + .enable_reg = PCA9450_BUCK5CTRL, + .enable_mask = BUCK5_ENMODE_MASK, + .owner = THIS_MODULE, + }, + { + .name = "BUCK6", + .id = PCA9450_BUCK6, + .ops = &pca9450_buck_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_BUCK6_VOLTAGE_NUM, + .linear_ranges = pca9450_buck456_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(pca9450_buck456_voltage_ranges), + .vsel_reg = PCA9450_BUCK6OUT, + .vsel_mask = BUCK6OUT_MASK, + .enable_reg = PCA9450_BUCK6CTRL, + .enable_mask = BUCK6_ENMODE_MASK, + .owner = THIS_MODULE, + }, + { + .name = "LDO1", + .id = PCA9450_LDO1, + .ops = &pca9450_ldo_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_LDO1_VOLTAGE_NUM, + .linear_ranges = pca9450_ldo1_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(pca9450_ldo1_voltage_ranges), + .vsel_reg = PCA9450_LDO1CTRL, + .vsel_mask = LDO1OUT_MASK, + .enable_reg = PCA9450_LDO1CTRL, + .enable_mask = LDO1_EN_MASK, + .owner = THIS_MODULE, + }, + /* + * LDO2 0.9V + * Fixed voltage + */ + { + .name = "LDO2", + .id = PCA9450_LDO2, + .ops = &pca9450_fixed_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_LDO2_VOLTAGE_NUM, + .min_uV = 900000, + .enable_reg = PCA9450_LDO2CTRL, + .enable_mask = LDO2_EN_MASK, + .owner = THIS_MODULE, + }, + { + .name = "LDO3", + .id = PCA9450_LDO3, + .ops = &pca9450_ldo_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_LDO3_VOLTAGE_NUM, + .linear_ranges = pca9450_ldo34_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(pca9450_ldo34_voltage_ranges), + .vsel_reg = PCA9450_LDO3CTRL, + .vsel_mask = LDO3OUT_MASK, + .enable_reg = PCA9450_LDO3CTRL, + .enable_mask = LDO3_EN_MASK, + .owner = THIS_MODULE, + }, + { + .name = "LDO4", + .id = PCA9450_LDO4, + .ops = &pca9450_ldo_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_LDO4_VOLTAGE_NUM, + .linear_ranges = pca9450_ldo34_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(pca9450_ldo34_voltage_ranges), + .vsel_reg = PCA9450_LDO4CTRL, + .vsel_mask = LDO4OUT_MASK, + .enable_reg = PCA9450_LDO4CTRL, + .enable_mask = LDO4_EN_MASK, + .owner = THIS_MODULE, + }, + { + .name = "LDO5", + .id = PCA9450_LDO5, + .ops = &pca9450_ldo_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PCA9450_LDO5_VOLTAGE_NUM, + .linear_ranges = pca9450_ldo5_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(pca9450_ldo5_voltage_ranges), + .vsel_reg = PCA9450_LDO5CTRL_H, + .vsel_mask = LDO5HOUT_MASK, + .enable_reg = PCA9450_LDO5CTRL_H, + .enable_mask = LDO5H_EN_MASK, + .owner = THIS_MODULE, + }, +}; + +#ifdef CONFIG_OF + +static struct of_regulator_match pca9450_matches[] = { + { .name = "buck1", }, + { .name = "buck2", }, + { .name = "buck3", }, + { .name = "buck4", }, + { .name = "buck5", }, + { .name = "buck6", }, + { .name = "ldo1", }, + { .name = "ldo2", }, + { .name = "ldo3", }, + { .name = "ldo4", }, + { .name = "ldo5", }, +}; + +/* + * @brief parse pca9450 regulator device tree + * @param pdev platform device of pca9450 regulator + * @param pca9450_reg_matches return regualtor matches + * @retval 0 parse success + * @retval NULL parse fail + */ +static int pca9450_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **reg_matches) +{ + struct device_node *np, *regulators; + struct of_regulator_match *matches; + int ret, count; + + np = of_node_get(pdev->dev.parent->of_node); + regulators = of_find_node_by_name(np, "regulators"); + if (!regulators) { + dev_err(&pdev->dev, "regulator node not found\n"); + return -EINVAL; + } + + count = ARRAY_SIZE(pca9450_matches); + matches = pca9450_matches; + + ret = of_regulator_match(&pdev->dev, regulators, matches, count); + of_node_put(regulators); + if (ret < 0) { + dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", + ret); + return ret; + } + + *reg_matches = matches; + + return 0; +} +#else +static inline int pca9450_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **reg_matches) +{ + *reg_matches = NULL; + return 0; +} +#endif + +/* @brief directly set raw value to chip register, format: 'register value' */ +static ssize_t pca9450_sysfs_set_registers(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct pca9450_pmic *pmic = dev_get_drvdata(dev); + ssize_t ret = 0; + unsigned int reg; + unsigned int val; + + ret = sscanf(buf, "%x %x", ®, &val); + if (ret < 1) { + pmic->reg_index = -1; + dev_err(pmic->dev, "registers set: \n"); + return count; + } + + if (ret == 1 && reg < PCA9450_MAX_REGISTER) { + pmic->reg_index = reg; + dev_info(pmic->dev, "registers set: reg=0x%x\n", reg); + return count; + } + + if (reg >= PCA9450_MAX_REGISTER) { + dev_err(pmic->dev, "reg=%d out of Max=%d\n", reg, + PCA9450_MAX_REGISTER); + return -EINVAL; + } + dev_info(pmic->dev, "registers set: reg=0x%x, val=0x%x\n", reg, val); + ret = pca9450_reg_write(pmic->mfd, reg, val); + if (ret < 0) + return ret; + return count; +} + +/* @brief print value of chip register, format: 'register=value' */ +static ssize_t pca9450_sysfs_print_reg(struct pca9450_pmic *pmic, + u8 reg, + char *buf) +{ + int ret = pca9450_reg_read(pmic->mfd, reg); + + if (ret < 0) + return sprintf(buf, "%#.2x=error %d\n", reg, ret); + return sprintf(buf, "[0x%.2X] = %.2X\n", reg, ret); +} + +/* + * @brief show all raw values of chip register, format per line: + * 'register=value' + */ +static ssize_t pca9450_sysfs_show_registers(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pca9450_pmic *pmic = dev_get_drvdata(dev); + ssize_t ret = 0; + int i; + + dev_info(pmic->dev, "register: index[0x%x]\n", pmic->reg_index); + if (pmic->reg_index >= 0) + ret += pca9450_sysfs_print_reg(pmic, pmic->reg_index, + buf + ret); + else + for (i = 0; i < PCA9450_MAX_REGISTER; i++) + ret += pca9450_sysfs_print_reg(pmic, i, buf + ret); + + return ret; +} + +static DEVICE_ATTR(registers, 0644, + pca9450_sysfs_show_registers, pca9450_sysfs_set_registers); + +/* @brief device sysfs attribute table, about o */ +static struct attribute *clk_attributes[] = { + &dev_attr_registers.attr, + NULL +}; + +static const struct attribute_group clk_attr_group = { + .attrs = clk_attributes, +}; + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_OF +/* + * @brief buck1/2 dvs enable/voltage from device tree + * @param pdev platform device pointer + * @param buck_dvs pointer + * @return void + */ +static void of_pca9450_buck_dvs(struct platform_device *pdev, + struct pca9450_buck_dvs *buck_dvs) +{ + struct device_node *pmic_np; + + pmic_np = of_node_get(pdev->dev.parent->of_node); + if (!pmic_np) { + dev_err(&pdev->dev, "could not find pmic sub-node\n"); + return; + } + if (of_get_property(pmic_np, "pca9450,pmic-buck1-uses-i2c-dvs", NULL)) { + if (of_property_read_u32_array(pmic_np, + "pca9450,pmic-buck1-dvs-voltage", + &buck_dvs[0].voltage[0], + PCA9450_DVS0_1)) { + dev_err(&pdev->dev, "buck1 voltages not specified\n"); + } + } + + if (of_get_property(pmic_np, "pca9450,pmic-buck2-uses-i2c-dvs", NULL)) { + if (of_property_read_u32_array(pmic_np, + "pca9450,pmic-buck2-dvs-voltage", + &buck_dvs[1].voltage[0], + PCA9450_DVS0_1)) { + dev_err(&pdev->dev, "buck2 voltages not specified\n"); + } + } + + if (of_get_property(pmic_np, "pca9450,pmic-buck3-uses-i2c-dvs", NULL)) { + if (of_property_read_u32_array(pmic_np, + "pca9450,pmic-buck3-dvs-voltage", + &buck_dvs[2].voltage[0], + PCA9450_DVS0)) { + dev_err(&pdev->dev, "buck3 voltages not specified\n"); + } + } +} +#else +static void of_pca9450_buck_dvs(struct platform_device *pdev, + struct pca9450_buck_dvs *buck_dvs) +{ + buck_dvs[0].voltage[0] = BUCK1OUT_DVS0_DEFAULT; + buck_dvs[0].voltage[1] = BUCK1OUT_DVS1_DEFAULT; + buck_dvs[1].voltage[0] = BUCK2OUT_DVS0_DEFAULT; + buck_dvs[1].voltage[1] = BUCK2OUT_DVS1_DEFAULT; + buck_dvs[2].voltage[0] = BUCK3OUT_DVS0_DEFAULT; + buck_dvs[2].voltage[1] = 0; /* Not supported */ +} +#endif + +static int pca9450_buck123_dvs_init(struct pca9450_pmic *pmic) +{ + struct pca9450 *pca9450 = pmic->mfd; + struct pca9450_buck_dvs *buck_dvs = &pmic->buck_dvs[0]; + int i, ret, val, selector = 0; + u8 reg_dvs0, reg_dvs1; + u8 reg_dvs0_msk, reg_dvs1_msk; + + for (i = 0; i < PCA9450_DVS_BUCK_NUM; i++, buck_dvs++) { + switch (i) { + case 0: + default: + reg_dvs0 = PCA9450_BUCK1OUT_DVS0; + reg_dvs0_msk = BUCK1OUT_DVS0_MASK; + reg_dvs1 = PCA9450_BUCK1OUT_DVS1; + reg_dvs1_msk = BUCK1OUT_DVS1_MASK; + break; + case 1: + reg_dvs0 = PCA9450_BUCK2OUT_DVS0; + reg_dvs0_msk = BUCK2OUT_DVS0_MASK; + reg_dvs1 = PCA9450_BUCK2OUT_DVS1; + reg_dvs1_msk = BUCK2OUT_DVS1_MASK; + break; + case 2: + reg_dvs0 = PCA9450_BUCK3OUT_DVS0; + reg_dvs0_msk = BUCK3OUT_DVS0_MASK; + reg_dvs1 = PCA9450_BUCK3OUT_DVS1; + reg_dvs1_msk = BUCK3OUT_DVS1_MASK; + break; + } + + dev_dbg(pmic->dev, "Buck%d: DVS0-DVS1[%d - %d].\n", i+1, + buck_dvs->voltage[0], buck_dvs->voltage[1]); + if (reg_dvs0 > 0) { + selector = regulator_map_voltage_iterate(pmic->rdev[i], + buck_dvs->voltage[0], + buck_dvs->voltage[0]); + if (selector < 0) { + dev_dbg(pmic->dev, + "not found selector for DVS0 [%d]\n", + buck_dvs->voltage[0]); + } else { + val = (selector & reg_dvs0_msk); + ret = pca9450_reg_write(pca9450, reg_dvs0, val); + if (ret < 0) + return ret; + } + } + if (reg_dvs1 > 0) { + selector = regulator_map_voltage_iterate(pmic->rdev[i], + buck_dvs->voltage[1], + buck_dvs->voltage[1]); + if (selector < 0) { + dev_dbg(pmic->dev, + "not found selector for DVS1 [%d]\n", + buck_dvs->voltage[1]); + } else { + val = (selector & reg_dvs1_msk); + ret = pca9450_reg_write(pca9450, reg_dvs1, val); + if (ret < 0) + return ret; + } + } + } + return 0; +} + +/* + * @brief pca9450 pmic interrupt + * @param irq system irq + * @param pwrsys pca9450 power device of system + * @retval IRQ_HANDLED success + * @retval IRQ_NONE error + */ +static irqreturn_t pca9450_pmic_interrupt(int irq, void *pwrsys) +{ + struct device *dev = pwrsys; + struct pca9450 *mfd = dev_get_drvdata(dev->parent); + int reg; + + reg = pca9450_reg_read(mfd, PCA9450_INT1); + if (reg < 0) + return IRQ_NONE; + + if (reg & IRQ_PWRON) + dev_dbg(dev, "IRQ_PWRON\n"); + if (reg & IRQ_WDOGB) + dev_dbg(dev, "IRQ_WDOGB\n"); + if (reg & IRQ_VR_FLT1) + dev_dbg(dev, "IRQ_VR_FLT1\n"); + if (reg & IRQ_VR_FLT2) + dev_dbg(dev, "IRQ_VR_FLT2\n"); + if (reg & IRQ_LOWVSYS) + dev_dbg(dev, "IRQ_LOWVSYS\n"); + if (reg & IRQ_THERM_105) + dev_dbg(dev, "IRQ_THERM_105\n"); + if (reg & IRQ_THERM_125) + dev_dbg(dev, "IRQ_THERM_125\n"); + + return IRQ_HANDLED; +} + +/* + * @brief probe pca9450 regulator device + * @param pdev pca9450 regulator platform device + * @retval 0 success + * @retval negative fail + */ +static int pca9450_probe(struct platform_device *pdev) +{ + struct pca9450_pmic *pmic; + struct pca9450_board *pdata; + struct regulator_config config = {}; + struct pca9450 *pca9450 = dev_get_drvdata(pdev->dev.parent); + struct of_regulator_match *matches = NULL; + int i = 0, err, irq = 0, ret = 0; + + pmic = kzalloc(sizeof(*pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + memcpy(pmic->descs, pca9450_regulators, sizeof(pmic->descs)); + + pmic->dev = &pdev->dev; + pmic->mfd = pca9450; + platform_set_drvdata(pdev, pmic); + pdata = dev_get_platdata(pca9450->dev); + + if (!pdata && pca9450->dev->of_node) { + pca9450_parse_dt_reg_data(pdev, &matches); + if (matches == NULL) { + dev_err(&pdev->dev, "Platform data not found\n"); + return -EINVAL; + } + } + + /* Get buck dvs parameters */ + of_pca9450_buck_dvs(pdev, &pmic->buck_dvs[0]); + + for (i = 0; i < PCA9450_REGULATOR_CNT; i++) { + struct regulator_init_data *init_data; + struct regulator_desc *desc; + struct regulator_dev *rdev; + + desc = &pmic->descs[i]; + desc->name = pca9450_matches[i].name; + + if (pdata) + init_data = pdata->init_data[i]; + else + init_data = pca9450_matches[i].init_data; + + config.dev = pmic->dev; + config.init_data = init_data; + config.driver_data = pmic; + config.regmap = pca9450->regmap; + if (matches) + config.of_node = matches[i].of_node; + dev_dbg(config.dev, "regulator register name '%s'\n", + desc->name); + + rdev = regulator_register(desc, &config); + if (IS_ERR(rdev)) { + dev_err(pca9450->dev, + "failed to register %s regulator\n", + desc->name); + err = PTR_ERR(rdev); + goto err; + } + pmic->rdev[i] = rdev; + } + + /* Init sysfs registers */ + pmic->reg_index = -1; + + err = sysfs_create_group(&pdev->dev.kobj, &clk_attr_group); + if (err != 0) { + dev_err(&pdev->dev, "Failed to create sysfs: %d\n", err); + goto err; + } + + /* Init Buck1/2/3 dvs */ + err = pca9450_buck123_dvs_init(pmic); + if (err != 0) { + dev_err(&pdev->dev, "Failed to buck123 dvs: %d\n", err); + goto err; + } + + /* Add Interrupt */ + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_warn(&pdev->dev, "platform irq error # %d\n", irq); + return -ENXIO; + } + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + pca9450_pmic_interrupt, + IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME, + dev_name(&pdev->dev), &pdev->dev); + if (ret < 0) + dev_err(&pdev->dev, "IRQ %d is not free.\n", irq); + + /* Un-mask IRQ Interrupt */ + ret = pca9450_reg_write(pca9450, PCA9450_INT1_MSK, 0); + if (ret < 0) { + dev_err(&pdev->dev, "Write 'PCA9450_REG_MIRQ': failed!\n"); + ret = -EIO; + goto err; + } + + return 0; + +err: + while (--i >= 0) + regulator_unregister(pmic->rdev[i]); + + kfree(pmic); + return err; +} + +/* + * @brief remove pca9450 regulator device + * @param pdev pca9450 regulator platform device + * @return 0 + */ +static int __exit pca9450_remove(struct platform_device *pdev) +{ + struct pca9450_pmic *pmic = platform_get_drvdata(pdev); + int i; + + sysfs_remove_group(&pdev->dev.kobj, &clk_attr_group); + + for (i = 0; i < PCA9450_REGULATOR_CNT; i++) + regulator_unregister(pmic->rdev[i]); + + kfree(pmic); + return 0; +} + +static struct platform_driver pca9450_driver = { + .driver = { + .name = "pca9450-pmic", + .owner = THIS_MODULE, + }, + .probe = pca9450_probe, + .remove = pca9450_remove, +}; +module_platform_driver(pca9450_driver); + +MODULE_DESCRIPTION("PCA9450 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:pca9450-pmic"); diff --git include/linux/mfd/pca9450.h include/linux/mfd/pca9450.h new file mode 100644 index 000000000000..b689c2dd3b94 --- /dev/null +++ include/linux/mfd/pca9450.h @@ -0,0 +1,355 @@ +/** + * @file pca9450.h NXP PCA9450 header file + * + * Copyright 2019 NXP + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __LINUX_MFD_PCA9450_H +#define __LINUX_MFD_PCA9450_H + +#include + +enum { + PCA9450_BUCK1 = 0, + PCA9450_BUCK2, + PCA9450_BUCK3, + PCA9450_BUCK4, + PCA9450_BUCK5, + PCA9450_BUCK6, + PCA9450_LDO1, + PCA9450_LDO2, + PCA9450_LDO3, + PCA9450_LDO4, + PCA9450_LDO5, + PCA9450_REGULATOR_CNT, +}; + +#define PCA9450_SUPPLY_STATE_ENABLED 0x1 + +#define PCA9450_BUCK1_VOLTAGE_NUM 0x80 +#define PCA9450_BUCK2_VOLTAGE_NUM 0x80 +#define PCA9450_BUCK3_VOLTAGE_NUM 0x80 +#define PCA9450_BUCK4_VOLTAGE_NUM 0x80 + +#define PCA9450_BUCK5_VOLTAGE_NUM 0x80 +#define PCA9450_BUCK6_VOLTAGE_NUM 0x80 + +#define PCA9450_LDO1_VOLTAGE_NUM 0x08 +#define PCA9450_LDO2_VOLTAGE_NUM 0x08 +#define PCA9450_LDO3_VOLTAGE_NUM 0x20 +#define PCA9450_LDO4_VOLTAGE_NUM 0x20 +#define PCA9450_LDO5_VOLTAGE_NUM 0x10 + +enum { + PCA9450_REG_DEV_ID = 0x00, + PCA9450_INT1 = 0x01, + PCA9450_INT1_MSK = 0x02, + PCA9450_STATUS1 = 0x03, + PCA9450_STATUS2 = 0x04, + PCA9450_PWRON_STAT = 0x05, + PCA9450_SW_RST = 0x06, + PCA9450_PWR_CTRL = 0x07, + PCA9450_RESET_CTRL = 0x08, + PCA9450_CONFIG1 = 0x09, + PCA9450_CONFIG2 = 0x0A, + PCA9450_BUCK123_DVS = 0x0C, + PCA9450_BUCK1OUT_LIMIT = 0x0D, + PCA9450_BUCK2OUT_LIMIT = 0x0E, + PCA9450_BUCK3OUT_LIMIT = 0x0F, + PCA9450_BUCK1CTRL = 0x10, + PCA9450_BUCK1OUT_DVS0 = 0x11, + PCA9450_BUCK1OUT_DVS1 = 0x12, + PCA9450_BUCK2CTRL = 0x13, + PCA9450_BUCK2OUT_DVS0 = 0x14, + PCA9450_BUCK2OUT_DVS1 = 0x15, + PCA9450_BUCK3CTRL = 0x16, + PCA9450_BUCK3OUT_DVS0 = 0x17, + PCA9450_BUCK3OUT_DVS1 = 0x18, + PCA9450_BUCK4CTRL = 0x19, + PCA9450_BUCK4OUT = 0x1A, + PCA9450_BUCK5CTRL = 0x1B, + PCA9450_BUCK5OUT = 0x1C, + PCA9450_BUCK6CTRL = 0x1D, + PCA9450_BUCK6OUT = 0x1E, + PCA9450_LDO_AD_CTRL = 0x20, + PCA9450_LDO1CTRL = 0x21, + PCA9450_LDO2CTRL = 0x22, + PCA9450_LDO3CTRL = 0x23, + PCA9450_LDO4CTRL = 0x24, + PCA9450_LDO5CTRL_L = 0x25, + PCA9450_LDO5CTRL_H = 0x26, + PCA9450_LOADSW_CTRL = 0x2A, + PCA9450_VRFLT1_STS = 0x2B, + PCA9450_VRFLT2_STS = 0x2C, + PCA9450_VRFLT1_MASK = 0x2D, + PCA9450_VRFLT2_MASK = 0x2E, + PCA9450_MAX_REGISTER = 0x2F, +}; + +/* PCA9450 BUCK ENMODE bits */ +#define BUCK_ENMODE_OFF 0x00 +#define BUCK_ENMODE_ONREQ 0x01 +#define BUCK_ENMODE_ONREQ_STBYREQ 0x02 +#define BUCK_ENMODE_ON 0x03 + +/* PCA9450_REG_BUCK1_CTRL bits */ +#define BUCK1_RAMP_MASK 0xC0 +#define BUCK1_RAMP_25MV 0x0 +#define BUCK1_RAMP_12P5MV 0x1 +#define BUCK1_RAMP_6P25MV 0x2 +#define BUCK1_RAMP_3P125MV 0x3 +#define BUCK1_DVS_CTRL 0x10 +#define BUCK1_AD 0x08 +#define BUCK1_FPWM 0x04 +#define BUCK1_ENMODE_MASK 0x03 + +/* PCA9450_REG_BUCK2_CTRL bits */ +#define BUCK2_RAMP_MASK 0xC0 +#define BUCK2_RAMP_25MV 0x0 +#define BUCK2_RAMP_12P5MV 0x1 +#define BUCK2_RAMP_6P25MV 0x2 +#define BUCK2_RAMP_3P125MV 0x3 +#define BUCK2_DVS_CTRL 0x10 +#define BUCK2_AD 0x08 +#define BUCK2_FPWM 0x04 +#define BUCK2_ENMODE_MASK 0x03 + +/* PCA9450_REG_BUCK3_CTRL bits */ +#define BUCK3_RAMP_MASK 0xC0 +#define BUCK3_RAMP_25MV 0x0 +#define BUCK3_RAMP_12P5MV 0x1 +#define BUCK3_RAMP_6P25MV 0x2 +#define BUCK3_RAMP_3P125MV 0x3 +#define BUCK3_DVS_CTRL 0x10 +#define BUCK3_AD 0x08 +#define BUCK3_FPWM 0x04 +#define BUCK3_ENMODE_MASK 0x03 + +/* PCA9450_REG_BUCK4_CTRL bits */ +#define BUCK4_AD 0x08 +#define BUCK4_FPWM 0x04 +#define BUCK4_ENMODE_MASK 0x03 + +/* PCA9450_REG_BUCK5_CTRL bits */ +#define BUCK5_AD 0x08 +#define BUCK5_FPWM 0x04 +#define BUCK5_ENMODE_MASK 0x03 + +/* PCA9450_REG_BUCK6_CTRL bits */ +#define BUCK6_AD 0x08 +#define BUCK6_FPWM 0x04 +#define BUCK6_ENMODE_MASK 0x03 + +/* PCA9450_BUCK1OUT_DVS0 bits */ +#define BUCK1OUT_DVS0_MASK 0x7F +#define BUCK1OUT_DVS0_DEFAULT 0x14 + +/* PCA9450_BUCK1OUT_DVS1 bits */ +#define BUCK1OUT_DVS1_MASK 0x7F +#define BUCK1OUT_DVS1_DEFAULT 0x14 + +/* PCA9450_BUCK2OUT_DVS0 bits */ +#define BUCK2OUT_DVS0_MASK 0x7F +#define BUCK2OUT_DVS0_DEFAULT 0x14 + +/* PCA9450_BUCK2OUT_DVS1 bits */ +#define BUCK2OUT_DVS1_MASK 0x7F +#define BUCK2OUT_DVS1_DEFAULT 0x14 + +/* PCA9450_BUCK3OUT_DVS0 bits */ +#define BUCK3OUT_DVS0_MASK 0x7F +#define BUCK3OUT_DVS0_DEFAULT 0x14 + +/* PCA9450_BUCK3OUT_DVS1 bits */ +#define BUCK3OUT_DVS1_MASK 0x7F +#define BUCK3OUT_DVS1_DEFAULT 0x14 + +/* PCA9450_REG_BUCK4OUT bits */ +#define BUCK4OUT_MASK 0x7F +#define BUCK4OUT_DEFAULT 0x6C + +/* PCA9450_REG_BUCK5OUT bits */ +#define BUCK5OUT_MASK 0x7F +#define BUCK5OUT_DEFAULT 0x30 + +/* PCA9450_REG_BUCK6OUT bits */ +#define BUCK6OUT_MASK 0x7F +#define BUCK6OUT_DEFAULT 0x14 + +/* PCA9450_REG_IRQ bits */ +#define IRQ_PWRON 0x80 +#define IRQ_WDOGB 0x40 +#define IRQ_VR_FLT1 0x10 +#define IRQ_VR_FLT2 0x08 +#define IRQ_LOWVSYS 0x04 +#define IRQ_THERM_105 0x02 +#define IRQ_THERM_125 0x01 + +/* PCA9450 interrupt masks */ +enum { + PCA9450_INT_MASK = 0xDF, +}; +/* PCA9450 interrupt irqs */ +enum { + PCA9450_IRQ = 0x0, +}; + +/* PCA9450_REG_LDO1_VOLT bits */ +#define LDO1_EN_MASK 0xC0 +#define LDO1OUT_MASK 0x07 + +/* PCA9450_REG_LDO2_VOLT bits */ +#define LDO2_EN_MASK 0xC0 +#define LDO2OUT_MASK 0x07 + +/* PCA9450_REG_LDO3_VOLT bits */ +#define LDO3_EN_MASK 0xC0 +#define LDO3OUT_MASK 0x0F + +/* PCA9450_REG_LDO4_VOLT bits */ +#define LDO4_EN_MASK 0xC0 +#define LDO4OUT_MASK 0x0F + +/* PCA9450_REG_LDO5_VOLT bits */ +#define LDO5L_EN_MASK 0xC0 +#define LDO5LOUT_MASK 0x0F + +#define LDO5H_EN_MASK 0xC0 +#define LDO5HOUT_MASK 0x0F + +/* + * @brief Board platform data may be used to initialize regulators. + */ + +struct pca9450_board { + struct regulator_init_data *init_data[PCA9450_REGULATOR_CNT]; + int gpio_intr; + int irq_base; +}; + +/* + * @brief pca9450 sub-driver chip access routines + */ + +struct pca9450 { + struct device *dev; + struct i2c_client *i2c_client; + struct regmap *regmap; + struct mutex io_mutex; + unsigned int id; + + /* IRQ Handling */ + int chip_irq; + struct regmap_irq_chip_data *irq_data; + + /* Client devices */ + struct pca9450_pmic *pmic; + struct pca9450_power *power; + + struct pca9450_board *of_plat_data; +}; + +static inline int pca9450_chip_id(struct pca9450 *pca9450) +{ + return pca9450->id; +} + +/* + * @brief pca9450_reg_read + * read single register's value of pca9450 + * @param pca9450 device to read + * @param reg register address + * @return register value if success + * error number if fail + */ +static inline int pca9450_reg_read(struct pca9450 *pca9450, u8 reg) +{ + int r, val; + + r = regmap_read(pca9450->regmap, reg, &val); + if (r < 0) + return r; + + return val; +} + +/* + * @brief pca9450_reg_write + * write single register of pca9450 + * @param pca9450 device to write + * @param reg register address + * @param val value to write + * @retval 0 if success + * @retval negative error number if fail + */ + +static inline int pca9450_reg_write(struct pca9450 *pca9450, u8 reg, + unsigned int val) +{ + return regmap_write(pca9450->regmap, reg, val); +} + +/* + * @brief pca9450_set_bits + * set bits in one register of pca9450 + * @param pca9450 device to read + * @param reg register address + * @param mask mask bits + * @retval 0 if success + * @retval negative error number if fail + */ +static inline int pca9450_set_bits(struct pca9450 *pca9450, u8 reg, u8 mask) +{ + return regmap_update_bits(pca9450->regmap, reg, mask, mask); +} + +/* + * @brief pca9450_clear_bits + * clear bits in one register of pca9450 + * @param pca9450 device to read + * @param reg register address + * @param mask mask bits + * @retval 0 if success + * @retval negative error number if fail + */ + +static inline int pca9450_clear_bits(struct pca9450 *pca9450, u8 reg, + u8 mask) +{ + return regmap_update_bits(pca9450->regmap, reg, mask, 0); +} + +/* + * @brief pca9450_update_bits + * update bits in one register of pca9450 + * @param pca9450 device to read + * @param reg register address + * @param mask mask bits + * @param val value to update + * @retval 0 if success + * @retval negative error number if fail + */ + +static inline int pca9450_update_bits(struct pca9450 *pca9450, u8 reg, + u8 mask, u8 val) +{ + return regmap_update_bits(pca9450->regmap, reg, mask, val); +} + +/* + * @brief pca9450 platform data type + */ +struct pca9450_gpo_plat_data { + u32 drv; + int gpio_base; +}; + +u8 ext_pca9450_reg_read8(u8 reg); +int ext_pca9450_reg_write8(int reg, u8 val); +#endif /* __LINUX_MFD_PCA9450_H */