linux/drivers/regulator/arizona-ldo1.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3// arizona-ldo1.c  --  LDO1 supply for Arizona devices
   4//
   5// Copyright 2012 Wolfson Microelectronics PLC.
   6//
   7// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   8
   9#include <linux/module.h>
  10#include <linux/moduleparam.h>
  11#include <linux/init.h>
  12#include <linux/bitops.h>
  13#include <linux/err.h>
  14#include <linux/of.h>
  15#include <linux/gpio/consumer.h>
  16#include <linux/platform_device.h>
  17#include <linux/regulator/driver.h>
  18#include <linux/regulator/machine.h>
  19#include <linux/regulator/of_regulator.h>
  20#include <linux/slab.h>
  21
  22#include <linux/regulator/arizona-ldo1.h>
  23
  24#include <linux/mfd/arizona/core.h>
  25#include <linux/mfd/arizona/pdata.h>
  26#include <linux/mfd/arizona/registers.h>
  27
  28#include <linux/mfd/madera/core.h>
  29#include <linux/mfd/madera/pdata.h>
  30#include <linux/mfd/madera/registers.h>
  31
  32struct arizona_ldo1 {
  33        struct regulator_dev *regulator;
  34        struct regmap *regmap;
  35
  36        struct regulator_consumer_supply supply;
  37        struct regulator_init_data init_data;
  38
  39        struct gpio_desc *ena_gpiod;
  40};
  41
  42static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev,
  43                                           unsigned sel)
  44{
  45        struct regmap *regmap = rdev_get_regmap(rdev);
  46        unsigned int val;
  47        int ret;
  48
  49        if (sel == rdev->desc->n_voltages - 1)
  50                val = ARIZONA_LDO1_HI_PWR;
  51        else
  52                val = 0;
  53
  54        ret = regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_2,
  55                                 ARIZONA_LDO1_HI_PWR, val);
  56        if (ret != 0)
  57                return ret;
  58
  59        if (val)
  60                return 0;
  61
  62        return regulator_set_voltage_sel_regmap(rdev, sel);
  63}
  64
  65static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev)
  66{
  67        struct regmap *regmap = rdev_get_regmap(rdev);
  68        unsigned int val;
  69        int ret;
  70
  71        ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_2, &val);
  72        if (ret != 0)
  73                return ret;
  74
  75        if (val & ARIZONA_LDO1_HI_PWR)
  76                return rdev->desc->n_voltages - 1;
  77
  78        return regulator_get_voltage_sel_regmap(rdev);
  79}
  80
  81static const struct regulator_ops arizona_ldo1_hc_ops = {
  82        .list_voltage = regulator_list_voltage_linear_range,
  83        .map_voltage = regulator_map_voltage_linear_range,
  84        .get_voltage_sel = arizona_ldo1_hc_get_voltage_sel,
  85        .set_voltage_sel = arizona_ldo1_hc_set_voltage_sel,
  86        .get_bypass = regulator_get_bypass_regmap,
  87        .set_bypass = regulator_set_bypass_regmap,
  88};
  89
  90static const struct linear_range arizona_ldo1_hc_ranges[] = {
  91        REGULATOR_LINEAR_RANGE(900000, 0, 0x6, 50000),
  92        REGULATOR_LINEAR_RANGE(1800000, 0x7, 0x7, 0),
  93};
  94
  95static const struct regulator_desc arizona_ldo1_hc = {
  96        .name = "LDO1",
  97        .supply_name = "LDOVDD",
  98        .type = REGULATOR_VOLTAGE,
  99        .ops = &arizona_ldo1_hc_ops,
 100
 101        .vsel_reg = ARIZONA_LDO1_CONTROL_1,
 102        .vsel_mask = ARIZONA_LDO1_VSEL_MASK,
 103        .bypass_reg = ARIZONA_LDO1_CONTROL_1,
 104        .bypass_mask = ARIZONA_LDO1_BYPASS,
 105        .linear_ranges = arizona_ldo1_hc_ranges,
 106        .n_linear_ranges = ARRAY_SIZE(arizona_ldo1_hc_ranges),
 107        .n_voltages = 8,
 108        .enable_time = 1500,
 109        .ramp_delay = 24000,
 110
 111        .owner = THIS_MODULE,
 112};
 113
 114static const struct regulator_ops arizona_ldo1_ops = {
 115        .list_voltage = regulator_list_voltage_linear,
 116        .map_voltage = regulator_map_voltage_linear,
 117        .get_voltage_sel = regulator_get_voltage_sel_regmap,
 118        .set_voltage_sel = regulator_set_voltage_sel_regmap,
 119};
 120
 121static const struct regulator_desc arizona_ldo1 = {
 122        .name = "LDO1",
 123        .supply_name = "LDOVDD",
 124        .type = REGULATOR_VOLTAGE,
 125        .ops = &arizona_ldo1_ops,
 126
 127        .vsel_reg = ARIZONA_LDO1_CONTROL_1,
 128        .vsel_mask = ARIZONA_LDO1_VSEL_MASK,
 129        .min_uV = 900000,
 130        .uV_step = 25000,
 131        .n_voltages = 13,
 132        .enable_time = 500,
 133        .ramp_delay = 24000,
 134
 135        .owner = THIS_MODULE,
 136};
 137
 138static const struct regulator_init_data arizona_ldo1_dvfs = {
 139        .constraints = {
 140                .min_uV = 1200000,
 141                .max_uV = 1800000,
 142                .valid_ops_mask = REGULATOR_CHANGE_STATUS |
 143                                  REGULATOR_CHANGE_VOLTAGE,
 144        },
 145        .num_consumer_supplies = 1,
 146};
 147
 148static const struct regulator_init_data arizona_ldo1_default = {
 149        .constraints = {
 150                .valid_ops_mask = REGULATOR_CHANGE_STATUS,
 151        },
 152        .num_consumer_supplies = 1,
 153};
 154
 155static const struct regulator_init_data arizona_ldo1_wm5110 = {
 156        .constraints = {
 157                .min_uV = 1175000,
 158                .max_uV = 1200000,
 159                .valid_ops_mask = REGULATOR_CHANGE_STATUS |
 160                                  REGULATOR_CHANGE_VOLTAGE,
 161        },
 162        .num_consumer_supplies = 1,
 163};
 164
 165static const struct regulator_desc madera_ldo1 = {
 166        .name = "LDO1",
 167        .supply_name = "LDOVDD",
 168        .type = REGULATOR_VOLTAGE,
 169        .ops = &arizona_ldo1_ops,
 170
 171        .vsel_reg = MADERA_LDO1_CONTROL_1,
 172        .vsel_mask = MADERA_LDO1_VSEL_MASK,
 173        .min_uV = 900000,
 174        .uV_step = 25000,
 175        .n_voltages = 13,
 176        .enable_time = 3000,
 177
 178        .owner = THIS_MODULE,
 179};
 180
 181static const struct regulator_init_data madera_ldo1_default = {
 182        .constraints = {
 183                .min_uV = 1200000,
 184                .max_uV = 1200000,
 185                .valid_ops_mask = REGULATOR_CHANGE_STATUS,
 186        },
 187        .num_consumer_supplies = 1,
 188};
 189
 190static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata,
 191                                     struct regulator_config *config,
 192                                     const struct regulator_desc *desc,
 193                                     bool *external_dcvdd)
 194{
 195        struct arizona_ldo1 *ldo1 = config->driver_data;
 196        struct device_node *np = config->dev->of_node;
 197        struct device_node *init_node, *dcvdd_node;
 198        struct regulator_init_data *init_data;
 199
 200        init_node = of_get_child_by_name(np, "ldo1");
 201        dcvdd_node = of_parse_phandle(np, "DCVDD-supply", 0);
 202
 203        if (init_node) {
 204                config->of_node = init_node;
 205
 206                init_data = of_get_regulator_init_data(config->dev, init_node,
 207                                                       desc);
 208                if (init_data) {
 209                        init_data->consumer_supplies = &ldo1->supply;
 210                        init_data->num_consumer_supplies = 1;
 211
 212                        if (dcvdd_node && dcvdd_node != init_node)
 213                                *external_dcvdd = true;
 214
 215                        pdata->init_data = init_data;
 216                }
 217        } else if (dcvdd_node) {
 218                *external_dcvdd = true;
 219        }
 220
 221        of_node_put(dcvdd_node);
 222
 223        return 0;
 224}
 225
 226static int arizona_ldo1_common_init(struct platform_device *pdev,
 227                                    struct arizona_ldo1 *ldo1,
 228                                    const struct regulator_desc *desc,
 229                                    struct arizona_ldo1_pdata *pdata,
 230                                    bool *external_dcvdd)
 231{
 232        struct device *parent_dev = pdev->dev.parent;
 233        struct regulator_config config = { };
 234        int ret;
 235
 236        *external_dcvdd = false;
 237
 238        ldo1->supply.supply = "DCVDD";
 239        ldo1->init_data.consumer_supplies = &ldo1->supply;
 240        ldo1->supply.dev_name = dev_name(parent_dev);
 241
 242        config.dev = parent_dev;
 243        config.driver_data = ldo1;
 244        config.regmap = ldo1->regmap;
 245
 246        if (IS_ENABLED(CONFIG_OF)) {
 247                if (!dev_get_platdata(parent_dev)) {
 248                        ret = arizona_ldo1_of_get_pdata(pdata,
 249                                                        &config, desc,
 250                                                        external_dcvdd);
 251                        if (ret < 0)
 252                                return ret;
 253                }
 254        }
 255
 256        /* We assume that high output = regulator off
 257         * Don't use devm, since we need to get against the parent device
 258         * so clean up would happen at the wrong time
 259         */
 260        config.ena_gpiod = gpiod_get_optional(parent_dev, "wlf,ldoena",
 261                                GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
 262        if (IS_ERR(config.ena_gpiod))
 263                return PTR_ERR(config.ena_gpiod);
 264
 265        ldo1->ena_gpiod = config.ena_gpiod;
 266
 267        if (pdata->init_data)
 268                config.init_data = pdata->init_data;
 269        else
 270                config.init_data = &ldo1->init_data;
 271
 272        /*
 273         * LDO1 can only be used to supply DCVDD so if it has no
 274         * consumers then DCVDD is supplied externally.
 275         */
 276        if (config.init_data->num_consumer_supplies == 0)
 277                *external_dcvdd = true;
 278
 279        ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config);
 280
 281        of_node_put(config.of_node);
 282
 283        if (IS_ERR(ldo1->regulator)) {
 284                ret = PTR_ERR(ldo1->regulator);
 285                dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n",
 286                        ret);
 287                return ret;
 288        }
 289
 290        platform_set_drvdata(pdev, ldo1);
 291
 292        return 0;
 293}
 294
 295static int arizona_ldo1_probe(struct platform_device *pdev)
 296{
 297        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
 298        struct arizona_ldo1 *ldo1;
 299        const struct regulator_desc *desc;
 300        bool external_dcvdd;
 301        int ret;
 302
 303        ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
 304        if (!ldo1)
 305                return -ENOMEM;
 306
 307        ldo1->regmap = arizona->regmap;
 308
 309        /*
 310         * Since the chip usually supplies itself we provide some
 311         * default init_data for it.  This will be overridden with
 312         * platform data if provided.
 313         */
 314        switch (arizona->type) {
 315        case WM5102:
 316        case WM8997:
 317        case WM8998:
 318        case WM1814:
 319                desc = &arizona_ldo1_hc;
 320                ldo1->init_data = arizona_ldo1_dvfs;
 321                break;
 322        case WM5110:
 323        case WM8280:
 324                desc = &arizona_ldo1;
 325                ldo1->init_data = arizona_ldo1_wm5110;
 326                break;
 327        default:
 328                desc = &arizona_ldo1;
 329                ldo1->init_data = arizona_ldo1_default;
 330                break;
 331        }
 332
 333        ret = arizona_ldo1_common_init(pdev, ldo1, desc,
 334                                       &arizona->pdata.ldo1,
 335                                       &external_dcvdd);
 336        if (ret == 0)
 337                arizona->external_dcvdd = external_dcvdd;
 338
 339        return ret;
 340}
 341
 342static int arizona_ldo1_remove(struct platform_device *pdev)
 343{
 344        struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev);
 345
 346        if (ldo1->ena_gpiod)
 347                gpiod_put(ldo1->ena_gpiod);
 348
 349        return 0;
 350}
 351
 352static int madera_ldo1_probe(struct platform_device *pdev)
 353{
 354        struct madera *madera = dev_get_drvdata(pdev->dev.parent);
 355        struct arizona_ldo1 *ldo1;
 356        bool external_dcvdd;
 357        int ret;
 358
 359        ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
 360        if (!ldo1)
 361                return -ENOMEM;
 362
 363        ldo1->regmap = madera->regmap;
 364
 365        ldo1->init_data = madera_ldo1_default;
 366
 367        ret = arizona_ldo1_common_init(pdev, ldo1, &madera_ldo1,
 368                                       &madera->pdata.ldo1,
 369                                       &external_dcvdd);
 370        if (ret)
 371                return ret;
 372
 373        madera->internal_dcvdd = !external_dcvdd;
 374
 375        return 0;
 376}
 377
 378static struct platform_driver arizona_ldo1_driver = {
 379        .probe = arizona_ldo1_probe,
 380        .remove = arizona_ldo1_remove,
 381        .driver         = {
 382                .name   = "arizona-ldo1",
 383        },
 384};
 385
 386static struct platform_driver madera_ldo1_driver = {
 387        .probe = madera_ldo1_probe,
 388        .remove = arizona_ldo1_remove,
 389        .driver         = {
 390                .name   = "madera-ldo1",
 391        },
 392};
 393
 394static struct platform_driver * const madera_ldo1_drivers[] = {
 395        &arizona_ldo1_driver,
 396        &madera_ldo1_driver,
 397};
 398
 399static int __init arizona_ldo1_init(void)
 400{
 401        return platform_register_drivers(madera_ldo1_drivers,
 402                                         ARRAY_SIZE(madera_ldo1_drivers));
 403}
 404module_init(arizona_ldo1_init);
 405
 406static void __exit madera_ldo1_exit(void)
 407{
 408        platform_unregister_drivers(madera_ldo1_drivers,
 409                                    ARRAY_SIZE(madera_ldo1_drivers));
 410}
 411module_exit(madera_ldo1_exit);
 412
 413/* Module information */
 414MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 415MODULE_DESCRIPTION("Arizona LDO1 driver");
 416MODULE_LICENSE("GPL");
 417MODULE_ALIAS("platform:arizona-ldo1");
 418MODULE_ALIAS("platform:madera-ldo1");
 419