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 regulator_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 regulator_linear_range lochnagar_vddcore_ranges[] = {
 101        REGULATOR_LINEAR_RANGE(600000, 0x8, 0x41, 12500),
 102};
 103
 104enum lochnagar_regulators {
 105        LOCHNAGAR_MICVDD,
 106        LOCHNAGAR_MIC1VDD,
 107        LOCHNAGAR_MIC2VDD,
 108        LOCHNAGAR_VDDCORE,
 109};
 110
 111static int lochnagar_micbias_of_parse(struct device_node *np,
 112                                      const struct regulator_desc *desc,
 113                                      struct regulator_config *config)
 114{
 115        struct lochnagar *lochnagar = config->driver_data;
 116        int shift = (desc->id - LOCHNAGAR_MIC1VDD) *
 117                    LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT;
 118        int mask = LOCHNAGAR2_P1_MICBIAS_SRC_MASK << shift;
 119        unsigned int val;
 120        int ret;
 121
 122        ret = of_property_read_u32(np, "cirrus,micbias-input", &val);
 123        if (ret >= 0) {
 124                mutex_lock(&lochnagar->analogue_config_lock);
 125                ret = regmap_update_bits(lochnagar->regmap,
 126                                         LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
 127                                         mask, val << shift);
 128                mutex_unlock(&lochnagar->analogue_config_lock);
 129                if (ret < 0) {
 130                        dev_err(lochnagar->dev,
 131                                "Failed to update micbias source: %d\n", ret);
 132                        return ret;
 133                }
 134        }
 135
 136        return 0;
 137}
 138
 139static const struct regulator_desc lochnagar_regulators[] = {
 140        [LOCHNAGAR_MICVDD] = {
 141                .name = "MICVDD",
 142                .supply_name = "SYSVDD",
 143                .type = REGULATOR_VOLTAGE,
 144                .n_voltages = 32,
 145                .ops = &lochnagar_micvdd_ops,
 146
 147                .id = LOCHNAGAR_MICVDD,
 148                .of_match = of_match_ptr("MICVDD"),
 149
 150                .enable_reg = LOCHNAGAR2_MICVDD_CTRL1,
 151                .enable_mask = LOCHNAGAR2_MICVDD_REG_ENA_MASK,
 152                .vsel_reg = LOCHNAGAR2_MICVDD_CTRL2,
 153                .vsel_mask = LOCHNAGAR2_MICVDD_VSEL_MASK,
 154
 155                .linear_ranges = lochnagar_micvdd_ranges,
 156                .n_linear_ranges = ARRAY_SIZE(lochnagar_micvdd_ranges),
 157
 158                .enable_time = 3000,
 159                .ramp_delay = 1000,
 160
 161                .owner = THIS_MODULE,
 162        },
 163        [LOCHNAGAR_MIC1VDD] = {
 164                .name = "MIC1VDD",
 165                .supply_name = "MICBIAS1",
 166                .type = REGULATOR_VOLTAGE,
 167                .ops = &lochnagar_micbias_ops,
 168
 169                .id = LOCHNAGAR_MIC1VDD,
 170                .of_match = of_match_ptr("MIC1VDD"),
 171                .of_parse_cb = lochnagar_micbias_of_parse,
 172
 173                .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
 174                .enable_mask = LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK,
 175
 176                .owner = THIS_MODULE,
 177        },
 178        [LOCHNAGAR_MIC2VDD] = {
 179                .name = "MIC2VDD",
 180                .supply_name = "MICBIAS2",
 181                .type = REGULATOR_VOLTAGE,
 182                .ops = &lochnagar_micbias_ops,
 183
 184                .id = LOCHNAGAR_MIC2VDD,
 185                .of_match = of_match_ptr("MIC2VDD"),
 186                .of_parse_cb = lochnagar_micbias_of_parse,
 187
 188                .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
 189                .enable_mask = LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK,
 190
 191                .owner = THIS_MODULE,
 192        },
 193        [LOCHNAGAR_VDDCORE] = {
 194                .name = "VDDCORE",
 195                .supply_name = "SYSVDD",
 196                .type = REGULATOR_VOLTAGE,
 197                .n_voltages = 66,
 198                .ops = &lochnagar_vddcore_ops,
 199
 200                .id = LOCHNAGAR_VDDCORE,
 201                .of_match = of_match_ptr("VDDCORE"),
 202
 203                .enable_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL1,
 204                .enable_mask = LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK,
 205                .vsel_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL2,
 206                .vsel_mask = LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK,
 207
 208                .linear_ranges = lochnagar_vddcore_ranges,
 209                .n_linear_ranges = ARRAY_SIZE(lochnagar_vddcore_ranges),
 210
 211                .enable_time = 3000,
 212                .ramp_delay = 1000,
 213
 214                .owner = THIS_MODULE,
 215        },
 216};
 217
 218static const struct of_device_id lochnagar_of_match[] = {
 219        {
 220                .compatible = "cirrus,lochnagar2-micvdd",
 221                .data = &lochnagar_regulators[LOCHNAGAR_MICVDD],
 222        },
 223        {
 224                .compatible = "cirrus,lochnagar2-mic1vdd",
 225                .data = &lochnagar_regulators[LOCHNAGAR_MIC1VDD],
 226        },
 227        {
 228                .compatible = "cirrus,lochnagar2-mic2vdd",
 229                .data = &lochnagar_regulators[LOCHNAGAR_MIC2VDD],
 230        },
 231        {
 232                .compatible = "cirrus,lochnagar2-vddcore",
 233                .data = &lochnagar_regulators[LOCHNAGAR_VDDCORE],
 234        },
 235        {}
 236};
 237MODULE_DEVICE_TABLE(of, lochnagar_of_match);
 238
 239static int lochnagar_regulator_probe(struct platform_device *pdev)
 240{
 241        struct device *dev = &pdev->dev;
 242        struct lochnagar *lochnagar = dev_get_drvdata(dev->parent);
 243        struct regulator_config config = { };
 244        const struct of_device_id *of_id;
 245        const struct regulator_desc *desc;
 246        struct regulator_dev *rdev;
 247        int ret;
 248
 249        config.dev = dev;
 250        config.regmap = lochnagar->regmap;
 251        config.driver_data = lochnagar;
 252
 253        of_id = of_match_device(lochnagar_of_match, dev);
 254        if (!of_id)
 255                return -EINVAL;
 256
 257        desc = of_id->data;
 258
 259        rdev = devm_regulator_register(dev, desc, &config);
 260        if (IS_ERR(rdev)) {
 261                ret = PTR_ERR(rdev);
 262                dev_err(dev, "Failed to register %s regulator: %d\n",
 263                        desc->name, ret);
 264                return ret;
 265        }
 266
 267        return 0;
 268}
 269
 270static struct platform_driver lochnagar_regulator_driver = {
 271        .driver = {
 272                .name = "lochnagar-regulator",
 273                .of_match_table = of_match_ptr(lochnagar_of_match),
 274        },
 275
 276        .probe = lochnagar_regulator_probe,
 277};
 278module_platform_driver(lochnagar_regulator_driver);
 279
 280MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
 281MODULE_DESCRIPTION("Regulator driver for Cirrus Logic Lochnagar Board");
 282MODULE_LICENSE("GPL v2");
 283MODULE_ALIAS("platform:lochnagar-regulator");
 284