[PATCH 09/10] regulator: Add LM3631 driver
From: Milo Kim
Date: Fri Feb 14 2014 - 01:32:55 EST
LM3631 regulator driver has 5 regulators.
One boost output and four LDOs are used for the display module.
Boost voltage is configurable but always on.
Supported operations for LDOs are enabled/disabled and voltage change.
Cc: Mark Brown <broonie@xxxxxxxxxx>
Signed-off-by: Milo Kim <milo.kim@xxxxxx>
---
drivers/regulator/Kconfig | 8 +
drivers/regulator/Makefile | 1 +
drivers/regulator/lm3631-regulator.c | 285 ++++++++++++++++++++++++++++++++++
3 files changed, 294 insertions(+)
create mode 100644 drivers/regulator/lm3631-regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 6a79328..9809d7b 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -221,6 +221,14 @@ config REGULATOR_ISL6271A
help
This driver supports ISL6271A voltage regulator chip.
+config REGULATOR_LM3631
+ tristate "TI LM3631 voltage regulators"
+ depends on MFD_TI_LMU
+ help
+ This driver supports LM3631 voltage regulators for the LCD bias.
+ One boost output voltage is configurable and always on.
+ Four LDOs are used for the display module.
+
config REGULATOR_LP3971
tristate "National Semiconductors LP3971 PMIC regulator driver"
depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 979f9dd..b20070d 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
+obj-$(CONFIG_REGULATOR_LM3631) += lm3631-regulator.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o
diff --git a/drivers/regulator/lm3631-regulator.c b/drivers/regulator/lm3631-regulator.c
new file mode 100644
index 0000000..930bad4
--- /dev/null
+++ b/drivers/regulator/lm3631-regulator.c
@@ -0,0 +1,285 @@
+/*
+ * TI LM3631 Regulator Driver
+ *
+ * Copyright 2014 Texas Instruments
+ *
+ * Author: Milo Kim <milo.kim@xxxxxx>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/mfd/ti-lmu.h>
+#include <linux/mfd/ti-lmu-register.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+#define ENABLE_TIME_USEC 1000
+
+enum lm3631_regulator_id {
+ LM3631_BOOST, /* Boost output */
+ LM3631_LDO_CONT, /* Display panel controller */
+ LM3631_LDO_OREF, /* Gamma reference */
+ LM3631_LDO_POS, /* Positive display bias output */
+ LM3631_LDO_NEG, /* Negative display bias output */
+};
+
+struct lm3631_regulator {
+ struct ti_lmu *lmu;
+ struct regulator_desc *desc;
+ struct regulator_dev *regulator;
+};
+
+static const int lm3631_boost_vtbl[] = {
+ 4500000, 4550000, 4600000, 4650000, 4700000, 4750000, 4800000, 4850000,
+ 4900000, 4950000, 5000000, 5050000, 5100000, 5150000, 5200000, 5250000,
+ 5300000, 5350000, 5400000, 5450000, 5500000, 5550000, 5600000, 5650000,
+ 5700000, 5750000, 5800000, 5850000, 5900000, 5950000, 6000000, 6050000,
+ 6100000, 6150000, 6200000, 6250000, 6300000, 6350000,
+};
+
+static const int lm3631_ldo_cont_vtbl[] = {
+ 1800000, 2300000, 2800000, 3300000,
+};
+
+static const int lm3631_ldo_target_vtbl[] = {
+ 4000000, 4050000, 4100000, 4150000, 4200000, 4250000, 4300000, 4350000,
+ 4400000, 4450000, 4500000, 4550000, 4600000, 4650000, 4700000, 4750000,
+ 4800000, 4850000, 4900000, 4950000, 5000000, 5050000, 5100000, 5150000,
+ 5200000, 5250000, 5300000, 5350000, 5400000, 5450000, 5500000, 5550000,
+ 5600000, 5650000, 5700000, 5750000, 5800000, 5850000, 5900000, 5950000,
+ 6000000,
+};
+
+const int ldo_cont_enable_time[] = {
+ 0, 2000, 5000, 10000, 20000, 50000, 100000, 200000,
+};
+
+static int lm3631_regulator_enable_time(struct regulator_dev *rdev)
+{
+ struct lm3631_regulator *lm3631_regulator = rdev_get_drvdata(rdev);
+ enum lm3631_regulator_id id = rdev_get_id(rdev);
+ u8 val, addr, mask;
+
+ switch (id) {
+ case LM3631_LDO_CONT:
+ addr = LM3631_REG_ENTIME_VCONT;
+ mask = LM3631_ENTIME_CONT_MASK;
+ break;
+ case LM3631_LDO_OREF:
+ addr = LM3631_REG_ENTIME_VOREF;
+ mask = LM3631_ENTIME_MASK;
+ break;
+ case LM3631_LDO_POS:
+ addr = LM3631_REG_ENTIME_VPOS;
+ mask = LM3631_ENTIME_MASK;
+ break;
+ case LM3631_LDO_NEG:
+ addr = LM3631_REG_ENTIME_VNEG;
+ mask = LM3631_ENTIME_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ti_lmu_read_byte(lm3631_regulator->lmu, addr, &val))
+ return -EINVAL;
+
+ val = (val & mask) >> LM3631_ENTIME_SHIFT;
+
+ if (id == LM3631_LDO_CONT)
+ return ldo_cont_enable_time[val];
+ else
+ return ENABLE_TIME_USEC * val;
+}
+
+static struct regulator_ops lm3631_boost_voltage_table_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops lm3631_regulator_voltage_table_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable_time = lm3631_regulator_enable_time,
+};
+
+static struct regulator_desc lm3631_regulator_desc[] = {
+ {
+ .name = "vboost",
+ .id = LM3631_BOOST,
+ .ops = &lm3631_boost_voltage_table_ops,
+ .n_voltages = ARRAY_SIZE(lm3631_boost_vtbl),
+ .volt_table = lm3631_boost_vtbl,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_BOOST,
+ .vsel_mask = LM3631_VOUT_MASK,
+ },
+ {
+ .name = "ldo_cont",
+ .id = LM3631_LDO_CONT,
+ .ops = &lm3631_regulator_voltage_table_ops,
+ .n_voltages = ARRAY_SIZE(lm3631_ldo_cont_vtbl),
+ .volt_table = lm3631_ldo_cont_vtbl,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_CONT,
+ .vsel_mask = LM3631_VOUT_CONT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL2,
+ .enable_mask = LM3631_EN_CONT_MASK,
+ },
+ {
+ .name = "ldo_oref",
+ .id = LM3631_LDO_OREF,
+ .ops = &lm3631_regulator_voltage_table_ops,
+ .n_voltages = ARRAY_SIZE(lm3631_ldo_target_vtbl),
+ .volt_table = lm3631_ldo_target_vtbl,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_OREF,
+ .vsel_mask = LM3631_VOUT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL1,
+ .enable_mask = LM3631_EN_OREF_MASK,
+ },
+ {
+ .name = "ldo_vpos",
+ .id = LM3631_LDO_POS,
+ .ops = &lm3631_regulator_voltage_table_ops,
+ .n_voltages = ARRAY_SIZE(lm3631_ldo_target_vtbl),
+ .volt_table = lm3631_ldo_target_vtbl,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_POS,
+ .vsel_mask = LM3631_VOUT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL1,
+ .enable_mask = LM3631_EN_VPOS_MASK,
+ },
+ {
+ .name = "ldo_vneg",
+ .id = LM3631_LDO_NEG,
+ .ops = &lm3631_regulator_voltage_table_ops,
+ .n_voltages = ARRAY_SIZE(lm3631_ldo_target_vtbl),
+ .volt_table = lm3631_ldo_target_vtbl,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_NEG,
+ .vsel_mask = LM3631_VOUT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL1,
+ .enable_mask = LM3631_EN_VNEG_MASK,
+ },
+};
+
+static struct of_regulator_match lm3631_regulator_matches[] = {
+ { .name = "vboost", .driver_data = (void *)LM3631_BOOST, },
+ { .name = "vcont", .driver_data = (void *)LM3631_LDO_CONT, },
+ { .name = "voref", .driver_data = (void *)LM3631_LDO_OREF, },
+ { .name = "vpos", .driver_data = (void *)LM3631_LDO_POS, },
+ { .name = "vneg", .driver_data = (void *)LM3631_LDO_NEG, },
+};
+
+static struct regulator_init_data *
+lm3631_regulator_parse_dt(struct device *dev,
+ struct lm3631_regulator *lm3631_regulator, int id)
+{
+ struct device_node *node = dev->of_node;
+ int count;
+
+ count = of_regulator_match(dev, node, &lm3631_regulator_matches[id], 1);
+ if (count <= 0)
+ return ERR_PTR(-ENODEV);
+
+ return lm3631_regulator_matches[id].init_data;
+}
+
+static int lm3631_regulator_probe(struct platform_device *pdev)
+{
+ struct ti_lmu *lmu = dev_get_drvdata(pdev->dev.parent);
+ struct lm3631_regulator *lm3631_regulator;
+ struct regulator_init_data *init_data;
+ struct regulator_config cfg = { };
+ struct regulator_dev *rdev;
+ int id = pdev->id;
+ int ret;
+
+ lm3631_regulator = devm_kzalloc(&pdev->dev, sizeof(*lm3631_regulator),
+ GFP_KERNEL);
+ if (!lm3631_regulator)
+ return -ENOMEM;
+
+ lm3631_regulator->lmu = lmu;
+
+ init_data = lmu->pdata->regulator_data[id];
+ if (!init_data) {
+ if (IS_ENABLED(CONFIG_OF)) {
+ init_data = lm3631_regulator_parse_dt(&pdev->dev,
+ lm3631_regulator,
+ id);
+ if (IS_ERR(init_data))
+ return PTR_ERR(init_data);
+ }
+ }
+
+ cfg.dev = pdev->dev.parent;
+ cfg.init_data = init_data;
+ cfg.driver_data = lm3631_regulator;
+ cfg.regmap = lmu->regmap;
+
+ rdev = regulator_register(&lm3631_regulator_desc[id], &cfg);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&pdev->dev, "[%d] regulator register err: %d\n",
+ id + 1, ret);
+ return ret;
+ }
+
+ lm3631_regulator->regulator = rdev;
+ platform_set_drvdata(pdev, lm3631_regulator);
+
+ return 0;
+}
+
+static int lm3631_regulator_remove(struct platform_device *pdev)
+{
+ struct lm3631_regulator *lm3631_regulator = platform_get_drvdata(pdev);
+
+ regulator_unregister(lm3631_regulator->regulator);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lm3631_regulator_of_match[] = {
+ { .compatible = "ti,lm3631-regulator", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lm3631_regulator_of_match);
+#endif
+
+static struct platform_driver lm3631_regulator_driver = {
+ .probe = lm3631_regulator_probe,
+ .remove = lm3631_regulator_remove,
+ .driver = {
+ .name = "lm3631-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(lm3631_regulator_of_match),
+ },
+};
+
+module_platform_driver(lm3631_regulator_driver);
+
+MODULE_DESCRIPTION("TI LM3631 Regulator Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3631-regulator");
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/