[PATCH 3/4] regulator: Add labibb driver

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

 



This patch adds labibb regulator driver for supporting LCD mode display
on SDM845 platform.

Signed-off-by: Nisha Kumari <nishakumari@xxxxxxxxxxxxxx>
---
 drivers/regulator/Kconfig                 |  10 +
 drivers/regulator/Makefile                |   1 +
 drivers/regulator/qcom-labibb-regulator.c | 438 ++++++++++++++++++++++++++++++
 3 files changed, 449 insertions(+)
 create mode 100644 drivers/regulator/qcom-labibb-regulator.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index b7f249e..ab9d272 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -1057,5 +1057,15 @@ config REGULATOR_WM8994
 	  This driver provides support for the voltage regulators on the
 	  WM8994 CODEC.
 
+config REGULATOR_QCOM_LABIBB
+	tristate "QCOM LAB/IBB regulator support"
+	depends on SPMI || COMPILE_TEST
+	help
+	  This driver supports Qualcomm's LAB/IBB regulators present on the
+	  Qualcomm's PMIC chip pmi8998. QCOM LAB and IBB are SPMI
+	  based PMIC implementations. LAB can be used as positive
+	  boost regulator and IBB can be used as a negative boost regulator
+	  for LCD display panel.
+
 endif
 
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 1169f8a..f123f3e 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
 obj-$(CONFIG_REGULATOR_MT6323)	+= mt6323-regulator.o
 obj-$(CONFIG_REGULATOR_MT6380)	+= mt6380-regulator.o
 obj-$(CONFIG_REGULATOR_MT6397)	+= mt6397-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_LABIBB) += qcom-labibb-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c
new file mode 100644
index 0000000..0c68883
--- /dev/null
+++ b/drivers/regulator/qcom-labibb-regulator.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+#define REG_PERPH_TYPE                  0x04
+#define QCOM_LAB_TYPE			0x24
+#define QCOM_IBB_TYPE			0x20
+
+#define REG_LAB_STATUS1			0x08
+#define REG_LAB_ENABLE_CTL		0x46
+#define LAB_STATUS1_VREG_OK_BIT		BIT(7)
+#define LAB_ENABLE_CTL_EN		BIT(7)
+
+#define REG_IBB_STATUS1			0x08
+#define REG_IBB_ENABLE_CTL		0x46
+#define IBB_STATUS1_VREG_OK_BIT		BIT(7)
+#define IBB_ENABLE_CTL_MASK		(BIT(7) | BIT(6))
+#define IBB_CONTROL_ENABLE		BIT(7)
+
+#define POWER_DELAY			8000
+
+struct lab_regulator {
+	struct regulator_dev		*rdev;
+	int				vreg_enabled;
+};
+
+struct ibb_regulator {
+	struct regulator_dev		*rdev;
+	int				vreg_enabled;
+};
+
+struct qcom_labibb {
+	struct device			*dev;
+	struct regmap			*regmap;
+	u16				lab_base;
+	u16				ibb_base;
+	struct lab_regulator		lab_vreg;
+	struct ibb_regulator		ibb_vreg;
+};
+
+static int qcom_labibb_read(struct qcom_labibb *labibb, u16 address,
+			    u8 *val, int count)
+{
+	int ret;
+
+	ret = regmap_bulk_read(labibb->regmap, address, val, count);
+	if (ret < 0)
+		dev_err(labibb->dev, "spmi read failed ret=%d\n", ret);
+
+	return ret;
+}
+
+static int qcom_labibb_write(struct qcom_labibb *labibb, u16 address,
+			     u8 *val, int count)
+{
+	int ret;
+
+	ret = regmap_bulk_write(labibb->regmap, address, val, count);
+	if (ret < 0)
+		dev_err(labibb->dev, "spmi write failed: ret=%d\n", ret);
+
+	return ret;
+}
+
+static int qcom_labibb_masked_write(struct qcom_labibb *labibb, u16 address,
+				    u8 mask, u8 val)
+{
+	int ret;
+
+	ret = regmap_update_bits(labibb->regmap, address, mask, val);
+	if (ret < 0)
+		dev_err(labibb->dev, "spmi write failed: ret=%d\n", ret);
+
+	return ret;
+}
+
+static int qcom_enable_ibb(struct qcom_labibb *labibb, bool enable)
+{
+	int ret;
+	u8 val = enable ? IBB_CONTROL_ENABLE : 0;
+
+	ret = qcom_labibb_masked_write(labibb,
+				       labibb->ibb_base + REG_IBB_ENABLE_CTL,
+				       IBB_ENABLE_CTL_MASK, val);
+	if (ret < 0)
+		dev_err(labibb->dev, "Unable to configure IBB_ENABLE_CTL ret=%d\n",
+			ret);
+
+	return ret;
+}
+
+static int qcom_lab_regulator_enable(struct regulator_dev *rdev)
+{
+	int ret;
+	u8 val;
+	struct qcom_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	val = LAB_ENABLE_CTL_EN;
+	ret = qcom_labibb_write(labibb,
+				labibb->lab_base + REG_LAB_ENABLE_CTL,
+				&val, 1);
+	if (ret < 0) {
+		dev_err(labibb->dev, "Write register failed ret = %d\n", ret);
+		return ret;
+	}
+
+	/* Wait for a small period before reading REG_LAB_STATUS1 */
+	usleep_range(POWER_DELAY, POWER_DELAY + 100);
+
+	ret = qcom_labibb_read(labibb, labibb->lab_base +
+			       REG_LAB_STATUS1, &val, 1);
+	if (ret < 0) {
+		dev_err(labibb->dev, "Read register failed ret = %d\n", ret);
+		return ret;
+	}
+
+	if (!(val & LAB_STATUS1_VREG_OK_BIT)) {
+		dev_err(labibb->dev, "Can't enable LAB\n");
+		return -EINVAL;
+	}
+
+	labibb->lab_vreg.vreg_enabled = 1;
+
+	return 0;
+}
+
+static int qcom_lab_regulator_disable(struct regulator_dev *rdev)
+{
+	int ret;
+	u8 val = 0;
+	struct qcom_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	ret = qcom_labibb_write(labibb,
+				labibb->lab_base + REG_LAB_ENABLE_CTL,
+				&val, 1);
+	if (ret < 0) {
+		dev_err(labibb->dev, "Write register failed ret = %d\n", ret);
+		return ret;
+	}
+	/* after this delay, lab should get disabled */
+	usleep_range(POWER_DELAY, POWER_DELAY + 100);
+
+	ret = qcom_labibb_read(labibb, labibb->lab_base +
+			       REG_LAB_STATUS1, &val, 1);
+	if (ret < 0) {
+		dev_err(labibb->dev, "Read register failed ret = %d\n", ret);
+		return ret;
+	}
+
+	if (val & LAB_STATUS1_VREG_OK_BIT) {
+		dev_err(labibb->dev, "Can't disable LAB\n");
+		return -EINVAL;
+	}
+
+	labibb->lab_vreg.vreg_enabled = 0;
+
+	return 0;
+}
+
+static int qcom_lab_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	int ret;
+	u8 val;
+	struct qcom_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	ret = qcom_labibb_read(labibb, labibb->lab_base +
+			       REG_LAB_STATUS1, &val, 1);
+	if (ret < 0) {
+		dev_err(labibb->dev, "Read register failed ret = %d\n", ret);
+		return ret;
+	}
+
+	return val & LAB_STATUS1_VREG_OK_BIT;
+}
+
+static struct regulator_ops qcom_lab_ops = {
+	.enable			= qcom_lab_regulator_enable,
+	.disable		= qcom_lab_regulator_disable,
+	.is_enabled		= qcom_lab_regulator_is_enabled,
+};
+
+static const struct regulator_desc lab_desc = {
+	.name = "lab_reg",
+	.ops = &qcom_lab_ops,
+	.type = REGULATOR_VOLTAGE,
+	.owner = THIS_MODULE,
+};
+
+static int qcom_ibb_regulator_enable(struct regulator_dev *rdev)
+{
+	int ret, retries = 10;
+	u8 val;
+	struct qcom_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	ret = qcom_enable_ibb(labibb, 1);
+	if (ret < 0) {
+		dev_err(labibb->dev, "Unable to set IBB mode ret= %d\n", ret);
+		return ret;
+	}
+
+	while (retries--) {
+		/* Wait for a small period before reading IBB_STATUS1 */
+		usleep_range(POWER_DELAY, POWER_DELAY + 100);
+
+		ret = qcom_labibb_read(labibb, labibb->ibb_base +
+				       REG_IBB_STATUS1, &val, 1);
+		if (ret < 0) {
+			dev_err(labibb->dev,
+				"Read register failed ret = %d\n", ret);
+			return ret;
+		}
+
+		if (val & IBB_STATUS1_VREG_OK_BIT) {
+			labibb->ibb_vreg.vreg_enabled = 1;
+			return 0;
+		}
+	}
+
+	dev_err(labibb->dev, "Can't enable IBB\n");
+	return -EINVAL;
+}
+
+static int qcom_ibb_regulator_disable(struct regulator_dev *rdev)
+{
+	int ret, retries = 2;
+	u8 val;
+	struct qcom_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	ret = qcom_enable_ibb(labibb, 0);
+	if (ret < 0) {
+		dev_err(labibb->dev,
+			"Unable to set IBB_MODULE_EN ret = %d\n", ret);
+		return ret;
+	}
+
+	/* poll IBB_STATUS to make sure ibb had been disabled */
+	while (retries--) {
+		usleep_range(POWER_DELAY, POWER_DELAY + 100);
+		ret = qcom_labibb_read(labibb, labibb->ibb_base +
+				       REG_IBB_STATUS1, &val, 1);
+		if (ret < 0) {
+			dev_err(labibb->dev, "Read register failed ret = %d\n",
+				ret);
+			return ret;
+		}
+
+		if (!(val & IBB_STATUS1_VREG_OK_BIT)) {
+			labibb->ibb_vreg.vreg_enabled = 0;
+			return 0;
+		}
+	}
+	dev_err(labibb->dev, "Can't disable IBB\n");
+	return -EINVAL;
+}
+
+static int qcom_ibb_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	int ret;
+	u8 val;
+	struct qcom_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	ret = qcom_labibb_read(labibb, labibb->ibb_base +
+			REG_IBB_STATUS1, &val, 1);
+	if (ret < 0) {
+		dev_err(labibb->dev, "Read register failed ret = %d\n", ret);
+		return ret;
+	}
+
+	return(val & IBB_STATUS1_VREG_OK_BIT);
+}
+
+static struct regulator_ops qcom_ibb_ops = {
+	.enable			= qcom_ibb_regulator_enable,
+	.disable		= qcom_ibb_regulator_disable,
+	.is_enabled		= qcom_ibb_regulator_is_enabled,
+};
+
+static const struct regulator_desc ibb_desc = {
+	.name = "ibb_reg",
+	.ops = &qcom_ibb_ops,
+	.type = REGULATOR_VOLTAGE,
+	.owner = THIS_MODULE,
+};
+
+static int register_lab_regulator(struct qcom_labibb *labibb,
+				  struct device_node *of_node)
+{
+	int ret = 0;
+	struct regulator_init_data *init_data;
+	struct regulator_config cfg = {};
+
+	cfg.dev = labibb->dev;
+	cfg.driver_data = labibb;
+	cfg.of_node = of_node;
+	init_data =
+		of_get_regulator_init_data(labibb->dev,
+					   of_node, &lab_desc);
+	if (!init_data) {
+		dev_err(labibb->dev,
+			"unable to get init data for LAB\n");
+		return -ENOMEM;
+	}
+	cfg.init_data = init_data;
+
+	labibb->lab_vreg.rdev = devm_regulator_register(labibb->dev, &lab_desc,
+							&cfg);
+	if (IS_ERR(labibb->lab_vreg.rdev)) {
+		ret = PTR_ERR(labibb->lab_vreg.rdev);
+		dev_err(labibb->dev,
+			"unable to register LAB regulator");
+		return ret;
+	}
+	return 0;
+}
+
+static int register_ibb_regulator(struct qcom_labibb *labibb,
+				  struct device_node *of_node)
+{
+	int ret;
+	struct regulator_init_data *init_data;
+	struct regulator_config cfg = {};
+
+	cfg.dev = labibb->dev;
+	cfg.driver_data = labibb;
+	cfg.of_node = of_node;
+	init_data =
+		of_get_regulator_init_data(labibb->dev,
+					   of_node, &ibb_desc);
+	if (!init_data) {
+		dev_err(labibb->dev,
+			"unable to get init data for IBB\n");
+		return -ENOMEM;
+	}
+	cfg.init_data = init_data;
+
+	labibb->ibb_vreg.rdev = devm_regulator_register(labibb->dev, &ibb_desc,
+							&cfg);
+	if (IS_ERR(labibb->ibb_vreg.rdev)) {
+		ret = PTR_ERR(labibb->ibb_vreg.rdev);
+		dev_err(labibb->dev,
+			"unable to register IBB regulator");
+		return ret;
+	}
+	return 0;
+}
+
+static int qcom_labibb_regulator_probe(struct platform_device *pdev)
+{
+	struct qcom_labibb *labibb;
+	struct device_node *child;
+	unsigned int base;
+	u8 type;
+	int ret;
+
+	labibb = devm_kzalloc(&pdev->dev, sizeof(*labibb), GFP_KERNEL);
+	if (!labibb)
+		return -ENOMEM;
+
+	labibb->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!labibb->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -ENODEV;
+	}
+
+	labibb->dev = &pdev->dev;
+
+	for_each_available_child_of_node(pdev->dev.of_node, child) {
+		ret = of_property_read_u32(child, "reg", &base);
+		if (ret < 0) {
+			dev_err(&pdev->dev,
+				"Couldn't find reg in node = %s ret = %d\n",
+				child->full_name, ret);
+			return ret;
+		}
+
+		ret = qcom_labibb_read(labibb, base + REG_PERPH_TYPE,
+				       &type, 1);
+		if (ret < 0) {
+			dev_err(labibb->dev,
+				"Peripheral type read failed ret=%d\n",
+				ret);
+		}
+
+		switch (type) {
+		case QCOM_LAB_TYPE:
+			labibb->lab_base = base;
+			ret = register_lab_regulator(labibb, child);
+			if (ret < 0) {
+				dev_err(labibb->dev,
+					"Failed LAB regulator registration");
+				return ret;
+			}
+			break;
+
+		case QCOM_IBB_TYPE:
+			labibb->ibb_base = base;
+			ret = register_ibb_regulator(labibb, child);
+			if (ret < 0) {
+				dev_err(labibb->dev,
+					"Failed IBB regulator registration");
+				return ret;
+			}
+			break;
+
+		default:
+			dev_err(labibb->dev,
+				"qcom_labibb: unknown peripheral type\n");
+			return -EINVAL;
+		}
+	}
+
+	dev_set_drvdata(&pdev->dev, labibb);
+	return 0;
+}
+
+static const struct of_device_id qcom_labibb_match_table[] = {
+	{ .compatible = "qcom,lab-ibb-regulator", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qcom_labibb_match_table);
+
+static struct platform_driver qcom_labibb_regulator_driver = {
+	.driver		= {
+		.name		= "qcom-lab-ibb-regulator",
+		.of_match_table	= qcom_labibb_match_table,
+	},
+	.probe		= qcom_labibb_regulator_probe,
+};
+module_platform_driver(qcom_labibb_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm labibb driver");
+MODULE_LICENSE("GPL v2");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project




[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