linux/drivers/regulator/mp886x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// MP8867/MP8869 regulator driver
   4//
   5// Copyright (C) 2020 Synaptics Incorporated
   6//
   7// Author: Jisheng Zhang <jszhang@kernel.org>
   8
   9#include <linux/gpio/consumer.h>
  10#include <linux/i2c.h>
  11#include <linux/module.h>
  12#include <linux/of_device.h>
  13#include <linux/regmap.h>
  14#include <linux/regulator/driver.h>
  15#include <linux/regulator/of_regulator.h>
  16
  17#define MP886X_VSEL             0x00
  18#define  MP886X_V_BOOT          (1 << 7)
  19#define MP886X_SYSCNTLREG1      0x01
  20#define  MP886X_MODE            (1 << 0)
  21#define  MP886X_GO              (1 << 6)
  22#define  MP886X_EN              (1 << 7)
  23
  24struct mp886x_device_info {
  25        struct device *dev;
  26        struct regulator_desc desc;
  27        struct regulator_init_data *regulator;
  28        struct gpio_desc *en_gpio;
  29        u32 r[2];
  30        unsigned int sel;
  31};
  32
  33static int mp886x_set_mode(struct regulator_dev *rdev, unsigned int mode)
  34{
  35        switch (mode) {
  36        case REGULATOR_MODE_FAST:
  37                regmap_update_bits(rdev->regmap, MP886X_SYSCNTLREG1,
  38                                   MP886X_MODE, MP886X_MODE);
  39                break;
  40        case REGULATOR_MODE_NORMAL:
  41                regmap_update_bits(rdev->regmap, MP886X_SYSCNTLREG1,
  42                                   MP886X_MODE, 0);
  43                break;
  44        default:
  45                return -EINVAL;
  46        }
  47        return 0;
  48}
  49
  50static unsigned int mp886x_get_mode(struct regulator_dev *rdev)
  51{
  52        u32 val;
  53        int ret;
  54
  55        ret = regmap_read(rdev->regmap, MP886X_SYSCNTLREG1, &val);
  56        if (ret < 0)
  57                return ret;
  58        if (val & MP886X_MODE)
  59                return REGULATOR_MODE_FAST;
  60        else
  61                return REGULATOR_MODE_NORMAL;
  62}
  63
  64static int mp8869_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
  65{
  66        int ret;
  67
  68        ret = regmap_update_bits(rdev->regmap, MP886X_SYSCNTLREG1,
  69                                 MP886X_GO, MP886X_GO);
  70        if (ret < 0)
  71                return ret;
  72
  73        sel <<= ffs(rdev->desc->vsel_mask) - 1;
  74        return regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
  75                                  MP886X_V_BOOT | rdev->desc->vsel_mask, sel);
  76}
  77
  78static inline unsigned int mp8869_scale(unsigned int uv, u32 r1, u32 r2)
  79{
  80        u32 tmp = uv * r1 / r2;
  81
  82        return uv + tmp;
  83}
  84
  85static int mp8869_get_voltage_sel(struct regulator_dev *rdev)
  86{
  87        struct mp886x_device_info *di = rdev_get_drvdata(rdev);
  88        int ret, uv;
  89        unsigned int val;
  90        bool fbloop;
  91
  92        ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
  93        if (ret)
  94                return ret;
  95
  96        fbloop = val & MP886X_V_BOOT;
  97        if (fbloop) {
  98                uv = rdev->desc->min_uV;
  99                uv = mp8869_scale(uv, di->r[0], di->r[1]);
 100                return regulator_map_voltage_linear(rdev, uv, uv);
 101        }
 102
 103        val &= rdev->desc->vsel_mask;
 104        val >>= ffs(rdev->desc->vsel_mask) - 1;
 105
 106        return val;
 107}
 108
 109static const struct regulator_ops mp8869_regulator_ops = {
 110        .set_voltage_sel = mp8869_set_voltage_sel,
 111        .get_voltage_sel = mp8869_get_voltage_sel,
 112        .set_voltage_time_sel = regulator_set_voltage_time_sel,
 113        .map_voltage = regulator_map_voltage_linear,
 114        .list_voltage = regulator_list_voltage_linear,
 115        .enable = regulator_enable_regmap,
 116        .disable = regulator_disable_regmap,
 117        .is_enabled = regulator_is_enabled_regmap,
 118        .set_mode = mp886x_set_mode,
 119        .get_mode = mp886x_get_mode,
 120};
 121
 122static int mp8867_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
 123{
 124        struct mp886x_device_info *di = rdev_get_drvdata(rdev);
 125        int ret, delta;
 126
 127        ret = mp8869_set_voltage_sel(rdev, sel);
 128        if (ret < 0)
 129                return ret;
 130
 131        delta = di->sel - sel;
 132        if (abs(delta) <= 5)
 133                ret = regmap_update_bits(rdev->regmap, MP886X_SYSCNTLREG1,
 134                                         MP886X_GO, 0);
 135        di->sel = sel;
 136
 137        return ret;
 138}
 139
 140static int mp8867_get_voltage_sel(struct regulator_dev *rdev)
 141{
 142        struct mp886x_device_info *di = rdev_get_drvdata(rdev);
 143        int ret, uv;
 144        unsigned int val;
 145        bool fbloop;
 146
 147        ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
 148        if (ret)
 149                return ret;
 150
 151        fbloop = val & MP886X_V_BOOT;
 152
 153        val &= rdev->desc->vsel_mask;
 154        val >>= ffs(rdev->desc->vsel_mask) - 1;
 155
 156        if (fbloop) {
 157                uv = regulator_list_voltage_linear(rdev, val);
 158                uv = mp8869_scale(uv, di->r[0], di->r[1]);
 159                return regulator_map_voltage_linear(rdev, uv, uv);
 160        }
 161
 162        return val;
 163}
 164
 165static const struct regulator_ops mp8867_regulator_ops = {
 166        .set_voltage_sel = mp8867_set_voltage_sel,
 167        .get_voltage_sel = mp8867_get_voltage_sel,
 168        .set_voltage_time_sel = regulator_set_voltage_time_sel,
 169        .map_voltage = regulator_map_voltage_linear,
 170        .list_voltage = regulator_list_voltage_linear,
 171        .enable = regulator_enable_regmap,
 172        .disable = regulator_disable_regmap,
 173        .is_enabled = regulator_is_enabled_regmap,
 174        .set_mode = mp886x_set_mode,
 175        .get_mode = mp886x_get_mode,
 176};
 177
 178static int mp886x_regulator_register(struct mp886x_device_info *di,
 179                                     struct regulator_config *config)
 180{
 181        struct regulator_desc *rdesc = &di->desc;
 182        struct regulator_dev *rdev;
 183
 184        rdesc->name = "mp886x-reg";
 185        rdesc->supply_name = "vin";
 186        rdesc->ops = of_device_get_match_data(di->dev);
 187        rdesc->type = REGULATOR_VOLTAGE;
 188        rdesc->n_voltages = 128;
 189        rdesc->enable_reg = MP886X_SYSCNTLREG1;
 190        rdesc->enable_mask = MP886X_EN;
 191        rdesc->min_uV = 600000;
 192        rdesc->uV_step = 10000;
 193        rdesc->vsel_reg = MP886X_VSEL;
 194        rdesc->vsel_mask = 0x3f;
 195        rdesc->owner = THIS_MODULE;
 196
 197        rdev = devm_regulator_register(di->dev, &di->desc, config);
 198        if (IS_ERR(rdev))
 199                return PTR_ERR(rdev);
 200        di->sel = rdesc->ops->get_voltage_sel(rdev);
 201        return 0;
 202}
 203
 204static const struct regmap_config mp886x_regmap_config = {
 205        .reg_bits = 8,
 206        .val_bits = 8,
 207};
 208
 209static int mp886x_i2c_probe(struct i2c_client *client,
 210                            const struct i2c_device_id *id)
 211{
 212        struct device *dev = &client->dev;
 213        struct device_node *np = dev->of_node;
 214        struct mp886x_device_info *di;
 215        struct regulator_config config = { };
 216        struct regmap *regmap;
 217        int ret;
 218
 219        di = devm_kzalloc(dev, sizeof(struct mp886x_device_info), GFP_KERNEL);
 220        if (!di)
 221                return -ENOMEM;
 222
 223        di->regulator = of_get_regulator_init_data(dev, np, &di->desc);
 224        if (!di->regulator) {
 225                dev_err(dev, "Platform data not found!\n");
 226                return -EINVAL;
 227        }
 228
 229        ret = of_property_read_u32_array(np, "mps,fb-voltage-divider",
 230                                         di->r, 2);
 231        if (ret)
 232                return ret;
 233
 234        di->en_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
 235        if (IS_ERR(di->en_gpio))
 236                return PTR_ERR(di->en_gpio);
 237
 238        di->dev = dev;
 239
 240        regmap = devm_regmap_init_i2c(client, &mp886x_regmap_config);
 241        if (IS_ERR(regmap)) {
 242                dev_err(dev, "Failed to allocate regmap!\n");
 243                return PTR_ERR(regmap);
 244        }
 245        i2c_set_clientdata(client, di);
 246
 247        config.dev = di->dev;
 248        config.init_data = di->regulator;
 249        config.regmap = regmap;
 250        config.driver_data = di;
 251        config.of_node = np;
 252
 253        ret = mp886x_regulator_register(di, &config);
 254        if (ret < 0)
 255                dev_err(dev, "Failed to register regulator!\n");
 256        return ret;
 257}
 258
 259static const struct of_device_id mp886x_dt_ids[] = {
 260        {
 261                .compatible = "mps,mp8867",
 262                .data = &mp8867_regulator_ops
 263        },
 264        {
 265                .compatible = "mps,mp8869",
 266                .data = &mp8869_regulator_ops
 267        },
 268        { }
 269};
 270MODULE_DEVICE_TABLE(of, mp886x_dt_ids);
 271
 272static const struct i2c_device_id mp886x_id[] = {
 273        { "mp886x", },
 274        { },
 275};
 276MODULE_DEVICE_TABLE(i2c, mp886x_id);
 277
 278static struct i2c_driver mp886x_regulator_driver = {
 279        .driver = {
 280                .name = "mp886x-regulator",
 281                .of_match_table = of_match_ptr(mp886x_dt_ids),
 282        },
 283        .probe = mp886x_i2c_probe,
 284        .id_table = mp886x_id,
 285};
 286module_i2c_driver(mp886x_regulator_driver);
 287
 288MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
 289MODULE_DESCRIPTION("MP886x regulator driver");
 290MODULE_LICENSE("GPL v2");
 291