1517 lines
40 KiB
Diff
1517 lines
40 KiB
Diff
diff --git drivers/mfd/Kconfig drivers/mfd/Kconfig
|
|
index 997a6172735e..e33bd218301b 100644
|
|
--- linux-imx/drivers/mfd/Kconfig
|
|
+++ linux-imx/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
|
|
--- linux-imx/drivers/mfd/Makefile
|
|
+++ linux-imx/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
|
|
+++ linux-imx/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 <linux/module.h>
|
|
+#include <linux/moduleparam.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/irq.h>
|
|
+#include <linux/irqdomain.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/of_gpio.h>
|
|
+#include <linux/mfd/core.h>
|
|
+#include <linux/mfd/pca9450.h>
|
|
+
|
|
+/* @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 <john.lee@nxp.com>");
|
|
+MODULE_DESCRIPTION("PCA9450 chip multi-function driver");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git drivers/regulator/Kconfig drivers/regulator/Kconfig
|
|
index 5361947ea726..e5b3c9ffb982 100644
|
|
--- linux-imx/drivers/regulator/Kconfig
|
|
+++ linux-imx/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
|
|
--- linux-imx/drivers/regulator/Makefile
|
|
+++ linux-imx/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
|
|
+++ linux-imx/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 <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/regulator/driver.h>
|
|
+#include <linux/regulator/machine.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/mfd/pca9450.h>
|
|
+#include <linux/regulator/of_regulator.h>
|
|
+
|
|
+#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: <reg> <value>\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
|
|
+++ linux-imx/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 <linux/regmap.h>
|
|
+
|
|
+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 */
|