Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v4 0/6] mfd/regulator/clk: bd71837: ROHM BD71837 PMIC driver
From: Mark Brown @ 2018-05-30 11:00 UTC (permalink / raw)
  To: Matti Vaittinen
  Cc: Matti Vaittinen, mturquette, sboyd, robh+dt, mark.rutland,
	lee.jones, lgirdwood, linux-clk, devicetree, linux-kernel,
	mikko.mutanen, heikki.haikola
In-Reply-To: <20180530090512.GC13528@localhost.localdomain>

[-- Attachment #1: Type: text/plain, Size: 1908 bytes --]

On Wed, May 30, 2018 at 12:05:12PM +0300, Matti Vaittinen wrote:

> Other 4 can be used on PWM or PFM switching mode. When PWM is used
> voltages can be changed without disabling regulator. On PFM this should
> not be done. These latter 4 regulators can be forced to PWM mode via
> control bit in register. This driver does not support controlling this
> mode though. So this driver version just checks if regulator is enabled
> before changing the voltage and if it is the voltage change fails with
> -EBUSY

It probably should support setting modes (especially if the device isn't
smart enough to automatically shift which sounds like the case) but
that's a separate thing.

> My question is whether it would be good idea to also read the 'force
> PWM' bit when voltage is changed and allow the change if PWM mode is
> forced to be used? Problem is that the check and voltage change can't be
> atomic so there is a chance someone changes the mode (bypassing the
> driver and regulator core) after this check but before we get to modify
> the voltage. Furthermore, I doubt the 'force PWM' is widely used (but I
> can't say for sure as I can't imagine all use cases) as it is not so
> power efficient.

Why would anything else be changing the mode configuration of the
regulator while the system is running?  That sounds like a bad idea.
In any case if the hardware really needs to be manually put into force
PWM to change voltage then the simplest thing would be to just move it
into force PWM mode, do the change, then change back if it wasn't
already in force PWM mode.

The tradeoff with forced PWM mode is that the quality of regulation will
be a lot better, especially if the load changes suddenly (as things like
CPUs often do).  Most hardware that's at all current is able respond to
changes in load and switch modes automatically when it's appropriate,
except possibly in some very low power modes.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Applied "regulator: bd71837: BD71837 PMIC regulator driver" to the regulator tree
From: Mark Brown @ 2018-05-30 11:02 UTC (permalink / raw)
  To: Matti Vaittinen
  Cc: Mark Brown, mturquette, sboyd, robh+dt, mark.rutland, lee.jones,
	lgirdwood
In-Reply-To: <72e644d9855621ea8cfd7ec555e2da02403f21f6.1527669443.git.matti.vaittinen@fi.rohmeurope.com>

The patch

   regulator: bd71837: BD71837 PMIC regulator driver

has been applied to the regulator tree at

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From ba08799e90b5935a3df20766a73b5841046f6832 Mon Sep 17 00:00:00 2001
From: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Date: Wed, 30 May 2018 11:43:43 +0300
Subject: [PATCH] regulator: bd71837: BD71837 PMIC regulator driver

Support for controlling the 8 bucks and 7 LDOs the PMIC contains.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 drivers/regulator/Kconfig             |  11 +
 drivers/regulator/Makefile            |   1 +
 drivers/regulator/bd71837-regulator.c | 640 ++++++++++++++++++++++++++
 3 files changed, 652 insertions(+)
 create mode 100644 drivers/regulator/bd71837-regulator.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 4efae3b7e746..5dbccf5f3037 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -180,6 +180,17 @@ config REGULATOR_BCM590XX
 	  BCM590xx PMUs. This will enable support for the software
 	  controllable LDO/Switching regulators.
 
+config REGULATOR_BD71837
+	tristate "ROHM BD71837 Power Regulator"
+	depends on MFD_BD71837
+	help
+	  This driver supports voltage regulators on ROHM BD71837 PMIC.
+	  This will enable support for the software controllable buck
+	  and LDO regulators.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called bd71837-regulator.
+
 config REGULATOR_BD9571MWV
 	tristate "ROHM BD9571MWV Regulators"
 	depends on MFD_BD9571MWV
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index d81fb02bd6e9..bd818ceb7c72 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
 obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
 obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
 obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
+obj-$(CONFIG_REGULATOR_BD71837) += bd71837-regulator.o
 obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
 obj-$(CONFIG_REGULATOR_DA903X)	+= da903x.o
 obj-$(CONFIG_REGULATOR_DA9052)	+= da9052-regulator.o
diff --git a/drivers/regulator/bd71837-regulator.c b/drivers/regulator/bd71837-regulator.c
new file mode 100644
index 000000000000..6eae4d0432a2
--- /dev/null
+++ b/drivers/regulator/bd71837-regulator.c
@@ -0,0 +1,640 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 ROHM Semiconductors
+// bd71837-regulator.c ROHM BD71837MWV regulator driver
+
+#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/bd71837.h>
+#include <linux/regulator/of_regulator.h>
+
+struct bd71837_pmic {
+	struct regulator_desc descs[BD71837_REGULATOR_CNT];
+	struct bd71837 *mfd;
+	struct platform_device *pdev;
+	struct regulator_dev *rdev[BD71837_REGULATOR_CNT];
+};
+
+/*
+ * BUCK1/2/3/4
+ * BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting
+ * 00: 10.00mV/usec 10mV 1uS
+ * 01: 5.00mV/usec	10mV 2uS
+ * 10: 2.50mV/usec	10mV 4uS
+ * 11: 1.25mV/usec	10mV 8uS
+ */
+static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev,
+					   int ramp_delay)
+{
+	struct bd71837_pmic *pmic = rdev_get_drvdata(rdev);
+	struct bd71837 *mfd = pmic->mfd;
+	int id = rdev->desc->id;
+	unsigned int ramp_value = BUCK_RAMPRATE_10P00MV;
+
+	dev_dbg(&(pmic->pdev->dev), "Buck[%d] Set Ramp = %d\n", id + 1,
+		ramp_delay);
+	switch (ramp_delay) {
+	case 1 ... 1250:
+		ramp_value = BUCK_RAMPRATE_1P25MV;
+		break;
+	case 1251 ... 2500:
+		ramp_value = BUCK_RAMPRATE_2P50MV;
+		break;
+	case 2501 ... 5000:
+		ramp_value = BUCK_RAMPRATE_5P00MV;
+		break;
+	case 5001 ... 10000:
+		ramp_value = BUCK_RAMPRATE_10P00MV;
+		break;
+	default:
+		ramp_value = BUCK_RAMPRATE_10P00MV;
+		dev_err(&pmic->pdev->dev,
+			"%s: ramp_delay: %d not supported, setting 10000mV//us\n",
+			rdev->desc->name, ramp_delay);
+	}
+
+	return regmap_update_bits(mfd->regmap, BD71837_REG_BUCK1_CTRL + id,
+				  BUCK_RAMPRATE_MASK, ramp_value << 6);
+}
+
+/* Bucks 1 to 4 support DVS. PWM mode is used when voltage is changed.
+ * Bucks 5 to 8 and LDOs can use PFM and must be disabled when voltage
+ * is changed. Hence we return -EBUSY for these if voltage is changed
+ * when BUCK/LDO is enabled.
+ */
+static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev,
+						    unsigned int sel)
+{
+	int ret;
+
+	ret = regulator_is_enabled_regmap(rdev);
+	if (!ret)
+		ret = regulator_set_voltage_sel_regmap(rdev, sel);
+	else if (ret == 1)
+		ret = -EBUSY;
+	return ret;
+}
+
+static struct regulator_ops bd71837_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 = bd71837_set_voltage_sel_restricted,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops bd71837_ldo_regulator_nolinear_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.list_voltage = regulator_list_voltage_table,
+	.set_voltage_sel = bd71837_set_voltage_sel_restricted,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops bd71837_buck_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 = bd71837_set_voltage_sel_restricted,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_time_sel = regulator_set_voltage_time_sel,
+};
+
+static struct regulator_ops bd71837_buck_regulator_nolinear_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.list_voltage = regulator_list_voltage_table,
+	.set_voltage_sel = bd71837_set_voltage_sel_restricted,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_time_sel = regulator_set_voltage_time_sel,
+};
+
+static struct regulator_ops bd71837_buck1234_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,
+	.set_voltage_time_sel = regulator_set_voltage_time_sel,
+	.set_ramp_delay = bd71837_buck1234_set_ramp_delay,
+};
+
+/*
+ * BUCK1/2/3/4
+ * 0.70 to 1.30V (10mV step)
+ */
+static const struct regulator_linear_range bd71837_buck1234_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(700000, 0x00, 0x3C, 10000),
+	REGULATOR_LINEAR_RANGE(1300000, 0x3D, 0x3F, 0),
+};
+
+/*
+ * BUCK5
+ * 0.9V to 1.35V ()
+ */
+static const struct regulator_linear_range bd71837_buck5_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(700000, 0x00, 0x03, 100000),
+	REGULATOR_LINEAR_RANGE(1050000, 0x04, 0x05, 50000),
+	REGULATOR_LINEAR_RANGE(1200000, 0x06, 0x07, 150000),
+};
+
+/*
+ * BUCK6
+ * 3.0V to 3.3V (step 100mV)
+ */
+static const struct regulator_linear_range bd71837_buck6_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
+};
+
+/*
+ * BUCK7
+ * 000 = 1.605V
+ * 001 = 1.695V
+ * 010 = 1.755V
+ * 011 = 1.8V (Initial)
+ * 100 = 1.845V
+ * 101 = 1.905V
+ * 110 = 1.95V
+ * 111 = 1.995V
+ */
+static const unsigned int buck_7_volts[] = {
+	1605000, 1695000, 1755000, 1800000, 1845000, 1905000, 1950000, 1995000
+};
+
+/*
+ * BUCK8
+ * 0.8V to 1.40V (step 10mV)
+ */
+static const struct regulator_linear_range bd71837_buck8_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(800000, 0x00, 0x3C, 10000),
+	REGULATOR_LINEAR_RANGE(1400000, 0x3D, 0x3F, 0),
+};
+
+/*
+ * LDO1
+ * 3.0 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo1_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
+};
+
+/*
+ * LDO2
+ * 0.8 or 0.9V
+ */
+const unsigned int ldo_2_volts[] = {
+	900000, 800000
+};
+
+/*
+ * LDO3
+ * 1.8 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo3_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
+};
+
+/*
+ * LDO4
+ * 0.9 to 1.8V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo4_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(900000, 0x00, 0x09, 100000),
+	REGULATOR_LINEAR_RANGE(1800000, 0x0A, 0x0F, 0),
+};
+
+/*
+ * LDO5
+ * 1.8 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo5_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
+};
+
+/*
+ * LDO6
+ * 0.9 to 1.8V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo6_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(900000, 0x00, 0x09, 100000),
+	REGULATOR_LINEAR_RANGE(1800000, 0x0A, 0x0F, 0),
+};
+
+/*
+ * LDO7
+ * 1.8 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo7_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
+};
+
+static const struct regulator_desc bd71837_regulators[] = {
+	{
+		.name = "buck1",
+		.of_match = of_match_ptr("BUCK1"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_BUCK1,
+		.ops = &bd71837_buck1234_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_BUCK1_VOLTAGE_NUM,
+		.linear_ranges = bd71837_buck1234_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+		.vsel_reg = BD71837_REG_BUCK1_VOLT_RUN,
+		.vsel_mask = BUCK1_RUN_MASK,
+		.enable_reg = BD71837_REG_BUCK1_CTRL,
+		.enable_mask = BD71837_BUCK_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "buck2",
+		.of_match = of_match_ptr("BUCK2"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_BUCK2,
+		.ops = &bd71837_buck1234_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_BUCK2_VOLTAGE_NUM,
+		.linear_ranges = bd71837_buck1234_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+		.vsel_reg = BD71837_REG_BUCK2_VOLT_RUN,
+		.vsel_mask = BUCK2_RUN_MASK,
+		.enable_reg = BD71837_REG_BUCK2_CTRL,
+		.enable_mask = BD71837_BUCK_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "buck3",
+		.of_match = of_match_ptr("BUCK3"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_BUCK3,
+		.ops = &bd71837_buck1234_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_BUCK3_VOLTAGE_NUM,
+		.linear_ranges = bd71837_buck1234_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+		.vsel_reg = BD71837_REG_BUCK3_VOLT_RUN,
+		.vsel_mask = BUCK3_RUN_MASK,
+		.enable_reg = BD71837_REG_BUCK3_CTRL,
+		.enable_mask = BD71837_BUCK_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "buck4",
+		.of_match = of_match_ptr("BUCK4"),
+		.regulators_node = of_match_ptr("regulators"),
+			.id = BD71837_BUCK4,
+		.ops = &bd71837_buck1234_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_BUCK4_VOLTAGE_NUM,
+		.linear_ranges = bd71837_buck1234_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+		.vsel_reg = BD71837_REG_BUCK4_VOLT_RUN,
+		.vsel_mask = BUCK4_RUN_MASK,
+		.enable_reg = BD71837_REG_BUCK4_CTRL,
+		.enable_mask = BD71837_BUCK_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "buck5",
+		.of_match = of_match_ptr("BUCK5"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_BUCK5,
+		.ops = &bd71837_buck_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_BUCK5_VOLTAGE_NUM,
+		.linear_ranges = bd71837_buck5_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_buck5_voltage_ranges),
+		.vsel_reg = BD71837_REG_BUCK5_VOLT,
+		.vsel_mask = BUCK5_MASK,
+		.enable_reg = BD71837_REG_BUCK5_CTRL,
+		.enable_mask = BD71837_BUCK_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "buck6",
+		.of_match = of_match_ptr("BUCK6"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_BUCK6,
+		.ops = &bd71837_buck_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_BUCK6_VOLTAGE_NUM,
+		.linear_ranges = bd71837_buck6_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_buck6_voltage_ranges),
+		.vsel_reg = BD71837_REG_BUCK6_VOLT,
+		.vsel_mask = BUCK6_MASK,
+		.enable_reg = BD71837_REG_BUCK6_CTRL,
+		.enable_mask = BD71837_BUCK_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "buck7",
+		.of_match = of_match_ptr("BUCK7"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_BUCK7,
+		.ops = &bd71837_buck_regulator_nolinear_ops,
+		.type = REGULATOR_VOLTAGE,
+		.volt_table = &buck_7_volts[0],
+		.n_voltages = ARRAY_SIZE(buck_7_volts),
+		.vsel_reg = BD71837_REG_BUCK7_VOLT,
+		.vsel_mask = BUCK7_MASK,
+		.enable_reg = BD71837_REG_BUCK7_CTRL,
+		.enable_mask = BD71837_BUCK_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "buck8",
+		.of_match = of_match_ptr("BUCK8"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_BUCK8,
+		.ops = &bd71837_buck_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_BUCK8_VOLTAGE_NUM,
+		.linear_ranges = bd71837_buck8_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_buck8_voltage_ranges),
+		.vsel_reg = BD71837_REG_BUCK8_VOLT,
+		.vsel_mask = BUCK8_MASK,
+		.enable_reg = BD71837_REG_BUCK8_CTRL,
+		.enable_mask = BD71837_BUCK_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "ldo1",
+		.of_match = of_match_ptr("LDO1"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_LDO1,
+		.ops = &bd71837_ldo_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_LDO1_VOLTAGE_NUM,
+		.linear_ranges = bd71837_ldo1_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo1_voltage_ranges),
+		.vsel_reg = BD71837_REG_LDO1_VOLT,
+		.vsel_mask = LDO1_MASK,
+		.enable_reg = BD71837_REG_LDO1_VOLT,
+		.enable_mask = BD71837_LDO_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "ldo2",
+		.of_match = of_match_ptr("LDO2"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_LDO2,
+		.ops = &bd71837_ldo_regulator_nolinear_ops,
+		.type = REGULATOR_VOLTAGE,
+		.volt_table = &ldo_2_volts[0],
+		.vsel_reg = BD71837_REG_LDO2_VOLT,
+		.vsel_mask = LDO2_MASK,
+		.n_voltages = ARRAY_SIZE(ldo_2_volts),
+		.n_voltages = BD71837_LDO2_VOLTAGE_NUM,
+		.enable_reg = BD71837_REG_LDO2_VOLT,
+		.enable_mask = BD71837_LDO_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "ldo3",
+		.of_match = of_match_ptr("LDO3"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_LDO3,
+		.ops = &bd71837_ldo_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_LDO3_VOLTAGE_NUM,
+		.linear_ranges = bd71837_ldo3_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo3_voltage_ranges),
+		.vsel_reg = BD71837_REG_LDO3_VOLT,
+		.vsel_mask = LDO3_MASK,
+		.enable_reg = BD71837_REG_LDO3_VOLT,
+		.enable_mask = BD71837_LDO_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "ldo4",
+		.of_match = of_match_ptr("LDO4"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_LDO4,
+		.ops = &bd71837_ldo_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_LDO4_VOLTAGE_NUM,
+		.linear_ranges = bd71837_ldo4_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo4_voltage_ranges),
+		.vsel_reg = BD71837_REG_LDO4_VOLT,
+		.vsel_mask = LDO4_MASK,
+		.enable_reg = BD71837_REG_LDO4_VOLT,
+		.enable_mask = BD71837_LDO_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "ldo5",
+		.of_match = of_match_ptr("LDO5"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_LDO5,
+		.ops = &bd71837_ldo_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_LDO5_VOLTAGE_NUM,
+		.linear_ranges = bd71837_ldo5_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo5_voltage_ranges),
+		/* LDO5 is supplied by buck6 */
+		.supply_name = "buck6",
+		.vsel_reg = BD71837_REG_LDO5_VOLT,
+		.vsel_mask = LDO5_MASK,
+		.enable_reg = BD71837_REG_LDO5_VOLT,
+		.enable_mask = BD71837_LDO_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "ldo6",
+		.of_match = of_match_ptr("LDO6"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_LDO6,
+		.ops = &bd71837_ldo_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_LDO6_VOLTAGE_NUM,
+		.linear_ranges = bd71837_ldo6_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo6_voltage_ranges),
+		/* LDO6 is supplied by buck7 */
+		.supply_name = "buck7",
+		.vsel_reg = BD71837_REG_LDO6_VOLT,
+		.vsel_mask = LDO6_MASK,
+		.enable_reg = BD71837_REG_LDO6_VOLT,
+		.enable_mask = BD71837_LDO_EN,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "ldo7",
+		.of_match = of_match_ptr("LDO7"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = BD71837_LDO7,
+		.ops = &bd71837_ldo_regulator_ops,
+		.type = REGULATOR_VOLTAGE,
+		.n_voltages = BD71837_LDO7_VOLTAGE_NUM,
+		.linear_ranges = bd71837_ldo7_voltage_ranges,
+		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo7_voltage_ranges),
+		.vsel_reg = BD71837_REG_LDO7_VOLT,
+		.vsel_mask = LDO7_MASK,
+		.enable_reg = BD71837_REG_LDO7_VOLT,
+		.enable_mask = BD71837_LDO_EN,
+		.owner = THIS_MODULE,
+	},
+};
+
+struct reg_init {
+	unsigned int reg;
+	unsigned int mask;
+};
+
+static int bd71837_probe(struct platform_device *pdev)
+{
+	struct bd71837_pmic *pmic;
+	struct bd71837_board *pdata;
+	struct regulator_config config = { 0 };
+	struct reg_init pmic_regulator_inits[] = {
+		{
+			.reg = BD71837_REG_BUCK1_CTRL,
+			.mask = BD71837_BUCK_SEL,
+		}, {
+			.reg = BD71837_REG_BUCK2_CTRL,
+			.mask = BD71837_BUCK_SEL,
+		}, {
+			.reg = BD71837_REG_BUCK3_CTRL,
+			.mask = BD71837_BUCK_SEL,
+		}, {
+			.reg = BD71837_REG_BUCK4_CTRL,
+			.mask = BD71837_BUCK_SEL,
+		}, {
+			.reg = BD71837_REG_BUCK5_CTRL,
+			.mask = BD71837_BUCK_SEL,
+		}, {
+			.reg = BD71837_REG_BUCK6_CTRL,
+			.mask = BD71837_BUCK_SEL,
+		}, {
+			.reg = BD71837_REG_BUCK7_CTRL,
+			.mask = BD71837_BUCK_SEL,
+		}, {
+			.reg = BD71837_REG_BUCK8_CTRL,
+			.mask = BD71837_BUCK_SEL,
+		}, {
+			.reg = BD71837_REG_LDO1_VOLT,
+			.mask = BD71837_LDO_SEL,
+		}, {
+			.reg = BD71837_REG_LDO2_VOLT,
+			.mask = BD71837_LDO_SEL,
+		}, {
+			.reg = BD71837_REG_LDO3_VOLT,
+			.mask = BD71837_LDO_SEL,
+		}, {
+			.reg = BD71837_REG_LDO4_VOLT,
+			.mask = BD71837_LDO_SEL,
+		}, {
+			.reg = BD71837_REG_LDO5_VOLT,
+			.mask = BD71837_LDO_SEL,
+		}, {
+			.reg = BD71837_REG_LDO6_VOLT,
+			.mask = BD71837_LDO_SEL,
+		}, {
+			.reg = BD71837_REG_LDO7_VOLT,
+			.mask = BD71837_LDO_SEL,
+		}
+	};
+
+	int i, err;
+
+	pmic = devm_kzalloc(&pdev->dev, sizeof(struct bd71837_pmic),
+			    GFP_KERNEL);
+	if (!pmic)
+		return -ENOMEM;
+
+	memcpy(pmic->descs, bd71837_regulators, sizeof(pmic->descs));
+
+	pmic->pdev = pdev;
+	pmic->mfd = dev_get_drvdata(pdev->dev.parent);
+
+	if (!pmic->mfd) {
+		dev_err(&pdev->dev, "No MFD driver data\n");
+		err = -EINVAL;
+		goto err;
+	}
+	platform_set_drvdata(pdev, pmic);
+	pdata = dev_get_platdata(pmic->mfd->dev);
+
+	/* Register LOCK release */
+	err = regmap_update_bits(pmic->mfd->regmap, BD71837_REG_REGLOCK,
+				 (REGLOCK_PWRSEQ | REGLOCK_VREG), 0);
+	if (err) {
+		dev_err(&pmic->pdev->dev, "Failed to unlock PMIC (%d)\n", err);
+		goto err;
+	} else {
+		dev_dbg(&pmic->pdev->dev, "%s: Unlocked lock register 0x%x\n",
+			__func__, BD71837_REG_REGLOCK);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pmic_regulator_inits); i++) {
+
+		struct regulator_desc *desc;
+		struct regulator_dev *rdev;
+
+		desc = &pmic->descs[i];
+
+		if (pdata)
+			config.init_data = pdata->init_data[i];
+
+		config.dev = pdev->dev.parent;
+		config.driver_data = pmic;
+		config.regmap = pmic->mfd->regmap;
+
+		rdev = devm_regulator_register(&pdev->dev, desc, &config);
+		if (IS_ERR(rdev)) {
+			dev_err(pmic->mfd->dev,
+				"failed to register %s regulator\n",
+				desc->name);
+			err = PTR_ERR(rdev);
+			goto err;
+		}
+		/* Regulator register gets the regulator constraints and
+		 * applies them (set_machine_constraints). This should have
+		 * turned the control register(s) to correct values and we
+		 * can now switch the control from PMIC state machine to the
+		 * register interface
+		 */
+		err = regmap_update_bits(pmic->mfd->regmap,
+					 pmic_regulator_inits[i].reg,
+					 pmic_regulator_inits[i].mask,
+					 0xFFFFFFFF);
+		if (err) {
+			dev_err(&pmic->pdev->dev,
+				"Failed to write BUCK/LDO SEL bit for (%s)\n",
+				desc->name);
+			goto err;
+		}
+
+		pmic->rdev[i] = rdev;
+	}
+
+	return 0;
+
+err:
+	return err;
+}
+
+static struct platform_driver bd71837_regulator = {
+	.driver = {
+		.name = "bd71837-pmic",
+		.owner = THIS_MODULE,
+	},
+	.probe = bd71837_probe,
+};
+
+module_platform_driver(bd71837_regulator);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD71837 voltage regulator driver");
+MODULE_LICENSE("GPL");
-- 
2.17.0

^ permalink raw reply related

* Applied "regulator: bd71837: Devicetree bindings for BD71837 regulators" to the regulator tree
From: Mark Brown @ 2018-05-30 11:02 UTC (permalink / raw)
  To: Matti Vaittinen
  Cc: Mark Brown, mturquette, sboyd, robh+dt, mark.rutland, lee.jones,
	lgirdwood
In-Reply-To: <20180528090101.GD8778@localhost.localdomain>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 5681 bytes --]

The patch

   regulator: bd71837: Devicetree bindings for BD71837 regulators

has been applied to the regulator tree at

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 196c813570afd0d52d453ef3b77c4c15ca760327 Mon Sep 17 00:00:00 2001
From: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Date: Wed, 30 May 2018 11:42:32 +0300
Subject: [PATCH] regulator: bd71837: Devicetree bindings for BD71837
 regulators

Document devicetree bindings for ROHM BD71837 PMIC regulators.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 .../regulator/rohm,bd71837-regulator.txt      | 126 ++++++++++++++++++
 1 file changed, 126 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.txt

diff --git a/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.txt b/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.txt
new file mode 100644
index 000000000000..4edf3137d9f7
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.txt
@@ -0,0 +1,126 @@
+ROHM BD71837 Power Management Integrated Circuit (PMIC) regulator bindings
+
+BD71837MWV is a programmable Power Management
+IC (PMIC) for powering single-core, dual-core, and
+quad-core SoC’s such as NXP-i.MX 8M. It is optimized
+for low BOM cost and compact solution footprint. It
+integrates 8 Buck regulators and 7 LDO’s to provide all
+the power rails required by the SoC and the commonly
+used peripherals.
+
+Required properties:
+ - regulator-name: should be "buck1", ..., "buck8" and "ldo1", ..., "ldo7"
+
+List of regulators provided by this controller. BD71837 regulators node
+should be sub node of the BD71837 MFD node. See BD71837 MFD bindings at
+Documentation/devicetree/bindings/mfd/rohm,bd71837-pmic.txt
+Regulator nodes should be named to BUCK_<number> and LDO_<number>. The
+definition for each of these nodes is defined using the standard
+binding for regulators at
+Documentation/devicetree/bindings/regulator/regulator.txt.
+Note that if BD71837 starts at RUN state you probably want to use
+regulator-boot-on at least for BUCK6 and BUCK7 so that those are not
+disabled by driver at startup. LDO5 and LDO6 are supplied by those and
+if they are disabled at startup the voltage monitoring for LDO5/LDO6 will
+cause PMIC to reset.
+
+The valid names for regulator nodes are:
+BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6, BUCK7, BUCK8
+LDO1, LDO2, LDO3, LDO4, LDO5, LDO6, LDO7
+
+Optional properties:
+- Any optional property defined in bindings/regulator/regulator.txt
+
+Example:
+regulators {
+	buck1: BUCK1 {
+		regulator-name = "buck1";
+		regulator-min-microvolt = <700000>;
+		regulator-max-microvolt = <1300000>;
+		regulator-boot-on;
+		regulator-ramp-delay = <1250>;
+	};
+	buck2: BUCK2 {
+		regulator-name = "buck2";
+		regulator-min-microvolt = <700000>;
+		regulator-max-microvolt = <1300000>;
+		regulator-boot-on;
+		regulator-always-on;
+		regulator-ramp-delay = <1250>;
+	};
+	buck3: BUCK3 {
+		regulator-name = "buck3";
+		regulator-min-microvolt = <700000>;
+		regulator-max-microvolt = <1300000>;
+		regulator-boot-on;
+	};
+	buck4: BUCK4 {
+		regulator-name = "buck4";
+		regulator-min-microvolt = <700000>;
+		regulator-max-microvolt = <1300000>;
+		regulator-boot-on;
+	};
+	buck5: BUCK5 {
+		regulator-name = "buck5";
+		regulator-min-microvolt = <700000>;
+		regulator-max-microvolt = <1350000>;
+		regulator-boot-on;
+	};
+	buck6: BUCK6 {
+		regulator-name = "buck6";
+		regulator-min-microvolt = <3000000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+	};
+	buck7: BUCK7 {
+		regulator-name = "buck7";
+		regulator-min-microvolt = <1605000>;
+		regulator-max-microvolt = <1995000>;
+		regulator-boot-on;
+	};
+	buck8: BUCK8 {
+		regulator-name = "buck8";
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <1400000>;
+	};
+
+	ldo1: LDO1 {
+		regulator-name = "ldo1";
+		regulator-min-microvolt = <3000000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+	};
+	ldo2: LDO2 {
+		regulator-name = "ldo2";
+		regulator-min-microvolt = <900000>;
+		regulator-max-microvolt = <900000>;
+		regulator-boot-on;
+	};
+	ldo3: LDO3 {
+		regulator-name = "ldo3";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+	};
+	ldo4: LDO4 {
+		regulator-name = "ldo4";
+		regulator-min-microvolt = <900000>;
+		regulator-max-microvolt = <1800000>;
+	};
+	ldo5: LDO5 {
+		regulator-name = "ldo5";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+	};
+	ldo6: LDO6 {
+		regulator-name = "ldo6";
+		regulator-min-microvolt = <900000>;
+		regulator-max-microvolt = <1800000>;
+	};
+	ldo7_reg: LDO7 {
+		regulator-name = "ldo7";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+	};
+};
+
+
-- 
2.17.0

^ permalink raw reply related

* [PATCH] arm64: dts: msm8996: Use dwc3-qcom glue driver for USB
From: Manu Gautam @ 2018-05-30 11:04 UTC (permalink / raw)
  To: andy.gross
  Cc: vivek.gautam, linux-soc, devicetree, linux-arm-msm, Manu Gautam

Move from dwc3-of-simple to dwc3-qcom glue driver to
support peripheral mode which requires qscratch wrapper
programming on VBUS event.

Signed-off-by: Manu Gautam <mgautam@codeaurora.org>
---
 arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi | 2 ++
 arch/arm64/boot/dts/qcom/msm8996.dtsi        | 6 ++++--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi
index f45a0ab30d30..83bc1b9ff6ef 100644
--- a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi
@@ -108,6 +108,7 @@
 
 		usb@6a00000 {
 			status = "okay";
+			extcon = <&usb3_id>;
 
 			dwc3@6a00000 {
 				extcon = <&usb3_id>;
@@ -124,6 +125,7 @@
 
 		usb@7600000 {
 			status = "okay";
+			extcon = <&usb2_id>;
 
 			dwc3@7600000 {
 				extcon = <&usb2_id>;
diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi
index 26292027ba9b..d30516c0db87 100644
--- a/arch/arm64/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi
@@ -777,7 +777,8 @@
 		};
 
 		usb2: usb@7600000 {
-			compatible = "qcom,dwc3";
+			compatible = "qcom,msm8996-dwc3", "qcom,dwc3";
+			reg = <0x76f8800 0x400>;
 			#address-cells = <1>;
 			#size-cells = <1>;
 			ranges;
@@ -805,7 +806,8 @@
 		};
 
 		usb3: usb@6a00000 {
-			compatible = "qcom,dwc3";
+			compatible = "qcom,msm8996-dwc3", "qcom,dwc3";
+			reg = <0x6af8800 0x400>;
 			#address-cells = <1>;
 			#size-cells = <1>;
 			ranges;
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


^ permalink raw reply related

* Re: [PATCH v14 0/2] Kryo CPU scaling driver
From: Ilia Lin @ 2018-05-30 11:06 UTC (permalink / raw)
  To: Viresh Kumar, Rafael J. Wysocki
  Cc: Viresh Kumar, Nishanth Menon, Stephen Boyd, Rob Herring,
	Mark Rutland, Rafael J. Wysocki, Linux PM,
	devicetree@vger.kernel.org, Linux Kernel Mailing List
In-Reply-To: <20180530091535.bzvhg4bvn32mqwgq@vireshk-i7>

Just like Viresh said, this is fixed in the v15.

On May 30, 2018 12:15:35 PM GMT+03:00, Viresh Kumar <viresh.kumar@linaro.org> wrote:
>On 30-05-18, 10:08, Rafael J. Wysocki wrote:
>> On Fri, May 25, 2018 at 2:07 PM, Ilia Lin <ilialin@codeaurora.org>
>wrote:
>> > [v14]
>> >  * Addressed comment from Sudeep about DT compatible
>> >  * Added MAINTAINERS entry
>> 
>> This causes a build issue to occur in my bleeding-edge branch.
>> 
>> Does it depend on anything new in arm-soc?
>
>He already sent a v15 and the changelog suggest he fixed that build
>issue.

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

^ permalink raw reply

* Re: Applied "regulator: bd71837: BD71837 PMIC regulator driver" to the regulator tree
From: Matti Vaittinen @ 2018-05-30 11:14 UTC (permalink / raw)
  To: Mark Brown
  Cc: Matti Vaittinen, mturquette, sboyd, robh+dt, mark.rutland,
	lee.jones, lgirdwood, mazziesaccount, linux-clk, devicetree,
	linux-kernel, mikko.mutanen, heikki.haikola
In-Reply-To: <E1fNysa-0006jU-41@debutante>

Hello All,

On Wed, May 30, 2018 at 12:02:56PM +0100, Mark Brown wrote:
> The patch
> 
>    regulator: bd71837: BD71837 PMIC regulator driver
> 
> has been applied to the regulator tree at
> 
>    https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git 

Does this mean this single patch was applied? I am sorry if I did not
follow correct policy/way of informing the dependencies - but there is a
dependency. The patch 1/6 contains the header file
include/linux/mfd/bd71837.h with bunch of definitions this patch is
requiring.

So If Mark can apply all of these patches (or at least the MFD parts
which is patch 1/6) to his tree - then we are Ok. But if the MFD needs
to go via Lee's tree - then I need some process guidance in order to
understand how this goes and what is expected from me =)

Br,
	Matti Vaittinen


> 
> All being well this means that it will be integrated into the linux-next
> tree (usually sometime in the next 24 hours) and sent to Linus during
> the next merge window (or sooner if it is a bug fix), however if
> problems are discovered then the patch may be dropped or reverted.  
> 
> You may get further e-mails resulting from automated or manual testing
> and review of the tree, please engage with people reporting problems and
> send followup patches addressing any issues that are reported if needed.
> 
> If any updates are required or you are submitting further changes they
> should be sent as incremental updates against current git, existing
> patches will not be replaced.
> 
> Please add any relevant lists and maintainers to the CCs when replying
> to this mail.
> 
> Thanks,
> Mark
> 
> From ba08799e90b5935a3df20766a73b5841046f6832 Mon Sep 17 00:00:00 2001
> From: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> Date: Wed, 30 May 2018 11:43:43 +0300
> Subject: [PATCH] regulator: bd71837: BD71837 PMIC regulator driver
> 
> Support for controlling the 8 bucks and 7 LDOs the PMIC contains.
> 
> Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
>  drivers/regulator/Kconfig             |  11 +
>  drivers/regulator/Makefile            |   1 +
>  drivers/regulator/bd71837-regulator.c | 640 ++++++++++++++++++++++++++
>  3 files changed, 652 insertions(+)
>  create mode 100644 drivers/regulator/bd71837-regulator.c
> 
> diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
> index 4efae3b7e746..5dbccf5f3037 100644
> --- a/drivers/regulator/Kconfig
> +++ b/drivers/regulator/Kconfig
> @@ -180,6 +180,17 @@ config REGULATOR_BCM590XX
>  	  BCM590xx PMUs. This will enable support for the software
>  	  controllable LDO/Switching regulators.
>  
> +config REGULATOR_BD71837
> +	tristate "ROHM BD71837 Power Regulator"
> +	depends on MFD_BD71837
> +	help
> +	  This driver supports voltage regulators on ROHM BD71837 PMIC.
> +	  This will enable support for the software controllable buck
> +	  and LDO regulators.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called bd71837-regulator.
> +
>  config REGULATOR_BD9571MWV
>  	tristate "ROHM BD9571MWV Regulators"
>  	depends on MFD_BD9571MWV
> diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
> index d81fb02bd6e9..bd818ceb7c72 100644
> --- a/drivers/regulator/Makefile
> +++ b/drivers/regulator/Makefile
> @@ -27,6 +27,7 @@ obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
>  obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
>  obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
>  obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
> +obj-$(CONFIG_REGULATOR_BD71837) += bd71837-regulator.o
>  obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
>  obj-$(CONFIG_REGULATOR_DA903X)	+= da903x.o
>  obj-$(CONFIG_REGULATOR_DA9052)	+= da9052-regulator.o
> diff --git a/drivers/regulator/bd71837-regulator.c b/drivers/regulator/bd71837-regulator.c
> new file mode 100644
> index 000000000000..6eae4d0432a2
> --- /dev/null
> +++ b/drivers/regulator/bd71837-regulator.c
> @@ -0,0 +1,640 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (C) 2018 ROHM Semiconductors
> +// bd71837-regulator.c ROHM BD71837MWV regulator driver
> +
> +#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/bd71837.h>
> +#include <linux/regulator/of_regulator.h>
> +
> +struct bd71837_pmic {
> +	struct regulator_desc descs[BD71837_REGULATOR_CNT];
> +	struct bd71837 *mfd;
> +	struct platform_device *pdev;
> +	struct regulator_dev *rdev[BD71837_REGULATOR_CNT];
> +};
> +
> +/*
> + * BUCK1/2/3/4
> + * BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting
> + * 00: 10.00mV/usec 10mV 1uS
> + * 01: 5.00mV/usec	10mV 2uS
> + * 10: 2.50mV/usec	10mV 4uS
> + * 11: 1.25mV/usec	10mV 8uS
> + */
> +static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev,
> +					   int ramp_delay)
> +{
> +	struct bd71837_pmic *pmic = rdev_get_drvdata(rdev);
> +	struct bd71837 *mfd = pmic->mfd;
> +	int id = rdev->desc->id;
> +	unsigned int ramp_value = BUCK_RAMPRATE_10P00MV;
> +
> +	dev_dbg(&(pmic->pdev->dev), "Buck[%d] Set Ramp = %d\n", id + 1,
> +		ramp_delay);
> +	switch (ramp_delay) {
> +	case 1 ... 1250:
> +		ramp_value = BUCK_RAMPRATE_1P25MV;
> +		break;
> +	case 1251 ... 2500:
> +		ramp_value = BUCK_RAMPRATE_2P50MV;
> +		break;
> +	case 2501 ... 5000:
> +		ramp_value = BUCK_RAMPRATE_5P00MV;
> +		break;
> +	case 5001 ... 10000:
> +		ramp_value = BUCK_RAMPRATE_10P00MV;
> +		break;
> +	default:
> +		ramp_value = BUCK_RAMPRATE_10P00MV;
> +		dev_err(&pmic->pdev->dev,
> +			"%s: ramp_delay: %d not supported, setting 10000mV//us\n",
> +			rdev->desc->name, ramp_delay);
> +	}
> +
> +	return regmap_update_bits(mfd->regmap, BD71837_REG_BUCK1_CTRL + id,
> +				  BUCK_RAMPRATE_MASK, ramp_value << 6);
> +}
> +
> +/* Bucks 1 to 4 support DVS. PWM mode is used when voltage is changed.
> + * Bucks 5 to 8 and LDOs can use PFM and must be disabled when voltage
> + * is changed. Hence we return -EBUSY for these if voltage is changed
> + * when BUCK/LDO is enabled.
> + */
> +static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev,
> +						    unsigned int sel)
> +{
> +	int ret;
> +
> +	ret = regulator_is_enabled_regmap(rdev);
> +	if (!ret)
> +		ret = regulator_set_voltage_sel_regmap(rdev, sel);
> +	else if (ret == 1)
> +		ret = -EBUSY;
> +	return ret;
> +}
> +
> +static struct regulator_ops bd71837_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 = bd71837_set_voltage_sel_restricted,
> +	.get_voltage_sel = regulator_get_voltage_sel_regmap,
> +};
> +
> +static struct regulator_ops bd71837_ldo_regulator_nolinear_ops = {
> +	.enable = regulator_enable_regmap,
> +	.disable = regulator_disable_regmap,
> +	.is_enabled = regulator_is_enabled_regmap,
> +	.list_voltage = regulator_list_voltage_table,
> +	.set_voltage_sel = bd71837_set_voltage_sel_restricted,
> +	.get_voltage_sel = regulator_get_voltage_sel_regmap,
> +};
> +
> +static struct regulator_ops bd71837_buck_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 = bd71837_set_voltage_sel_restricted,
> +	.get_voltage_sel = regulator_get_voltage_sel_regmap,
> +	.set_voltage_time_sel = regulator_set_voltage_time_sel,
> +};
> +
> +static struct regulator_ops bd71837_buck_regulator_nolinear_ops = {
> +	.enable = regulator_enable_regmap,
> +	.disable = regulator_disable_regmap,
> +	.is_enabled = regulator_is_enabled_regmap,
> +	.list_voltage = regulator_list_voltage_table,
> +	.set_voltage_sel = bd71837_set_voltage_sel_restricted,
> +	.get_voltage_sel = regulator_get_voltage_sel_regmap,
> +	.set_voltage_time_sel = regulator_set_voltage_time_sel,
> +};
> +
> +static struct regulator_ops bd71837_buck1234_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,
> +	.set_voltage_time_sel = regulator_set_voltage_time_sel,
> +	.set_ramp_delay = bd71837_buck1234_set_ramp_delay,
> +};
> +
> +/*
> + * BUCK1/2/3/4
> + * 0.70 to 1.30V (10mV step)
> + */
> +static const struct regulator_linear_range bd71837_buck1234_voltage_ranges[] = {
> +	REGULATOR_LINEAR_RANGE(700000, 0x00, 0x3C, 10000),
> +	REGULATOR_LINEAR_RANGE(1300000, 0x3D, 0x3F, 0),
> +};
> +
> +/*
> + * BUCK5
> + * 0.9V to 1.35V ()
> + */
> +static const struct regulator_linear_range bd71837_buck5_voltage_ranges[] = {
> +	REGULATOR_LINEAR_RANGE(700000, 0x00, 0x03, 100000),
> +	REGULATOR_LINEAR_RANGE(1050000, 0x04, 0x05, 50000),
> +	REGULATOR_LINEAR_RANGE(1200000, 0x06, 0x07, 150000),
> +};
> +
> +/*
> + * BUCK6
> + * 3.0V to 3.3V (step 100mV)
> + */
> +static const struct regulator_linear_range bd71837_buck6_voltage_ranges[] = {
> +	REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
> +};
> +
> +/*
> + * BUCK7
> + * 000 = 1.605V
> + * 001 = 1.695V
> + * 010 = 1.755V
> + * 011 = 1.8V (Initial)
> + * 100 = 1.845V
> + * 101 = 1.905V
> + * 110 = 1.95V
> + * 111 = 1.995V
> + */
> +static const unsigned int buck_7_volts[] = {
> +	1605000, 1695000, 1755000, 1800000, 1845000, 1905000, 1950000, 1995000
> +};
> +
> +/*
> + * BUCK8
> + * 0.8V to 1.40V (step 10mV)
> + */
> +static const struct regulator_linear_range bd71837_buck8_voltage_ranges[] = {
> +	REGULATOR_LINEAR_RANGE(800000, 0x00, 0x3C, 10000),
> +	REGULATOR_LINEAR_RANGE(1400000, 0x3D, 0x3F, 0),
> +};
> +
> +/*
> + * LDO1
> + * 3.0 to 3.3V (100mV step)
> + */
> +static const struct regulator_linear_range bd71837_ldo1_voltage_ranges[] = {
> +	REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
> +};
> +
> +/*
> + * LDO2
> + * 0.8 or 0.9V
> + */
> +const unsigned int ldo_2_volts[] = {
> +	900000, 800000
> +};
> +
> +/*
> + * LDO3
> + * 1.8 to 3.3V (100mV step)
> + */
> +static const struct regulator_linear_range bd71837_ldo3_voltage_ranges[] = {
> +	REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
> +};
> +
> +/*
> + * LDO4
> + * 0.9 to 1.8V (100mV step)
> + */
> +static const struct regulator_linear_range bd71837_ldo4_voltage_ranges[] = {
> +	REGULATOR_LINEAR_RANGE(900000, 0x00, 0x09, 100000),
> +	REGULATOR_LINEAR_RANGE(1800000, 0x0A, 0x0F, 0),
> +};
> +
> +/*
> + * LDO5
> + * 1.8 to 3.3V (100mV step)
> + */
> +static const struct regulator_linear_range bd71837_ldo5_voltage_ranges[] = {
> +	REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
> +};
> +
> +/*
> + * LDO6
> + * 0.9 to 1.8V (100mV step)
> + */
> +static const struct regulator_linear_range bd71837_ldo6_voltage_ranges[] = {
> +	REGULATOR_LINEAR_RANGE(900000, 0x00, 0x09, 100000),
> +	REGULATOR_LINEAR_RANGE(1800000, 0x0A, 0x0F, 0),
> +};
> +
> +/*
> + * LDO7
> + * 1.8 to 3.3V (100mV step)
> + */
> +static const struct regulator_linear_range bd71837_ldo7_voltage_ranges[] = {
> +	REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
> +};
> +
> +static const struct regulator_desc bd71837_regulators[] = {
> +	{
> +		.name = "buck1",
> +		.of_match = of_match_ptr("BUCK1"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_BUCK1,
> +		.ops = &bd71837_buck1234_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_BUCK1_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_buck1234_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
> +		.vsel_reg = BD71837_REG_BUCK1_VOLT_RUN,
> +		.vsel_mask = BUCK1_RUN_MASK,
> +		.enable_reg = BD71837_REG_BUCK1_CTRL,
> +		.enable_mask = BD71837_BUCK_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "buck2",
> +		.of_match = of_match_ptr("BUCK2"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_BUCK2,
> +		.ops = &bd71837_buck1234_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_BUCK2_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_buck1234_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
> +		.vsel_reg = BD71837_REG_BUCK2_VOLT_RUN,
> +		.vsel_mask = BUCK2_RUN_MASK,
> +		.enable_reg = BD71837_REG_BUCK2_CTRL,
> +		.enable_mask = BD71837_BUCK_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "buck3",
> +		.of_match = of_match_ptr("BUCK3"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_BUCK3,
> +		.ops = &bd71837_buck1234_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_BUCK3_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_buck1234_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
> +		.vsel_reg = BD71837_REG_BUCK3_VOLT_RUN,
> +		.vsel_mask = BUCK3_RUN_MASK,
> +		.enable_reg = BD71837_REG_BUCK3_CTRL,
> +		.enable_mask = BD71837_BUCK_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "buck4",
> +		.of_match = of_match_ptr("BUCK4"),
> +		.regulators_node = of_match_ptr("regulators"),
> +			.id = BD71837_BUCK4,
> +		.ops = &bd71837_buck1234_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_BUCK4_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_buck1234_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
> +		.vsel_reg = BD71837_REG_BUCK4_VOLT_RUN,
> +		.vsel_mask = BUCK4_RUN_MASK,
> +		.enable_reg = BD71837_REG_BUCK4_CTRL,
> +		.enable_mask = BD71837_BUCK_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "buck5",
> +		.of_match = of_match_ptr("BUCK5"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_BUCK5,
> +		.ops = &bd71837_buck_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_BUCK5_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_buck5_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_buck5_voltage_ranges),
> +		.vsel_reg = BD71837_REG_BUCK5_VOLT,
> +		.vsel_mask = BUCK5_MASK,
> +		.enable_reg = BD71837_REG_BUCK5_CTRL,
> +		.enable_mask = BD71837_BUCK_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "buck6",
> +		.of_match = of_match_ptr("BUCK6"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_BUCK6,
> +		.ops = &bd71837_buck_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_BUCK6_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_buck6_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_buck6_voltage_ranges),
> +		.vsel_reg = BD71837_REG_BUCK6_VOLT,
> +		.vsel_mask = BUCK6_MASK,
> +		.enable_reg = BD71837_REG_BUCK6_CTRL,
> +		.enable_mask = BD71837_BUCK_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "buck7",
> +		.of_match = of_match_ptr("BUCK7"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_BUCK7,
> +		.ops = &bd71837_buck_regulator_nolinear_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.volt_table = &buck_7_volts[0],
> +		.n_voltages = ARRAY_SIZE(buck_7_volts),
> +		.vsel_reg = BD71837_REG_BUCK7_VOLT,
> +		.vsel_mask = BUCK7_MASK,
> +		.enable_reg = BD71837_REG_BUCK7_CTRL,
> +		.enable_mask = BD71837_BUCK_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "buck8",
> +		.of_match = of_match_ptr("BUCK8"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_BUCK8,
> +		.ops = &bd71837_buck_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_BUCK8_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_buck8_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_buck8_voltage_ranges),
> +		.vsel_reg = BD71837_REG_BUCK8_VOLT,
> +		.vsel_mask = BUCK8_MASK,
> +		.enable_reg = BD71837_REG_BUCK8_CTRL,
> +		.enable_mask = BD71837_BUCK_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "ldo1",
> +		.of_match = of_match_ptr("LDO1"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_LDO1,
> +		.ops = &bd71837_ldo_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_LDO1_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_ldo1_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo1_voltage_ranges),
> +		.vsel_reg = BD71837_REG_LDO1_VOLT,
> +		.vsel_mask = LDO1_MASK,
> +		.enable_reg = BD71837_REG_LDO1_VOLT,
> +		.enable_mask = BD71837_LDO_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "ldo2",
> +		.of_match = of_match_ptr("LDO2"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_LDO2,
> +		.ops = &bd71837_ldo_regulator_nolinear_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.volt_table = &ldo_2_volts[0],
> +		.vsel_reg = BD71837_REG_LDO2_VOLT,
> +		.vsel_mask = LDO2_MASK,
> +		.n_voltages = ARRAY_SIZE(ldo_2_volts),
> +		.n_voltages = BD71837_LDO2_VOLTAGE_NUM,
> +		.enable_reg = BD71837_REG_LDO2_VOLT,
> +		.enable_mask = BD71837_LDO_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "ldo3",
> +		.of_match = of_match_ptr("LDO3"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_LDO3,
> +		.ops = &bd71837_ldo_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_LDO3_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_ldo3_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo3_voltage_ranges),
> +		.vsel_reg = BD71837_REG_LDO3_VOLT,
> +		.vsel_mask = LDO3_MASK,
> +		.enable_reg = BD71837_REG_LDO3_VOLT,
> +		.enable_mask = BD71837_LDO_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "ldo4",
> +		.of_match = of_match_ptr("LDO4"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_LDO4,
> +		.ops = &bd71837_ldo_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_LDO4_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_ldo4_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo4_voltage_ranges),
> +		.vsel_reg = BD71837_REG_LDO4_VOLT,
> +		.vsel_mask = LDO4_MASK,
> +		.enable_reg = BD71837_REG_LDO4_VOLT,
> +		.enable_mask = BD71837_LDO_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "ldo5",
> +		.of_match = of_match_ptr("LDO5"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_LDO5,
> +		.ops = &bd71837_ldo_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_LDO5_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_ldo5_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo5_voltage_ranges),
> +		/* LDO5 is supplied by buck6 */
> +		.supply_name = "buck6",
> +		.vsel_reg = BD71837_REG_LDO5_VOLT,
> +		.vsel_mask = LDO5_MASK,
> +		.enable_reg = BD71837_REG_LDO5_VOLT,
> +		.enable_mask = BD71837_LDO_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "ldo6",
> +		.of_match = of_match_ptr("LDO6"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_LDO6,
> +		.ops = &bd71837_ldo_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_LDO6_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_ldo6_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo6_voltage_ranges),
> +		/* LDO6 is supplied by buck7 */
> +		.supply_name = "buck7",
> +		.vsel_reg = BD71837_REG_LDO6_VOLT,
> +		.vsel_mask = LDO6_MASK,
> +		.enable_reg = BD71837_REG_LDO6_VOLT,
> +		.enable_mask = BD71837_LDO_EN,
> +		.owner = THIS_MODULE,
> +	},
> +	{
> +		.name = "ldo7",
> +		.of_match = of_match_ptr("LDO7"),
> +		.regulators_node = of_match_ptr("regulators"),
> +		.id = BD71837_LDO7,
> +		.ops = &bd71837_ldo_regulator_ops,
> +		.type = REGULATOR_VOLTAGE,
> +		.n_voltages = BD71837_LDO7_VOLTAGE_NUM,
> +		.linear_ranges = bd71837_ldo7_voltage_ranges,
> +		.n_linear_ranges = ARRAY_SIZE(bd71837_ldo7_voltage_ranges),
> +		.vsel_reg = BD71837_REG_LDO7_VOLT,
> +		.vsel_mask = LDO7_MASK,
> +		.enable_reg = BD71837_REG_LDO7_VOLT,
> +		.enable_mask = BD71837_LDO_EN,
> +		.owner = THIS_MODULE,
> +	},
> +};
> +
> +struct reg_init {
> +	unsigned int reg;
> +	unsigned int mask;
> +};
> +
> +static int bd71837_probe(struct platform_device *pdev)
> +{
> +	struct bd71837_pmic *pmic;
> +	struct bd71837_board *pdata;
> +	struct regulator_config config = { 0 };
> +	struct reg_init pmic_regulator_inits[] = {
> +		{
> +			.reg = BD71837_REG_BUCK1_CTRL,
> +			.mask = BD71837_BUCK_SEL,
> +		}, {
> +			.reg = BD71837_REG_BUCK2_CTRL,
> +			.mask = BD71837_BUCK_SEL,
> +		}, {
> +			.reg = BD71837_REG_BUCK3_CTRL,
> +			.mask = BD71837_BUCK_SEL,
> +		}, {
> +			.reg = BD71837_REG_BUCK4_CTRL,
> +			.mask = BD71837_BUCK_SEL,
> +		}, {
> +			.reg = BD71837_REG_BUCK5_CTRL,
> +			.mask = BD71837_BUCK_SEL,
> +		}, {
> +			.reg = BD71837_REG_BUCK6_CTRL,
> +			.mask = BD71837_BUCK_SEL,
> +		}, {
> +			.reg = BD71837_REG_BUCK7_CTRL,
> +			.mask = BD71837_BUCK_SEL,
> +		}, {
> +			.reg = BD71837_REG_BUCK8_CTRL,
> +			.mask = BD71837_BUCK_SEL,
> +		}, {
> +			.reg = BD71837_REG_LDO1_VOLT,
> +			.mask = BD71837_LDO_SEL,
> +		}, {
> +			.reg = BD71837_REG_LDO2_VOLT,
> +			.mask = BD71837_LDO_SEL,
> +		}, {
> +			.reg = BD71837_REG_LDO3_VOLT,
> +			.mask = BD71837_LDO_SEL,
> +		}, {
> +			.reg = BD71837_REG_LDO4_VOLT,
> +			.mask = BD71837_LDO_SEL,
> +		}, {
> +			.reg = BD71837_REG_LDO5_VOLT,
> +			.mask = BD71837_LDO_SEL,
> +		}, {
> +			.reg = BD71837_REG_LDO6_VOLT,
> +			.mask = BD71837_LDO_SEL,
> +		}, {
> +			.reg = BD71837_REG_LDO7_VOLT,
> +			.mask = BD71837_LDO_SEL,
> +		}
> +	};
> +
> +	int i, err;
> +
> +	pmic = devm_kzalloc(&pdev->dev, sizeof(struct bd71837_pmic),
> +			    GFP_KERNEL);
> +	if (!pmic)
> +		return -ENOMEM;
> +
> +	memcpy(pmic->descs, bd71837_regulators, sizeof(pmic->descs));
> +
> +	pmic->pdev = pdev;
> +	pmic->mfd = dev_get_drvdata(pdev->dev.parent);
> +
> +	if (!pmic->mfd) {
> +		dev_err(&pdev->dev, "No MFD driver data\n");
> +		err = -EINVAL;
> +		goto err;
> +	}
> +	platform_set_drvdata(pdev, pmic);
> +	pdata = dev_get_platdata(pmic->mfd->dev);
> +
> +	/* Register LOCK release */
> +	err = regmap_update_bits(pmic->mfd->regmap, BD71837_REG_REGLOCK,
> +				 (REGLOCK_PWRSEQ | REGLOCK_VREG), 0);
> +	if (err) {
> +		dev_err(&pmic->pdev->dev, "Failed to unlock PMIC (%d)\n", err);
> +		goto err;
> +	} else {
> +		dev_dbg(&pmic->pdev->dev, "%s: Unlocked lock register 0x%x\n",
> +			__func__, BD71837_REG_REGLOCK);
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(pmic_regulator_inits); i++) {
> +
> +		struct regulator_desc *desc;
> +		struct regulator_dev *rdev;
> +
> +		desc = &pmic->descs[i];
> +
> +		if (pdata)
> +			config.init_data = pdata->init_data[i];
> +
> +		config.dev = pdev->dev.parent;
> +		config.driver_data = pmic;
> +		config.regmap = pmic->mfd->regmap;
> +
> +		rdev = devm_regulator_register(&pdev->dev, desc, &config);
> +		if (IS_ERR(rdev)) {
> +			dev_err(pmic->mfd->dev,
> +				"failed to register %s regulator\n",
> +				desc->name);
> +			err = PTR_ERR(rdev);
> +			goto err;
> +		}
> +		/* Regulator register gets the regulator constraints and
> +		 * applies them (set_machine_constraints). This should have
> +		 * turned the control register(s) to correct values and we
> +		 * can now switch the control from PMIC state machine to the
> +		 * register interface
> +		 */
> +		err = regmap_update_bits(pmic->mfd->regmap,
> +					 pmic_regulator_inits[i].reg,
> +					 pmic_regulator_inits[i].mask,
> +					 0xFFFFFFFF);
> +		if (err) {
> +			dev_err(&pmic->pdev->dev,
> +				"Failed to write BUCK/LDO SEL bit for (%s)\n",
> +				desc->name);
> +			goto err;
> +		}
> +
> +		pmic->rdev[i] = rdev;
> +	}
> +
> +	return 0;
> +
> +err:
> +	return err;
> +}
> +
> +static struct platform_driver bd71837_regulator = {
> +	.driver = {
> +		.name = "bd71837-pmic",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = bd71837_probe,
> +};
> +
> +module_platform_driver(bd71837_regulator);
> +
> +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
> +MODULE_DESCRIPTION("BD71837 voltage regulator driver");
> +MODULE_LICENSE("GPL");
> -- 
> 2.17.0
> 

^ permalink raw reply

* Re: [PATCH v2 0/6] mfd/regulator/clk: bd71837: ROHM BD71837 PMIC driver
From: Lee Jones @ 2018-05-30 11:16 UTC (permalink / raw)
  To: Matti Vaittinen
  Cc: Matti Vaittinen, mturquette, sboyd, robh+dt, mark.rutland,
	lgirdwood, broonie, linux-clk, devicetree, linux-kernel,
	mikko.mutanen, heikki.haikola
In-Reply-To: <20180529083541.GA13528@localhost.localdomain>

On Tue, 29 May 2018, Matti Vaittinen wrote:

> Hello,
> 
> On Tue, May 29, 2018 at 08:39:58AM +0100, Lee Jones wrote:
> > On Mon, 28 May 2018, Matti Vaittinen wrote:
> > 
> > > Patch series adding support for ROHM BD71837 PMIC.
> > FYI, this patch-set is going to be difficult to manage since it was
> > not sent 'threaded'.
> > 
> > If/when you send a subsequent version, could you please ensure you
> > send the set threaded so the patches keep in relation to one another
> > as they are reviewed?
> 
> Thanks for the guidance. I have not sent so many patches to community so
> I am grateful also from all the practical tips =) Just one slight problem.
> I have only seen emails being threaded when one is replying to an email.
> So how should I send my patches in same thread? Just send first one and
> then send subsequent patches as replies?
> 
> I just killed some unused definitions and one unused variable from the
> code so I am about to send new version. I'll try doing that as a threaded
> series and resend all the patches as v3.

You don't need to do this manually.

Just use `git send-email` with the correct arguments.

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply

* Re: Applied "regulator: bd71837: BD71837 PMIC regulator driver" to the regulator tree
From: Mark Brown @ 2018-05-30 11:17 UTC (permalink / raw)
  To: Matti Vaittinen
  Cc: Matti Vaittinen, mturquette, sboyd, robh+dt, mark.rutland,
	lee.jones, lgirdwood, linux-clk, devicetree, linux-kernel,
	mikko.mutanen, heikki.haikola
In-Reply-To: <20180530111425.GD13528@localhost.localdomain>

[-- Attachment #1: Type: text/plain, Size: 557 bytes --]

On Wed, May 30, 2018 at 02:14:25PM +0300, Matti Vaittinen wrote:

> Does this mean this single patch was applied? I am sorry if I did not
> follow correct policy/way of informing the dependencies - but there is a
> dependency. The patch 1/6 contains the header file
> include/linux/mfd/bd71837.h with bunch of definitions this patch is
> requiring.

Your driver won't actually get built until the MFD parts end up in the
same tree since the regulator driver depends on the MFD so there's no
problem, the code will just sit there and not cause any problems.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* [PATCH v4 0/5] Reintroduce i.MX EPIT Timer
From: Clément Péron @ 2018-05-30 12:03 UTC (permalink / raw)
  To: Colin Didier, linux-arm-kernel, devicetree, linux-kernel
  Cc: Daniel Lezcano, Thomas Gleixner, Fabio Estevam,
	Vladimir Zapolskiy, Sascha Hauer, Rob Herring, NXP Linux Team,
	Pengutronix Kernel Team, Clément Peron

From: Clément Peron <clement.peron@devialet.com>

As suggested in the commit message we have added the device tree support,
proper bindings and we moved the driver into the correct folder.

Moreover we made some changes like use of relaxed IO accesor,
implement sched_clock, delay_timer and reduce the clockevents min_delta.

Changes since v3:
- Clean Kconfig
- Rename imx6q-epit to imx31-epit
- Update doc and bindings
- Indent and fix

Changes since v2 (Thanks Fabio Estevam):
- Removed unused ckil clock
- Add out_iounmap
- Check and handle if clk_prepare_enable failed
- Fix comment typo

Changes since v1 (Thanks Vladimir Zapolskiy):
- Add OF dependency in Kconfig
- Sort header
- Use BIT macro
- Remove useless comments
- Fix incorrect indent
- Fix memory leak
- Add check and handle possible returned error

Clément Peron (2):
  ARM: imx: remove inexistant EPIT timer init
  Documentation: DT: add i.MX EPIT timer binding

Colin Didier (3):
  clk: imx6: add EPIT clock support
  clocksource: add driver for i.MX EPIT timer
  ARM: dts: imx6qdl: add missing compatible and clock properties for
    EPIT

 .../devicetree/bindings/timer/fsl,imxepit.txt |  24 ++
 arch/arm/boot/dts/imx6qdl.dtsi                |  10 +
 arch/arm/mach-imx/common.h                    |   1 -
 drivers/clk/imx/clk-imx6q.c                   |   2 +
 drivers/clocksource/Kconfig                   |  11 +
 drivers/clocksource/Makefile                  |   1 +
 drivers/clocksource/timer-imx-epit.c          | 281 ++++++++++++++++++
 include/dt-bindings/clock/imx6qdl-clock.h     |   4 +-
 8 files changed, 332 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/timer/fsl,imxepit.txt
 create mode 100644 drivers/clocksource/timer-imx-epit.c

-- 
2.17.0

^ permalink raw reply

* [PATCH v4 1/5] ARM: imx: remove inexistant EPIT timer init
From: Clément Péron @ 2018-05-30 12:03 UTC (permalink / raw)
  To: Colin Didier, linux-arm-kernel, devicetree, linux-kernel
  Cc: Daniel Lezcano, Thomas Gleixner, Fabio Estevam,
	Vladimir Zapolskiy, Sascha Hauer, Rob Herring, NXP Linux Team,
	Pengutronix Kernel Team, Clément Peron
In-Reply-To: <20180530120327.27681-1-peron.clem@gmail.com>

From: Clément Peron <clement.peron@devialet.com>

i.MX EPIT timer has been removed but not the init function declaration.

Signed-off-by: Clément Peron <clement.peron@devialet.com>
Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com>
---
 arch/arm/mach-imx/common.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index c8d68e918b2f..18aae76fa2da 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -38,7 +38,6 @@ void imx21_soc_init(void);
 void imx27_soc_init(void);
 void imx31_soc_init(void);
 void imx35_soc_init(void);
-void epit_timer_init(void __iomem *base, int irq);
 int mx21_clocks_init(unsigned long lref, unsigned long fref);
 int mx27_clocks_init(unsigned long fref);
 int mx31_clocks_init(unsigned long fref);
-- 
2.17.0

^ permalink raw reply related

* [PATCH v4 2/5] clk: imx6: add EPIT clock support
From: Clément Péron @ 2018-05-30 12:03 UTC (permalink / raw)
  To: Colin Didier, linux-arm-kernel, devicetree, linux-kernel
  Cc: Daniel Lezcano, Thomas Gleixner, Fabio Estevam,
	Vladimir Zapolskiy, Sascha Hauer, Rob Herring, NXP Linux Team,
	Pengutronix Kernel Team, Clément Peron
In-Reply-To: <20180530120327.27681-1-peron.clem@gmail.com>

From: Colin Didier <colin.didier@devialet.com>

Add EPIT clock support to the i.MX6Q clocking infrastructure.

Signed-off-by: Colin Didier <colin.didier@devialet.com>
Signed-off-by: Clément Peron <clement.peron@devialet.com>
Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com>
---
 drivers/clk/imx/clk-imx6q.c               | 2 ++
 include/dt-bindings/clock/imx6qdl-clock.h | 4 +++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index 8d518ad5dc13..b9ea7037e193 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -753,6 +753,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
 	else
 		clk[IMX6Q_CLK_ECSPI5] = imx_clk_gate2("ecspi5",        "ecspi_root",        base + 0x6c, 8);
 	clk[IMX6QDL_CLK_ENET]         = imx_clk_gate2("enet",          "ipg",               base + 0x6c, 10);
+	clk[IMX6QDL_CLK_EPIT1]        = imx_clk_gate2("epit1",         "ipg",               base + 0x6c, 12);
+	clk[IMX6QDL_CLK_EPIT2]        = imx_clk_gate2("epit2",         "ipg",               base + 0x6c, 14);
 	clk[IMX6QDL_CLK_ESAI_EXTAL]   = imx_clk_gate2_shared("esai_extal",   "esai_podf",   base + 0x6c, 16, &share_count_esai);
 	clk[IMX6QDL_CLK_ESAI_IPG]     = imx_clk_gate2_shared("esai_ipg",   "ahb",           base + 0x6c, 16, &share_count_esai);
 	clk[IMX6QDL_CLK_ESAI_MEM]     = imx_clk_gate2_shared("esai_mem", "ahb",             base + 0x6c, 16, &share_count_esai);
diff --git a/include/dt-bindings/clock/imx6qdl-clock.h b/include/dt-bindings/clock/imx6qdl-clock.h
index da59fd9cdb5e..7ad171b8f3bf 100644
--- a/include/dt-bindings/clock/imx6qdl-clock.h
+++ b/include/dt-bindings/clock/imx6qdl-clock.h
@@ -271,6 +271,8 @@
 #define IMX6QDL_CLK_PRE_AXI			258
 #define IMX6QDL_CLK_MLB_SEL			259
 #define IMX6QDL_CLK_MLB_PODF			260
-#define IMX6QDL_CLK_END				261
+#define IMX6QDL_CLK_EPIT1			261
+#define IMX6QDL_CLK_EPIT2			262
+#define IMX6QDL_CLK_END				263
 
 #endif /* __DT_BINDINGS_CLOCK_IMX6QDL_H */
-- 
2.17.0

^ permalink raw reply related

* [PATCH v4 3/5] Documentation: DT: add i.MX EPIT timer binding
From: Clément Péron @ 2018-05-30 12:03 UTC (permalink / raw)
  To: Colin Didier, linux-arm-kernel, devicetree, linux-kernel
  Cc: Daniel Lezcano, Thomas Gleixner, Fabio Estevam,
	Vladimir Zapolskiy, Sascha Hauer, Rob Herring, NXP Linux Team,
	Pengutronix Kernel Team, Clément Peron
In-Reply-To: <20180530120327.27681-1-peron.clem@gmail.com>

From: Clément Peron <clement.peron@devialet.com>

Add devicetree binding document for NXP's i.MX SoC specific
EPIT timer driver.

Signed-off-by: Clément Peron <clement.peron@devialet.com>
---
 .../devicetree/bindings/timer/fsl,imxepit.txt | 24 +++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/timer/fsl,imxepit.txt

diff --git a/Documentation/devicetree/bindings/timer/fsl,imxepit.txt b/Documentation/devicetree/bindings/timer/fsl,imxepit.txt
new file mode 100644
index 000000000000..90112d58af10
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/fsl,imxepit.txt
@@ -0,0 +1,24 @@
+Binding for the i.MX EPIT timer
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible: should be "fsl,imx31-epit"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- interrupts: Should contain EPIT controller interrupt
+- clocks: list of clock specifiers, must contain an entry for each required
+  entry in clock-names
+- clock-names : should include entries "ipg", "per"
+
+Example for i.MX6QDL:
+	epit1: epit@20d0000 {
+		compatible = "fsl,imx6q-epit", "fsl,imx31-epit";
+		reg = <0x020d0000 0x4000>;
+		interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clks IMX6QDL_CLK_IPG_PER>,
+			<&clks IMX6QDL_CLK_EPIT1>;
+		clock-names = "ipg", "per";
+	};
-- 
2.17.0

^ permalink raw reply related

* [PATCH v4 4/5] clocksource: add driver for i.MX EPIT timer
From: Clément Péron @ 2018-05-30 12:03 UTC (permalink / raw)
  To: Colin Didier, linux-arm-kernel, devicetree, linux-kernel
  Cc: Daniel Lezcano, Thomas Gleixner, Fabio Estevam,
	Vladimir Zapolskiy, Sascha Hauer, Rob Herring, NXP Linux Team,
	Pengutronix Kernel Team, Clément Peron
In-Reply-To: <20180530120327.27681-1-peron.clem@gmail.com>

From: Colin Didier <colin.didier@devialet.com>

Add driver for NXP's EPIT timer used in i.MX 6 family of SoC.

Signed-off-by: Colin Didier <colin.didier@devialet.com>
Signed-off-by: Clément Peron <clement.peron@devialet.com>
---
 drivers/clocksource/Kconfig          |  11 ++
 drivers/clocksource/Makefile         |   1 +
 drivers/clocksource/timer-imx-epit.c | 281 +++++++++++++++++++++++++++
 3 files changed, 293 insertions(+)
 create mode 100644 drivers/clocksource/timer-imx-epit.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 8e8a09755d10..790478afd02c 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -576,6 +576,17 @@ config H8300_TPU
 	  This enables the clocksource for the H8300 platform with the
 	  H8S2678 cpu.
 
+config CLKSRC_IMX_EPIT
+	bool "Clocksource using i.MX EPIT"
+	depends on CLKDEV_LOOKUP && (ARCH_MXC || COMPILE_TEST)
+	select CLKSRC_MMIO
+	help
+	  This enables EPIT support available on some i.MX platforms.
+	  Normally you don't have a reason to do so as the EPIT has
+	  the same features and uses the same clocks as the GPT.
+	  Anyway, on some systems the GPT may be in use for other
+	  purposes.
+
 config CLKSRC_IMX_GPT
 	bool "Clocksource using i.MX GPT" if COMPILE_TEST
 	depends on ARM && CLKDEV_LOOKUP
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 00caf37e52f9..d9426f69ec69 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_INTEGRATOR_AP_TIMER)	+= timer-integrator-ap.o
 obj-$(CONFIG_CLKSRC_VERSATILE)		+= versatile.o
 obj-$(CONFIG_CLKSRC_MIPS_GIC)		+= mips-gic-timer.o
 obj-$(CONFIG_CLKSRC_TANGO_XTAL)		+= tango_xtal.o
+obj-$(CONFIG_CLKSRC_IMX_EPIT)		+= timer-imx-epit.o
 obj-$(CONFIG_CLKSRC_IMX_GPT)		+= timer-imx-gpt.o
 obj-$(CONFIG_CLKSRC_IMX_TPM)		+= timer-imx-tpm.o
 obj-$(CONFIG_ASM9260_TIMER)		+= asm9260_timer.o
diff --git a/drivers/clocksource/timer-imx-epit.c b/drivers/clocksource/timer-imx-epit.c
new file mode 100644
index 000000000000..7e92fcab10d3
--- /dev/null
+++ b/drivers/clocksource/timer-imx-epit.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * i.MX EPIT Timer
+ *
+ * Copyright (C) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2018 Colin Didier <colin.didier@devialet.com>
+ * Copyright (C) 2018 Clément Péron <clement.peron@devialet.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/sched_clock.h>
+#include <linux/slab.h>
+
+#define EPITCR				0x00
+#define EPITSR				0x04
+#define EPITLR				0x08
+#define EPITCMPR			0x0c
+#define EPITCNR				0x10
+
+#define EPITCR_EN			BIT(0)
+#define EPITCR_ENMOD			BIT(1)
+#define EPITCR_OCIEN			BIT(2)
+#define EPITCR_RLD			BIT(3)
+#define EPITCR_PRESC(x)			(((x) & 0xfff) << 4)
+#define EPITCR_SWR			BIT(16)
+#define EPITCR_IOVW			BIT(17)
+#define EPITCR_DBGEN			BIT(18)
+#define EPITCR_WAITEN			BIT(19)
+#define EPITCR_RES			BIT(20)
+#define EPITCR_STOPEN			BIT(21)
+#define EPITCR_OM_DISCON		(0 << 22)
+#define EPITCR_OM_TOGGLE		(1 << 22)
+#define EPITCR_OM_CLEAR			(2 << 22)
+#define EPITCR_OM_SET			(3 << 22)
+#define EPITCR_CLKSRC_OFF		(0 << 24)
+#define EPITCR_CLKSRC_PERIPHERAL	(1 << 24)
+#define EPITCR_CLKSRC_REF_HIGH		(2 << 24)
+#define EPITCR_CLKSRC_REF_LOW		(3 << 24)
+
+#define EPITSR_OCIF			BIT(0)
+
+struct epit_timer {
+	void __iomem *base;
+	int irq;
+	struct clk *clk_per;
+	struct clock_event_device ced;
+	struct irqaction act;
+};
+
+static void __iomem *sched_clock_reg;
+
+static inline struct epit_timer *to_epit_timer(struct clock_event_device *ced)
+{
+	return container_of(ced, struct epit_timer, ced);
+}
+
+static inline void epit_irq_disable(struct epit_timer *epittm)
+{
+	u32 val;
+
+	val = readl_relaxed(epittm->base + EPITCR);
+	writel_relaxed(val & ~EPITCR_OCIEN, epittm->base + EPITCR);
+}
+
+static inline void epit_irq_enable(struct epit_timer *epittm)
+{
+	u32 val;
+
+	val = readl_relaxed(epittm->base + EPITCR);
+	writel_relaxed(val | EPITCR_OCIEN, epittm->base + EPITCR);
+}
+
+static void epit_irq_acknowledge(struct epit_timer *epittm)
+{
+	writel_relaxed(EPITSR_OCIF, epittm->base + EPITSR);
+}
+
+static u64 notrace epit_read_sched_clock(void)
+{
+	return ~readl_relaxed(sched_clock_reg);
+}
+
+static int epit_set_next_event(unsigned long cycles,
+			       struct clock_event_device *ced)
+{
+	struct epit_timer *epittm = to_epit_timer(ced);
+	unsigned long tcmp;
+
+	tcmp = readl_relaxed(epittm->base + EPITCNR) - cycles;
+	writel_relaxed(tcmp, epittm->base + EPITCMPR);
+
+	return 0;
+}
+
+/* Left event sources disabled, no more interrupts appear */
+static int epit_shutdown(struct clock_event_device *ced)
+{
+	struct epit_timer *epittm = to_epit_timer(ced);
+	unsigned long flags;
+
+	/*
+	 * The timer interrupt generation is disabled at least
+	 * for enough time to call epit_set_next_event()
+	 */
+	local_irq_save(flags);
+
+	/* Disable interrupt in EPIT module */
+	epit_irq_disable(epittm);
+
+	/* Clear pending interrupt */
+	epit_irq_acknowledge(epittm);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static int epit_set_oneshot(struct clock_event_device *ced)
+{
+	struct epit_timer *epittm = to_epit_timer(ced);
+	unsigned long flags;
+
+	/*
+	 * The timer interrupt generation is disabled at least
+	 * for enough time to call epit_set_next_event()
+	 */
+	local_irq_save(flags);
+
+	/* Disable interrupt in EPIT module */
+	epit_irq_disable(epittm);
+
+	/* Clear pending interrupt, only while switching mode */
+	if (!clockevent_state_oneshot(ced))
+		epit_irq_acknowledge(epittm);
+
+	/*
+	 * Do not put overhead of interrupt enable/disable into
+	 * epit_set_next_event(), the core has about 4 minutes
+	 * to call epit_set_next_event() or shutdown clock after
+	 * mode switching
+	 */
+	epit_irq_enable(epittm);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static irqreturn_t epit_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *ced = dev_id;
+	struct epit_timer *epittm = to_epit_timer(ced);
+
+	epit_irq_acknowledge(epittm);
+
+	ced->event_handler(ced);
+
+	return IRQ_HANDLED;
+}
+
+static int __init epit_clocksource_init(struct epit_timer *epittm)
+{
+	unsigned int c = clk_get_rate(epittm->clk_per);
+
+	sched_clock_reg = epittm->base + EPITCNR;
+	sched_clock_register(epit_read_sched_clock, 32, c);
+
+	return clocksource_mmio_init(epittm->base + EPITCNR, "epit", c, 200, 32,
+				     clocksource_mmio_readl_down);
+}
+
+static int __init epit_clockevent_init(struct epit_timer *epittm)
+{
+	struct clock_event_device *ced = &epittm->ced;
+	struct irqaction *act = &epittm->act;
+
+	ced->name = "epit";
+	ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
+	ced->set_state_shutdown = epit_shutdown;
+	ced->tick_resume = epit_shutdown;
+	ced->set_state_oneshot = epit_set_oneshot;
+	ced->set_next_event = epit_set_next_event;
+	ced->rating = 200;
+	ced->cpumask = cpumask_of(0);
+	ced->irq = epittm->irq;
+	clockevents_config_and_register(ced, clk_get_rate(epittm->clk_per),
+					0xff, 0xfffffffe);
+
+	act->name = "i.MX EPIT Timer Tick",
+	act->flags = IRQF_TIMER | IRQF_IRQPOLL;
+	act->handler = epit_timer_interrupt;
+	act->dev_id = ced;
+
+	/* Make irqs happen */
+	return setup_irq(epittm->irq, act);
+}
+
+static int __init epit_timer_init(struct device_node *np)
+{
+	struct epit_timer *epittm;
+	struct clk *clk_ipg;
+	int ret;
+
+	epittm = kzalloc(sizeof(*epittm), GFP_KERNEL);
+	if (!epittm)
+		return -ENOMEM;
+
+	epittm->base = of_iomap(np, 0);
+	if (!epittm->base) {
+		ret = -ENXIO;
+		goto out_kfree;
+	}
+
+	epittm->irq = irq_of_parse_and_map(np, 0);
+	if (!epittm->irq) {
+		ret = -EINVAL;
+		goto out_iounmap;
+	}
+
+	clk_ipg = of_clk_get_by_name(np, "ipg");
+	if (IS_ERR(clk_ipg)) {
+		pr_err("i.MX EPIT: unable to get clk_ipg\n");
+		ret = PTR_ERR(clk_ipg);
+		goto out_iounmap;
+	}
+
+	ret = clk_prepare_enable(clk_ipg);
+	if (ret) {
+		pr_err("i.MX EPIT: unable to prepare+enable clk_ipg\n");
+		goto out_iounmap;
+	}
+
+	epittm->clk_per = of_clk_get_by_name(np, "per");
+	if (IS_ERR(epittm->clk_per)) {
+		pr_err("i.MX EPIT: unable to get clk_per\n");
+		ret = PTR_ERR(epittm->clk_per);
+		goto out_clk_ipg_disable;
+	}
+
+	ret = clk_prepare_enable(epittm->clk_per);
+	if (ret) {
+		pr_err("i.MX EPIT: unable to prepare+enable clk_per\n");
+		goto out_clk_ipg_disable;
+	}
+
+	/* Initialise to a known state (all timers off, and timing reset) */
+	writel_relaxed(0x0, epittm->base + EPITCR);
+	writel_relaxed(0xffffffff, epittm->base + EPITLR);
+	writel_relaxed(EPITCR_EN | EPITCR_CLKSRC_REF_HIGH | EPITCR_WAITEN,
+		       epittm->base + EPITCR);
+
+	ret = epit_clocksource_init(epittm);
+	if (ret) {
+		pr_err("i.MX EPIT: failed to init clocksource\n");
+		goto out_clk_per_disable;
+	}
+
+	ret = epit_clockevent_init(epittm);
+	if (ret) {
+		pr_err("i.MX EPIT: failed to init clockevent\n");
+		goto out_clk_per_disable;
+	}
+
+	return 0;
+
+out_clk_per_disable:
+	clk_disable_unprepare(epittm->clk_per);
+out_clk_ipg_disable:
+	clk_disable_unprepare(clk_ipg);
+out_iounmap:
+	iounmap(epittm->base);
+out_kfree:
+	kfree(epittm);
+
+	return ret;
+}
+TIMER_OF_DECLARE(epit_timer, "fsl,imx31-epit", epit_timer_init);
-- 
2.17.0

^ permalink raw reply related

* [PATCH v4 5/5] ARM: dts: imx6qdl: add missing compatible and clock properties for EPIT
From: Clément Péron @ 2018-05-30 12:03 UTC (permalink / raw)
  To: Colin Didier, linux-arm-kernel, devicetree, linux-kernel
  Cc: Daniel Lezcano, Thomas Gleixner, Fabio Estevam,
	Vladimir Zapolskiy, Sascha Hauer, Rob Herring, NXP Linux Team,
	Pengutronix Kernel Team, Clément Peron
In-Reply-To: <20180530120327.27681-1-peron.clem@gmail.com>

From: Colin Didier <colin.didier@devialet.com>

Add missing compatible and clock properties for EPIT node.

Signed-off-by: Colin Didier <colin.didier@devialet.com>
Signed-off-by: Clément Peron <clement.peron@devialet.com>
Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com>
---
 arch/arm/boot/dts/imx6qdl.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index c003e62bf290..0feec516847a 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -844,13 +844,23 @@
 			};
 
 			epit1: epit@20d0000 { /* EPIT1 */
+				compatible = "fsl,imx6q-epit", "fsl,imx31-epit";
 				reg = <0x020d0000 0x4000>;
 				interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&clks IMX6QDL_CLK_IPG_PER>,
+					 <&clks IMX6QDL_CLK_EPIT1>;
+				clock-names = "ipg", "per";
+				status = "disabled";
 			};
 
 			epit2: epit@20d4000 { /* EPIT2 */
+				compatible = "fsl,imx6q-epit", "fsl,imx31-epit";
 				reg = <0x020d4000 0x4000>;
 				interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&clks IMX6QDL_CLK_IPG_PER>,
+					 <&clks IMX6QDL_CLK_EPIT2>;
+				clock-names = "ipg", "per";
+				status = "disabled";
 			};
 
 			src: src@20d8000 {
-- 
2.17.0

^ permalink raw reply related

* Re: [PATCH 04/13] Bluetooth: btrtl: add support for retrieving the UART settings
From: kbuild test robot @ 2018-05-30 12:14 UTC (permalink / raw)
  To: Hans de Goede
  Cc: kbuild-all, Marcel Holtmann, Johan Hedberg, Martin Blumenstingl,
	robh, Jeremy Cline, linux-bluetooth, linux-serial, linux-acpi,
	devicetree
In-Reply-To: <20180527190457.2632-5-hdegoede@redhat.com>

Hi Martin,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on bluetooth/master]
[also build test WARNING on v4.17-rc7 next-20180529]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Hans-de-Goede/Bluetooth-Add-RTL8723BS-support/20180530-141115
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git master
reproduce:
        # apt-get install sparse
        make ARCH=x86_64 allmodconfig
        make C=1 CF=-D__CHECK_ENDIAN__


sparse warnings: (new ones prefixed by >>)

>> drivers/bluetooth/btrtl.c:594:14: sparse: symbol 'btrtl_convert_baudrate' was not declared. Should it be static?

Please review and possibly fold the followup patch.

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

^ permalink raw reply

* [RFC PATCH] Bluetooth: btrtl: btrtl_convert_baudrate() can be static
From: kbuild test robot @ 2018-05-30 12:14 UTC (permalink / raw)
  To: Hans de Goede
  Cc: kbuild-all, Marcel Holtmann, Johan Hedberg, Martin Blumenstingl,
	robh, Jeremy Cline, linux-bluetooth, linux-serial, linux-acpi,
	devicetree
In-Reply-To: <20180527190457.2632-5-hdegoede@redhat.com>


Fixes: 547b9528309e ("Bluetooth: btrtl: add support for retrieving the UART settings")
Signed-off-by: kbuild test robot <fengguang.wu@intel.com>
---
 btrtl.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index bc56ef7..99b77f6 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -591,7 +591,7 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
 }
 EXPORT_SYMBOL_GPL(btrtl_setup_realtek);
 
-unsigned int btrtl_convert_baudrate(u32 device_baudrate)
+static unsigned int btrtl_convert_baudrate(u32 device_baudrate)
 {
 	switch (device_baudrate) {
 	case 0x0252a00a:

^ permalink raw reply related

* [PATCH v2 00/10] Add TOSHIBA TC358764 DSI/LVDS bridge driver
From: Maciej Purski @ 2018-05-30 12:15 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, linux-samsung-soc, devicetree,
	dri-devel
  Cc: David Airlie, Rob Herring, Mark Rutland, Thierry Reding,
	Kukjin Kim, Krzysztof Kozlowski, Archit Taneja, Andrzej Hajda,
	Laurent Pinchart, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Marek Szyprowski, Bartlomiej Zolnierkiewicz,
	Maciej Purski
In-Reply-To: <CGME20180530121622eucas1p1015b65cd12a544b1ea72fe6bfd1a3ccc@eucas1p1.samsung.com>

Hi all,

this patchset is a next attempt to add the tc358764 driver.
The previous one can be found here:

https://lists.freedesktop.org/archives/dri-devel/2014-February/053705.html

Back then, TC358764 was added as a panel driver.

The bridge is supposed to be a DSI peripheral. Currently exynos_dsi accepts only panels
as its peripherals. Therefore, some logic in exynos_dsi had to be ammended. That is implemented
in first 4 patches.

Apart from the driver this patchset adds support for BOE HV070WSA-100 panel, which is used by
TC358764 and dts nodes to exynos5250.dtsi and exynos5250-arndale.dtsi.

Best regards,

Maciej Purski

---

Changes in v2:
- fix commits authorship
- don't call pm_runtime_put_sync() in exyons_dsi_disable(), if pm_runtime_get_sync()
  has not been called
- squash dts commits
- merge some redundant regulators in tc358764 bindings and in DTS
- fix kbuild robot errors

Andrzej Hajda (6):
  panel/hv070wsa-100: add DT bindings
  drm/panel: add support for BOE HV070WSA-100 panel to simple-panel
  dt-bindings: tc358754: add DT bindings
  drm/bridge: tc358764: Add DSI to LVDS bridge driver
  ARM: dts: exynos5250: add DSI node
  ARM: dts: exynos5250-arndale: add DSI and panel nodes

Maciej Purski (4):
  drm/exynos: rename "bridge_node" to "mic_bridge_node"
  drm/exynos: move pm_runtime_get_sync() to exynos_dsi_init()
  drm/exynos: move connector creation to attach callback
  drm/exynos: add non-panel path to exynos_dsi_enable()

 .../bindings/display/bridge/toshiba,tc358764.txt   |  37 ++
 .../bindings/display/panel/boe,hv070wsa-100.txt    |   7 +
 arch/arm/boot/dts/exynos5250-arndale.dts           |  61 +++
 arch/arm/boot/dts/exynos5250.dtsi                  |  21 +
 drivers/gpu/drm/bridge/Kconfig                     |   9 +
 drivers/gpu/drm/bridge/Makefile                    |   1 +
 drivers/gpu/drm/bridge/tc358764.c                  | 547 +++++++++++++++++++++
 drivers/gpu/drm/exynos/exynos_drm_dsi.c            |  88 ++--
 drivers/gpu/drm/panel/panel-simple.c               |  25 +
 9 files changed, 755 insertions(+), 41 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt
 create mode 100644 Documentation/devicetree/bindings/display/panel/boe,hv070wsa-100.txt
 create mode 100644 drivers/gpu/drm/bridge/tc358764.c

-- 
2.7.4

^ permalink raw reply

* [PATCH v2 01/10] drm/exynos: rename "bridge_node" to "mic_bridge_node"
From: Maciej Purski @ 2018-05-30 12:15 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, linux-samsung-soc, devicetree,
	dri-devel
  Cc: David Airlie, Rob Herring, Mark Rutland, Thierry Reding,
	Kukjin Kim, Krzysztof Kozlowski, Archit Taneja, Andrzej Hajda,
	Laurent Pinchart, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Marek Szyprowski, Bartlomiej Zolnierkiewicz,
	Maciej Purski
In-Reply-To: <1527682561-1386-1-git-send-email-m.purski@samsung.com>

When adding support for peripheral out bridges, the "bridge" name
becomes imprecise as it refers to a different device than the
"out_bridge".

Signed-off-by: Maciej Purski <m.purski@samsung.com>
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index eae44fd..9599e6b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -279,7 +279,7 @@ struct exynos_dsi {
 	struct list_head transfer_list;
 
 	const struct exynos_dsi_driver_data *driver_data;
-	struct device_node *bridge_node;
+	struct device_node *mic_bridge_node;
 };
 
 #define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
@@ -1631,7 +1631,7 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
 	if (ret < 0)
 		return ret;
 
-	dsi->bridge_node = of_graph_get_remote_node(node, DSI_PORT_IN, 0);
+	dsi->mic_bridge_node = of_graph_get_remote_node(node, DSI_PORT_IN, 0);
 
 	return 0;
 }
@@ -1642,7 +1642,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
 	struct drm_encoder *encoder = dev_get_drvdata(dev);
 	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
 	struct drm_device *drm_dev = data;
-	struct drm_bridge *bridge;
+	struct drm_bridge *mic_bridge;
 	int ret;
 
 	drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs,
@@ -1661,10 +1661,10 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
-	if (dsi->bridge_node) {
-		bridge = of_drm_find_bridge(dsi->bridge_node);
-		if (bridge)
-			drm_bridge_attach(encoder, bridge, NULL);
+	if (dsi->mic_bridge_node) {
+		mic_bridge = of_drm_find_bridge(dsi->mic_bridge_node);
+		if (mic_bridge)
+			drm_bridge_attach(encoder, mic_bridge, NULL);
 	}
 
 	return mipi_dsi_host_register(&dsi->dsi_host);
@@ -1783,7 +1783,7 @@ static int exynos_dsi_remove(struct platform_device *pdev)
 {
 	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
 
-	of_node_put(dsi->bridge_node);
+	of_node_put(dsi->mic_bridge_node);
 
 	pm_runtime_disable(&pdev->dev);
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 02/10] drm/exynos: move pm_runtime_get_sync() to exynos_dsi_init()
From: Maciej Purski @ 2018-05-30 12:15 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, linux-samsung-soc, devicetree,
	dri-devel
  Cc: David Airlie, Rob Herring, Mark Rutland, Thierry Reding,
	Kukjin Kim, Krzysztof Kozlowski, Archit Taneja, Andrzej Hajda,
	Laurent Pinchart, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Marek Szyprowski, Bartlomiej Zolnierkiewicz,
	Maciej Purski
In-Reply-To: <1527682561-1386-1-git-send-email-m.purski@samsung.com>

In order to allow bridge drivers to use DSI transfers in their
pre_enable callbacks, pm_runtime_get_sync() should be performed before
exynos_dsi_enable(). DSIM_STATE_ENABLED flag now should not guard
from calling dsi_host_transfer() before enabling.

Signed-off-by: Maciej Purski <m.purski@samsung.com>
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 9599e6b..85eb2262 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1312,6 +1312,7 @@ static int exynos_dsi_init(struct exynos_dsi *dsi)
 {
 	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
 
+	pm_runtime_get_sync(dsi->dev);
 	exynos_dsi_reset(dsi);
 	exynos_dsi_enable_irq(dsi);
 
@@ -1388,7 +1389,6 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
 	ret = drm_panel_prepare(dsi->panel);
 	if (ret < 0) {
 		dsi->state &= ~DSIM_STATE_ENABLED;
-		pm_runtime_put_sync(dsi->dev);
 		return;
 	}
 
@@ -1400,7 +1400,6 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
 		dsi->state &= ~DSIM_STATE_ENABLED;
 		exynos_dsi_set_display_enable(dsi, false);
 		drm_panel_unprepare(dsi->panel);
-		pm_runtime_put_sync(dsi->dev);
 		return;
 	}
 
@@ -1422,7 +1421,10 @@ static void exynos_dsi_disable(struct drm_encoder *encoder)
 
 	dsi->state &= ~DSIM_STATE_ENABLED;
 
-	pm_runtime_put_sync(dsi->dev);
+	if (dsi->state & DSIM_STATE_INITIALIZED) {
+		pm_runtime_put_sync(dsi->dev);
+		dsi->state &= ~DSIM_STATE_INITIALIZED;
+	}
 }
 
 static enum drm_connector_status
@@ -1566,9 +1568,6 @@ static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
 	struct exynos_dsi_transfer xfer;
 	int ret;
 
-	if (!(dsi->state & DSIM_STATE_ENABLED))
-		return -EINVAL;
-
 	if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
 		ret = exynos_dsi_init(dsi);
 		if (ret)
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 03/10] drm/exynos: move connector creation to attach callback
From: Maciej Purski @ 2018-05-30 12:15 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, linux-samsung-soc, devicetree,
	dri-devel
  Cc: David Airlie, Rob Herring, Mark Rutland, Thierry Reding,
	Kukjin Kim, Krzysztof Kozlowski, Archit Taneja, Andrzej Hajda,
	Laurent Pinchart, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Marek Szyprowski, Bartlomiej Zolnierkiewicz,
	Maciej Purski
In-Reply-To: <1527682561-1386-1-git-send-email-m.purski@samsung.com>

The current implementation assumes that the only possible peripheral
device for DSIM is a panel. Using an output bridge should also be
possible.

If an output bridge in available, don't create a new connector.
Instead add bridge to DSIM encdoer in dsi_host_attach().

Signed-off-by: Maciej Purski <m.purski@samsung.com>
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 35 +++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 85eb2262..7b50bad 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1501,7 +1501,28 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
 				  struct mipi_dsi_device *device)
 {
 	struct exynos_dsi *dsi = host_to_dsi(host);
-	struct drm_device *drm = dsi->connector.dev;
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_device *drm = encoder->dev;
+	struct drm_bridge *out_bridge;
+
+	out_bridge  = of_drm_find_bridge(device->dev.of_node);
+	if (out_bridge) {
+		drm_bridge_attach(encoder, out_bridge, NULL);
+	} else {
+		int ret = exynos_dsi_create_connector(encoder);
+
+		if (ret) {
+			DRM_ERROR("failed to create connector ret = %d\n", ret);
+			drm_encoder_cleanup(encoder);
+			return ret;
+		}
+
+		dsi->panel = of_drm_find_panel(device->dev.of_node);
+		if (dsi->panel) {
+			drm_panel_attach(dsi->panel, &dsi->connector);
+			dsi->connector.status = connector_status_connected;
+		}
+	}
 
 	/*
 	 * This is a temporary solution and should be made by more generic way.
@@ -1520,11 +1541,6 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
 	dsi->lanes = device->lanes;
 	dsi->format = device->format;
 	dsi->mode_flags = device->mode_flags;
-	dsi->panel = of_drm_find_panel(device->dev.of_node);
-	if (dsi->panel) {
-		drm_panel_attach(dsi->panel, &dsi->connector);
-		dsi->connector.status = connector_status_connected;
-	}
 	exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode =
 			!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO);
 
@@ -1653,13 +1669,6 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
 	if (ret < 0)
 		return ret;
 
-	ret = exynos_dsi_create_connector(encoder);
-	if (ret) {
-		DRM_ERROR("failed to create connector ret = %d\n", ret);
-		drm_encoder_cleanup(encoder);
-		return ret;
-	}
-
 	if (dsi->mic_bridge_node) {
 		mic_bridge = of_drm_find_bridge(dsi->mic_bridge_node);
 		if (mic_bridge)
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 04/10] drm/exynos: add non-panel path to exynos_dsi_enable()
From: Maciej Purski @ 2018-05-30 12:15 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, linux-samsung-soc, devicetree,
	dri-devel
  Cc: Mark Rutland, Maciej Purski, Archit Taneja, Joonyoung Shim,
	Bartlomiej Zolnierkiewicz, David Airlie, Seung-Woo Kim,
	Krzysztof Kozlowski, Inki Dae, Andrzej Hajda, Kyungmin Park,
	Rob Herring, Thierry Reding, Kukjin Kim, Marek Szyprowski,
	Laurent Pinchart
In-Reply-To: <1527682561-1386-1-git-send-email-m.purski@samsung.com>

As DSIM can now have a bridge connected as a peripheral, it should be
possible to successfully enable exynos_dsi, when there is no panel
provided.

Signed-off-by: Maciej Purski <m.purski@samsung.com>
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 7b50bad..7f6a0b1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1382,27 +1382,25 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
 	if (dsi->state & DSIM_STATE_ENABLED)
 		return;
 
-	pm_runtime_get_sync(dsi->dev);
-
-	dsi->state |= DSIM_STATE_ENABLED;
-
-	ret = drm_panel_prepare(dsi->panel);
-	if (ret < 0) {
-		dsi->state &= ~DSIM_STATE_ENABLED;
-		return;
+	if (dsi->panel) {
+		ret = drm_panel_prepare(dsi->panel);
+		if (ret < 0)
+			return;
 	}
 
 	exynos_dsi_set_display_mode(dsi);
 	exynos_dsi_set_display_enable(dsi, true);
 
-	ret = drm_panel_enable(dsi->panel);
-	if (ret < 0) {
-		dsi->state &= ~DSIM_STATE_ENABLED;
-		exynos_dsi_set_display_enable(dsi, false);
-		drm_panel_unprepare(dsi->panel);
-		return;
+	if (dsi->panel) {
+		ret = drm_panel_enable(dsi->panel);
+		if (ret < 0) {
+			exynos_dsi_set_display_enable(dsi, false);
+			drm_panel_unprepare(dsi->panel);
+			return;
+		}
 	}
 
+	dsi->state |= DSIM_STATE_ENABLED;
 	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
 }
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 05/10] panel/hv070wsa-100: add DT bindings
From: Maciej Purski @ 2018-05-30 12:15 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, linux-samsung-soc, devicetree,
	dri-devel
  Cc: David Airlie, Rob Herring, Mark Rutland, Thierry Reding,
	Kukjin Kim, Krzysztof Kozlowski, Archit Taneja, Andrzej Hajda,
	Laurent Pinchart, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Marek Szyprowski, Bartlomiej Zolnierkiewicz,
	Maciej Purski
In-Reply-To: <1527682561-1386-1-git-send-email-m.purski@samsung.com>

From: Andrzej Hajda <a.hajda@samsung.com>

The patch adds bindings to BOE HV070-WSA WSVGA panel.
Bindings are compatible with simple panel bindings.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Maciej Purski <m.purski@samsung.com>
---
 .../devicetree/bindings/display/panel/boe,hv070wsa-100.txt         | 7 +++++++
 1 file changed, 7 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/boe,hv070wsa-100.txt

diff --git a/Documentation/devicetree/bindings/display/panel/boe,hv070wsa-100.txt b/Documentation/devicetree/bindings/display/panel/boe,hv070wsa-100.txt
new file mode 100644
index 0000000..bfc20ac
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/boe,hv070wsa-100.txt
@@ -0,0 +1,7 @@
+BOE HV070WSA-100 7.01" WSVGA TFT LCD panel
+
+Required properties:
+- compatible: should be "boe,hv070wsa-100"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 06/10] drm/panel: add support for BOE HV070WSA-100 panel to simple-panel
From: Maciej Purski @ 2018-05-30 12:15 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, linux-samsung-soc, devicetree,
	dri-devel
  Cc: David Airlie, Rob Herring, Mark Rutland, Thierry Reding,
	Kukjin Kim, Krzysztof Kozlowski, Archit Taneja, Andrzej Hajda,
	Laurent Pinchart, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Marek Szyprowski, Bartlomiej Zolnierkiewicz,
	Maciej Purski
In-Reply-To: <1527682561-1386-1-git-send-email-m.purski@samsung.com>

From: Andrzej Hajda <a.hajda@samsung.com>

The patch adds support for BOE HV070WSA-100 WSVGA 7.01 inch panel
in panel-simple driver. The panel is used in Exynos5250-arndale boards.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Maciej Purski <m.purski@samsung.com>
---
 drivers/gpu/drm/panel/panel-simple.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index cbf1ab4..d5da58d 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -745,6 +745,28 @@ static const struct panel_desc avic_tm070ddh03 = {
 	},
 };
 
+static const struct drm_display_mode boe_hv070wsa_mode = {
+	.clock = 40800,
+	.hdisplay = 1024,
+	.hsync_start = 1024 + 90,
+	.hsync_end = 1024 + 90 + 90,
+	.htotal = 1024 + 90 + 90 + 90,
+	.vdisplay = 600,
+	.vsync_start = 600 + 3,
+	.vsync_end = 600 + 3 + 4,
+	.vtotal = 600 + 3 + 4 + 3,
+	.vrefresh = 60,
+};
+
+static const struct panel_desc boe_hv070wsa = {
+	.modes = &boe_hv070wsa_mode,
+	.num_modes = 1,
+	.size = {
+		.width = 154,
+		.height = 90,
+	},
+};
+
 static const struct drm_display_mode boe_nv101wxmn51_modes[] = {
 	{
 		.clock = 71900,
@@ -2113,6 +2135,9 @@ static const struct of_device_id platform_of_match[] = {
 		.compatible = "avic,tm070ddh03",
 		.data = &avic_tm070ddh03,
 	}, {
+		.compatible = "boe,hv070wsa-100",
+		.data = &boe_hv070wsa
+	}, {
 		.compatible = "boe,nv101wxmn51",
 		.data = &boe_nv101wxmn51,
 	}, {
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 07/10] dt-bindings: tc358754: add DT bindings
From: Maciej Purski @ 2018-05-30 12:15 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, linux-samsung-soc, devicetree,
	dri-devel
  Cc: David Airlie, Rob Herring, Mark Rutland, Thierry Reding,
	Kukjin Kim, Krzysztof Kozlowski, Archit Taneja, Andrzej Hajda,
	Laurent Pinchart, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Marek Szyprowski, Bartlomiej Zolnierkiewicz,
	Maciej Purski
In-Reply-To: <1527682561-1386-1-git-send-email-m.purski@samsung.com>

From: Andrzej Hajda <a.hajda@samsung.com>

The patch adds bindings to Toshiba DSI/LVDS bridge TC358764.
Bindings describe power supplies, reset gpio and video interfaces.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Maciej Purski <m.purski@samsung.com>
---
 .../bindings/display/bridge/toshiba,tc358764.txt   | 37 ++++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt

diff --git a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt
new file mode 100644
index 0000000..6eda14f
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt
@@ -0,0 +1,37 @@
+TC358764 MIPI-DSI to LVDS panel bridge
+
+Required properties:
+  - compatible: "toshiba,tc358764"
+  - reg: the virtual channel number of a DSI peripheral
+  - vddc-supply: core voltage supply, 1.2V
+  - vddio-supply: I/O voltage supply, 1.8V or 3.3V
+  - vddlvds-supply: LVDS1/2 voltage supply, 3.3V
+  - reset-gpios: a GPIO spec for the reset pin
+
+The device node can contain zero to two 'port' child nodes, each with one
+child 'endpoint' node, according to the bindings defined in [1].
+The following are properties specific to those nodes.
+
+port:
+  - reg: (required) can be 0 for DSI port or 1 for LVDS port;
+
+[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
+
+Example:
+
+	bridge@0 {
+		reg = <0>;
+		compatible = "toshiba,tc358764";
+		vddc-supply = <&vcc_1v2_reg>;
+		vddio-supply = <&vcc_1v8_reg>;
+		vddlvds-supply = <&vcc_3v3_reg>;
+		reset-gpios = <&gpd1 6 GPIO_ACTIVE_LOW>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		port@1 {
+			reg = <1>;
+			lvds_ep: endpoint {
+				remote-endpoint = <&panel_ep>;
+			};
+		};
+	};
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 08/10] drm/bridge: tc358764: Add DSI to LVDS bridge driver
From: Maciej Purski @ 2018-05-30 12:15 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, linux-samsung-soc, devicetree,
	dri-devel
  Cc: David Airlie, Rob Herring, Mark Rutland, Thierry Reding,
	Kukjin Kim, Krzysztof Kozlowski, Archit Taneja, Andrzej Hajda,
	Laurent Pinchart, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Marek Szyprowski, Bartlomiej Zolnierkiewicz,
	Maciej Purski
In-Reply-To: <1527682561-1386-1-git-send-email-m.purski@samsung.com>

From: Andrzej Hajda <a.hajda@samsung.com>

Add a drm_bridge driver for the Toshiba TC358764 DSI to LVDS bridge.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Maciej Purski <m.purski@samsung.com>
---
 drivers/gpu/drm/bridge/Kconfig    |   9 +
 drivers/gpu/drm/bridge/Makefile   |   1 +
 drivers/gpu/drm/bridge/tc358764.c | 547 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 557 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/tc358764.c

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index fa2c799..9bd3eb8 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -110,6 +110,15 @@ config DRM_THINE_THC63LVD1024
 	---help---
 	  Thine THC63LVD1024 LVDS/parallel converter driver.
 
+config DRM_TOSHIBA_TC358764
+	tristate "TC358764 DSI/LVDS bridge"
+	depends on DRM && DRM_PANEL
+	depends on OF
+	select DRM_MIPI_DSI
+	select VIDEOMODE_HELPERS
+	help
+	  Toshiba TC358764 DSI/LVDS bridge driver
+
 config DRM_TOSHIBA_TC358767
 	tristate "Toshiba TC358767 eDP bridge"
 	depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 35f88d4..bf7c0ce 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
 obj-$(CONFIG_DRM_SII902X) += sii902x.o
 obj-$(CONFIG_DRM_SII9234) += sii9234.o
 obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o
+obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o
 obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
 obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
 obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c
new file mode 100644
index 0000000..3109eba
--- /dev/null
+++ b/drivers/gpu/drm/bridge/tc358764.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd
+ *
+ * Authors:
+ *	Andrzej Hajda <a.hajda@samsung.com>
+ *	Maciej Purski <m.purski@samsung.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ *
+ */
+
+#include <drm/drm_atomic_helper.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/of_graph.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#define FLD_MASK(start, end)    (((1 << ((start) - (end) + 1)) - 1) << (end))
+#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
+
+/* PPI layer registers */
+#define PPI_STARTPPI		0x0104 /* START control bit */
+#define PPI_LPTXTIMECNT		0x0114 /* LPTX timing signal */
+#define PPI_LANEENABLE		0x0134 /* Enables each lane */
+#define PPI_TX_RX_TA		0x013C /* BTA timing parameters */
+#define PPI_D0S_CLRSIPOCOUNT	0x0164 /* Assertion timer for Lane 0 */
+#define PPI_D1S_CLRSIPOCOUNT	0x0168 /* Assertion timer for Lane 1 */
+#define PPI_D2S_CLRSIPOCOUNT	0x016C /* Assertion timer for Lane 2 */
+#define PPI_D3S_CLRSIPOCOUNT	0x0170 /* Assertion timer for Lane 3 */
+#define PPI_START_FUNCTION	1
+
+/* DSI layer registers */
+#define DSI_STARTDSI		0x0204 /* START control bit of DSI-TX */
+#define DSI_LANEENABLE		0x0210 /* Enables each lane */
+#define DSI_RX_START		1
+
+/* Video path registers */
+#define VP_CTRL			0x0450 /* Video Path Control */
+#define VP_CTRL_MSF(v)		FLD_VAL(v, 0, 0) /* Magic square in RGB666 */
+#define VP_CTRL_VTGEN(v)	FLD_VAL(v, 4, 4) /* Use chip clock for timing */
+#define VP_CTRL_EVTMODE(v)	FLD_VAL(v, 5, 5) /* Event mode */
+#define VP_CTRL_RGB888(v)	FLD_VAL(v, 8, 8) /* RGB888 mode */
+#define VP_CTRL_VSDELAY(v)	FLD_VAL(v, 31, 20) /* VSYNC delay */
+#define VP_CTRL_HSPOL		BIT(17) /* Polarity of HSYNC signal */
+#define VP_CTRL_DEPOL		BIT(18) /* Polarity of DE signal */
+#define VP_CTRL_VSPOL		BIT(19) /* Polarity of VSYNC signal */
+#define VP_HTIM1		0x0454 /* Horizontal Timing Control 1 */
+#define VP_HTIM1_HBP(v)		FLD_VAL(v, 24, 16)
+#define VP_HTIM1_HSYNC(v)	FLD_VAL(v, 8, 0)
+#define VP_HTIM2		0x0458 /* Horizontal Timing Control 2 */
+#define VP_HTIM2_HFP(v)		FLD_VAL(v, 24, 16)
+#define VP_HTIM2_HACT(v)	FLD_VAL(v, 10, 0)
+#define VP_VTIM1		0x045C /* Vertical Timing Control 1 */
+#define VP_VTIM1_VBP(v)		FLD_VAL(v, 23, 16)
+#define VP_VTIM1_VSYNC(v)	FLD_VAL(v, 7, 0)
+#define VP_VTIM2		0x0460 /* Vertical Timing Control 2 */
+#define VP_VTIM2_VFP(v)		FLD_VAL(v, 23, 16)
+#define VP_VTIM2_VACT(v)	FLD_VAL(v, 10, 0)
+#define VP_VFUEN		0x0464 /* Video Frame Timing Update Enable */
+
+/* LVDS registers */
+#define LV_MX0003		0x0480 /* Mux input bit 0 to 3 */
+#define LV_MX0407		0x0484 /* Mux input bit 4 to 7 */
+#define LV_MX0811		0x0488 /* Mux input bit 8 to 11 */
+#define LV_MX1215		0x048C /* Mux input bit 12 to 15 */
+#define LV_MX1619		0x0490 /* Mux input bit 16 to 19 */
+#define LV_MX2023		0x0494 /* Mux input bit 20 to 23 */
+#define LV_MX2427		0x0498 /* Mux input bit 24 to 27 */
+#define LV_MX(b0, b1, b2, b3)	(FLD_VAL(b0, 4, 0) | FLD_VAL(b1, 12, 8) | \
+				FLD_VAL(b2, 20, 16) | FLD_VAL(b3, 28, 24))
+
+/* Input bit numbers used in mux registers */
+enum {
+	LVI_R0,
+	LVI_R1,
+	LVI_R2,
+	LVI_R3,
+	LVI_R4,
+	LVI_R5,
+	LVI_R6,
+	LVI_R7,
+	LVI_G0,
+	LVI_G1,
+	LVI_G2,
+	LVI_G3,
+	LVI_G4,
+	LVI_G5,
+	LVI_G6,
+	LVI_G7,
+	LVI_B0,
+	LVI_B1,
+	LVI_B2,
+	LVI_B3,
+	LVI_B4,
+	LVI_B5,
+	LVI_B6,
+	LVI_B7,
+	LVI_HS,
+	LVI_VS,
+	LVI_DE,
+	LVI_L0
+};
+
+#define LV_CFG			0x049C /* LVDS Configuration */
+#define LV_PHY0			0x04A0 /* LVDS PHY 0 */
+#define LV_PHY0_RST(v)		FLD_VAL(v, 22, 22) /* PHY reset */
+#define LV_PHY0_IS(v)		FLD_VAL(v, 15, 14)
+#define LV_PHY0_ND(v)		FLD_VAL(v, 4, 0) /* Frequency range select */
+#define LV_PHY0_PRBS_ON(v)	FLD_VAL(v, 20, 16) /* Clock/Data Flag pins */
+
+/* System registers */
+#define SYS_RST			0x0504 /* System Reset */
+#define SYS_ID			0x0580 /* System ID */
+
+#define SYS_RST_I2CS		BIT(0) /* Reset I2C-Slave controller */
+#define SYS_RST_I2CM		BIT(1) /* Reset I2C-Master controller */
+#define SYS_RST_LCD		BIT(2) /* Reset LCD controller */
+#define SYS_RST_BM		BIT(3) /* Reset Bus Management controller */
+#define SYS_RST_DSIRX		BIT(4) /* Reset DSI-RX and App controller */
+#define SYS_RST_REG		BIT(5) /* Reset Register module */
+
+#define LPX_PERIOD		2
+#define TTA_SURE		3
+#define TTA_GET			0x20000
+
+/* Lane enable PPI and DSI register bits */
+#define LANEENABLE_CLEN		BIT(0)
+#define LANEENABLE_L0EN		BIT(1)
+#define LANEENABLE_L1EN		BIT(2)
+#define LANEENABLE_L2EN		BIT(3)
+#define LANEENABLE_L3EN		BIT(4)
+
+/* LVCFG fields */
+#define LV_CFG_LVEN		BIT(0)
+#define LV_CFG_LVDLINK		BIT(1)
+#define LV_CFG_CLKPOL1		BIT(2)
+#define LV_CFG_CLKPOL2		BIT(3)
+
+static const char * const tc358764_supplies[] = {
+	"vddc", "vddio", "vddmipi", "vddlvds133", "vddlvds112"
+};
+
+struct tc358764 {
+	struct device *dev;
+	struct drm_bridge bridge;
+	struct drm_connector connector;
+	struct regulator_bulk_data supplies[ARRAY_SIZE(tc358764_supplies)];
+	struct gpio_desc *gpio_reset;
+
+	struct drm_panel *panel;
+};
+
+static int tc358764_read(struct tc358764 *ctx, u16 addr, u32 *val)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
+	struct mipi_dsi_msg msg = {
+		.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM,
+		.channel = dsi->channel,
+		.flags = MIPI_DSI_MSG_USE_LPM,
+		.tx_buf = &addr,
+		.tx_len = 2,
+		.rx_buf = val,
+		.rx_len = 4
+	};
+	ssize_t ret;
+
+	if (!ops || !ops->transfer)
+		return -EINVAL;
+
+	cpu_to_le16s(&addr);
+
+	ret = ops->transfer(dsi->host, &msg);
+	if (ret >= 0)
+		le32_to_cpus(val);
+
+	dev_dbg(ctx->dev, "read: %d, addr: %d\n", addr, *val);
+
+	return ret;
+}
+
+static int tc358764_write(struct tc358764 *ctx, u16 addr, u32 val)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
+	u8 data[6];
+	int ret;
+	struct mipi_dsi_msg msg = {
+		.type = MIPI_DSI_GENERIC_LONG_WRITE,
+		.channel = dsi->channel,
+		.flags = MIPI_DSI_MSG_USE_LPM | MIPI_DSI_MSG_REQ_ACK,
+		.tx_buf = data,
+		.tx_len = 6
+	};
+
+	if (!ops || !ops->transfer)
+		return -EINVAL;
+
+	data[0] = addr;
+	data[1] = addr >> 8;
+	data[2] = val;
+	data[3] = val >> 8;
+	data[4] = val >> 16;
+	data[5] = val >> 24;
+
+	ret = ops->transfer(dsi->host, &msg);
+
+	return ret;
+}
+
+static inline struct tc358764 *bridge_to_tc358764(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct tc358764, bridge);
+}
+
+static inline
+struct tc358764 *connector_to_tc358764(struct drm_connector *connector)
+{
+	return container_of(connector, struct tc358764, connector);
+}
+
+static int tc358764_init(struct tc358764 *ctx)
+{
+	u32 v = 0;
+
+	tc358764_read(ctx, SYS_ID, &v);
+	dev_info(ctx->dev, "ID: %#x\n", v);
+
+	/* configure PPI counters */
+	tc358764_write(ctx, PPI_TX_RX_TA, TTA_GET | TTA_SURE);
+	tc358764_write(ctx, PPI_LPTXTIMECNT, LPX_PERIOD);
+	tc358764_write(ctx, PPI_D0S_CLRSIPOCOUNT, 5);
+	tc358764_write(ctx, PPI_D1S_CLRSIPOCOUNT, 5);
+	tc358764_write(ctx, PPI_D2S_CLRSIPOCOUNT, 5);
+	tc358764_write(ctx, PPI_D3S_CLRSIPOCOUNT, 5);
+
+	/* enable four data lanes and clock lane */
+	tc358764_write(ctx, PPI_LANEENABLE, LANEENABLE_L3EN | LANEENABLE_L2EN |
+		       LANEENABLE_L1EN | LANEENABLE_L0EN | LANEENABLE_CLEN);
+	tc358764_write(ctx, DSI_LANEENABLE, LANEENABLE_L3EN | LANEENABLE_L2EN |
+		       LANEENABLE_L1EN | LANEENABLE_L0EN | LANEENABLE_CLEN);
+
+	/* start */
+	tc358764_write(ctx, PPI_STARTPPI, PPI_START_FUNCTION);
+	tc358764_write(ctx, DSI_STARTDSI, DSI_RX_START);
+
+	/* configure video path */
+	tc358764_write(ctx, VP_CTRL, VP_CTRL_VSDELAY(15) | VP_CTRL_RGB888(1) |
+		       VP_CTRL_EVTMODE(1) | VP_CTRL_HSPOL | VP_CTRL_VSPOL);
+
+	/* reset PHY */
+	tc358764_write(ctx, LV_PHY0, LV_PHY0_RST(1) |
+		       LV_PHY0_PRBS_ON(4) | LV_PHY0_IS(2) | LV_PHY0_ND(6));
+	tc358764_write(ctx, LV_PHY0, LV_PHY0_PRBS_ON(4) | LV_PHY0_IS(2) |
+		       LV_PHY0_ND(6));
+
+	/* reset bridge */
+	tc358764_write(ctx, SYS_RST, SYS_RST_LCD);
+
+	/* set bit order */
+	tc358764_write(ctx, LV_MX0003, LV_MX(LVI_R0, LVI_R1, LVI_R2, LVI_R3));
+	tc358764_write(ctx, LV_MX0407, LV_MX(LVI_R4, LVI_R7, LVI_R5, LVI_G0));
+	tc358764_write(ctx, LV_MX0811, LV_MX(LVI_G1, LVI_G2, LVI_G6, LVI_G7));
+	tc358764_write(ctx, LV_MX1215, LV_MX(LVI_G3, LVI_G4, LVI_G5, LVI_B0));
+	tc358764_write(ctx, LV_MX1619, LV_MX(LVI_B6, LVI_B7, LVI_B1, LVI_B2));
+	tc358764_write(ctx, LV_MX2023, LV_MX(LVI_B3, LVI_B4, LVI_B5, LVI_L0));
+	tc358764_write(ctx, LV_MX2427, LV_MX(LVI_HS, LVI_VS, LVI_DE, LVI_R6));
+	tc358764_write(ctx, LV_CFG, LV_CFG_CLKPOL2 | LV_CFG_CLKPOL1 |
+		       LV_CFG_LVEN);
+
+	return 0;
+}
+
+static void tc358764_reset(struct tc358764 *ctx)
+{
+	msleep(20);
+	gpiod_set_value(ctx->gpio_reset, 0);
+	msleep(20);
+	gpiod_set_value(ctx->gpio_reset, 1);
+	msleep(40);
+}
+
+static void tc358764_poweroff(struct tc358764 *ctx)
+{
+	int ret;
+
+	tc358764_reset(ctx);
+
+	drm_panel_disable(ctx->panel);
+	msleep(40);
+
+	ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+	if (ret < 0)
+		dev_err(ctx->dev, "error disabling regulators (%d)\n", ret);
+}
+
+static int tc358764_get_modes(struct drm_connector *connector)
+{
+	struct tc358764 *ctx = connector_to_tc358764(connector);
+
+	if (ctx->panel && ctx->panel->funcs && ctx->panel->funcs->get_modes)
+		return ctx->panel->funcs->get_modes(ctx->panel);
+
+	return 0;
+}
+
+static const
+struct drm_connector_helper_funcs tc358764_connector_helper_funcs = {
+	.get_modes = tc358764_get_modes,
+};
+
+static const struct drm_connector_funcs tc358764_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = drm_connector_cleanup,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static void tc358764_disable(struct drm_bridge *bridge)
+{
+	struct tc358764 *ctx = bridge_to_tc358764(bridge);
+
+	tc358764_poweroff(ctx);
+}
+
+static void tc358764_pre_enable(struct drm_bridge *bridge)
+{
+	struct tc358764 *ctx = bridge_to_tc358764(bridge);
+	int ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies),
+					ctx->supplies);
+	if (ret < 0)
+		dev_err(ctx->dev, "error enabling regulators (%d)\n", ret);
+
+	tc358764_reset(ctx);
+	tc358764_init(ctx);
+}
+
+static void tc358764_enable(struct drm_bridge *bridge)
+{
+	struct tc358764 *ctx = bridge_to_tc358764(bridge);
+	int ret;
+
+	drm_panel_prepare(ctx->panel);
+
+	ret = drm_panel_enable(ctx->panel);
+	if (ret < 0)
+		pr_err("panel enable failed\n");
+
+	msleep(40);
+}
+
+static int tc358764_attach(struct drm_bridge *bridge)
+{
+	struct tc358764 *ctx = bridge_to_tc358764(bridge);
+	struct drm_device *drm = bridge->dev;
+	int ret;
+
+	if (!bridge->encoder) {
+		DRM_ERROR("Encoder not found\n");
+		return -ENODEV;
+	}
+
+	ctx->connector.polled = DRM_CONNECTOR_POLL_HPD;
+	ret = drm_connector_init(drm, &ctx->connector,
+				 &tc358764_connector_funcs,
+				 DRM_MODE_CONNECTOR_LVDS);
+	if (ret) {
+		DRM_ERROR("Failed to initialize connector\n");
+		return ret;
+	}
+
+	drm_connector_helper_add(&ctx->connector,
+				 &tc358764_connector_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&ctx->connector, bridge->encoder);
+
+	if (ctx->panel)
+		drm_panel_attach(ctx->panel, &ctx->connector);
+
+	drm_atomic_helper_connector_reset(&ctx->connector);
+	drm_connector_register(&ctx->connector);
+
+	return 0;
+}
+
+static const struct drm_bridge_funcs tc358764_bridge_funcs = {
+	.disable = tc358764_disable,
+	.enable = tc358764_enable,
+	.pre_enable = tc358764_pre_enable,
+	.attach = tc358764_attach,
+};
+
+static struct device_node *tc358764_of_find_panel_node(struct device *dev)
+{
+	struct device_node *np, *ep;
+
+	ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, 0);
+	if (!ep) {
+		pr_err("faile to get endpoint\n");
+		return NULL;
+	}
+
+	np = of_graph_get_remote_port_parent(ep);
+
+	return np;
+}
+
+static int tc358764_parse_dt(struct tc358764 *ctx)
+{
+	struct device *dev = ctx->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *lvds;
+
+	ctx->gpio_reset = devm_gpiod_get_from_of_node(dev, np, "reset", 0,
+						      GPIOD_OUT_LOW,
+						      "tc358764-reset");
+	if (IS_ERR(ctx->gpio_reset)) {
+		dev_err(dev, "no reset GPIO pin provided\n");
+		return PTR_ERR(ctx->gpio_reset);
+	}
+
+	lvds = tc358764_of_find_panel_node(ctx->dev);
+	if (!lvds) {
+		dev_err(dev, "cannot find panel node\n");
+		return -EINVAL;
+	}
+
+	ctx->panel = of_drm_find_panel(lvds);
+	if (!ctx->panel) {
+		dev_err(dev, "panel not registered\n");
+		return -EPROBE_DEFER;
+	}
+
+	return 0;
+}
+
+static int tc358764_configure_regulators(struct tc358764 *ctx)
+{
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(ctx->supplies); ++i)
+		ctx->supplies[i].supply = tc358764_supplies[i];
+
+	ret = devm_regulator_bulk_get(ctx->dev, ARRAY_SIZE(ctx->supplies),
+				      ctx->supplies);
+	if (ret < 0)
+		dev_err(ctx->dev, "failed to get regulators: %d\n", ret);
+
+	return ret;
+}
+
+static int tc358764_probe(struct mipi_dsi_device *dsi)
+{
+	struct device *dev = &dsi->dev;
+	struct tc358764 *ctx;
+	int ret;
+
+	ctx = devm_kzalloc(dev, sizeof(struct tc358764), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, ctx);
+
+	ctx->dev = dev;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST
+		| MIPI_DSI_MODE_VIDEO_AUTO_VERT;
+
+	ret = tc358764_parse_dt(ctx);
+	if (ret < 0)
+		return ret;
+
+	ret = tc358764_configure_regulators(ctx);
+	if (ret < 0)
+		return ret;
+
+	ctx->bridge.funcs = &tc358764_bridge_funcs;
+	ctx->bridge.of_node = dev->of_node;
+
+	drm_bridge_add(&ctx->bridge);
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0) {
+		drm_bridge_remove(&ctx->bridge);
+		dev_err(dev, "failed to attach dsi\n");
+	}
+
+	return ret;
+}
+
+static int tc358764_remove(struct mipi_dsi_device *dsi)
+{
+	struct tc358764 *ctx = mipi_dsi_get_drvdata(dsi);
+
+	tc358764_poweroff(ctx);
+
+	mipi_dsi_detach(dsi);
+	drm_bridge_remove(&ctx->bridge);
+
+	return 0;
+}
+
+static const struct of_device_id tc358764_of_match[] = {
+	{ .compatible = "toshiba,tc358764" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, tc358764_of_match);
+
+static struct mipi_dsi_driver tc358764_driver = {
+	.probe = tc358764_probe,
+	.remove = tc358764_remove,
+	.driver = {
+		.name = "tc358764",
+		.owner = THIS_MODULE,
+		.of_match_table = tc358764_of_match,
+	},
+};
+module_mipi_dsi_driver(tc358764_driver);
+
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_AUTHOR("Maciej Purski <m.purski@samsung.com>");
+MODULE_DESCRIPTION("MIPI-DSI based Driver for TC358764 DSI/LVDS Bridge");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox