linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V2] regulator: pv88060: new regulator driver
@ 2015-11-19  0:59 James Ban
  2015-11-20 18:20 ` Applied "regulator: pv88060: new regulator driver" to the regulator tree Mark Brown
  0 siblings, 1 reply; 2+ messages in thread
From: James Ban @ 2015-11-19  0:59 UTC (permalink / raw)
  To: LINUXKERNEL, Liam Girdwood, Mark Brown
  Cc: David Dajun Chen, Support Opensource


From: James Ban <James.Ban.opensource@diasemi.com>

This is the driver for the Powerventure PV88060 BUCKs and LDOs regulator.
It communicates via an I2C bus to the device.

Signed-off-by: James Ban <James.Ban.opensource@diasemi.com>

---
Changes since PATCH V1
- Removed parsing the DT manually.
- Removed setting the constraints in driver.
- Removed printing an error after parsing dt.

This patch applies against linux-next and next-20151115 


 .../devicetree/bindings/regulator/pv88060.txt      |  124 ++++++
 drivers/regulator/Kconfig                          |    8 +
 drivers/regulator/Makefile                         |    1 +
 drivers/regulator/pv88060-regulator.c              |  437 ++++++++++++++++++++
 drivers/regulator/pv88060-regulator.h              |   69 ++++
 5 files changed, 639 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/pv88060.txt
 create mode 100644 drivers/regulator/pv88060-regulator.c
 create mode 100644 drivers/regulator/pv88060-regulator.h

diff --git a/Documentation/devicetree/bindings/regulator/pv88060.txt b/Documentation/devicetree/bindings/regulator/pv88060.txt
new file mode 100644
index 0000000..10a6dad
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/pv88060.txt
@@ -0,0 +1,124 @@
+* Powerventure Semiconductor PV88060 Voltage Regulator
+
+Required properties:
+- compatible: "pvs,pv88060".
+- reg: I2C slave address, usually 0x49.
+- interrupts: the interrupt outputs of the controller
+- regulators: A node that houses a sub-node for each regulator within the
+  device. Each sub-node is identified using the node's name, with valid
+  values listed below. The content of each sub-node is defined by the
+  standard binding for regulators; see regulator.txt.
+  BUCK1, LDO1, LDO2, LDO3, LDO4, LDO5, LDO6, LDO7, SW1, SW2, SW3, SW4,
+  SW5, and SW6.
+
+Optional properties:
+- Any optional property defined in regulator.txt
+
+Example
+
+	pmic: pv88060@49 {
+		compatible = "pvs,pv88060";
+		reg = <0x49>;
+		interrupt-parent = <&gpio>;
+		interrupts = <24 24>;
+
+		regulators {
+			BUCK1 {
+				regulator-name = "buck1";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <4387500>;
+				regulator-min-microamp 	= <1496000>;
+				regulator-max-microamp 	= <4189000>;
+				regulator-boot-on;
+			};
+
+			LDO1 {
+				regulator-name = "ldo1";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO2 {
+				regulator-name = "ldo2";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO3 {
+				regulator-name = "ldo3";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO4 {
+				regulator-name = "ldo4";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO5 {
+				regulator-name = "ldo5";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO6 {
+				regulator-name = "ldo6";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO7 {
+				regulator-name = "ldo7";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			SW1 {
+				regulator-name = "sw1";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+			};
+
+			SW2 {
+				regulator-name = "sw2";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+				regulator-boot-on;
+			};
+
+			SW3 {
+				regulator-name = "sw3";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+				regulator-boot-on;
+			};
+
+			SW4 {
+				regulator-name = "sw4";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+				regulator-boot-on;
+			};
+
+			SW5 {
+				regulator-name = "sw5";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+				regulator-boot-on;
+			};
+
+			SW6 {
+				regulator-name = "sw6";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+			};
+		};
+	};
\ No newline at end of file
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 8df0b0e..c61f72f 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -504,6 +504,14 @@ config REGULATOR_PFUZE100
 	  Say y here to support the regulators found on the Freescale
 	  PFUZE100/PFUZE200 PMIC.
 
+config REGULATOR_PV88060
+	tristate "Powerventure Semiconductor PV88060 regulator"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say y here to support the voltage regulators and convertors
+	  PV88060
+
 config REGULATOR_PWM
 	tristate "PWM voltage regulator"
 	depends on PWM
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 0f81749..b11c8a4 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
 obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
 obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
+obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o
 obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
 obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
 obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
diff --git a/drivers/regulator/pv88060-regulator.c b/drivers/regulator/pv88060-regulator.c
new file mode 100644
index 0000000..60b16d8
--- /dev/null
+++ b/drivers/regulator/pv88060-regulator.c
@@ -0,0 +1,437 @@
+/*
+ * pv88060-regulator.c - Regulator device driver for PV88060
+ * Copyright (C) 2015  Powerventure Semiconductor Ltd.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include "pv88060-regulator.h"
+
+#define PV88060_MAX_REGULATORS	14
+
+/* PV88060 REGULATOR IDs */
+enum {
+	/* BUCKs */
+	PV88060_ID_BUCK1,
+
+	/* LDOs */
+	PV88060_ID_LDO1,
+	PV88060_ID_LDO2,
+	PV88060_ID_LDO3,
+	PV88060_ID_LDO4,
+	PV88060_ID_LDO5,
+	PV88060_ID_LDO6,
+	PV88060_ID_LDO7,
+
+	/* SWTs */
+	PV88060_ID_SW1,
+	PV88060_ID_SW2,
+	PV88060_ID_SW3,
+	PV88060_ID_SW4,
+	PV88060_ID_SW5,
+	PV88060_ID_SW6,
+};
+
+struct pv88060_regulator {
+	struct regulator_desc desc;
+	/* Current limiting */
+	unsigned	n_current_limits;
+	const int	*current_limits;
+	unsigned int limit_mask;
+	unsigned int conf;		/* buck configuration register */
+};
+
+struct pv88060 {
+	struct device *dev;
+	struct regmap *regmap;
+	struct regulator_dev *rdev[PV88060_MAX_REGULATORS];
+};
+
+static const struct regmap_config pv88060_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+/* Current limits array (in uA) for BUCK1
+ * Entry indexes corresponds to register values.
+ */
+
+static const int pv88060_buck1_limits[] = {
+	1496000, 2393000, 3291000, 4189000
+};
+
+static unsigned int pv88060_buck_get_mode(struct regulator_dev *rdev)
+{
+	struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+	unsigned int data;
+	int ret, mode = 0;
+
+	ret = regmap_read(rdev->regmap, info->conf, &data);
+	if (ret < 0)
+		return ret;
+
+	switch (data & PV88060_BUCK_MODE_MASK) {
+	case PV88060_BUCK_MODE_SYNC:
+		mode = REGULATOR_MODE_FAST;
+		break;
+	case PV88060_BUCK_MODE_AUTO:
+		mode = REGULATOR_MODE_NORMAL;
+		break;
+	case PV88060_BUCK_MODE_SLEEP:
+		mode = REGULATOR_MODE_STANDBY;
+		break;
+	}
+
+	return mode;
+}
+
+static int pv88060_buck_set_mode(struct regulator_dev *rdev,
+					unsigned int mode)
+{
+	struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+	int val = 0;
+
+	switch (mode) {
+	case REGULATOR_MODE_FAST:
+		val = PV88060_BUCK_MODE_SYNC;
+		break;
+	case REGULATOR_MODE_NORMAL:
+		val = PV88060_BUCK_MODE_AUTO;
+		break;
+	case REGULATOR_MODE_STANDBY:
+		val = PV88060_BUCK_MODE_SLEEP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return regmap_update_bits(rdev->regmap, info->conf,
+					PV88060_BUCK_MODE_MASK, val);
+}
+
+static int pv88060_set_current_limit(struct regulator_dev *rdev, int min,
+				    int max)
+{
+	struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+	int i;
+
+	/* search for closest to maximum */
+	for (i = info->n_current_limits; i >= 0; i--) {
+		if (min <= info->current_limits[i]
+			&& max >= info->current_limits[i]) {
+			return regmap_update_bits(rdev->regmap,
+				info->conf,
+				info->limit_mask,
+				i << PV88060_BUCK_ILIM_SHIFT);
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int pv88060_get_current_limit(struct regulator_dev *rdev)
+{
+	struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+	unsigned int data;
+	int ret;
+
+	ret = regmap_read(rdev->regmap, info->conf, &data);
+	if (ret < 0)
+		return ret;
+
+	data = (data & info->limit_mask) >> PV88060_BUCK_ILIM_SHIFT;
+	return info->current_limits[data];
+}
+
+static struct regulator_ops pv88060_buck_ops = {
+	.get_mode = pv88060_buck_get_mode,
+	.set_mode = pv88060_buck_set_mode,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.list_voltage = regulator_list_voltage_linear,
+	.set_current_limit = pv88060_set_current_limit,
+	.get_current_limit = pv88060_get_current_limit,
+};
+
+static struct regulator_ops pv88060_ldo_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.list_voltage = regulator_list_voltage_linear,
+};
+
+#define PV88060_BUCK(chip, regl_name, min, step, max, limits_array) \
+{\
+	.desc	=	{\
+		.id = chip##_ID_##regl_name,\
+		.name = __stringify(chip##_##regl_name),\
+		.of_match = of_match_ptr(#regl_name),\
+		.regulators_node = of_match_ptr("regulators"),\
+		.type = REGULATOR_VOLTAGE,\
+		.owner = THIS_MODULE,\
+		.ops = &pv88060_buck_ops,\
+		.min_uV = min,\
+		.uV_step = step,\
+		.n_voltages = ((max) - (min))/(step) + 1,\
+		.enable_reg = PV88060_REG_##regl_name##_CONF0,\
+		.enable_mask = PV88060_BUCK_EN, \
+		.vsel_reg = PV88060_REG_##regl_name##_CONF0,\
+		.vsel_mask = PV88060_VBUCK_MASK,\
+	},\
+	.current_limits = limits_array,\
+	.n_current_limits = ARRAY_SIZE(limits_array),\
+	.limit_mask = PV88060_BUCK_ILIM_MASK, \
+	.conf = PV88060_REG_##regl_name##_CONF1,\
+}
+
+#define PV88060_LDO(chip, regl_name, min, step, max) \
+{\
+	.desc	=	{\
+		.id = chip##_ID_##regl_name,\
+		.name = __stringify(chip##_##regl_name),\
+		.of_match = of_match_ptr(#regl_name),\
+		.regulators_node = of_match_ptr("regulators"),\
+		.type = REGULATOR_VOLTAGE,\
+		.owner = THIS_MODULE,\
+		.ops = &pv88060_ldo_ops,\
+		.min_uV = min, \
+		.uV_step = step, \
+		.n_voltages = (step) ? ((max - min) / step + 1) : 1, \
+		.enable_reg = PV88060_REG_##regl_name##_CONF, \
+		.enable_mask = PV88060_LDO_EN, \
+		.vsel_reg = PV88060_REG_##regl_name##_CONF, \
+		.vsel_mask = PV88060_VLDO_MASK, \
+	},\
+}
+
+#define PV88060_SW(chip, regl_name, max) \
+{\
+	.desc	=	{\
+		.id = chip##_ID_##regl_name,\
+		.name = __stringify(chip##_##regl_name),\
+		.of_match = of_match_ptr(#regl_name),\
+		.regulators_node = of_match_ptr("regulators"),\
+		.type = REGULATOR_VOLTAGE,\
+		.owner = THIS_MODULE,\
+		.ops = &pv88060_ldo_ops,\
+		.min_uV = max,\
+		.uV_step = 0,\
+		.n_voltages = 1,\
+		.enable_reg = PV88060_REG_##regl_name##_CONF,\
+		.enable_mask = PV88060_SW_EN,\
+	},\
+}
+
+static const struct pv88060_regulator pv88060_regulator_info[] = {
+	PV88060_BUCK(PV88060, BUCK1, 2800000, 12500, 4387500,
+		pv88060_buck1_limits),
+	PV88060_LDO(PV88060, LDO1, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO2, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO3, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO4, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO5, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO6, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO7, 1200000, 50000, 3350000),
+	PV88060_SW(PV88060, SW1, 5000000),
+	PV88060_SW(PV88060, SW2, 5000000),
+	PV88060_SW(PV88060, SW3, 5000000),
+	PV88060_SW(PV88060, SW4, 5000000),
+	PV88060_SW(PV88060, SW5, 5000000),
+	PV88060_SW(PV88060, SW6, 5000000),
+};
+
+static irqreturn_t pv88060_irq_handler(int irq, void *data)
+{
+	struct pv88060 *chip = data;
+	int i, reg_val, err, ret = IRQ_NONE;
+
+	err = regmap_read(chip->regmap, PV88060_REG_EVENT_A, &reg_val);
+	if (err < 0)
+		goto error_i2c;
+
+	if (reg_val & PV88060_E_VDD_FLT) {
+		for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+			if (chip->rdev[i] != NULL) {
+				regulator_notifier_call_chain(chip->rdev[i],
+					REGULATOR_EVENT_UNDER_VOLTAGE,
+					NULL);
+			}
+		}
+
+		err = regmap_update_bits(chip->regmap, PV88060_REG_EVENT_A,
+			PV88060_E_VDD_FLT, PV88060_E_VDD_FLT);
+		if (err < 0)
+			goto error_i2c;
+
+		ret = IRQ_HANDLED;
+	}
+
+	if (reg_val & PV88060_E_OVER_TEMP) {
+		for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+			if (chip->rdev[i] != NULL) {
+				regulator_notifier_call_chain(chip->rdev[i],
+					REGULATOR_EVENT_OVER_TEMP,
+					NULL);
+			}
+		}
+
+		err = regmap_update_bits(chip->regmap, PV88060_REG_EVENT_A,
+			PV88060_E_OVER_TEMP, PV88060_E_OVER_TEMP);
+		if (err < 0)
+			goto error_i2c;
+
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+
+error_i2c:
+	dev_err(chip->dev, "I2C error : %d\n", err);
+	return IRQ_NONE;
+}
+
+/*
+ * I2C driver interface functions
+ */
+static int pv88060_i2c_probe(struct i2c_client *i2c,
+		const struct i2c_device_id *id)
+{
+	struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev);
+	struct pv88060 *chip;
+	struct regulator_config config = { };
+	int error, i, ret = 0;
+
+	chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88060), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->dev = &i2c->dev;
+	chip->regmap = devm_regmap_init_i2c(i2c, &pv88060_regmap_config);
+	if (IS_ERR(chip->regmap)) {
+		error = PTR_ERR(chip->regmap);
+		dev_err(chip->dev, "Failed to allocate register map: %d\n",
+			error);
+		return error;
+	}
+
+	i2c_set_clientdata(i2c, chip);
+
+	if (i2c->irq != 0) {
+		ret = regmap_write(chip->regmap, PV88060_REG_MASK_A, 0xFF);
+		if (ret < 0) {
+			dev_err(chip->dev,
+				"Failed to mask A reg: %d\n", ret);
+			return ret;
+		}
+
+		regmap_write(chip->regmap, PV88060_REG_MASK_B, 0xFF);
+		if (ret < 0) {
+			dev_err(chip->dev,
+				"Failed to mask B reg: %d\n", ret);
+			return ret;
+		}
+
+		regmap_write(chip->regmap, PV88060_REG_MASK_C, 0xFF);
+		if (ret < 0) {
+			dev_err(chip->dev,
+				"Failed to mask C reg: %d\n", ret);
+			return ret;
+		}
+
+		ret = request_threaded_irq(i2c->irq, NULL,
+					pv88060_irq_handler,
+					IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+					"pv88060", chip);
+		if (ret != 0) {
+			dev_err(chip->dev, "Failed to request IRQ: %d\n",
+				i2c->irq);
+			return ret;
+		}
+
+		ret = regmap_update_bits(chip->regmap, PV88060_REG_MASK_A,
+			PV88060_M_VDD_FLT | PV88060_M_OVER_TEMP, 0);
+		if (ret < 0) {
+			dev_err(chip->dev,
+				"Failed to update mask reg: %d\n", ret);
+			return ret;
+		}
+
+	} else {
+		dev_warn(chip->dev, "No IRQ configured\n");
+	}
+
+	config.dev = chip->dev;
+	config.regmap = chip->regmap;
+
+	for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+		if (init_data)
+			config.init_data = &init_data[i];
+
+		config.driver_data = (void *)&pv88060_regulator_info[i];
+		chip->rdev[i] = devm_regulator_register(chip->dev,
+			&pv88060_regulator_info[i].desc, &config);
+		if (IS_ERR(chip->rdev[i])) {
+			dev_err(chip->dev,
+				"Failed to register PV88060 regulator\n");
+			return PTR_ERR(chip->rdev[i]);
+		}
+	}
+
+	return 0;
+}
+
+static const struct i2c_device_id pv88060_i2c_id[] = {
+	{"pv88060", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, pv88060_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pv88060_dt_ids[] = {
+	{ .compatible = "pvs,pv88060", .data = &pv88060_i2c_id[0] },
+	{},
+};
+MODULE_DEVICE_TABLE(of, pv88060_dt_ids);
+#endif
+
+static struct i2c_driver pv88060_regulator_driver = {
+	.driver = {
+		.name = "pv88060",
+		.of_match_table = of_match_ptr(pv88060_dt_ids),
+	},
+	.probe = pv88060_i2c_probe,
+	.id_table = pv88060_i2c_id,
+};
+
+module_i2c_driver(pv88060_regulator_driver);
+
+MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88060");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/pv88060-regulator.h b/drivers/regulator/pv88060-regulator.h
new file mode 100644
index 0000000..02ca920
--- /dev/null
+++ b/drivers/regulator/pv88060-regulator.h
@@ -0,0 +1,69 @@
+/*
+ * pv88060-regulator.h - Regulator definitions for PV88060
+ * Copyright (C) 2015 Powerventure Semiconductor Ltd.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#ifndef __PV88060_REGISTERS_H__
+#define __PV88060_REGISTERS_H__
+
+/* System Control and Event Registers */
+#define	PV88060_REG_EVENT_A			0x04
+#define	PV88060_REG_MASK_A			0x08
+#define	PV88060_REG_MASK_B			0x09
+#define	PV88060_REG_MASK_C			0x0A
+
+/* Regulator Registers */
+#define	PV88060_REG_BUCK1_CONF0			0x1B
+#define	PV88060_REG_BUCK1_CONF1			0x1C
+#define	PV88060_REG_LDO1_CONF			0x1D
+#define	PV88060_REG_LDO2_CONF			0x1E
+#define	PV88060_REG_LDO3_CONF			0x1F
+#define	PV88060_REG_LDO4_CONF			0x20
+#define	PV88060_REG_LDO5_CONF			0x21
+#define	PV88060_REG_LDO6_CONF			0x22
+#define	PV88060_REG_LDO7_CONF			0x23
+
+#define	PV88060_REG_SW1_CONF			0x3B
+#define	PV88060_REG_SW2_CONF			0x3C
+#define	PV88060_REG_SW3_CONF			0x3D
+#define	PV88060_REG_SW4_CONF			0x3E
+#define	PV88060_REG_SW5_CONF			0x3F
+#define	PV88060_REG_SW6_CONF			0x40
+
+/* PV88060_REG_EVENT_A (addr=0x04) */
+#define	PV88060_E_VDD_FLT			0x01
+#define	PV88060_E_OVER_TEMP			0x02
+
+/* PV88060_REG_MASK_A (addr=0x08) */
+#define	PV88060_M_VDD_FLT			0x01
+#define	PV88060_M_OVER_TEMP			0x02
+
+/* PV88060_REG_BUCK1_CONF0 (addr=0x1B) */
+#define	PV88060_BUCK_EN			0x80
+#define PV88060_VBUCK_MASK			0x7F
+/* PV88060_REG_LDO1/2/3/4/5/6/7_CONT */
+#define	PV88060_LDO_EN			0x40
+#define PV88060_VLDO_MASK			0x3F
+/* PV88060_REG_SW1/2/3/4/5_CONF */
+#define	PV88060_SW_EN			0x80
+
+/* PV88060_REG_BUCK1_CONF1 (addr=0x1C) */
+#define	PV88060_BUCK_ILIM_SHIFT			2
+#define	PV88060_BUCK_ILIM_MASK			0x0C
+#define	PV88060_BUCK_MODE_SHIFT			0
+#define	PV88060_BUCK_MODE_MASK			0x03
+#define	PV88060_BUCK_MODE_SLEEP			0x00
+#define	PV88060_BUCK_MODE_AUTO			0x01
+#define	PV88060_BUCK_MODE_SYNC			0x02
+
+#endif	/* __PV88060_REGISTERS_H__ */
-- 
end-of-patch for PATCH V2


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Applied "regulator: pv88060: new regulator driver" to the regulator tree
  2015-11-19  0:59 [PATCH V2] regulator: pv88060: new regulator driver James Ban
@ 2015-11-20 18:20 ` Mark Brown
  0 siblings, 0 replies; 2+ messages in thread
From: Mark Brown @ 2015-11-20 18:20 UTC (permalink / raw)
  To: James Ban, Mark Brown; +Cc: linux-kernel

The patch

   regulator: pv88060: new regulator driver

has been applied to the regulator tree at

   git://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 f307a7e9b7af401d459d26f98497c9cec766a41f Mon Sep 17 00:00:00 2001
From: James Ban <James.Ban.opensource@diasemi.com>
Date: Thu, 19 Nov 2015 09:59:15 +0900
Subject: [PATCH] regulator: pv88060: new regulator driver

This is the driver for the Powerventure PV88060 BUCKs and LDOs regulator.
It communicates via an I2C bus to the device.

Signed-off-by: James Ban <James.Ban.opensource@diasemi.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 .../devicetree/bindings/regulator/pv88060.txt      | 124 ++++++
 drivers/regulator/Kconfig                          |   8 +
 drivers/regulator/Makefile                         |   1 +
 drivers/regulator/pv88060-regulator.c              | 437 +++++++++++++++++++++
 drivers/regulator/pv88060-regulator.h              |  69 ++++
 5 files changed, 639 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/pv88060.txt
 create mode 100644 drivers/regulator/pv88060-regulator.c
 create mode 100644 drivers/regulator/pv88060-regulator.h

diff --git a/Documentation/devicetree/bindings/regulator/pv88060.txt b/Documentation/devicetree/bindings/regulator/pv88060.txt
new file mode 100644
index 000000000000..10a6dadc008e
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/pv88060.txt
@@ -0,0 +1,124 @@
+* Powerventure Semiconductor PV88060 Voltage Regulator
+
+Required properties:
+- compatible: "pvs,pv88060".
+- reg: I2C slave address, usually 0x49.
+- interrupts: the interrupt outputs of the controller
+- regulators: A node that houses a sub-node for each regulator within the
+  device. Each sub-node is identified using the node's name, with valid
+  values listed below. The content of each sub-node is defined by the
+  standard binding for regulators; see regulator.txt.
+  BUCK1, LDO1, LDO2, LDO3, LDO4, LDO5, LDO6, LDO7, SW1, SW2, SW3, SW4,
+  SW5, and SW6.
+
+Optional properties:
+- Any optional property defined in regulator.txt
+
+Example
+
+	pmic: pv88060@49 {
+		compatible = "pvs,pv88060";
+		reg = <0x49>;
+		interrupt-parent = <&gpio>;
+		interrupts = <24 24>;
+
+		regulators {
+			BUCK1 {
+				regulator-name = "buck1";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <4387500>;
+				regulator-min-microamp 	= <1496000>;
+				regulator-max-microamp 	= <4189000>;
+				regulator-boot-on;
+			};
+
+			LDO1 {
+				regulator-name = "ldo1";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO2 {
+				regulator-name = "ldo2";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO3 {
+				regulator-name = "ldo3";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO4 {
+				regulator-name = "ldo4";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO5 {
+				regulator-name = "ldo5";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO6 {
+				regulator-name = "ldo6";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			LDO7 {
+				regulator-name = "ldo7";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3350000>;
+				regulator-boot-on;
+			};
+
+			SW1 {
+				regulator-name = "sw1";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+			};
+
+			SW2 {
+				regulator-name = "sw2";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+				regulator-boot-on;
+			};
+
+			SW3 {
+				regulator-name = "sw3";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+				regulator-boot-on;
+			};
+
+			SW4 {
+				regulator-name = "sw4";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+				regulator-boot-on;
+			};
+
+			SW5 {
+				regulator-name = "sw5";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+				regulator-boot-on;
+			};
+
+			SW6 {
+				regulator-name = "sw6";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+			};
+		};
+	};
\ No newline at end of file
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 8df0b0e62976..c61f72ff1dfd 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -504,6 +504,14 @@ config REGULATOR_PFUZE100
 	  Say y here to support the regulators found on the Freescale
 	  PFUZE100/PFUZE200 PMIC.
 
+config REGULATOR_PV88060
+	tristate "Powerventure Semiconductor PV88060 regulator"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say y here to support the voltage regulators and convertors
+	  PV88060
+
 config REGULATOR_PWM
 	tristate "PWM voltage regulator"
 	depends on PWM
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 0f8174913c17..b11c8a4f873a 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
 obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
 obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
+obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o
 obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
 obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
 obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
diff --git a/drivers/regulator/pv88060-regulator.c b/drivers/regulator/pv88060-regulator.c
new file mode 100644
index 000000000000..60b16d835df7
--- /dev/null
+++ b/drivers/regulator/pv88060-regulator.c
@@ -0,0 +1,437 @@
+/*
+ * pv88060-regulator.c - Regulator device driver for PV88060
+ * Copyright (C) 2015  Powerventure Semiconductor Ltd.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include "pv88060-regulator.h"
+
+#define PV88060_MAX_REGULATORS	14
+
+/* PV88060 REGULATOR IDs */
+enum {
+	/* BUCKs */
+	PV88060_ID_BUCK1,
+
+	/* LDOs */
+	PV88060_ID_LDO1,
+	PV88060_ID_LDO2,
+	PV88060_ID_LDO3,
+	PV88060_ID_LDO4,
+	PV88060_ID_LDO5,
+	PV88060_ID_LDO6,
+	PV88060_ID_LDO7,
+
+	/* SWTs */
+	PV88060_ID_SW1,
+	PV88060_ID_SW2,
+	PV88060_ID_SW3,
+	PV88060_ID_SW4,
+	PV88060_ID_SW5,
+	PV88060_ID_SW6,
+};
+
+struct pv88060_regulator {
+	struct regulator_desc desc;
+	/* Current limiting */
+	unsigned	n_current_limits;
+	const int	*current_limits;
+	unsigned int limit_mask;
+	unsigned int conf;		/* buck configuration register */
+};
+
+struct pv88060 {
+	struct device *dev;
+	struct regmap *regmap;
+	struct regulator_dev *rdev[PV88060_MAX_REGULATORS];
+};
+
+static const struct regmap_config pv88060_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+/* Current limits array (in uA) for BUCK1
+ * Entry indexes corresponds to register values.
+ */
+
+static const int pv88060_buck1_limits[] = {
+	1496000, 2393000, 3291000, 4189000
+};
+
+static unsigned int pv88060_buck_get_mode(struct regulator_dev *rdev)
+{
+	struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+	unsigned int data;
+	int ret, mode = 0;
+
+	ret = regmap_read(rdev->regmap, info->conf, &data);
+	if (ret < 0)
+		return ret;
+
+	switch (data & PV88060_BUCK_MODE_MASK) {
+	case PV88060_BUCK_MODE_SYNC:
+		mode = REGULATOR_MODE_FAST;
+		break;
+	case PV88060_BUCK_MODE_AUTO:
+		mode = REGULATOR_MODE_NORMAL;
+		break;
+	case PV88060_BUCK_MODE_SLEEP:
+		mode = REGULATOR_MODE_STANDBY;
+		break;
+	}
+
+	return mode;
+}
+
+static int pv88060_buck_set_mode(struct regulator_dev *rdev,
+					unsigned int mode)
+{
+	struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+	int val = 0;
+
+	switch (mode) {
+	case REGULATOR_MODE_FAST:
+		val = PV88060_BUCK_MODE_SYNC;
+		break;
+	case REGULATOR_MODE_NORMAL:
+		val = PV88060_BUCK_MODE_AUTO;
+		break;
+	case REGULATOR_MODE_STANDBY:
+		val = PV88060_BUCK_MODE_SLEEP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return regmap_update_bits(rdev->regmap, info->conf,
+					PV88060_BUCK_MODE_MASK, val);
+}
+
+static int pv88060_set_current_limit(struct regulator_dev *rdev, int min,
+				    int max)
+{
+	struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+	int i;
+
+	/* search for closest to maximum */
+	for (i = info->n_current_limits; i >= 0; i--) {
+		if (min <= info->current_limits[i]
+			&& max >= info->current_limits[i]) {
+			return regmap_update_bits(rdev->regmap,
+				info->conf,
+				info->limit_mask,
+				i << PV88060_BUCK_ILIM_SHIFT);
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int pv88060_get_current_limit(struct regulator_dev *rdev)
+{
+	struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+	unsigned int data;
+	int ret;
+
+	ret = regmap_read(rdev->regmap, info->conf, &data);
+	if (ret < 0)
+		return ret;
+
+	data = (data & info->limit_mask) >> PV88060_BUCK_ILIM_SHIFT;
+	return info->current_limits[data];
+}
+
+static struct regulator_ops pv88060_buck_ops = {
+	.get_mode = pv88060_buck_get_mode,
+	.set_mode = pv88060_buck_set_mode,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.list_voltage = regulator_list_voltage_linear,
+	.set_current_limit = pv88060_set_current_limit,
+	.get_current_limit = pv88060_get_current_limit,
+};
+
+static struct regulator_ops pv88060_ldo_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.list_voltage = regulator_list_voltage_linear,
+};
+
+#define PV88060_BUCK(chip, regl_name, min, step, max, limits_array) \
+{\
+	.desc	=	{\
+		.id = chip##_ID_##regl_name,\
+		.name = __stringify(chip##_##regl_name),\
+		.of_match = of_match_ptr(#regl_name),\
+		.regulators_node = of_match_ptr("regulators"),\
+		.type = REGULATOR_VOLTAGE,\
+		.owner = THIS_MODULE,\
+		.ops = &pv88060_buck_ops,\
+		.min_uV = min,\
+		.uV_step = step,\
+		.n_voltages = ((max) - (min))/(step) + 1,\
+		.enable_reg = PV88060_REG_##regl_name##_CONF0,\
+		.enable_mask = PV88060_BUCK_EN, \
+		.vsel_reg = PV88060_REG_##regl_name##_CONF0,\
+		.vsel_mask = PV88060_VBUCK_MASK,\
+	},\
+	.current_limits = limits_array,\
+	.n_current_limits = ARRAY_SIZE(limits_array),\
+	.limit_mask = PV88060_BUCK_ILIM_MASK, \
+	.conf = PV88060_REG_##regl_name##_CONF1,\
+}
+
+#define PV88060_LDO(chip, regl_name, min, step, max) \
+{\
+	.desc	=	{\
+		.id = chip##_ID_##regl_name,\
+		.name = __stringify(chip##_##regl_name),\
+		.of_match = of_match_ptr(#regl_name),\
+		.regulators_node = of_match_ptr("regulators"),\
+		.type = REGULATOR_VOLTAGE,\
+		.owner = THIS_MODULE,\
+		.ops = &pv88060_ldo_ops,\
+		.min_uV = min, \
+		.uV_step = step, \
+		.n_voltages = (step) ? ((max - min) / step + 1) : 1, \
+		.enable_reg = PV88060_REG_##regl_name##_CONF, \
+		.enable_mask = PV88060_LDO_EN, \
+		.vsel_reg = PV88060_REG_##regl_name##_CONF, \
+		.vsel_mask = PV88060_VLDO_MASK, \
+	},\
+}
+
+#define PV88060_SW(chip, regl_name, max) \
+{\
+	.desc	=	{\
+		.id = chip##_ID_##regl_name,\
+		.name = __stringify(chip##_##regl_name),\
+		.of_match = of_match_ptr(#regl_name),\
+		.regulators_node = of_match_ptr("regulators"),\
+		.type = REGULATOR_VOLTAGE,\
+		.owner = THIS_MODULE,\
+		.ops = &pv88060_ldo_ops,\
+		.min_uV = max,\
+		.uV_step = 0,\
+		.n_voltages = 1,\
+		.enable_reg = PV88060_REG_##regl_name##_CONF,\
+		.enable_mask = PV88060_SW_EN,\
+	},\
+}
+
+static const struct pv88060_regulator pv88060_regulator_info[] = {
+	PV88060_BUCK(PV88060, BUCK1, 2800000, 12500, 4387500,
+		pv88060_buck1_limits),
+	PV88060_LDO(PV88060, LDO1, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO2, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO3, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO4, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO5, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO6, 1200000, 50000, 3350000),
+	PV88060_LDO(PV88060, LDO7, 1200000, 50000, 3350000),
+	PV88060_SW(PV88060, SW1, 5000000),
+	PV88060_SW(PV88060, SW2, 5000000),
+	PV88060_SW(PV88060, SW3, 5000000),
+	PV88060_SW(PV88060, SW4, 5000000),
+	PV88060_SW(PV88060, SW5, 5000000),
+	PV88060_SW(PV88060, SW6, 5000000),
+};
+
+static irqreturn_t pv88060_irq_handler(int irq, void *data)
+{
+	struct pv88060 *chip = data;
+	int i, reg_val, err, ret = IRQ_NONE;
+
+	err = regmap_read(chip->regmap, PV88060_REG_EVENT_A, &reg_val);
+	if (err < 0)
+		goto error_i2c;
+
+	if (reg_val & PV88060_E_VDD_FLT) {
+		for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+			if (chip->rdev[i] != NULL) {
+				regulator_notifier_call_chain(chip->rdev[i],
+					REGULATOR_EVENT_UNDER_VOLTAGE,
+					NULL);
+			}
+		}
+
+		err = regmap_update_bits(chip->regmap, PV88060_REG_EVENT_A,
+			PV88060_E_VDD_FLT, PV88060_E_VDD_FLT);
+		if (err < 0)
+			goto error_i2c;
+
+		ret = IRQ_HANDLED;
+	}
+
+	if (reg_val & PV88060_E_OVER_TEMP) {
+		for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+			if (chip->rdev[i] != NULL) {
+				regulator_notifier_call_chain(chip->rdev[i],
+					REGULATOR_EVENT_OVER_TEMP,
+					NULL);
+			}
+		}
+
+		err = regmap_update_bits(chip->regmap, PV88060_REG_EVENT_A,
+			PV88060_E_OVER_TEMP, PV88060_E_OVER_TEMP);
+		if (err < 0)
+			goto error_i2c;
+
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+
+error_i2c:
+	dev_err(chip->dev, "I2C error : %d\n", err);
+	return IRQ_NONE;
+}
+
+/*
+ * I2C driver interface functions
+ */
+static int pv88060_i2c_probe(struct i2c_client *i2c,
+		const struct i2c_device_id *id)
+{
+	struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev);
+	struct pv88060 *chip;
+	struct regulator_config config = { };
+	int error, i, ret = 0;
+
+	chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88060), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->dev = &i2c->dev;
+	chip->regmap = devm_regmap_init_i2c(i2c, &pv88060_regmap_config);
+	if (IS_ERR(chip->regmap)) {
+		error = PTR_ERR(chip->regmap);
+		dev_err(chip->dev, "Failed to allocate register map: %d\n",
+			error);
+		return error;
+	}
+
+	i2c_set_clientdata(i2c, chip);
+
+	if (i2c->irq != 0) {
+		ret = regmap_write(chip->regmap, PV88060_REG_MASK_A, 0xFF);
+		if (ret < 0) {
+			dev_err(chip->dev,
+				"Failed to mask A reg: %d\n", ret);
+			return ret;
+		}
+
+		regmap_write(chip->regmap, PV88060_REG_MASK_B, 0xFF);
+		if (ret < 0) {
+			dev_err(chip->dev,
+				"Failed to mask B reg: %d\n", ret);
+			return ret;
+		}
+
+		regmap_write(chip->regmap, PV88060_REG_MASK_C, 0xFF);
+		if (ret < 0) {
+			dev_err(chip->dev,
+				"Failed to mask C reg: %d\n", ret);
+			return ret;
+		}
+
+		ret = request_threaded_irq(i2c->irq, NULL,
+					pv88060_irq_handler,
+					IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+					"pv88060", chip);
+		if (ret != 0) {
+			dev_err(chip->dev, "Failed to request IRQ: %d\n",
+				i2c->irq);
+			return ret;
+		}
+
+		ret = regmap_update_bits(chip->regmap, PV88060_REG_MASK_A,
+			PV88060_M_VDD_FLT | PV88060_M_OVER_TEMP, 0);
+		if (ret < 0) {
+			dev_err(chip->dev,
+				"Failed to update mask reg: %d\n", ret);
+			return ret;
+		}
+
+	} else {
+		dev_warn(chip->dev, "No IRQ configured\n");
+	}
+
+	config.dev = chip->dev;
+	config.regmap = chip->regmap;
+
+	for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+		if (init_data)
+			config.init_data = &init_data[i];
+
+		config.driver_data = (void *)&pv88060_regulator_info[i];
+		chip->rdev[i] = devm_regulator_register(chip->dev,
+			&pv88060_regulator_info[i].desc, &config);
+		if (IS_ERR(chip->rdev[i])) {
+			dev_err(chip->dev,
+				"Failed to register PV88060 regulator\n");
+			return PTR_ERR(chip->rdev[i]);
+		}
+	}
+
+	return 0;
+}
+
+static const struct i2c_device_id pv88060_i2c_id[] = {
+	{"pv88060", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, pv88060_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pv88060_dt_ids[] = {
+	{ .compatible = "pvs,pv88060", .data = &pv88060_i2c_id[0] },
+	{},
+};
+MODULE_DEVICE_TABLE(of, pv88060_dt_ids);
+#endif
+
+static struct i2c_driver pv88060_regulator_driver = {
+	.driver = {
+		.name = "pv88060",
+		.of_match_table = of_match_ptr(pv88060_dt_ids),
+	},
+	.probe = pv88060_i2c_probe,
+	.id_table = pv88060_i2c_id,
+};
+
+module_i2c_driver(pv88060_regulator_driver);
+
+MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88060");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/pv88060-regulator.h b/drivers/regulator/pv88060-regulator.h
new file mode 100644
index 000000000000..02ca9203a172
--- /dev/null
+++ b/drivers/regulator/pv88060-regulator.h
@@ -0,0 +1,69 @@
+/*
+ * pv88060-regulator.h - Regulator definitions for PV88060
+ * Copyright (C) 2015 Powerventure Semiconductor Ltd.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#ifndef __PV88060_REGISTERS_H__
+#define __PV88060_REGISTERS_H__
+
+/* System Control and Event Registers */
+#define	PV88060_REG_EVENT_A			0x04
+#define	PV88060_REG_MASK_A			0x08
+#define	PV88060_REG_MASK_B			0x09
+#define	PV88060_REG_MASK_C			0x0A
+
+/* Regulator Registers */
+#define	PV88060_REG_BUCK1_CONF0			0x1B
+#define	PV88060_REG_BUCK1_CONF1			0x1C
+#define	PV88060_REG_LDO1_CONF			0x1D
+#define	PV88060_REG_LDO2_CONF			0x1E
+#define	PV88060_REG_LDO3_CONF			0x1F
+#define	PV88060_REG_LDO4_CONF			0x20
+#define	PV88060_REG_LDO5_CONF			0x21
+#define	PV88060_REG_LDO6_CONF			0x22
+#define	PV88060_REG_LDO7_CONF			0x23
+
+#define	PV88060_REG_SW1_CONF			0x3B
+#define	PV88060_REG_SW2_CONF			0x3C
+#define	PV88060_REG_SW3_CONF			0x3D
+#define	PV88060_REG_SW4_CONF			0x3E
+#define	PV88060_REG_SW5_CONF			0x3F
+#define	PV88060_REG_SW6_CONF			0x40
+
+/* PV88060_REG_EVENT_A (addr=0x04) */
+#define	PV88060_E_VDD_FLT			0x01
+#define	PV88060_E_OVER_TEMP			0x02
+
+/* PV88060_REG_MASK_A (addr=0x08) */
+#define	PV88060_M_VDD_FLT			0x01
+#define	PV88060_M_OVER_TEMP			0x02
+
+/* PV88060_REG_BUCK1_CONF0 (addr=0x1B) */
+#define	PV88060_BUCK_EN			0x80
+#define PV88060_VBUCK_MASK			0x7F
+/* PV88060_REG_LDO1/2/3/4/5/6/7_CONT */
+#define	PV88060_LDO_EN			0x40
+#define PV88060_VLDO_MASK			0x3F
+/* PV88060_REG_SW1/2/3/4/5_CONF */
+#define	PV88060_SW_EN			0x80
+
+/* PV88060_REG_BUCK1_CONF1 (addr=0x1C) */
+#define	PV88060_BUCK_ILIM_SHIFT			2
+#define	PV88060_BUCK_ILIM_MASK			0x0C
+#define	PV88060_BUCK_MODE_SHIFT			0
+#define	PV88060_BUCK_MODE_MASK			0x03
+#define	PV88060_BUCK_MODE_SLEEP			0x00
+#define	PV88060_BUCK_MODE_AUTO			0x01
+#define	PV88060_BUCK_MODE_SYNC			0x02
+
+#endif	/* __PV88060_REGISTERS_H__ */
-- 
2.6.2


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2015-11-20 18:21 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-19  0:59 [PATCH V2] regulator: pv88060: new regulator driver James Ban
2015-11-20 18:20 ` Applied "regulator: pv88060: new regulator driver" to the regulator tree Mark Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).