Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Lochnagar regulator driver
0004 //
0005 // Copyright (c) 2017-2018 Cirrus Logic, Inc. and
0006 //                         Cirrus Logic International Semiconductor Ltd.
0007 //
0008 // Author: Charles Keepax <ckeepax@opensource.cirrus.com>
0009 
0010 #include <linux/bitops.h>
0011 #include <linux/device.h>
0012 #include <linux/err.h>
0013 #include <linux/module.h>
0014 #include <linux/mutex.h>
0015 #include <linux/of.h>
0016 #include <linux/of_device.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/regmap.h>
0019 #include <linux/regulator/driver.h>
0020 #include <linux/regulator/machine.h>
0021 #include <linux/regulator/of_regulator.h>
0022 
0023 #include <linux/mfd/lochnagar.h>
0024 #include <linux/mfd/lochnagar1_regs.h>
0025 #include <linux/mfd/lochnagar2_regs.h>
0026 
0027 static const struct regulator_ops lochnagar_micvdd_ops = {
0028     .enable = regulator_enable_regmap,
0029     .disable = regulator_disable_regmap,
0030     .is_enabled = regulator_is_enabled_regmap,
0031 
0032     .list_voltage = regulator_list_voltage_linear_range,
0033     .map_voltage = regulator_map_voltage_linear_range,
0034 
0035     .get_voltage_sel = regulator_get_voltage_sel_regmap,
0036     .set_voltage_sel = regulator_set_voltage_sel_regmap,
0037 };
0038 
0039 static const struct linear_range lochnagar_micvdd_ranges[] = {
0040     REGULATOR_LINEAR_RANGE(1000000, 0,    0xC, 50000),
0041     REGULATOR_LINEAR_RANGE(1700000, 0xD, 0x1F, 100000),
0042 };
0043 
0044 static int lochnagar_micbias_enable(struct regulator_dev *rdev)
0045 {
0046     struct lochnagar *lochnagar = rdev_get_drvdata(rdev);
0047     int ret;
0048 
0049     mutex_lock(&lochnagar->analogue_config_lock);
0050 
0051     ret = regulator_enable_regmap(rdev);
0052     if (ret < 0)
0053         goto err;
0054 
0055     ret = lochnagar_update_config(lochnagar);
0056 
0057 err:
0058     mutex_unlock(&lochnagar->analogue_config_lock);
0059 
0060     return ret;
0061 }
0062 
0063 static int lochnagar_micbias_disable(struct regulator_dev *rdev)
0064 {
0065     struct lochnagar *lochnagar = rdev_get_drvdata(rdev);
0066     int ret;
0067 
0068     mutex_lock(&lochnagar->analogue_config_lock);
0069 
0070     ret = regulator_disable_regmap(rdev);
0071     if (ret < 0)
0072         goto err;
0073 
0074     ret = lochnagar_update_config(lochnagar);
0075 
0076 err:
0077     mutex_unlock(&lochnagar->analogue_config_lock);
0078 
0079     return ret;
0080 }
0081 
0082 static const struct regulator_ops lochnagar_micbias_ops = {
0083     .enable = lochnagar_micbias_enable,
0084     .disable = lochnagar_micbias_disable,
0085     .is_enabled = regulator_is_enabled_regmap,
0086 };
0087 
0088 static const struct regulator_ops lochnagar_vddcore_ops = {
0089     .enable = regulator_enable_regmap,
0090     .disable = regulator_disable_regmap,
0091     .is_enabled = regulator_is_enabled_regmap,
0092 
0093     .list_voltage = regulator_list_voltage_linear_range,
0094     .map_voltage = regulator_map_voltage_linear_range,
0095 
0096     .get_voltage_sel = regulator_get_voltage_sel_regmap,
0097     .set_voltage_sel = regulator_set_voltage_sel_regmap,
0098 };
0099 
0100 static const struct linear_range lochnagar_vddcore_ranges[] = {
0101     REGULATOR_LINEAR_RANGE(600000, 0,    0x7, 0),
0102     REGULATOR_LINEAR_RANGE(600000, 0x8, 0x41, 12500),
0103 };
0104 
0105 enum lochnagar_regulators {
0106     LOCHNAGAR_MICVDD,
0107     LOCHNAGAR_MIC1VDD,
0108     LOCHNAGAR_MIC2VDD,
0109     LOCHNAGAR_VDDCORE,
0110 };
0111 
0112 static int lochnagar_micbias_of_parse(struct device_node *np,
0113                       const struct regulator_desc *desc,
0114                       struct regulator_config *config)
0115 {
0116     struct lochnagar *lochnagar = config->driver_data;
0117     int shift = (desc->id - LOCHNAGAR_MIC1VDD) *
0118             LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT;
0119     int mask = LOCHNAGAR2_P1_MICBIAS_SRC_MASK << shift;
0120     unsigned int val;
0121     int ret;
0122 
0123     ret = of_property_read_u32(np, "cirrus,micbias-input", &val);
0124     if (ret >= 0) {
0125         mutex_lock(&lochnagar->analogue_config_lock);
0126         ret = regmap_update_bits(lochnagar->regmap,
0127                      LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
0128                      mask, val << shift);
0129         mutex_unlock(&lochnagar->analogue_config_lock);
0130         if (ret < 0) {
0131             dev_err(lochnagar->dev,
0132                 "Failed to update micbias source: %d\n", ret);
0133             return ret;
0134         }
0135     }
0136 
0137     return 0;
0138 }
0139 
0140 static const struct regulator_desc lochnagar_regulators[] = {
0141     [LOCHNAGAR_MICVDD] = {
0142         .name = "MICVDD",
0143         .supply_name = "SYSVDD",
0144         .type = REGULATOR_VOLTAGE,
0145         .n_voltages = 32,
0146         .ops = &lochnagar_micvdd_ops,
0147 
0148         .id = LOCHNAGAR_MICVDD,
0149         .of_match = of_match_ptr("MICVDD"),
0150 
0151         .enable_reg = LOCHNAGAR2_MICVDD_CTRL1,
0152         .enable_mask = LOCHNAGAR2_MICVDD_REG_ENA_MASK,
0153         .vsel_reg = LOCHNAGAR2_MICVDD_CTRL2,
0154         .vsel_mask = LOCHNAGAR2_MICVDD_VSEL_MASK,
0155 
0156         .linear_ranges = lochnagar_micvdd_ranges,
0157         .n_linear_ranges = ARRAY_SIZE(lochnagar_micvdd_ranges),
0158 
0159         .enable_time = 3000,
0160         .ramp_delay = 1000,
0161 
0162         .owner = THIS_MODULE,
0163     },
0164     [LOCHNAGAR_MIC1VDD] = {
0165         .name = "MIC1VDD",
0166         .supply_name = "MICBIAS1",
0167         .type = REGULATOR_VOLTAGE,
0168         .ops = &lochnagar_micbias_ops,
0169 
0170         .id = LOCHNAGAR_MIC1VDD,
0171         .of_match = of_match_ptr("MIC1VDD"),
0172         .of_parse_cb = lochnagar_micbias_of_parse,
0173 
0174         .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
0175         .enable_mask = LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK,
0176 
0177         .owner = THIS_MODULE,
0178     },
0179     [LOCHNAGAR_MIC2VDD] = {
0180         .name = "MIC2VDD",
0181         .supply_name = "MICBIAS2",
0182         .type = REGULATOR_VOLTAGE,
0183         .ops = &lochnagar_micbias_ops,
0184 
0185         .id = LOCHNAGAR_MIC2VDD,
0186         .of_match = of_match_ptr("MIC2VDD"),
0187         .of_parse_cb = lochnagar_micbias_of_parse,
0188 
0189         .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
0190         .enable_mask = LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK,
0191 
0192         .owner = THIS_MODULE,
0193     },
0194     [LOCHNAGAR_VDDCORE] = {
0195         .name = "VDDCORE",
0196         .supply_name = "SYSVDD",
0197         .type = REGULATOR_VOLTAGE,
0198         .n_voltages = 66,
0199         .ops = &lochnagar_vddcore_ops,
0200 
0201         .id = LOCHNAGAR_VDDCORE,
0202         .of_match = of_match_ptr("VDDCORE"),
0203 
0204         .enable_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL1,
0205         .enable_mask = LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK,
0206         .vsel_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL2,
0207         .vsel_mask = LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK,
0208 
0209         .linear_ranges = lochnagar_vddcore_ranges,
0210         .n_linear_ranges = ARRAY_SIZE(lochnagar_vddcore_ranges),
0211 
0212         .enable_time = 3000,
0213         .ramp_delay = 1000,
0214         .off_on_delay = 15000,
0215 
0216         .owner = THIS_MODULE,
0217     },
0218 };
0219 
0220 static const struct of_device_id lochnagar_of_match[] = {
0221     {
0222         .compatible = "cirrus,lochnagar2-micvdd",
0223         .data = &lochnagar_regulators[LOCHNAGAR_MICVDD],
0224     },
0225     {
0226         .compatible = "cirrus,lochnagar2-mic1vdd",
0227         .data = &lochnagar_regulators[LOCHNAGAR_MIC1VDD],
0228     },
0229     {
0230         .compatible = "cirrus,lochnagar2-mic2vdd",
0231         .data = &lochnagar_regulators[LOCHNAGAR_MIC2VDD],
0232     },
0233     {
0234         .compatible = "cirrus,lochnagar2-vddcore",
0235         .data = &lochnagar_regulators[LOCHNAGAR_VDDCORE],
0236     },
0237     {}
0238 };
0239 MODULE_DEVICE_TABLE(of, lochnagar_of_match);
0240 
0241 static int lochnagar_regulator_probe(struct platform_device *pdev)
0242 {
0243     struct device *dev = &pdev->dev;
0244     struct lochnagar *lochnagar = dev_get_drvdata(dev->parent);
0245     struct regulator_config config = { };
0246     const struct of_device_id *of_id;
0247     const struct regulator_desc *desc;
0248     struct regulator_dev *rdev;
0249     int ret;
0250 
0251     config.dev = dev;
0252     config.regmap = lochnagar->regmap;
0253     config.driver_data = lochnagar;
0254 
0255     of_id = of_match_device(lochnagar_of_match, dev);
0256     if (!of_id)
0257         return -EINVAL;
0258 
0259     desc = of_id->data;
0260 
0261     rdev = devm_regulator_register(dev, desc, &config);
0262     if (IS_ERR(rdev)) {
0263         ret = PTR_ERR(rdev);
0264         dev_err(dev, "Failed to register %s regulator: %d\n",
0265             desc->name, ret);
0266         return ret;
0267     }
0268 
0269     return 0;
0270 }
0271 
0272 static struct platform_driver lochnagar_regulator_driver = {
0273     .driver = {
0274         .name = "lochnagar-regulator",
0275         .of_match_table = of_match_ptr(lochnagar_of_match),
0276     },
0277 
0278     .probe = lochnagar_regulator_probe,
0279 };
0280 module_platform_driver(lochnagar_regulator_driver);
0281 
0282 MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
0283 MODULE_DESCRIPTION("Regulator driver for Cirrus Logic Lochnagar Board");
0284 MODULE_LICENSE("GPL v2");
0285 MODULE_ALIAS("platform:lochnagar-regulator");