diff mbox

[v6,3/7] regulator: add pbias regulator support

Message ID 1387456720-7202-4-git-send-email-balajitk@ti.com
State Superseded, archived
Headers show

Commit Message

balajitk@ti.com Dec. 19, 2013, 12:38 p.m. UTC
pbias register controls internal power supply to sd card i/o pads
in most OMAPs (OMAP2-5, DRA7).
Control bits for selecting voltage level and
enabling/disabling are in the same PBIAS register.

Signed-off-by: Balaji T K <balajitk@ti.com>
---
Add support for sim pbias and get pbias register offset via
standard reg property

 .../bindings/regulator/pbias-regulator.txt         |   17 ++
 drivers/regulator/Kconfig                          |    9 +
 drivers/regulator/Makefile                         |    1 +
 drivers/regulator/pbias-regulator.c                |  255 ++++++++++++++++++++
 4 files changed, 282 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/regulator/pbias-regulator.txt
 create mode 100644 drivers/regulator/pbias-regulator.c

Comments

Mark Brown Dec. 19, 2013, 12:51 p.m. UTC | #1
On Thu, Dec 19, 2013 at 06:08:36PM +0530, Balaji T K wrote:
> pbias register controls internal power supply to sd card i/o pads
> in most OMAPs (OMAP2-5, DRA7).
> Control bits for selecting voltage level and
> enabling/disabling are in the same PBIAS register.

This is basically fine from a regulator point of view but I'd like to
see some review from the OMAP side given the concerns raised last time.
Tony Lindgren Dec. 19, 2013, 4:33 p.m. UTC | #2
Looks good to me, just few minor comments below.

* Balaji T K <balajitk@ti.com> [131219 04:40]:
> --- /dev/null
> +++ b/drivers/regulator/pbias-regulator.c
> @@ -0,0 +1,255 @@
> +/*
> + * pbias-regulator.c
> + *
> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
> + * Author: Balaji T K <balajitk@ti.com>

Maybe use 2013 here instead?

> +static int pbias_regulator_enable(struct regulator_dev *rdev)
> +{
> +	struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
> +	const struct pbias_reg_info *info = data->info;
> +	int ret;
> +
> +	ret = regmap_update_bits(data->syscon, data->pbias_reg,
> +					info->enable_mask, info->enable);
> +
> +	return ret;
> +}

Do we need need to check the values after enable here? AFAIK setting
the PBIAS voltage change can also fail and that's probably why it has
also the interrupt available.

> +static const struct pbias_reg_info pbias_mmc_omap3 = {
> +	.enable = BIT(1),
> +	.enable_mask = BIT(1),
> +	.vmode = BIT(0),
> +	.name = "pbias_mmc_omap3"
> +};
> +
> +static const struct pbias_reg_info pbias_sim_omap3 = {
> +	.enable = BIT(9),
> +	.enable_mask = BIT(9),
> +	.vmode = BIT(8),
> +	.name = "pbias_sim_omap3"
> +};
> +
> +static const struct pbias_reg_info pbias_mmc_omap4 = {
> +	.enable = BIT(26) | BIT(22),
> +	.enable_mask = BIT(26) | BIT(25) | BIT(22),
> +	.vmode = BIT(21),
> +	.name = "pbias_mmc_omap4"
> +};
> +
> +static const struct pbias_reg_info pbias_mmc_omap5 = {
> +	.enable = BIT(27) | BIT(26),
> +	.enable_mask = BIT(27) | BIT(25) | BIT(26),
> +	.vmode = BIT(21),
> +	.name = "pbias_mmc_omap5"
> +};
>
> +static struct of_regulator_match pbias_matches[] = {
> +	{ .name = "pbias_mmc_omap3", .driver_data = (void *)&pbias_mmc_omap3},
> +	{ .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3},
> +	{ .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4},
> +	{ .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5},
> +};

We probably need also pbias_mmc_omap2430 as that regiter mapping is
separate from omap3?

Other than that, good to see this finally happen. This should allow us to
get rid of most of the platform data callbacks for omap_hsmmc.c driver.

Regards,

Tony
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
balajitk@ti.com Dec. 20, 2013, 9:47 a.m. UTC | #3
On Thursday 19 December 2013 10:03 PM, Tony Lindgren wrote:
> Looks good to me, just few minor comments below.
>
> * Balaji T K <balajitk@ti.com> [131219 04:40]:
>> --- /dev/null
>> +++ b/drivers/regulator/pbias-regulator.c
>> @@ -0,0 +1,255 @@
>> +/*
>> + * pbias-regulator.c
>> + *
>> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
>> + * Author: Balaji T K <balajitk@ti.com>
>
> Maybe use 2013 here instead?

yes

>
>> +static int pbias_regulator_enable(struct regulator_dev *rdev)
>> +{
>> +	struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
>> +	const struct pbias_reg_info *info = data->info;
>> +	int ret;
>> +
>> +	ret = regmap_update_bits(data->syscon, data->pbias_reg,
>> +					info->enable_mask, info->enable);
>> +
>> +	return ret;
>> +}
>
> Do we need need to check the values after enable here? AFAIK setting
> the PBIAS voltage change can also fail and that's probably why it has

failure due to mismatch in input voltage, should to be avoided and should
be taken care in s/w by the caller before pbias regulator set voltage/enable.

> also the interrupt available.
>

But interrupt was never used/tested AFAIK, there is some settling time
before the generated interrupt status is truely valid, so pbias interrupt is not
reliable.

>> +static const struct pbias_reg_info pbias_mmc_omap3 = {
>> +	.enable = BIT(1),
>> +	.enable_mask = BIT(1),
>> +	.vmode = BIT(0),
>> +	.name = "pbias_mmc_omap3"
>> +};
>> +
>> +static const struct pbias_reg_info pbias_sim_omap3 = {
>> +	.enable = BIT(9),
>> +	.enable_mask = BIT(9),
>> +	.vmode = BIT(8),
>> +	.name = "pbias_sim_omap3"
>> +};
>> +
>> +static const struct pbias_reg_info pbias_mmc_omap4 = {
>> +	.enable = BIT(26) | BIT(22),
>> +	.enable_mask = BIT(26) | BIT(25) | BIT(22),
>> +	.vmode = BIT(21),
>> +	.name = "pbias_mmc_omap4"
>> +};
>> +
>> +static const struct pbias_reg_info pbias_mmc_omap5 = {
>> +	.enable = BIT(27) | BIT(26),
>> +	.enable_mask = BIT(27) | BIT(25) | BIT(26),
>> +	.vmode = BIT(21),
>> +	.name = "pbias_mmc_omap5"
>> +};
>>
>> +static struct of_regulator_match pbias_matches[] = {
>> +	{ .name = "pbias_mmc_omap3", .driver_data = (void *)&pbias_mmc_omap3},
>> +	{ .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3},
>> +	{ .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4},
>> +	{ .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5},
>> +};
>
> We probably need also pbias_mmc_omap2430 as that regiter mapping is
> separate from omap3?
>

between omap2430 and omap3430, 3460 pbias register address are different,
other than that enable,enable_mask and vmode are
one and the same, so re-used "pbias_mmc_omap3" name and struct pbias_reg_info pbias_mmc_omap3
for omap2430 too, save one entry in of_regulator_match!

If separate name is needed for omap2430, I can add one for 2430,
and reuse the "const struct pbias_reg_info pbias_mmc_omap3" of omap3
since the bit map for enable/disable and voltage configuration will be same.
Then pbias_matches will look like.

 >> +static struct of_regulator_match pbias_matches[] = {
 >> +	{ .name = "pbias_mmc_omap2430", .driver_data = (void *)&pbias_mmc_omap3},
 >> +	{ .name = "pbias_mmc_omap3", .driver_data = (void *)&pbias_mmc_omap3},
 >> +	{ .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3},
 >> +	{ .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4},
 >> +	{ .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5},
 >> +};

Let me know if you still think that separate regulator name is needed for 2430,
I can respin this series.

> Other than that, good to see this finally happen. This should allow us to
> get rid of most of the platform data callbacks for omap_hsmmc.c driver.
>
> Regards,
>
> Tony
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tony Lindgren Dec. 20, 2013, 3:57 p.m. UTC | #4
* Balaji T K <balajitk@ti.com> [131220 01:49]:
> On Thursday 19 December 2013 10:03 PM, Tony Lindgren wrote:
> >>+static int pbias_regulator_enable(struct regulator_dev *rdev)
> >>+{
> >>+	struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
> >>+	const struct pbias_reg_info *info = data->info;
> >>+	int ret;
> >>+
> >>+	ret = regmap_update_bits(data->syscon, data->pbias_reg,
> >>+					info->enable_mask, info->enable);
> >>+
> >>+	return ret;
> >>+}
> >
> >Do we need need to check the values after enable here? AFAIK setting
> >the PBIAS voltage change can also fail and that's probably why it has
> 
> failure due to mismatch in input voltage, should to be avoided and should
> be taken care in s/w by the caller before pbias regulator set voltage/enable.
> 
> >also the interrupt available.
> >
> 
> But interrupt was never used/tested AFAIK, there is some settling time
> before the generated interrupt status is truely valid, so pbias interrupt is not
> reliable.

OK. Do we need the standard regulator property startup-delay-us for the
PBIAS regulator then? Or if it's always fixed, I guess it could be done
in the pbias_regulator_enable()?
 
> >We probably need also pbias_mmc_omap2430 as that regiter mapping is
> >separate from omap3?
> >
> 
> between omap2430 and omap3430, 3460 pbias register address are different,
> other than that enable,enable_mask and vmode are
> one and the same, so re-used "pbias_mmc_omap3" name and struct pbias_reg_info pbias_mmc_omap3
> for omap2430 too, save one entry in of_regulator_match!
> 
> If separate name is needed for omap2430, I can add one for 2430,
> and reuse the "const struct pbias_reg_info pbias_mmc_omap3" of omap3
> since the bit map for enable/disable and voltage configuration will be same.
> Then pbias_matches will look like.

If they truly are compatible, then usually the earliest revision name is
used. So I guess we should use the omap2430 naming instead of omap3 naming.
 
> >> +static struct of_regulator_match pbias_matches[] = {
> >> +	{ .name = "pbias_mmc_omap2430", .driver_data = (void *)&pbias_mmc_omap3},
> >> +	{ .name = "pbias_mmc_omap3", .driver_data = (void *)&pbias_mmc_omap3},
> >> +	{ .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3},
> >> +	{ .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4},
> >> +	{ .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5},
> >> +};
> 
> Let me know if you still think that separate regulator name is needed for 2430,
> I can respin this series.

Sounds like using the omap2430 naming would solve that.

Regards,

Tony 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Brown Dec. 20, 2013, 4:09 p.m. UTC | #5
On Fri, Dec 20, 2013 at 07:57:21AM -0800, Tony Lindgren wrote:
> * Balaji T K <balajitk@ti.com> [131220 01:49]:

> > But interrupt was never used/tested AFAIK, there is some settling time
> > before the generated interrupt status is truely valid, so pbias interrupt is not
> > reliable.

> OK. Do we need the standard regulator property startup-delay-us for the
> PBIAS regulator then? Or if it's always fixed, I guess it could be done
> in the pbias_regulator_enable()?

That delay is supposed to be the time for the startup of the supply
rather than any detection code.  It should be set using enable_time in
the driver if it's not system dependent - the property is there for
cases where the delay depends on system configuration (eg, due to the
capacitor values).
Tony Lindgren Dec. 20, 2013, 4:13 p.m. UTC | #6
* Mark Brown <broonie@kernel.org> [131220 08:10]:
> On Fri, Dec 20, 2013 at 07:57:21AM -0800, Tony Lindgren wrote:
> > * Balaji T K <balajitk@ti.com> [131220 01:49]:
> 
> > > But interrupt was never used/tested AFAIK, there is some settling time
> > > before the generated interrupt status is truely valid, so pbias interrupt is not
> > > reliable.
> 
> > OK. Do we need the standard regulator property startup-delay-us for the
> > PBIAS regulator then? Or if it's always fixed, I guess it could be done
> > in the pbias_regulator_enable()?
> 
> That delay is supposed to be the time for the startup of the supply
> rather than any detection code.  It should be set using enable_time in
> the driver if it's not system dependent - the property is there for
> cases where the delay depends on system configuration (eg, due to the
> capacitor values).

OK thanks. Let's try enable_time first then as we've had a fixed value
for years for it. If there's some difference based on the card
capacitance etc that can be added if needed.

Regards,

Tony
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
balajitk@ti.com Dec. 20, 2013, 4:44 p.m. UTC | #7
On Friday 20 December 2013 09:43 PM, Tony Lindgren wrote:
> * Mark Brown <broonie@kernel.org> [131220 08:10]:
>> On Fri, Dec 20, 2013 at 07:57:21AM -0800, Tony Lindgren wrote:
>>> * Balaji T K <balajitk@ti.com> [131220 01:49]:
>>
>>>> But interrupt was never used/tested AFAIK, there is some settling time
>>>> before the generated interrupt status is truely valid, so pbias interrupt is not
>>>> reliable.
>>
>>> OK. Do we need the standard regulator property startup-delay-us for the
>>> PBIAS regulator then? Or if it's always fixed, I guess it could be done
>>> in the pbias_regulator_enable()?
>>
>> That delay is supposed to be the time for the startup of the supply
>> rather than any detection code.  It should be set using enable_time in
>> the driver if it's not system dependent - the property is there for
>> cases where the delay depends on system configuration (eg, due to the
>> capacitor values).
>
> OK thanks. Let's try enable_time first then as we've had a fixed value
> for years for it. If there's some difference based on the card
> capacitance etc that can be added if needed.

I used regulator-enable-ramp-delay to set constraints->enable_time based on SoC.
Since it is not board dependent, I will drop regulator-enable-ramp-delay from dt and
pass the value via driver_data.

Thanks and Regards,
Balaji T K
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/regulator/pbias-regulator.txt b/Documentation/devicetree/bindings/regulator/pbias-regulator.txt
new file mode 100644
index 0000000..64352c1
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/pbias-regulator.txt
@@ -0,0 +1,17 @@ 
+PBIAS internal regulator for SD card dual voltage i/o pads on OMAP SoCs.
+
+Required properties:
+- compatible:
+  - "ti,pbias-omap" for OMAP2, OMAP3, OMAP4, OMAP5, DRA7
+
+Optional properties:
+- Any optional property defined in bindings/regulator/regulator.txt
+
+Example:
+
+		pbias_regulator: pbias_regulator {
+			regulator-name = "pbias_mmc_omap4";
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <3000000>;
+			regulator-enable-ramp-delay = <100>;
+	};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index ce785f4..741e8fb 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -369,6 +369,15 @@  config REGULATOR_PALMAS
 	  on the muxing. This is handled automatically in the driver by
 	  reading the mux info from OTP.
 
+config REGULATOR_PBIAS
+	tristate "PBIAS OMAP regulator driver"
+	depends on (ARCH_OMAP || COMPILE_TEST) && MFD_SYSCON
+	help
+	 Say y here to support pbias regulator for mmc1:SD card i/o
+	 on OMAP SoCs.
+	 This driver provides support for OMAP pbias modelled
+	 regulators.
+
 config REGULATOR_PCAP
 	tristate "Motorola PCAP2 regulator driver"
 	depends on EZX_PCAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 01c597e..16000fa 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -52,6 +52,7 @@  obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
 obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
 obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
 obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
+obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
 obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
 obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
 obj-$(CONFIG_REGULATOR_RC5T583)  += rc5t583-regulator.o
diff --git a/drivers/regulator/pbias-regulator.c b/drivers/regulator/pbias-regulator.c
new file mode 100644
index 0000000..42883a0
--- /dev/null
+++ b/drivers/regulator/pbias-regulator.c
@@ -0,0 +1,255 @@ 
+/*
+ * pbias-regulator.c
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Balaji T K <balajitk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+struct pbias_reg_info {
+	u32 enable;
+	u32 enable_mask;
+	u32 vmode;
+	char *name;
+};
+
+struct pbias_regulator_data {
+	struct regulator_desc desc;
+	void __iomem *pbias_addr;
+	unsigned int pbias_reg;
+	struct regulator_dev *dev;
+	struct regmap *syscon;
+	const struct pbias_reg_info *info;
+	int voltage;
+};
+
+static int pbias_regulator_set_voltage(struct regulator_dev *dev,
+			int min_uV, int max_uV, unsigned *selector)
+{
+	struct pbias_regulator_data *data = rdev_get_drvdata(dev);
+	const struct pbias_reg_info *info = data->info;
+	int ret, vmode;
+
+	if (min_uV <= 1800000)
+		vmode = 0;
+	else if (min_uV > 1800000)
+		vmode = info->vmode;
+
+	ret = regmap_update_bits(data->syscon, data->pbias_reg,
+						info->vmode, vmode);
+
+	return ret;
+}
+
+static int pbias_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+	const struct pbias_reg_info *info = data->info;
+	int value, voltage;
+
+	regmap_read(data->syscon, data->pbias_reg, &value);
+	value &= info->vmode;
+
+	voltage = value ? 3000000 : 1800000;
+
+	return voltage;
+}
+
+static int pbias_regulator_enable(struct regulator_dev *rdev)
+{
+	struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+	const struct pbias_reg_info *info = data->info;
+	int ret;
+
+	ret = regmap_update_bits(data->syscon, data->pbias_reg,
+					info->enable_mask, info->enable);
+
+	return ret;
+}
+
+static int pbias_regulator_disable(struct regulator_dev *rdev)
+{
+	struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+	const struct pbias_reg_info *info = data->info;
+	int ret;
+
+	ret = regmap_update_bits(data->syscon, data->pbias_reg,
+						info->enable_mask, 0);
+	return ret;
+}
+
+static int pbias_regulator_is_enable(struct regulator_dev *rdev)
+{
+	struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+	const struct pbias_reg_info *info = data->info;
+	int value;
+
+	regmap_read(data->syscon, data->pbias_reg, &value);
+
+	return (value & info->enable_mask) == info->enable_mask;
+}
+
+static struct regulator_ops pbias_regulator_voltage_ops = {
+	.set_voltage	= pbias_regulator_set_voltage,
+	.get_voltage	= pbias_regulator_get_voltage,
+	.enable		= pbias_regulator_enable,
+	.disable	= pbias_regulator_disable,
+	.is_enabled	= pbias_regulator_is_enable,
+};
+
+static const struct pbias_reg_info pbias_mmc_omap3 = {
+	.enable = BIT(1),
+	.enable_mask = BIT(1),
+	.vmode = BIT(0),
+	.name = "pbias_mmc_omap3"
+};
+
+static const struct pbias_reg_info pbias_sim_omap3 = {
+	.enable = BIT(9),
+	.enable_mask = BIT(9),
+	.vmode = BIT(8),
+	.name = "pbias_sim_omap3"
+};
+
+static const struct pbias_reg_info pbias_mmc_omap4 = {
+	.enable = BIT(26) | BIT(22),
+	.enable_mask = BIT(26) | BIT(25) | BIT(22),
+	.vmode = BIT(21),
+	.name = "pbias_mmc_omap4"
+};
+
+static const struct pbias_reg_info pbias_mmc_omap5 = {
+	.enable = BIT(27) | BIT(26),
+	.enable_mask = BIT(27) | BIT(25) | BIT(26),
+	.vmode = BIT(21),
+	.name = "pbias_mmc_omap5"
+};
+
+static struct of_regulator_match pbias_matches[] = {
+	{ .name = "pbias_mmc_omap3", .driver_data = (void *)&pbias_mmc_omap3},
+	{ .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3},
+	{ .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4},
+	{ .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5},
+};
+#define PBIAS_NUM_REGS	ARRAY_SIZE(pbias_matches)
+
+static const struct of_device_id pbias_of_match[] = {
+	{ .compatible = "ti,pbias-omap", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, pbias_of_match);
+
+static int pbias_regulator_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *dev_node;
+	struct pbias_regulator_data *drvdata;
+	struct resource *res;
+	struct regulator_config cfg = { };
+	struct regmap *syscon;
+	const struct pbias_reg_info *info;
+	int ret = 0;
+	int count, idx, data_idx = 0;
+
+	count = of_regulator_match(&pdev->dev, np, pbias_matches,
+						PBIAS_NUM_REGS);
+	if (count < 0)
+		return count;
+
+	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pbias_regulator_data)
+			       * count, GFP_KERNEL);
+	if (drvdata == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate device data\n");
+		return -ENOMEM;
+	}
+
+	dev_node = of_get_parent(np);
+	if (!dev_node)
+		return -ENODEV;
+
+	syscon = syscon_node_to_regmap(dev_node);
+	of_node_put(dev_node);
+	if (IS_ERR(syscon))
+		return PTR_ERR(syscon);
+
+	cfg.dev = &pdev->dev;
+
+	for (idx = 0; idx < PBIAS_NUM_REGS, data_idx < count; idx++) {
+		if (!pbias_matches[idx].init_data ||
+			!pbias_matches[idx].of_node)
+			continue;
+
+		info = pbias_matches[idx].driver_data;
+		if (!info)
+			return -ENODEV;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		if (!res)
+			return -EINVAL;
+
+		drvdata[data_idx].pbias_reg = res->start;
+		drvdata[data_idx].syscon = syscon;
+		drvdata[data_idx].info = info;
+		drvdata[data_idx].desc.name = info->name;
+		drvdata[data_idx].desc.owner = THIS_MODULE;
+		drvdata[data_idx].desc.type = REGULATOR_VOLTAGE;
+		drvdata[data_idx].desc.ops = &pbias_regulator_voltage_ops;
+		drvdata[data_idx].desc.n_voltages = 2;
+
+		cfg.init_data = pbias_matches[idx].init_data;
+		cfg.driver_data = &drvdata[data_idx];
+		cfg.of_node = pbias_matches[idx].of_node;
+
+		drvdata[data_idx].dev = devm_regulator_register(&pdev->dev,
+					&drvdata[data_idx].desc, &cfg);
+		if (IS_ERR(drvdata[data_idx].dev)) {
+			ret = PTR_ERR(drvdata[data_idx].dev);
+			dev_err(&pdev->dev,
+				"Failed to register regulator: %d\n", ret);
+			goto err_regulator;
+		}
+		data_idx++;
+	}
+
+	platform_set_drvdata(pdev, drvdata);
+
+err_regulator:
+	return ret;
+}
+
+static struct platform_driver pbias_regulator_driver = {
+	.probe		= pbias_regulator_probe,
+	.driver		= {
+		.name		= "pbias-regulator",
+		.owner		= THIS_MODULE,
+		.of_match_table = of_match_ptr(pbias_of_match),
+	},
+};
+
+module_platform_driver(pbias_regulator_driver);
+
+MODULE_AUTHOR("Balaji T K <balajitk@ti.com>");
+MODULE_DESCRIPTION("pbias voltage regulator");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pbias-regulator");