1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * fixed.c 4 * 5 * Copyright 2008 Wolfson Microelectronics PLC. 6 * 7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8 * 9 * Copyright (c) 2009 Nokia Corporation 10 * Roger Quadros <ext-roger.quadros@nokia.com> 11 * 12 * This is useful for systems with mixed controllable and 13 * non-controllable regulators, as well as for allowing testing on 14 * systems with no controllable regulators. 15 */ 16 17 #include <linux/err.h> 18 #include <linux/mutex.h> 19 #include <linux/module.h> 20 #include <linux/platform_device.h> 21 #include <linux/regulator/driver.h> 22 #include <linux/regulator/fixed.h> 23 #include <linux/gpio/consumer.h> 24 #include <linux/slab.h> 25 #include <linux/of.h> 26 #include <linux/regulator/of_regulator.h> 27 #include <linux/regulator/machine.h> 28 29 struct fixed_voltage_data { 30 struct regulator_desc desc; 31 struct regulator_dev *dev; 32 }; 33 34 35 /** 36 * of_get_fixed_voltage_config - extract fixed_voltage_config structure info 37 * @dev: device requesting for fixed_voltage_config 38 * @desc: regulator description 39 * 40 * Populates fixed_voltage_config structure by extracting data from device 41 * tree node, returns a pointer to the populated structure of NULL if memory 42 * alloc fails. 43 */ 44 static struct fixed_voltage_config * 45 of_get_fixed_voltage_config(struct device *dev, 46 const struct regulator_desc *desc) 47 { 48 struct fixed_voltage_config *config; 49 struct device_node *np = dev->of_node; 50 struct regulator_init_data *init_data; 51 52 config = devm_kzalloc(dev, sizeof(struct fixed_voltage_config), 53 GFP_KERNEL); 54 if (!config) 55 return ERR_PTR(-ENOMEM); 56 57 config->init_data = of_get_regulator_init_data(dev, dev->of_node, desc); 58 if (!config->init_data) 59 return ERR_PTR(-EINVAL); 60 61 init_data = config->init_data; 62 init_data->constraints.apply_uV = 0; 63 64 config->supply_name = init_data->constraints.name; 65 if (init_data->constraints.min_uV == init_data->constraints.max_uV) { 66 config->microvolts = init_data->constraints.min_uV; 67 } else { 68 dev_err(dev, 69 "Fixed regulator specified with variable voltages\n"); 70 return ERR_PTR(-EINVAL); 71 } 72 73 if (init_data->constraints.boot_on) 74 config->enabled_at_boot = true; 75 76 of_property_read_u32(np, "startup-delay-us", &config->startup_delay); 77 78 if (of_find_property(np, "vin-supply", NULL)) 79 config->input_supply = "vin"; 80 81 return config; 82 } 83 84 static struct regulator_ops fixed_voltage_ops = { 85 }; 86 87 static int reg_fixed_voltage_probe(struct platform_device *pdev) 88 { 89 struct fixed_voltage_config *config; 90 struct fixed_voltage_data *drvdata; 91 struct regulator_config cfg = { }; 92 enum gpiod_flags gflags; 93 int ret; 94 95 drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data), 96 GFP_KERNEL); 97 if (!drvdata) 98 return -ENOMEM; 99 100 if (pdev->dev.of_node) { 101 config = of_get_fixed_voltage_config(&pdev->dev, 102 &drvdata->desc); 103 if (IS_ERR(config)) 104 return PTR_ERR(config); 105 } else { 106 config = dev_get_platdata(&pdev->dev); 107 } 108 109 if (!config) 110 return -ENOMEM; 111 112 drvdata->desc.name = devm_kstrdup(&pdev->dev, 113 config->supply_name, 114 GFP_KERNEL); 115 if (drvdata->desc.name == NULL) { 116 dev_err(&pdev->dev, "Failed to allocate supply name\n"); 117 return -ENOMEM; 118 } 119 drvdata->desc.type = REGULATOR_VOLTAGE; 120 drvdata->desc.owner = THIS_MODULE; 121 drvdata->desc.ops = &fixed_voltage_ops; 122 123 drvdata->desc.enable_time = config->startup_delay; 124 125 if (config->input_supply) { 126 drvdata->desc.supply_name = devm_kstrdup(&pdev->dev, 127 config->input_supply, 128 GFP_KERNEL); 129 if (!drvdata->desc.supply_name) { 130 dev_err(&pdev->dev, 131 "Failed to allocate input supply\n"); 132 return -ENOMEM; 133 } 134 } 135 136 if (config->microvolts) 137 drvdata->desc.n_voltages = 1; 138 139 drvdata->desc.fixed_uV = config->microvolts; 140 141 /* 142 * The signal will be inverted by the GPIO core if flagged so in the 143 * decriptor. 144 */ 145 if (config->enabled_at_boot) 146 gflags = GPIOD_OUT_HIGH; 147 else 148 gflags = GPIOD_OUT_LOW; 149 150 /* 151 * Some fixed regulators share the enable line between two 152 * regulators which makes it necessary to get a handle on the 153 * same descriptor for two different consumers. This will get 154 * the GPIO descriptor, but only the first call will initialize 155 * it so any flags such as inversion or open drain will only 156 * be set up by the first caller and assumed identical on the 157 * next caller. 158 * 159 * FIXME: find a better way to deal with this. 160 */ 161 gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE; 162 163 /* 164 * Do not use devm* here: the regulator core takes over the 165 * lifecycle management of the GPIO descriptor. 166 */ 167 cfg.ena_gpiod = gpiod_get_optional(&pdev->dev, NULL, gflags); 168 if (IS_ERR(cfg.ena_gpiod)) 169 return PTR_ERR(cfg.ena_gpiod); 170 171 cfg.dev = &pdev->dev; 172 cfg.init_data = config->init_data; 173 cfg.driver_data = drvdata; 174 cfg.of_node = pdev->dev.of_node; 175 176 drvdata->dev = devm_regulator_register(&pdev->dev, &drvdata->desc, 177 &cfg); 178 if (IS_ERR(drvdata->dev)) { 179 ret = PTR_ERR(drvdata->dev); 180 dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); 181 return ret; 182 } 183 184 platform_set_drvdata(pdev, drvdata); 185 186 dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name, 187 drvdata->desc.fixed_uV); 188 189 return 0; 190 } 191 192 #if defined(CONFIG_OF) 193 static const struct of_device_id fixed_of_match[] = { 194 { .compatible = "regulator-fixed", }, 195 {}, 196 }; 197 MODULE_DEVICE_TABLE(of, fixed_of_match); 198 #endif 199 200 static struct platform_driver regulator_fixed_voltage_driver = { 201 .probe = reg_fixed_voltage_probe, 202 .driver = { 203 .name = "reg-fixed-voltage", 204 .of_match_table = of_match_ptr(fixed_of_match), 205 }, 206 }; 207 208 static int __init regulator_fixed_voltage_init(void) 209 { 210 return platform_driver_register(®ulator_fixed_voltage_driver); 211 } 212 subsys_initcall(regulator_fixed_voltage_init); 213 214 static void __exit regulator_fixed_voltage_exit(void) 215 { 216 platform_driver_unregister(®ulator_fixed_voltage_driver); 217 } 218 module_exit(regulator_fixed_voltage_exit); 219 220 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 221 MODULE_DESCRIPTION("Fixed voltage regulator"); 222 MODULE_LICENSE("GPL"); 223 MODULE_ALIAS("platform:reg-fixed-voltage"); 224