Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // arizona-ldo1.c  --  LDO1 supply for Arizona devices
0004 //
0005 // Copyright 2012 Wolfson Microelectronics PLC.
0006 //
0007 // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
0008 
0009 #include <linux/module.h>
0010 #include <linux/moduleparam.h>
0011 #include <linux/init.h>
0012 #include <linux/bitops.h>
0013 #include <linux/err.h>
0014 #include <linux/of.h>
0015 #include <linux/gpio/consumer.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/regulator/driver.h>
0018 #include <linux/regulator/machine.h>
0019 #include <linux/regulator/of_regulator.h>
0020 #include <linux/slab.h>
0021 
0022 #include <linux/regulator/arizona-ldo1.h>
0023 
0024 #include <linux/mfd/arizona/core.h>
0025 #include <linux/mfd/arizona/pdata.h>
0026 #include <linux/mfd/arizona/registers.h>
0027 
0028 #include <linux/mfd/madera/core.h>
0029 #include <linux/mfd/madera/pdata.h>
0030 #include <linux/mfd/madera/registers.h>
0031 
0032 struct arizona_ldo1 {
0033     struct regulator_dev *regulator;
0034     struct regmap *regmap;
0035 
0036     struct regulator_consumer_supply supply;
0037     struct regulator_init_data init_data;
0038 
0039     struct gpio_desc *ena_gpiod;
0040 };
0041 
0042 static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev,
0043                        unsigned sel)
0044 {
0045     struct regmap *regmap = rdev_get_regmap(rdev);
0046     unsigned int val;
0047     int ret;
0048 
0049     if (sel == rdev->desc->n_voltages - 1)
0050         val = ARIZONA_LDO1_HI_PWR;
0051     else
0052         val = 0;
0053 
0054     ret = regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_2,
0055                  ARIZONA_LDO1_HI_PWR, val);
0056     if (ret != 0)
0057         return ret;
0058 
0059     if (val)
0060         return 0;
0061 
0062     return regulator_set_voltage_sel_regmap(rdev, sel);
0063 }
0064 
0065 static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev)
0066 {
0067     struct regmap *regmap = rdev_get_regmap(rdev);
0068     unsigned int val;
0069     int ret;
0070 
0071     ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_2, &val);
0072     if (ret != 0)
0073         return ret;
0074 
0075     if (val & ARIZONA_LDO1_HI_PWR)
0076         return rdev->desc->n_voltages - 1;
0077 
0078     return regulator_get_voltage_sel_regmap(rdev);
0079 }
0080 
0081 static const struct regulator_ops arizona_ldo1_hc_ops = {
0082     .list_voltage = regulator_list_voltage_linear_range,
0083     .map_voltage = regulator_map_voltage_linear_range,
0084     .get_voltage_sel = arizona_ldo1_hc_get_voltage_sel,
0085     .set_voltage_sel = arizona_ldo1_hc_set_voltage_sel,
0086     .get_bypass = regulator_get_bypass_regmap,
0087     .set_bypass = regulator_set_bypass_regmap,
0088 };
0089 
0090 static const struct linear_range arizona_ldo1_hc_ranges[] = {
0091     REGULATOR_LINEAR_RANGE(900000, 0, 0x6, 50000),
0092     REGULATOR_LINEAR_RANGE(1800000, 0x7, 0x7, 0),
0093 };
0094 
0095 static const struct regulator_desc arizona_ldo1_hc = {
0096     .name = "LDO1",
0097     .supply_name = "LDOVDD",
0098     .type = REGULATOR_VOLTAGE,
0099     .ops = &arizona_ldo1_hc_ops,
0100 
0101     .vsel_reg = ARIZONA_LDO1_CONTROL_1,
0102     .vsel_mask = ARIZONA_LDO1_VSEL_MASK,
0103     .bypass_reg = ARIZONA_LDO1_CONTROL_1,
0104     .bypass_mask = ARIZONA_LDO1_BYPASS,
0105     .linear_ranges = arizona_ldo1_hc_ranges,
0106     .n_linear_ranges = ARRAY_SIZE(arizona_ldo1_hc_ranges),
0107     .n_voltages = 8,
0108     .enable_time = 1500,
0109     .ramp_delay = 24000,
0110 
0111     .owner = THIS_MODULE,
0112 };
0113 
0114 static const struct regulator_ops arizona_ldo1_ops = {
0115     .list_voltage = regulator_list_voltage_linear,
0116     .map_voltage = regulator_map_voltage_linear,
0117     .get_voltage_sel = regulator_get_voltage_sel_regmap,
0118     .set_voltage_sel = regulator_set_voltage_sel_regmap,
0119 };
0120 
0121 static const struct regulator_desc arizona_ldo1 = {
0122     .name = "LDO1",
0123     .supply_name = "LDOVDD",
0124     .type = REGULATOR_VOLTAGE,
0125     .ops = &arizona_ldo1_ops,
0126 
0127     .vsel_reg = ARIZONA_LDO1_CONTROL_1,
0128     .vsel_mask = ARIZONA_LDO1_VSEL_MASK,
0129     .min_uV = 900000,
0130     .uV_step = 25000,
0131     .n_voltages = 13,
0132     .enable_time = 500,
0133     .ramp_delay = 24000,
0134 
0135     .owner = THIS_MODULE,
0136 };
0137 
0138 static const struct regulator_init_data arizona_ldo1_dvfs = {
0139     .constraints = {
0140         .min_uV = 1200000,
0141         .max_uV = 1800000,
0142         .valid_ops_mask = REGULATOR_CHANGE_STATUS |
0143                   REGULATOR_CHANGE_VOLTAGE,
0144     },
0145     .num_consumer_supplies = 1,
0146 };
0147 
0148 static const struct regulator_init_data arizona_ldo1_default = {
0149     .constraints = {
0150         .valid_ops_mask = REGULATOR_CHANGE_STATUS,
0151     },
0152     .num_consumer_supplies = 1,
0153 };
0154 
0155 static const struct regulator_init_data arizona_ldo1_wm5110 = {
0156     .constraints = {
0157         .min_uV = 1175000,
0158         .max_uV = 1200000,
0159         .valid_ops_mask = REGULATOR_CHANGE_STATUS |
0160                   REGULATOR_CHANGE_VOLTAGE,
0161     },
0162     .num_consumer_supplies = 1,
0163 };
0164 
0165 static const struct regulator_desc madera_ldo1 = {
0166     .name = "LDO1",
0167     .supply_name = "LDOVDD",
0168     .type = REGULATOR_VOLTAGE,
0169     .ops = &arizona_ldo1_ops,
0170 
0171     .vsel_reg = MADERA_LDO1_CONTROL_1,
0172     .vsel_mask = MADERA_LDO1_VSEL_MASK,
0173     .min_uV = 900000,
0174     .uV_step = 25000,
0175     .n_voltages = 13,
0176     .enable_time = 3000,
0177 
0178     .owner = THIS_MODULE,
0179 };
0180 
0181 static const struct regulator_init_data madera_ldo1_default = {
0182     .constraints = {
0183         .min_uV = 1200000,
0184         .max_uV = 1200000,
0185         .valid_ops_mask = REGULATOR_CHANGE_STATUS,
0186     },
0187     .num_consumer_supplies = 1,
0188 };
0189 
0190 static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata,
0191                      struct regulator_config *config,
0192                      const struct regulator_desc *desc,
0193                      bool *external_dcvdd)
0194 {
0195     struct arizona_ldo1 *ldo1 = config->driver_data;
0196     struct device_node *np = config->dev->of_node;
0197     struct device_node *init_node, *dcvdd_node;
0198     struct regulator_init_data *init_data;
0199 
0200     init_node = of_get_child_by_name(np, "ldo1");
0201     dcvdd_node = of_parse_phandle(np, "DCVDD-supply", 0);
0202 
0203     if (init_node) {
0204         config->of_node = init_node;
0205 
0206         init_data = of_get_regulator_init_data(config->dev, init_node,
0207                                desc);
0208         if (init_data) {
0209             init_data->consumer_supplies = &ldo1->supply;
0210             init_data->num_consumer_supplies = 1;
0211 
0212             if (dcvdd_node && dcvdd_node != init_node)
0213                 *external_dcvdd = true;
0214 
0215             pdata->init_data = init_data;
0216         }
0217     } else if (dcvdd_node) {
0218         *external_dcvdd = true;
0219     }
0220 
0221     of_node_put(dcvdd_node);
0222 
0223     return 0;
0224 }
0225 
0226 static int arizona_ldo1_common_init(struct platform_device *pdev,
0227                     struct arizona_ldo1 *ldo1,
0228                     const struct regulator_desc *desc,
0229                     struct arizona_ldo1_pdata *pdata,
0230                     bool *external_dcvdd)
0231 {
0232     struct device *parent_dev = pdev->dev.parent;
0233     struct regulator_config config = { };
0234     int ret;
0235 
0236     *external_dcvdd = false;
0237 
0238     ldo1->supply.supply = "DCVDD";
0239     ldo1->init_data.consumer_supplies = &ldo1->supply;
0240     ldo1->supply.dev_name = dev_name(parent_dev);
0241 
0242     config.dev = parent_dev;
0243     config.driver_data = ldo1;
0244     config.regmap = ldo1->regmap;
0245 
0246     if (IS_ENABLED(CONFIG_OF)) {
0247         if (!dev_get_platdata(parent_dev)) {
0248             ret = arizona_ldo1_of_get_pdata(pdata,
0249                             &config, desc,
0250                             external_dcvdd);
0251             if (ret < 0)
0252                 return ret;
0253         }
0254     }
0255 
0256     /* We assume that high output = regulator off
0257      * Don't use devm, since we need to get against the parent device
0258      * so clean up would happen at the wrong time
0259      */
0260     config.ena_gpiod = gpiod_get_optional(parent_dev, "wlf,ldoena",
0261                 GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
0262     if (IS_ERR(config.ena_gpiod))
0263         return PTR_ERR(config.ena_gpiod);
0264 
0265     ldo1->ena_gpiod = config.ena_gpiod;
0266 
0267     if (pdata->init_data)
0268         config.init_data = pdata->init_data;
0269     else
0270         config.init_data = &ldo1->init_data;
0271 
0272     /*
0273      * LDO1 can only be used to supply DCVDD so if it has no
0274      * consumers then DCVDD is supplied externally.
0275      */
0276     if (config.init_data->num_consumer_supplies == 0)
0277         *external_dcvdd = true;
0278 
0279     ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config);
0280 
0281     of_node_put(config.of_node);
0282 
0283     if (IS_ERR(ldo1->regulator)) {
0284         ret = PTR_ERR(ldo1->regulator);
0285         dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n",
0286             ret);
0287         return ret;
0288     }
0289 
0290     platform_set_drvdata(pdev, ldo1);
0291 
0292     return 0;
0293 }
0294 
0295 static int arizona_ldo1_probe(struct platform_device *pdev)
0296 {
0297     struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
0298     struct arizona_ldo1 *ldo1;
0299     const struct regulator_desc *desc;
0300     bool external_dcvdd;
0301     int ret;
0302 
0303     ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
0304     if (!ldo1)
0305         return -ENOMEM;
0306 
0307     ldo1->regmap = arizona->regmap;
0308 
0309     /*
0310      * Since the chip usually supplies itself we provide some
0311      * default init_data for it.  This will be overridden with
0312      * platform data if provided.
0313      */
0314     switch (arizona->type) {
0315     case WM5102:
0316     case WM8997:
0317     case WM8998:
0318     case WM1814:
0319         desc = &arizona_ldo1_hc;
0320         ldo1->init_data = arizona_ldo1_dvfs;
0321         break;
0322     case WM5110:
0323     case WM8280:
0324         desc = &arizona_ldo1;
0325         ldo1->init_data = arizona_ldo1_wm5110;
0326         break;
0327     default:
0328         desc = &arizona_ldo1;
0329         ldo1->init_data = arizona_ldo1_default;
0330         break;
0331     }
0332 
0333     ret = arizona_ldo1_common_init(pdev, ldo1, desc,
0334                        &arizona->pdata.ldo1,
0335                        &external_dcvdd);
0336     if (ret == 0)
0337         arizona->external_dcvdd = external_dcvdd;
0338 
0339     return ret;
0340 }
0341 
0342 static int arizona_ldo1_remove(struct platform_device *pdev)
0343 {
0344     struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev);
0345 
0346     if (ldo1->ena_gpiod)
0347         gpiod_put(ldo1->ena_gpiod);
0348 
0349     return 0;
0350 }
0351 
0352 static int madera_ldo1_probe(struct platform_device *pdev)
0353 {
0354     struct madera *madera = dev_get_drvdata(pdev->dev.parent);
0355     struct arizona_ldo1 *ldo1;
0356     bool external_dcvdd;
0357     int ret;
0358 
0359     ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
0360     if (!ldo1)
0361         return -ENOMEM;
0362 
0363     ldo1->regmap = madera->regmap;
0364 
0365     ldo1->init_data = madera_ldo1_default;
0366 
0367     ret = arizona_ldo1_common_init(pdev, ldo1, &madera_ldo1,
0368                        &madera->pdata.ldo1,
0369                        &external_dcvdd);
0370     if (ret)
0371         return ret;
0372 
0373     madera->internal_dcvdd = !external_dcvdd;
0374 
0375     return 0;
0376 }
0377 
0378 static struct platform_driver arizona_ldo1_driver = {
0379     .probe = arizona_ldo1_probe,
0380     .remove = arizona_ldo1_remove,
0381     .driver     = {
0382         .name   = "arizona-ldo1",
0383     },
0384 };
0385 
0386 static struct platform_driver madera_ldo1_driver = {
0387     .probe = madera_ldo1_probe,
0388     .remove = arizona_ldo1_remove,
0389     .driver     = {
0390         .name   = "madera-ldo1",
0391     },
0392 };
0393 
0394 static struct platform_driver * const madera_ldo1_drivers[] = {
0395     &arizona_ldo1_driver,
0396     &madera_ldo1_driver,
0397 };
0398 
0399 static int __init arizona_ldo1_init(void)
0400 {
0401     return platform_register_drivers(madera_ldo1_drivers,
0402                      ARRAY_SIZE(madera_ldo1_drivers));
0403 }
0404 module_init(arizona_ldo1_init);
0405 
0406 static void __exit madera_ldo1_exit(void)
0407 {
0408     platform_unregister_drivers(madera_ldo1_drivers,
0409                     ARRAY_SIZE(madera_ldo1_drivers));
0410 }
0411 module_exit(madera_ldo1_exit);
0412 
0413 /* Module information */
0414 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
0415 MODULE_DESCRIPTION("Arizona LDO1 driver");
0416 MODULE_LICENSE("GPL");
0417 MODULE_ALIAS("platform:arizona-ldo1");
0418 MODULE_ALIAS("platform:madera-ldo1");