regulator: max15569: Add regulator driver.
Ashwin Joshi [Tue, 6 Aug 2013 12:15:05 +0000 (17:15 +0530)]
Add regulator driver for max15569 chip. This chip is present on
automotive platform.

Bug 1319925

Change-Id: Ib02f94376ee3560c2891b78ec355c3f246b00dd4
Signed-off-by: Ashwin Joshi <asjoshi@nvidia.com>
Reviewed-on: http://git-master/r/262306
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>

drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/max15569-regulator.c [new file with mode: 0644]
include/linux/regulator/max15569-regulator.h [new file with mode: 0644]

index 03cb564..70eb651 100644 (file)
@@ -273,6 +273,12 @@ config REGULATOR_MAX77663
          via I2C bus. The provided regulator is suitable for Tegra
          chip to control Step-Down DC-DC and LDOs.
 
+config REGULATOR_MAX15569
+       tristate "Maxim 15569 voltage regulator"
+       help
+         This driver controls a Maxim 15569 voltage output regulator
+         via I2C bus.
+
 config REGULATOR_TWL4030
        bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC"
        depends on TWL4030_CORE
index 671aa82..c151994 100644 (file)
@@ -44,6 +44,7 @@ obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
 obj-$(CONFIG_REGULATOR_MAX8907C) += max8907c-regulator.o
 obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o
 obj-$(CONFIG_REGULATOR_MAX77663) += max77663-regulator.o
+obj-$(CONFIG_REGULATOR_MAX15569) += max15569-regulator.o
 obj-$(CONFIG_REGULATOR_AS3720) += as3720-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6591X) += tps6591x-regulator.o
 obj-$(CONFIG_REGULATOR_TPS65090) += tps65090-regulator.o
diff --git a/drivers/regulator/max15569-regulator.c b/drivers/regulator/max15569-regulator.c
new file mode 100644 (file)
index 0000000..8c15985
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * max15569-regulator.c -- max15569 regulator driver
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/max15569-regulator.h>
+#include <linux/slab.h>
+
+/* Register definitions */
+#define MAX15569_VOUTMAX_REG                   0x2
+#define MAX15569_STATUS_REG                    0x4
+#define MAX15569_MASK_REG                      0x5
+#define MAX15569_SLEW_RATE_REG                 0x6
+#define MAX15569_SETVOUT_REG                   0x7
+#define MAX15569_IMON_REG                      0x8
+
+#define MAX15569_MAX_REG                       0x9
+
+#define MAX15569_MIN_VOLTAGE   500000
+#define MAX15569_MAX_VOLTAGE   1520000
+#define MAX15569_VOLTAGE_STEP  10000
+#define MAX15569_MAX_SEL       0x7F
+#define MAX15569_MAX_SLEW_RATE  44
+#define MAX15569_MIN_SLEW_RATE  1
+
+#define MAX15569_STATUS_VRHOT  0x20
+#define MAX15569_STATUS_UV     0x10
+#define MAX15569_STATUS_OV     0x08
+#define MAX15569_STATUS_OC     0x4
+#define MAX15569_STATUS_VMERR  0x2
+#define MAX15569_STATUS_INT    0x1
+
+struct {
+       unsigned char soft_start_slew_rate;
+       unsigned char regular_slew_rate;
+} max15569_slewrate_table[] = {
+       {-1,    -1 },
+       { 9,    9},
+       { 9,    9},
+       { 9,    9},
+       { 9,    9},
+       { 0x19, 0x19},
+       { 0x19, 0x19},
+       { 0x23, 0x23},
+       { 3,    3},
+       { 3,    3},
+       { 0x17, 0x1e},
+       { 0x17, 0x1e},
+       { 0x20, 0x20},
+       { 0x20, 0x20},
+       { 0x20, 0x20},
+       { 0x20, 0x20},
+       { 0x20, 0x20},
+       { 0x0, 0x0},
+       { 0x0, 0x0},
+       { 0x0, 0x0},
+       { 0x0, 0x0},
+       { 0x10, 0x10},
+       { 0x10, 0x10},
+       { 0x10, 0x10},
+       { 0x10, 0x10},
+       { 0x25, 0x25},
+       { 0x25, 0x25},
+       { 0x25, 0x25},
+       { 0x25, 0x25},
+       { 0x25, 0x25},
+       { 0x25, 0x25},
+       { 0x25, 0x25},
+       { 0x25, 0x25},
+       { 0x5,  0x5},
+       { 0x5,  0x5},
+       { 0x5,  0x5},
+       { 0x5,  0x5},
+       { 0x5,  0x5},
+       { 0x5,  0x5},
+       { 0x5,  0x5},
+       { 0x5,  0x5},
+       { 0x15, 0x15},
+       { 0x15, 0x15},
+       { 0x15, 0x15},
+       { 0x15, 0x15},
+};
+
+/* MAX15569 chip information */
+struct max15569_chip {
+       struct device *dev;
+       struct regulator_desc desc;
+       struct regulator_dev *rdev;
+       struct regmap *regmap;
+
+       bool output_enabled;
+       unsigned int change_mv_per_us;
+};
+
+static int max15569_get_voltage_sel(struct regulator_dev *rdev)
+{
+       struct max15569_chip *max = rdev_get_drvdata(rdev);
+       unsigned int data;
+       int ret;
+       unsigned int reg = MAX15569_SETVOUT_REG;
+
+       ret = regmap_read(max->regmap, reg, &data);
+       if (ret < 0) {
+               dev_err(max->dev, "reg read failed, err %d\n", ret);
+               return ret;
+       }
+       return data;
+}
+
+static int max15569_set_voltage(struct regulator_dev *rdev,
+            int min_uV, int max_uV, unsigned *selector)
+{
+       struct max15569_chip *max = rdev_get_drvdata(rdev);
+       unsigned int reg = MAX15569_SETVOUT_REG;
+       int vsel;
+       int ret;
+
+       if ((max_uV < min_uV) || (max_uV < MAX15569_MIN_VOLTAGE) ||
+                       (min_uV > MAX15569_MAX_VOLTAGE))
+               return -EINVAL;
+
+       vsel = DIV_ROUND_UP(min_uV - MAX15569_MIN_VOLTAGE,
+                       MAX15569_VOLTAGE_STEP) + 0x1;
+       if (selector)
+               *selector = vsel;
+
+       ret = regmap_write(max->regmap, reg, vsel);
+       if (ret < 0)
+               dev_err(max->dev, "reg write failed, err %d\n", ret);
+       return ret;
+}
+
+static int max15569_list_voltage(struct regulator_dev *rdev,
+                                       unsigned selector)
+{
+       if (selector > MAX15569_MAX_SEL)
+               return -EINVAL;
+
+       return MAX15569_MIN_VOLTAGE + (selector - 0x1) * MAX15569_VOLTAGE_STEP;
+}
+
+static int max15569_set_voltage_time_sel(struct regulator_dev *rdev,
+               unsigned int old_selector, unsigned int new_selector)
+{
+       struct max15569_chip *max = rdev_get_drvdata(rdev);
+       int change_mv_per_us = max->change_mv_per_us;
+
+       if (change_mv_per_us > MAX15569_MAX_SLEW_RATE)
+               change_mv_per_us = MAX15569_MAX_SLEW_RATE;
+
+       if (change_mv_per_us < MAX15569_MIN_SLEW_RATE)
+               change_mv_per_us = MAX15569_MIN_SLEW_RATE;
+
+       return max->output_enabled ?
+               max15569_slewrate_table[max->change_mv_per_us].regular_slew_rate :
+               max15569_slewrate_table[max->change_mv_per_us].soft_start_slew_rate;
+}
+
+static int max15569_set_control_mode(struct regulator_dev *rdev,
+               unsigned int mode)
+{
+       if (mode != REGULATOR_CONTROL_MODE_I2C)
+               return -EINVAL;
+
+       return 0;
+}
+
+static unsigned int max15569_get_control_mode(struct regulator_dev *rdev)
+{
+       return REGULATOR_CONTROL_MODE_I2C;
+}
+
+static struct regulator_ops max15569_ops = {
+       .get_voltage_sel        = max15569_get_voltage_sel,
+       .set_voltage            = max15569_set_voltage,
+       .list_voltage           = max15569_list_voltage,
+       .set_voltage_time_sel   = max15569_set_voltage_time_sel,
+       .set_control_mode       = max15569_set_control_mode,
+       .get_control_mode       = max15569_get_control_mode,
+};
+
+static int max15569_init(struct max15569_chip *max15569,
+       struct max15569_regulator_platform_data *pdata)
+{
+       int ret;
+       int vsel;
+       unsigned int status;
+
+       max15569->output_enabled = true;
+
+       /* Set slew rate */
+       /* max15569->change_mv_per_us = min(44,max(1, pdata->slew_rate_mv_per_us)); */
+
+       if (max15569->change_mv_per_us > 44)
+               max15569->change_mv_per_us = 44;
+
+       if (max15569->change_mv_per_us < 1)
+               max15569->change_mv_per_us = 1;
+
+
+       vsel = max15569_slewrate_table[max15569->change_mv_per_us].regular_slew_rate;
+
+       ret = regmap_write(max15569->regmap, MAX15569_SLEW_RATE_REG, vsel);
+       if (ret < 0) {
+               dev_err(max15569->dev, "SLEW reg write failed, err %d\n", ret);
+               return ret;
+       }
+
+       /* Set base voltage */
+       vsel = DIV_ROUND_UP(pdata->base_voltage_uV -
+               MAX15569_MIN_VOLTAGE, MAX15569_VOLTAGE_STEP) + 0x1;
+       dev_err(max15569->dev, "Setting rail to vsel %x\n", vsel);
+       ret = regmap_write(max15569->regmap, MAX15569_SETVOUT_REG, vsel);
+       if (ret < 0) {
+               dev_err(max15569->dev, "BASE reg write failed, err %d\n", ret);
+               return ret;
+       }
+
+       /* setup max voltage */
+       if (pdata->max_voltage_uV) {
+               vsel = DIV_ROUND_UP(pdata->max_voltage_uV -
+                       MAX15569_MIN_VOLTAGE, MAX15569_VOLTAGE_STEP) + 0x1;
+               ret = regmap_write(max15569->regmap, MAX15569_VOUTMAX_REG, vsel);
+               if (ret < 0) {
+                       dev_err(max15569->dev, "VMAX write failed, err %d\n", ret);
+                       return ret;
+               }
+       }
+
+       ret = regmap_read(max15569->regmap, MAX15569_STATUS_REG, &status);
+       if (ret < 0) {
+               dev_err(max15569->dev, "STATUS reg read failed, err %d\n", ret);
+               return ret;
+       }
+
+       if (status & MAX15569_STATUS_VRHOT)
+               dev_err(max15569->dev, "VRHOT: regulator temperature beyond limit\n");
+
+       if (status & MAX15569_STATUS_UV)
+               dev_err(max15569->dev, "UV: regulator under-voltage condition\n");
+
+       if (status & MAX15569_STATUS_OV)
+               dev_err(max15569->dev, "UV: regulator over-voltage condition\n");
+
+       if (status & MAX15569_STATUS_OC)
+               dev_err(max15569->dev, "UV: regulator over-current condition\n");
+
+       return 0;
+}
+
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX15569_IMON_REG:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool is_read_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0x02:
+       case 0x4 ... 0x08:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool is_write_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0x02:
+       case 0x5 ... 0x7:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_config max15569_regmap_config = {
+       .reg_bits               = 8,
+       .val_bits               = 8,
+       .writeable_reg          = is_write_reg,
+       .readable_reg           = is_read_reg,
+       .volatile_reg           = is_volatile_reg,
+       .max_register           = MAX15569_MAX_REG - 1,
+       .cache_type             = REGCACHE_RBTREE,
+};
+
+static int max15569_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct max15569_regulator_platform_data *pdata;
+       struct regulator_dev *rdev;
+       struct max15569_chip *max;
+       struct regulator_config rconfig = { };
+       int ret;
+
+       pdata = client->dev.platform_data;
+       if (!pdata) {
+               dev_err(&client->dev, "No Platform data\n");
+               return -EINVAL;
+       }
+
+       max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL);
+       if (!max) {
+               dev_err(&client->dev, "Memory allocation failed\n");
+               return -ENOMEM;
+       }
+
+       max->dev = &client->dev;
+       max->desc.name = id->name;
+       max->desc.id = 0;
+       max->desc.ops = &max15569_ops;
+       max->desc.type = REGULATOR_VOLTAGE;
+       max->desc.owner = THIS_MODULE;
+       max->regmap = devm_regmap_init_i2c(client, &max15569_regmap_config);
+       if (IS_ERR(max->regmap)) {
+               ret = PTR_ERR(max->regmap);
+               dev_err(&client->dev, "regmap init failed, err %d\n", ret);
+               return ret;
+       }
+       i2c_set_clientdata(client, max);
+
+       ret = max15569_init(max, pdata);
+       if (ret < 0) {
+               dev_err(max->dev, "Init failed, err = %d\n", ret);
+               return ret;
+       }
+
+       /* Register the regulators */
+       rconfig.dev = &client->dev;
+       rconfig.of_node = NULL;
+       rconfig.init_data = pdata->reg_init_data;
+       rconfig.driver_data = max;
+       rdev = regulator_register(&max->desc, &rconfig);
+
+       if (IS_ERR(rdev)) {
+               dev_err(max->dev, "regulator register failed\n");
+               return PTR_ERR(rdev);
+       }
+
+       max->rdev = rdev;
+       return 0;
+}
+
+static int max15569_remove(struct i2c_client *client)
+{
+       struct max15569_chip *max = i2c_get_clientdata(client);
+
+       regulator_unregister(max->rdev);
+       return 0;
+}
+
+static const struct i2c_device_id max15569_id[] = {
+       {.name = "max15569",},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, max15569_id);
+
+static struct i2c_driver max15569_i2c_driver = {
+       .driver = {
+               .name = "max15569",
+               .owner = THIS_MODULE,
+       },
+       .probe = max15569_probe,
+       .remove = max15569_remove,
+       .id_table = max15569_id,
+};
+
+static int __init max15569_drv_init(void)
+{
+       return i2c_add_driver(&max15569_i2c_driver);
+}
+subsys_initcall(max15569_drv_init);
+
+static void __exit max15569_drv_cleanup(void)
+{
+       i2c_del_driver(&max15569_i2c_driver);
+}
+module_exit(max15569_drv_cleanup);
+
+MODULE_AUTHOR("NVIDIA Corporation");
+MODULE_DESCRIPTION("MAX15569 voltage regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/regulator/max15569-regulator.h b/include/linux/regulator/max15569-regulator.h
new file mode 100644 (file)
index 0000000..49eaf04
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * max15569-regulator.c -- max15569 regulator driver
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_REGULATOR_MAX15569_H
+#define __LINUX_REGULATOR_MAX15569_H
+
+/*
+ * struct max15569_regulator_platform_data - max15569 regulator platform data.
+ *
+ * @reg_init_data: The regulator init data.
+ * @max_voltage_uV: Maximum possible voltage for alarm.
+ * @base_voltage_uV: Base voltage
+ * slew_rate_uv_per_us: Slew rate uV/us. The values can be
+ *             4 to 44 mV/us.
+ */
+struct max15569_regulator_platform_data {
+       struct regulator_init_data *reg_init_data;
+       bool enable_overcurrent_alarm;
+       int max_voltage_uV;
+       int base_voltage_uV;
+       unsigned int slew_rate_mv_per_us;
+};
+
+#endif /* __LINUX_REGULATOR_MAX15569_H */