linux/drivers/regulator/lochnagar-regulator.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Lochnagar regulator driver
   4//
   5// Copyright (c) 2017-2018 Cirrus Logic, Inc. and
   6//                         Cirrus Logic International Semiconductor Ltd.
   7//
   8// Author: Charles Keepax <ckeepax@opensource.cirrus.com>
   9
  10#include <linux/bitops.h>
  11#include <linux/device.h>
  12#include <linux/err.h>
  13#include <linux/module.h>
  14#include <linux/mutex.h>
  15#include <linux/of.h>
  16#include <linux/of_device.h>
  17#include <linux/platform_device.h>
  18#include <linux/regmap.h>
  19#include <linux/regulator/driver.h>
  20#include <linux/regulator/machine.h>
  21#include <linux/regulator/of_regulator.h>
  22
  23#include <linux/mfd/lochnagar.h>
  24#include <linux/mfd/lochnagar1_regs.h>
  25#include <linux/mfd/lochnagar2_regs.h>
  26
  27static const struct regulator_ops lochnagar_micvdd_ops = {
  28        .enable = regulator_enable_regmap,
  29        .disable = regulator_disable_regmap,
  30        .is_enabled = regulator_is_enabled_regmap,
  31
  32        .list_voltage = regulator_list_voltage_linear_range,
  33        .map_voltage = regulator_map_voltage_linear_range,
  34
  35        .get_voltage_sel = regulator_get_voltage_sel_regmap,
  36        .set_voltage_sel = regulator_set_voltage_sel_regmap,
  37};
  38
  39static const struct linear_range lochnagar_micvdd_ranges[] = {
  40        REGULATOR_LINEAR_RANGE(1000000, 0,    0xC, 50000),
  41        REGULATOR_LINEAR_RANGE(1700000, 0xD, 0x1F, 100000),
  42};
  43
  44static int lochnagar_micbias_enable(struct regulator_dev *rdev)
  45{
  46        struct lochnagar *lochnagar = rdev_get_drvdata(rdev);
  47        int ret;
  48
  49        mutex_lock(&lochnagar->analogue_config_lock);
  50
  51        ret = regulator_enable_regmap(rdev);
  52        if (ret < 0)
  53                goto err;
  54
  55        ret = lochnagar_update_config(lochnagar);
  56
  57err:
  58        mutex_unlock(&lochnagar->analogue_config_lock);
  59
  60        return ret;
  61}
  62
  63static int lochnagar_micbias_disable(struct regulator_dev *rdev)
  64{
  65        struct lochnagar *lochnagar = rdev_get_drvdata(rdev);
  66        int ret;
  67
  68        mutex_lock(&lochnagar->analogue_config_lock);
  69
  70        ret = regulator_disable_regmap(rdev);
  71        if (ret < 0)
  72                goto err;
  73
  74        ret = lochnagar_update_config(lochnagar);
  75
  76err:
  77        mutex_unlock(&lochnagar->analogue_config_lock);
  78
  79        return ret;
  80}
  81
  82static const struct regulator_ops lochnagar_micbias_ops = {
  83        .enable = lochnagar_micbias_enable,
  84        .disable = lochnagar_micbias_disable,
  85        .is_enabled = regulator_is_enabled_regmap,
  86};
  87
  88static const struct regulator_ops lochnagar_vddcore_ops = {
  89        .enable = regulator_enable_regmap,
  90        .disable = regulator_disable_regmap,
  91        .is_enabled = regulator_is_enabled_regmap,
  92
  93        .list_voltage = regulator_list_voltage_linear_range,
  94        .map_voltage = regulator_map_voltage_linear_range,
  95
  96        .get_voltage_sel = regulator_get_voltage_sel_regmap,
  97        .set_voltage_sel = regulator_set_voltage_sel_regmap,
  98};
  99
 100static const struct linear_range lochnagar_vddcore_ranges[] = {
 101        REGULATOR_LINEAR_RANGE(600000, 0,    0x7, 0),
 102        REGULATOR_LINEAR_RANGE(600000, 0x8, 0x41, 12500),
 103};
 104
 105enum lochnagar_regulators {
 106        LOCHNAGAR_MICVDD,
 107        LOCHNAGAR_MIC1VDD,
 108        LOCHNAGAR_MIC2VDD,
 109        LOCHNAGAR_VDDCORE,
 110};
 111
 112static int lochnagar_micbias_of_parse(struct device_node *np,
 113                                      const struct regulator_desc *desc,
 114                                      struct regulator_config *config)
 115{
 116        struct lochnagar *lochnagar = config->driver_data;
 117        int shift = (desc->id - LOCHNAGAR_MIC1VDD) *
 118                    LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT;
 119        int mask = LOCHNAGAR2_P1_MICBIAS_SRC_MASK << shift;
 120        unsigned int val;
 121        int ret;
 122
 123        ret = of_property_read_u32(np, "cirrus,micbias-input", &val);
 124        if (ret >= 0) {
 125                mutex_lock(&lochnagar->analogue_config_lock);
 126                ret = regmap_update_bits(lochnagar->regmap,
 127                                         LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
 128                                         mask, val << shift);
 129                mutex_unlock(&lochnagar->analogue_config_lock);
 130                if (ret < 0) {
 131                        dev_err(lochnagar->dev,
 132                                "Failed to update micbias source: %d\n", ret);
 133                        return ret;
 134                }
 135        }
 136
 137        return 0;
 138}
 139
 140static const struct regulator_desc lochnagar_regulators[] = {
 141        [LOCHNAGAR_MICVDD] = {
 142                .name = "MICVDD",
 143                .supply_name = "SYSVDD",
 144                .type = REGULATOR_VOLTAGE,
 145                .n_voltages = 32,
 146                .ops = &lochnagar_micvdd_ops,
 147
 148                .id = LOCHNAGAR_MICVDD,
 149                .of_match = of_match_ptr("MICVDD"),
 150
 151                .enable_reg = LOCHNAGAR2_MICVDD_CTRL1,
 152                .enable_mask = LOCHNAGAR2_MICVDD_REG_ENA_MASK,
 153                .vsel_reg = LOCHNAGAR2_MICVDD_CTRL2,
 154                .vsel_mask = LOCHNAGAR2_MICVDD_VSEL_MASK,
 155
 156                .linear_ranges = lochnagar_micvdd_ranges,
 157                .n_linear_ranges = ARRAY_SIZE(lochnagar_micvdd_ranges),
 158
 159                .enable_time = 3000,
 160                .ramp_delay = 1000,
 161
 162                .owner = THIS_MODULE,
 163        },
 164        [LOCHNAGAR_MIC1VDD] = {
 165                .name = "MIC1VDD",
 166                .supply_name = "MICBIAS1",
 167                .type = REGULATOR_VOLTAGE,
 168                .ops = &lochnagar_micbias_ops,
 169
 170                .id = LOCHNAGAR_MIC1VDD,
 171                .of_match = of_match_ptr("MIC1VDD"),
 172                .of_parse_cb = lochnagar_micbias_of_parse,
 173
 174                .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
 175                .enable_mask = LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK,
 176
 177                .owner = THIS_MODULE,
 178        },
 179        [LOCHNAGAR_MIC2VDD] = {
 180                .name = "MIC2VDD",
 181                .supply_name = "MICBIAS2",
 182                .type = REGULATOR_VOLTAGE,
 183                .ops = &lochnagar_micbias_ops,
 184
 185                .id = LOCHNAGAR_MIC2VDD,
 186                .of_match = of_match_ptr("MIC2VDD"),
 187                .of_parse_cb = lochnagar_micbias_of_parse,
 188
 189                .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
 190                .enable_mask = LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK,
 191
 192                .owner = THIS_MODULE,
 193        },
 194        [LOCHNAGAR_VDDCORE] = {
 195                .name = "VDDCORE",
 196                .supply_name = "SYSVDD",
 197                .type = REGULATOR_VOLTAGE,
 198                .n_voltages = 66,
 199                .ops = &lochnagar_vddcore_ops,
 200
 201                .id = LOCHNAGAR_VDDCORE,
 202                .of_match = of_match_ptr("VDDCORE"),
 203
 204                .enable_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL1,
 205                .enable_mask = LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK,
 206                .vsel_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL2,
 207                .vsel_mask = LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK,
 208
 209                .linear_ranges = lochnagar_vddcore_ranges,
 210                .n_linear_ranges = ARRAY_SIZE(lochnagar_vddcore_ranges),
 211
 212                .enable_time = 3000,
 213                .ramp_delay = 1000,
 214                .off_on_delay = 15000,
 215
 216                .owner = THIS_MODULE,
 217        },
 218};
 219
 220static const struct of_device_id lochnagar_of_match[] = {
 221        {
 222                .compatible = "cirrus,lochnagar2-micvdd",
 223                .data = &lochnagar_regulators[LOCHNAGAR_MICVDD],
 224        },
 225        {
 226                .compatible = "cirrus,lochnagar2-mic1vdd",
 227                .data = &lochnagar_regulators[LOCHNAGAR_MIC1VDD],
 228        },
 229        {
 230                .compatible = "cirrus,lochnagar2-mic2vdd",
 231                .data = &lochnagar_regulators[LOCHNAGAR_MIC2VDD],
 232        },
 233        {
 234                .compatible = "cirrus,lochnagar2-vddcore",
 235                .data = &lochnagar_regulators[LOCHNAGAR_VDDCORE],
 236        },
 237        {}
 238};
 239MODULE_DEVICE_TABLE(of, lochnagar_of_match);
 240
 241static int lochnagar_regulator_probe(struct platform_device *pdev)
 242{
 243        struct device *dev = &pdev->dev;
 244        struct lochnagar *lochnagar = dev_get_drvdata(dev->parent);
 245        struct regulator_config config = { };
 246        const struct of_device_id *of_id;
 247        const struct regulator_desc *desc;
 248        struct regulator_dev *rdev;
 249        int ret;
 250
 251        config.dev = dev;
 252        config.regmap = lochnagar->regmap;
 253        config.driver_data = lochnagar;
 254
 255        of_id = of_match_device(lochnagar_of_match, dev);
 256        if (!of_id)
 257                return -EINVAL;
 258
 259        desc = of_id->data;
 260
 261        rdev = devm_regulator_register(dev, desc, &config);
 262        if (IS_ERR(rdev)) {
 263                ret = PTR_ERR(rdev);
 264                dev_err(dev, "Failed to register %s regulator: %d\n",
 265                        desc->name, ret);
 266                return ret;
 267        }
 268
 269        return 0;
 270}
 271
 272static struct platform_driver lochnagar_regulator_driver = {
 273        .driver = {
 274                .name = "lochnagar-regulator",
 275                .of_match_table = of_match_ptr(lochnagar_of_match),
 276        },
 277
 278        .probe = lochnagar_regulator_probe,
 279};
 280module_platform_driver(lochnagar_regulator_driver);
 281
 282MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
 283MODULE_DESCRIPTION("Regulator driver for Cirrus Logic Lochnagar Board");
 284MODULE_LICENSE("GPL v2");
 285MODULE_ALIAS("platform:lochnagar-regulator");
 286