2011-04-06 12:49:52

by Ashish Jangam

[permalink] [raw]
Subject: [PATCHv1 5/11] REGULATOR: Regulator module of DA9052 PMIC driver

Hi Mark,

Regulator Driver for Dialog Semiconductor DA9052 PMICs.

Changes made since last submission:
. read and write operation moved to MFD
. added macros

Some additional information of this patch:
. This patch is tested with the Samsung 6410 board using sysfs interface.

Linux Kernel Version: 2.6.37

Signed-off-by: D. Chen <[email protected]>
---
diff -Naur orig_linux-2.6.37/drivers/regulator/da9052-regulator.c linux-2.6.37/drivers/regulator/da9052-regulator.c
--- orig_linux-2.6.37/drivers/regulator/da9052-regulator.c 1970-01-01 05:00:00.000000000 +0500
+++ linux-2.6.37/drivers/regulator/da9052-regulator.c 2011-03-31 21:49:02.000000000 +0500
@@ -0,0 +1,442 @@
+/*
+* da9052-regulator.c: Regulator driver for DA9052
+*
+* Copyright(c) 2009 Dialog Semiconductor Ltd.
+*
+*Author: Dajun Chen <[email protected]>
+*
+* 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; either version 2 of the License, or
+* (at your option) any later version.
+*
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/pm.h>
+
+/* LDO and Buck index */
+#define DA9052_BUCK_CORE 0
+#define DA9052_BUCK_PRO 1
+#define DA9052_BUCK_MEM 2
+#define DA9052_BUCK_PERI 3
+
+#define DA9052_LDO2 4
+#define DA9052_LDO3 5
+
+/* Buck step size Macros */
+#define DA9052_BUCK_PERI_3uV_STEP 100000
+#define DA9052_BUCK_PERI_REG_MAP_UPTO_3uV 24
+#define DA9052_CONST_3uV 3000000
+
+static struct regulator_ops da9052_regulator_ops;
+
+static struct regulator_consumer_supply da9052_vddarm_consumers[] = {
+ {
+ .supply = "vcc",
+ }
+};
+
+#define DA9052_BUCKCORE_INIT(max, min) \
+{\
+ .constraints = {\
+ .max_uV = (max) * 1000,\
+ .min_uV = (min) * 1000,\
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE\
+ |REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE,\
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,\
+ },\
+ .num_consumer_supplies = ARRAY_SIZE(da9052_vddarm_consumers),\
+ .consumer_supplies = da9052_vddarm_consumers,\
+}
+
+
+#define DA9052_REGULATOR_INIT(max, min) \
+{\
+ .constraints = {\
+ .max_uV = (max) * 1000,\
+ .min_uV = (min) * 1000,\
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE\
+ |REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE,\
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,\
+ },\
+}
+
+struct regulator_init_data da9052_regulators_init[] = {
+ /* BUCKS */
+ DA9052_BUCKCORE_INIT(2075, 500),
+ DA9052_REGULATOR_INIT(2075, 500),
+ DA9052_REGULATOR_INIT(2500, 925),
+ DA9052_REGULATOR_INIT(3600, 1800),
+
+ /* LDO */
+ DA9052_REGULATOR_INIT(1800, 600),
+ DA9052_REGULATOR_INIT(1800, 600),
+ DA9052_REGULATOR_INIT(3300, 1725),
+ DA9052_REGULATOR_INIT(3300, 1725),
+ DA9052_REGULATOR_INIT(3600, 1200),
+ DA9052_REGULATOR_INIT(3600, 1200),
+ DA9052_REGULATOR_INIT(3600, 1200),
+ DA9052_REGULATOR_INIT(3600, 1200),
+ DA9052_REGULATOR_INIT(3650, 1250),
+ DA9052_REGULATOR_INIT(3600, 1200),
+
+};
+
+
+#define DA9052_LDO(_id, step, sbits, ebits) \
+{\
+ .reg_desc = {\
+ .name = "LDO" #_id,\
+ .ops = &da9052_regulator_ops,\
+ .type = REGULATOR_VOLTAGE,\
+ .id = _id,\
+ .owner = THIS_MODULE,\
+ },\
+ .step_uV = (step) * 1000,\
+ .volt_shift = (sbits),\
+ .enable_bit = (ebits),\
+}
+
+#define DA9052_DCDC(_id, step, sbits, ebits) \
+{\
+ .reg_desc = {\
+ .name = "BUCK" #_id,\
+ .ops = &da9052_regulator_ops,\
+ .type = REGULATOR_VOLTAGE,\
+ .id = _id,\
+ .owner = THIS_MODULE,\
+ },\
+ .step_uV = (step) * 1000,\
+ .volt_shift = (sbits),\
+ .enable_bit = (ebits),\
+}
+
+struct da9052_regulator_info {
+ struct regulator_desc reg_desc;
+ int step_uV;
+ unsigned char volt_shift;
+ unsigned char enable_bit;
+};
+
+struct da9052_regulator {
+ struct da9052 *da9052;
+ struct regulator_dev *regulators[];
+};
+
+struct da9052_regulator_info da9052_regulator_info[] = {
+ /* Buck1 - 4*/
+ DA9052_DCDC(0, 25, 6, 6),
+
+ DA9052_DCDC(1, 25, 6, 6),
+
+ DA9052_DCDC(2, 25, 6, 6),
+
+ DA9052_DCDC(3, 50, 5, 6),
+
+ /* LD01 - LDO10*/
+ DA9052_LDO(4, 50, 5, 6),
+
+ DA9052_LDO(5 , 25, 6, 6),
+
+ DA9052_LDO(6, 25, 6, 6),
+
+ DA9052_LDO(7, 25, 6, 6),
+
+ DA9052_LDO(8, 50, 6, 6),
+
+ DA9052_LDO(9, 50, 6, 6),
+
+ DA9052_LDO(10, 50, 6, 6),
+
+ DA9052_LDO(11, 50, 6, 6),
+
+ DA9052_LDO(12, 50, 6, 6),
+
+ DA9052_LDO(13, 50, 6, 6),
+
+};
+
+static inline int da9052_regulator_uvolts_to_regVal(struct regulator_dev *rdev,
+ unsigned int val)
+{
+ struct regulation_constraints *constraints = rdev->constraints;
+ int offset = rdev_get_id(rdev);
+ int reg_val = 0;
+
+ /* Care for the varying step size of BUCK PERI */
+ if ((offset == DA9052_BUCK_PERI) && (val >= DA9052_CONST_3uV)) {
+ reg_val = (DA9052_CONST_3uV - constraints->min_uV)/
+ (da9052_regulator_info[offset].step_uV);
+ reg_val += ((val - DA9052_CONST_3uV) /
+ (DA9052_BUCK_PERI_3uV_STEP));
+ } else{
+ reg_val = (val - constraints->min_uV)/
+ (da9052_regulator_info[offset].step_uV);
+
+ /* Validate reg_val */
+ if ((reg_val * da9052_regulator_info[offset].step_uV) +
+ constraints->min_uV > constraints->max_uV)
+ return -EINVAL;
+ }
+
+ return reg_val;
+
+}
+
+static inline int da9052_regulator_val_to_uvolts(struct regulator_dev *rdev,
+ unsigned int regval)
+{
+ struct regulation_constraints *constraints = rdev->constraints;
+ int offset = rdev_get_id(rdev);
+ int regval_uV = 0;
+
+ if (offset == DA9052_BUCK_PERI) {
+ if (regval >= DA9052_BUCK_PERI_REG_MAP_UPTO_3uV) {
+ regval_uV = ((DA9052_BUCK_PERI_REG_MAP_UPTO_3uV *
+ da9052_regulator_info[offset].step_uV)
+ + constraints->min_uV);
+ regval_uV += (regval -
+ DA9052_BUCK_PERI_REG_MAP_UPTO_3uV)
+ *(DA9052_BUCK_PERI_3uV_STEP);
+ } else {
+ regval_uV =
+ (regval * da9052_regulator_info[offset].step_uV)
+ + constraints->min_uV;
+ }
+ } else {
+ regval_uV = (regval * da9052_regulator_info[offset].step_uV) +
+ constraints->min_uV;
+ }
+
+ return regval_uV;
+}
+
+static int da9052_regulator_enable(struct regulator_dev *rdev)
+{
+ struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
+ int offset = rdev_get_id(rdev);
+ int ret = 0;
+
+ ret = da9052_reg_update(regulator->da9052,
+ DA9052_BUCKCORE_REG + offset,
+ 1 << da9052_regulator_info[offset].enable_bit,
+ 1 << da9052_regulator_info[offset].enable_bit);
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int da9052_regulator_disable(struct regulator_dev *rdev)
+{
+ struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
+ int offset = rdev_get_id(rdev);
+ int ret = 0;
+
+ ret = da9052_reg_update(regulator->da9052,
+ DA9052_BUCKCORE_REG + offset,
+ 1 << da9052_regulator_info[offset].enable_bit,
+ 0);
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int da9052_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
+ int offset = rdev_get_id(rdev);
+ int ret;
+
+ ret = da9052_reg_read(regulator->da9052,
+ DA9052_BUCKCORE_REG + offset);
+
+ if (ret < 0)
+ return -EIO;
+
+ return ret & (1 << da9052_regulator_info[offset].enable_bit);
+
+}
+
+static int da9052_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV)
+{
+ struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
+ struct regulation_constraints *constraints = rdev->constraints;
+ int offset = rdev_get_id(rdev);
+ int ret;
+ int reg_val = 0;
+
+ /* Compare voltage range */
+ if (min_uV > max_uV)
+ return -EINVAL;
+
+ if (min_uV < constraints->min_uV || min_uV > constraints->max_uV)
+ return -EINVAL;
+ if (max_uV < constraints->min_uV || max_uV > constraints->max_uV)
+ return -EINVAL;
+
+ reg_val = da9052_regulator_uvolts_to_regVal(rdev, min_uV);
+
+ ret = da9052_reg_update(regulator->da9052,
+ DA9052_BUCKCORE_REG + offset,
+ (1 << da9052_regulator_info[offset].volt_shift) - 1,
+ reg_val);
+ if (ret < 0)
+ return -EIO;
+
+ /* Set the GO LDO/BUCk bits so that the voltage changes */
+ switch (offset) {
+ case DA9052_LDO2:
+ ret = da9052_reg_update(regulator->da9052,
+ DA9052_SUPPLY_REG, 0,
+ DA9052_SUPPLY_VLDO2GO);
+ if (ret < 0)
+ return -EIO;
+ break;
+ case DA9052_LDO3:
+ ret = da9052_reg_update(regulator->da9052,
+ DA9052_SUPPLY_REG, 0,
+ DA9052_SUPPLY_VLDO3GO);
+ if (ret < 0)
+ return -EIO;
+ break;
+ case DA9052_BUCK_CORE:
+ ret = da9052_reg_update(regulator->da9052,
+ DA9052_SUPPLY_REG, 0,
+ DA9052_SUPPLY_VBCOREGO);
+ if (ret < 0)
+ return -EIO;
+ break;
+ case DA9052_BUCK_PRO:
+ ret = da9052_reg_update(regulator->da9052,
+ DA9052_SUPPLY_REG, 0,
+ DA9052_SUPPLY_VBPROGO);
+ if (ret < 0)
+ return -EIO;
+ break;
+ case DA9052_BUCK_MEM:
+ ret = da9052_reg_update(regulator->da9052,
+ DA9052_SUPPLY_REG, 0,
+ DA9052_SUPPLY_VBMEMGO);
+ if (ret < 0)
+ return -EIO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int da9052_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
+ int offset = rdev_get_id(rdev);
+ int ret;
+
+ ret = da9052_reg_read(regulator->da9052,
+ DA9052_BUCKCORE_REG + offset);
+ if (ret < 0)
+ return -EIO;
+
+ return da9052_regulator_val_to_uvolts(rdev,
+ ret &
+ ((1 << da9052_regulator_info[offset].volt_shift) - 1));
+}
+
+static struct regulator_ops da9052_regulator_ops = {
+ .is_enabled = da9052_regulator_is_enabled,
+ .enable = da9052_regulator_enable,
+ .disable = da9052_regulator_disable,
+ .get_voltage = da9052_regulator_get_voltage,
+ .set_voltage = da9052_regulator_set_voltage,
+};
+
+static int __devinit da9052_regulator_probe(struct platform_device *pdev)
+{
+ struct da9052_regulator *regulator;
+ struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_init_data *init_data;
+ int i, ret = 0;
+
+ regulator = kzalloc(sizeof(struct da9052_regulator), GFP_KERNEL);
+ if (!regulator)
+ return -ENOMEM;
+
+ regulator->da9052 = da9052;
+
+ for (i = 0; i < ARRAY_SIZE(da9052_regulator_info); i++) {
+ init_data = &da9052_regulators_init[i];
+ init_data->driver_data = da9052;
+ pdev->dev.platform_data = init_data;
+ regulator->regulators[i] = regulator_register(
+ &da9052_regulator_info[i].reg_desc,
+ &pdev->dev,
+ pdev->dev.platform_data,
+ regulator
+ );
+ if (IS_ERR(regulator->regulators[i])) {
+ ret = PTR_ERR(regulator->regulators[i]);
+ dev_err(da9052->dev, "Failed to register regulator[%d] \
+ %d \n", i+1, ret);
+ goto err;
+ }
+ }
+
+ platform_set_drvdata(pdev, regulator);
+
+ return 0;
+err:
+ while (--i >= 0)
+ regulator_unregister(regulator->regulators[i]);
+ kfree(regulator);
+ return ret;
+}
+
+static int __devexit da9052_regulator_remove(struct platform_device *pdev)
+{
+ struct da9052_regulator *regulator = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(da9052_regulator_info); i++)
+ regulator_unregister(regulator->regulators[i]);
+
+ kfree(regulator);
+ return 0;
+}
+
+static struct platform_driver da9052_regulator_driver = {
+ .driver.name = "da9052-regulator",
+ .driver.owner = THIS_MODULE,
+ .probe = da9052_regulator_probe,
+ .remove = __devexit_p(da9052_regulator_remove),
+};
+
+static int __init da9052_regulator_init(void)
+{
+ return platform_driver_register(&da9052_regulator_driver);
+}
+subsys_initcall(da9052_regulator_init);
+
+static void __exit da9052_regulator_exit(void)
+{
+ platform_driver_unregister(&da9052_regulator_driver);
+}
+module_exit(da9052_regulator_exit);
+
+MODULE_AUTHOR("David Dajun Chen <[email protected]>");
+MODULE_DESCRIPTION("Power Regulator driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:da9052-regulator");
diff -Naur orig_linux-2.6.37/drivers/regulator/Kconfig linux-2.6.37/drivers/regulator/Kconfig
--- orig_linux-2.6.37/drivers/regulator/Kconfig 2011-01-05 05:50:19.000000000 +0500
+++ linux-2.6.37/drivers/regulator/Kconfig 2011-03-31 21:48:51.000000000 +0500
@@ -158,6 +158,13 @@
Say y here to support the BUCKs and LDOs regulators found on
Dialog Semiconductor DA9030/DA9034 PMIC.

+config REGULATOR_DA9052
+ tristate "Dialog DA9052 regulators"
+ depends on PMIC_DA9052
+ help
+ Say y here to support the BUCKs and LDOs regulators found on
+ Dialog Semiconductor DA9052 PMIC.
+
config REGULATOR_PCF50633
tristate "PCF50633 regulator driver"
depends on MFD_PCF50633
diff -Naur orig_linux-2.6.37/drivers/regulator/Makefile linux-2.6.37/drivers/regulator/Makefile
--- orig_linux-2.6.37/drivers/regulator/Makefile 2011-01-05 05:50:19.000000000 +0500
+++ linux-2.6.37/drivers/regulator/Makefile 2011-03-31 21:48:45.000000000 +0500
@@ -27,6 +27,7 @@
obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
+obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o

Regards,
Ashish


????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?


2011-04-06 14:05:23

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCHv1 5/11] REGULATOR: Regulator module of DA9052 PMIC driver

On Wed, Apr 06, 2011 at 06:19:15PM +0530, Ashish Jangam wrote:

> Linux Kernel Version: 2.6.37

There is no point in submitting against old kernel versions, new drivers
can only be included in current development kernls (current development
targets 2.6.40). The 2.6.37 code is getting on for a year out of date
here. There's some API updates required before the driver will compile
with current kernels.

> +struct da9052_regulator_info da9052_regulator_info[] = {
> + /* Buck1 - 4*/
> + DA9052_DCDC(0, 25, 6, 6),
> +
> + DA9052_DCDC(1, 25, 6, 6),
> +
> + DA9052_DCDC(2, 25, 6, 6),
> +
> + DA9052_DCDC(3, 50, 5, 6),

You should be able to loose a lot of the blank lines in this table.

> +static inline int da9052_regulator_uvolts_to_regVal(struct regulator_dev *rdev,
> + unsigned int val)

MixedCase identifiers aren't the Linux kernel style.

> +static int da9052_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
> + int max_uV)
> +{
> + struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
> + struct regulation_constraints *constraints = rdev->constraints;
> + int offset = rdev_get_id(rdev);
> + int ret;
> + int reg_val = 0;
> +
> + /* Compare voltage range */
> + if (min_uV > max_uV)
> + return -EINVAL;
> +
> + if (min_uV < constraints->min_uV || min_uV > constraints->max_uV)
> + return -EINVAL;
> + if (max_uV < constraints->min_uV || max_uV > constraints->max_uV)
> + return -EINVAL;

The core will take care of all this for you.

> + /* Set the GO LDO/BUCk bits so that the voltage changes */
> + switch (offset) {
> + case DA9052_LDO2:
> + ret = da9052_reg_update(regulator->da9052,
> + DA9052_SUPPLY_REG, 0,
> + DA9052_SUPPLY_VLDO2GO);
> + if (ret < 0)
> + return -EIO;
> + break;

This case statement looks like it should be looking up information in
the driver private data instead.

> +static struct regulator_ops da9052_regulator_ops = {
> + .is_enabled = da9052_regulator_is_enabled,
> + .enable = da9052_regulator_enable,
> + .disable = da9052_regulator_disable,
> + .get_voltage = da9052_regulator_get_voltage,
> + .set_voltage = da9052_regulator_set_voltage,
> +};

You should also implement list_voltage().

> + for (i = 0; i < ARRAY_SIZE(da9052_regulator_info); i++) {
> + init_data = &da9052_regulators_init[i];
> + init_data->driver_data = da9052;
> + pdev->dev.platform_data = init_data;
> + regulator->regulators[i] = regulator_register(
> + &da9052_regulator_info[i].reg_desc,
> + &pdev->dev,
> + pdev->dev.platform_data,
> + regulator
> + );
> + if (IS_ERR(regulator->regulators[i])) {
> + ret = PTR_ERR(regulator->regulators[i]);
> + dev_err(da9052->dev, "Failed to register regulator[%d] \
> + %d \n", i+1, ret);
> + goto err;
> + }

It's easier and less error prone to just register all the regulators
then look the constraints up in a flat array.