[PATCH 09/10] regulator: Add LM3631 driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




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 devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux