linux/drivers/regulator/da9052-regulator.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3// da9052-regulator.c: Regulator driver for DA9052
   4//
   5// Copyright(c) 2011 Dialog Semiconductor Ltd.
   6//
   7// Author: David Dajun Chen <dchen@diasemi.com>
   8
   9#include <linux/module.h>
  10#include <linux/moduleparam.h>
  11#include <linux/init.h>
  12#include <linux/err.h>
  13#include <linux/platform_device.h>
  14#include <linux/regulator/driver.h>
  15#include <linux/regulator/machine.h>
  16#include <linux/of.h>
  17#include <linux/regulator/of_regulator.h>
  18
  19#include <linux/mfd/da9052/da9052.h>
  20#include <linux/mfd/da9052/reg.h>
  21#include <linux/mfd/da9052/pdata.h>
  22
  23/* Buck step size */
  24#define DA9052_BUCK_PERI_3uV_STEP               100000
  25#define DA9052_BUCK_PERI_REG_MAP_UPTO_3uV       24
  26#define DA9052_CONST_3uV                        3000000
  27
  28#define DA9052_MIN_UA           0
  29#define DA9052_MAX_UA           3
  30#define DA9052_CURRENT_RANGE    4
  31
  32/* Bit masks */
  33#define DA9052_BUCK_ILIM_MASK_EVEN      0x0c
  34#define DA9052_BUCK_ILIM_MASK_ODD       0xc0
  35
  36/* DA9052 REGULATOR IDs */
  37#define DA9052_ID_BUCK1         0
  38#define DA9052_ID_BUCK2         1
  39#define DA9052_ID_BUCK3         2
  40#define DA9052_ID_BUCK4         3
  41#define DA9052_ID_LDO1          4
  42#define DA9052_ID_LDO2          5
  43#define DA9052_ID_LDO3          6
  44#define DA9052_ID_LDO4          7
  45#define DA9052_ID_LDO5          8
  46#define DA9052_ID_LDO6          9
  47#define DA9052_ID_LDO7          10
  48#define DA9052_ID_LDO8          11
  49#define DA9052_ID_LDO9          12
  50#define DA9052_ID_LDO10         13
  51
  52static const u32 da9052_current_limits[3][4] = {
  53        {700000, 800000, 1000000, 1200000},     /* DA9052-BC BUCKs */
  54        {1600000, 2000000, 2400000, 3000000},   /* DA9053-AA/Bx BUCK-CORE */
  55        {800000, 1000000, 1200000, 1500000},    /* DA9053-AA/Bx BUCK-PRO,
  56                                                 * BUCK-MEM and BUCK-PERI
  57                                                */
  58};
  59
  60struct da9052_regulator_info {
  61        struct regulator_desc reg_desc;
  62        int step_uV;
  63        int min_uV;
  64        int max_uV;
  65        unsigned char activate_bit;
  66};
  67
  68struct da9052_regulator {
  69        struct da9052 *da9052;
  70        struct da9052_regulator_info *info;
  71        struct regulator_dev *rdev;
  72};
  73
  74static int verify_range(struct da9052_regulator_info *info,
  75                         int min_uV, int max_uV)
  76{
  77        if (min_uV > info->max_uV || max_uV < info->min_uV)
  78                return -EINVAL;
  79
  80        return 0;
  81}
  82
  83static int da9052_dcdc_get_current_limit(struct regulator_dev *rdev)
  84{
  85        struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
  86        int offset = rdev_get_id(rdev);
  87        int ret, row = 2;
  88
  89        ret = da9052_reg_read(regulator->da9052, DA9052_BUCKA_REG + offset/2);
  90        if (ret < 0)
  91                return ret;
  92
  93        /* Determine the even or odd position of the buck current limit
  94         * register field
  95        */
  96        if (offset % 2 == 0)
  97                ret = (ret & DA9052_BUCK_ILIM_MASK_EVEN) >> 2;
  98        else
  99                ret = (ret & DA9052_BUCK_ILIM_MASK_ODD) >> 6;
 100
 101        /* Select the appropriate current limit range */
 102        if (regulator->da9052->chip_id == DA9052)
 103                row = 0;
 104        else if (offset == 0)
 105                row = 1;
 106
 107        return da9052_current_limits[row][ret];
 108}
 109
 110static int da9052_dcdc_set_current_limit(struct regulator_dev *rdev, int min_uA,
 111                                          int max_uA)
 112{
 113        struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
 114        int offset = rdev_get_id(rdev);
 115        int reg_val = 0;
 116        int i, row = 2;
 117
 118        /* Select the appropriate current limit range */
 119        if (regulator->da9052->chip_id == DA9052)
 120                row = 0;
 121        else if (offset == 0)
 122                row = 1;
 123
 124        for (i = DA9052_CURRENT_RANGE - 1; i >= 0; i--) {
 125                if ((min_uA <= da9052_current_limits[row][i]) &&
 126                    (da9052_current_limits[row][i] <= max_uA)) {
 127                        reg_val = i;
 128                        break;
 129                }
 130        }
 131
 132        if (i < 0)
 133                return -EINVAL;
 134
 135        /* Determine the even or odd position of the buck current limit
 136         * register field
 137        */
 138        if (offset % 2 == 0)
 139                return da9052_reg_update(regulator->da9052,
 140                                         DA9052_BUCKA_REG + offset/2,
 141                                         DA9052_BUCK_ILIM_MASK_EVEN,
 142                                         reg_val << 2);
 143        else
 144                return da9052_reg_update(regulator->da9052,
 145                                         DA9052_BUCKA_REG + offset/2,
 146                                         DA9052_BUCK_ILIM_MASK_ODD,
 147                                         reg_val << 6);
 148}
 149
 150static int da9052_list_voltage(struct regulator_dev *rdev,
 151                                unsigned int selector)
 152{
 153        struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
 154        struct da9052_regulator_info *info = regulator->info;
 155        int id = rdev_get_id(rdev);
 156        int volt_uV;
 157
 158        if ((id == DA9052_ID_BUCK4) && (regulator->da9052->chip_id == DA9052)
 159                && (selector >= DA9052_BUCK_PERI_REG_MAP_UPTO_3uV)) {
 160                volt_uV = ((DA9052_BUCK_PERI_REG_MAP_UPTO_3uV * info->step_uV)
 161                          + info->min_uV);
 162                volt_uV += (selector - DA9052_BUCK_PERI_REG_MAP_UPTO_3uV)
 163                                    * (DA9052_BUCK_PERI_3uV_STEP);
 164        } else {
 165                volt_uV = (selector * info->step_uV) + info->min_uV;
 166        }
 167
 168        if (volt_uV > info->max_uV)
 169                return -EINVAL;
 170
 171        return volt_uV;
 172}
 173
 174static int da9052_map_voltage(struct regulator_dev *rdev,
 175                              int min_uV, int max_uV)
 176{
 177        struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
 178        struct da9052_regulator_info *info = regulator->info;
 179        int id = rdev_get_id(rdev);
 180        int ret, sel;
 181
 182        ret = verify_range(info, min_uV, max_uV);
 183        if (ret < 0)
 184                return ret;
 185
 186        if (min_uV < info->min_uV)
 187                min_uV = info->min_uV;
 188
 189        if ((id == DA9052_ID_BUCK4) && (regulator->da9052->chip_id == DA9052)
 190                && (min_uV >= DA9052_CONST_3uV)) {
 191                        sel = DA9052_BUCK_PERI_REG_MAP_UPTO_3uV +
 192                              DIV_ROUND_UP(min_uV - DA9052_CONST_3uV,
 193                                           DA9052_BUCK_PERI_3uV_STEP);
 194        } else {
 195                sel = DIV_ROUND_UP(min_uV - info->min_uV, info->step_uV);
 196        }
 197
 198        ret = da9052_list_voltage(rdev, sel);
 199        if (ret < 0)
 200                return ret;
 201
 202        return sel;
 203}
 204
 205static int da9052_regulator_set_voltage_sel(struct regulator_dev *rdev,
 206                                            unsigned int selector)
 207{
 208        struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
 209        struct da9052_regulator_info *info = regulator->info;
 210        int id = rdev_get_id(rdev);
 211        int ret;
 212
 213        ret = da9052_reg_update(regulator->da9052, rdev->desc->vsel_reg,
 214                                rdev->desc->vsel_mask, selector);
 215        if (ret < 0)
 216                return ret;
 217
 218        /* Some LDOs and DCDCs are DVC controlled which requires enabling of
 219         * the activate bit to implment the changes on the output.
 220         */
 221        switch (id) {
 222        case DA9052_ID_BUCK1:
 223        case DA9052_ID_BUCK2:
 224        case DA9052_ID_BUCK3:
 225        case DA9052_ID_LDO2:
 226        case DA9052_ID_LDO3:
 227                ret = da9052_reg_update(regulator->da9052, DA9052_SUPPLY_REG,
 228                                        info->activate_bit, info->activate_bit);
 229                break;
 230        }
 231
 232        return ret;
 233}
 234
 235static int da9052_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
 236                                                 unsigned int old_sel,
 237                                                 unsigned int new_sel)
 238{
 239        struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
 240        struct da9052_regulator_info *info = regulator->info;
 241        int id = rdev_get_id(rdev);
 242        int ret = 0;
 243
 244        /* The DVC controlled LDOs and DCDCs ramp with 6.25mV/µs after enabling
 245         * the activate bit.
 246         */
 247        switch (id) {
 248        case DA9052_ID_BUCK1:
 249        case DA9052_ID_BUCK2:
 250        case DA9052_ID_BUCK3:
 251        case DA9052_ID_LDO2:
 252        case DA9052_ID_LDO3:
 253                ret = DIV_ROUND_UP(abs(new_sel - old_sel) * info->step_uV,
 254                                   6250);
 255                break;
 256        }
 257
 258        return ret;
 259}
 260
 261static const struct regulator_ops da9052_dcdc_ops = {
 262        .get_current_limit = da9052_dcdc_get_current_limit,
 263        .set_current_limit = da9052_dcdc_set_current_limit,
 264
 265        .list_voltage = da9052_list_voltage,
 266        .map_voltage = da9052_map_voltage,
 267        .get_voltage_sel = regulator_get_voltage_sel_regmap,
 268        .set_voltage_sel = da9052_regulator_set_voltage_sel,
 269        .set_voltage_time_sel = da9052_regulator_set_voltage_time_sel,
 270        .is_enabled = regulator_is_enabled_regmap,
 271        .enable = regulator_enable_regmap,
 272        .disable = regulator_disable_regmap,
 273};
 274
 275static const struct regulator_ops da9052_ldo_ops = {
 276        .list_voltage = da9052_list_voltage,
 277        .map_voltage = da9052_map_voltage,
 278        .get_voltage_sel = regulator_get_voltage_sel_regmap,
 279        .set_voltage_sel = da9052_regulator_set_voltage_sel,
 280        .set_voltage_time_sel = da9052_regulator_set_voltage_time_sel,
 281        .is_enabled = regulator_is_enabled_regmap,
 282        .enable = regulator_enable_regmap,
 283        .disable = regulator_disable_regmap,
 284};
 285
 286#define DA9052_LDO(_id, _name, step, min, max, sbits, ebits, abits) \
 287{\
 288        .reg_desc = {\
 289                .name = #_name,\
 290                .of_match = of_match_ptr(#_name),\
 291                .regulators_node = of_match_ptr("regulators"),\
 292                .ops = &da9052_ldo_ops,\
 293                .type = REGULATOR_VOLTAGE,\
 294                .id = DA9052_ID_##_id,\
 295                .n_voltages = (max - min) / step + 1, \
 296                .owner = THIS_MODULE,\
 297                .vsel_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \
 298                .vsel_mask = (1 << (sbits)) - 1,\
 299                .enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \
 300                .enable_mask = 1 << (ebits),\
 301        },\
 302        .min_uV = (min) * 1000,\
 303        .max_uV = (max) * 1000,\
 304        .step_uV = (step) * 1000,\
 305        .activate_bit = (abits),\
 306}
 307
 308#define DA9052_DCDC(_id, _name, step, min, max, sbits, ebits, abits) \
 309{\
 310        .reg_desc = {\
 311                .name = #_name,\
 312                .of_match = of_match_ptr(#_name),\
 313                .regulators_node = of_match_ptr("regulators"),\
 314                .ops = &da9052_dcdc_ops,\
 315                .type = REGULATOR_VOLTAGE,\
 316                .id = DA9052_ID_##_id,\
 317                .n_voltages = (max - min) / step + 1, \
 318                .owner = THIS_MODULE,\
 319                .vsel_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \
 320                .vsel_mask = (1 << (sbits)) - 1,\
 321                .enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \
 322                .enable_mask = 1 << (ebits),\
 323        },\
 324        .min_uV = (min) * 1000,\
 325        .max_uV = (max) * 1000,\
 326        .step_uV = (step) * 1000,\
 327        .activate_bit = (abits),\
 328}
 329
 330static struct da9052_regulator_info da9052_regulator_info[] = {
 331        DA9052_DCDC(BUCK1, buck1, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBCOREGO),
 332        DA9052_DCDC(BUCK2, buck2, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBPROGO),
 333        DA9052_DCDC(BUCK3, buck3, 25, 950, 2525, 6, 6, DA9052_SUPPLY_VBMEMGO),
 334        DA9052_DCDC(BUCK4, buck4, 50, 1800, 3600, 5, 6, 0),
 335        DA9052_LDO(LDO1, ldo1, 50, 600, 1800, 5, 6, 0),
 336        DA9052_LDO(LDO2, ldo2, 25, 600, 1800, 6, 6, DA9052_SUPPLY_VLDO2GO),
 337        DA9052_LDO(LDO3, ldo3, 25, 1725, 3300, 6, 6, DA9052_SUPPLY_VLDO3GO),
 338        DA9052_LDO(LDO4, ldo4, 25, 1725, 3300, 6, 6, 0),
 339        DA9052_LDO(LDO5, ldo5, 50, 1200, 3600, 6, 6, 0),
 340        DA9052_LDO(LDO6, ldo6, 50, 1200, 3600, 6, 6, 0),
 341        DA9052_LDO(LDO7, ldo7, 50, 1200, 3600, 6, 6, 0),
 342        DA9052_LDO(LDO8, ldo8, 50, 1200, 3600, 6, 6, 0),
 343        DA9052_LDO(LDO9, ldo9, 50, 1250, 3650, 6, 6, 0),
 344        DA9052_LDO(LDO10, ldo10, 50, 1200, 3600, 6, 6, 0),
 345};
 346
 347static struct da9052_regulator_info da9053_regulator_info[] = {
 348        DA9052_DCDC(BUCK1, buck1, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBCOREGO),
 349        DA9052_DCDC(BUCK2, buck2, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBPROGO),
 350        DA9052_DCDC(BUCK3, buck3, 25, 950, 2525, 6, 6, DA9052_SUPPLY_VBMEMGO),
 351        DA9052_DCDC(BUCK4, buck4, 25, 950, 2525, 6, 6, 0),
 352        DA9052_LDO(LDO1, ldo1, 50, 600, 1800, 5, 6, 0),
 353        DA9052_LDO(LDO2, ldo2, 25, 600, 1800, 6, 6, DA9052_SUPPLY_VLDO2GO),
 354        DA9052_LDO(LDO3, ldo3, 25, 1725, 3300, 6, 6, DA9052_SUPPLY_VLDO3GO),
 355        DA9052_LDO(LDO4, ldo4, 25, 1725, 3300, 6, 6, 0),
 356        DA9052_LDO(LDO5, ldo5, 50, 1200, 3600, 6, 6, 0),
 357        DA9052_LDO(LDO6, ldo6, 50, 1200, 3600, 6, 6, 0),
 358        DA9052_LDO(LDO7, ldo7, 50, 1200, 3600, 6, 6, 0),
 359        DA9052_LDO(LDO8, ldo8, 50, 1200, 3600, 6, 6, 0),
 360        DA9052_LDO(LDO9, ldo9, 50, 1250, 3650, 6, 6, 0),
 361        DA9052_LDO(LDO10, ldo10, 50, 1200, 3600, 6, 6, 0),
 362};
 363
 364static inline struct da9052_regulator_info *find_regulator_info(u8 chip_id,
 365                                                                 int id)
 366{
 367        struct da9052_regulator_info *info;
 368        int i;
 369
 370        switch (chip_id) {
 371        case DA9052:
 372                for (i = 0; i < ARRAY_SIZE(da9052_regulator_info); i++) {
 373                        info = &da9052_regulator_info[i];
 374                        if (info->reg_desc.id == id)
 375                                return info;
 376                }
 377                break;
 378        case DA9053_AA:
 379        case DA9053_BA:
 380        case DA9053_BB:
 381        case DA9053_BC:
 382                for (i = 0; i < ARRAY_SIZE(da9053_regulator_info); i++) {
 383                        info = &da9053_regulator_info[i];
 384                        if (info->reg_desc.id == id)
 385                                return info;
 386                }
 387                break;
 388        }
 389
 390        return NULL;
 391}
 392
 393static int da9052_regulator_probe(struct platform_device *pdev)
 394{
 395        const struct mfd_cell *cell = mfd_get_cell(pdev);
 396        struct regulator_config config = { };
 397        struct da9052_regulator *regulator;
 398        struct da9052 *da9052;
 399        struct da9052_pdata *pdata;
 400
 401        regulator = devm_kzalloc(&pdev->dev, sizeof(struct da9052_regulator),
 402                                 GFP_KERNEL);
 403        if (!regulator)
 404                return -ENOMEM;
 405
 406        da9052 = dev_get_drvdata(pdev->dev.parent);
 407        pdata = dev_get_platdata(da9052->dev);
 408        regulator->da9052 = da9052;
 409
 410        regulator->info = find_regulator_info(regulator->da9052->chip_id,
 411                                              cell->id);
 412        if (regulator->info == NULL) {
 413                dev_err(&pdev->dev, "invalid regulator ID specified\n");
 414                return -EINVAL;
 415        }
 416
 417        config.dev = da9052->dev;
 418        config.driver_data = regulator;
 419        config.regmap = da9052->regmap;
 420        if (pdata)
 421                config.init_data = pdata->regulators[cell->id];
 422
 423        regulator->rdev = devm_regulator_register(&pdev->dev,
 424                                                  &regulator->info->reg_desc,
 425                                                  &config);
 426        if (IS_ERR(regulator->rdev)) {
 427                dev_err(&pdev->dev, "failed to register regulator %s\n",
 428                        regulator->info->reg_desc.name);
 429                return PTR_ERR(regulator->rdev);
 430        }
 431
 432        platform_set_drvdata(pdev, regulator);
 433
 434        return 0;
 435}
 436
 437static struct platform_driver da9052_regulator_driver = {
 438        .probe = da9052_regulator_probe,
 439        .driver = {
 440                .name = "da9052-regulator",
 441        },
 442};
 443
 444static int __init da9052_regulator_init(void)
 445{
 446        return platform_driver_register(&da9052_regulator_driver);
 447}
 448subsys_initcall(da9052_regulator_init);
 449
 450static void __exit da9052_regulator_exit(void)
 451{
 452        platform_driver_unregister(&da9052_regulator_driver);
 453}
 454module_exit(da9052_regulator_exit);
 455
 456MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
 457MODULE_DESCRIPTION("Power Regulator driver for Dialog DA9052 PMIC");
 458MODULE_LICENSE("GPL");
 459MODULE_ALIAS("platform:da9052-regulator");
 460