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