Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

regulator: add SCMI driver

Add a simple regulator based on SCMI Voltage Domain Protocol.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
----
v6 --> v7
- add proper blank lines between semantic blocks
- fix return value on error path of scmi_reg_is_enabled()
- use generic Failure message on err path of info_get()
- fix comment containing apostrophe

v3 --> v4
- using of_match_full_name core regulator flag
- avoid coccinelle falde complaints about pointer-sized allocations

v2 --> v3
- remove multiple linear mappings support
- removed duplicated voltage name printout
- added a few comments
- simplified return path in scmi_reg_set_voltage_sel()

v1 --> v2
- removed duplicate regulator naming
- removed redundant .get/set_voltage ops: only _sel variants implemented
- removed condexpr on fail path to increase readability

v0 --> v1
- fixed init_data constraint parsing
- fixes for v5.8 (linear_range.h)
- fixed commit message content and subject line format
- factored out SCMI core specific changes to distinct patch
- reworked Kconfig and Makefile to keep proper alphabetic order
- fixed SPDX comment style
- removed unneeded inline functions
- reworked conditionals for legibility
- fixed some return paths to properly report SCMI original errors codes
- added some more descriptive error messages when fw returns invalid ranges
- removed unneeded explicit devm_regulator_unregister from .remove()
Link: https://lore.kernel.org/r/20201123202336.46701-4-cristian.marussi@arm.com

Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Cristian Marussi and committed by
Mark Brown
0fbeae70 e8056bf0

+427
+9
drivers/regulator/Kconfig
··· 155 155 and Wolfson Microelectronic Arizona codecs 156 156 devices. 157 157 158 + config REGULATOR_ARM_SCMI 159 + tristate "SCMI based regulator driver" 160 + depends on ARM_SCMI_PROTOCOL && OF 161 + help 162 + This adds the regulator driver support for ARM platforms using SCMI 163 + protocol for device voltage management. 164 + This driver uses SCMI Message Protocol driver to interact with the 165 + firmware providing the device Voltage functionality. 166 + 158 167 config REGULATOR_AS3711 159 168 tristate "AS3711 PMIC" 160 169 depends on MFD_AS3711
+1
drivers/regulator/Makefile
··· 24 24 obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o 25 25 obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o 26 26 obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o 27 + obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o 27 28 obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o 28 29 obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o 29 30 obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
+417
drivers/regulator/scmi-regulator.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // 3 + // System Control and Management Interface (SCMI) based regulator driver 4 + // 5 + // Copyright (C) 2020 ARM Ltd. 6 + // 7 + // Implements a regulator driver on top of the SCMI Voltage Protocol. 8 + // 9 + // The ARM SCMI Protocol aims in general to hide as much as possible all the 10 + // underlying operational details while providing an abstracted interface for 11 + // its users to operate upon: as a consequence the resulting operational 12 + // capabilities and configurability of this regulator device are much more 13 + // limited than the ones usually available on a standard physical regulator. 14 + // 15 + // The supported SCMI regulator ops are restricted to the bare minimum: 16 + // 17 + // - 'status_ops': enable/disable/is_enabled 18 + // - 'voltage_ops': get_voltage_sel/set_voltage_sel 19 + // list_voltage/map_voltage 20 + // 21 + // Each SCMI regulator instance is associated, through the means of a proper DT 22 + // entry description, to a specific SCMI Voltage Domain. 23 + 24 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 25 + 26 + #include <linux/linear_range.h> 27 + #include <linux/module.h> 28 + #include <linux/of.h> 29 + #include <linux/regulator/driver.h> 30 + #include <linux/regulator/machine.h> 31 + #include <linux/regulator/of_regulator.h> 32 + #include <linux/scmi_protocol.h> 33 + #include <linux/slab.h> 34 + #include <linux/types.h> 35 + 36 + struct scmi_regulator { 37 + u32 id; 38 + struct scmi_device *sdev; 39 + struct regulator_dev *rdev; 40 + struct device_node *of_node; 41 + struct regulator_desc desc; 42 + struct regulator_config conf; 43 + }; 44 + 45 + struct scmi_regulator_info { 46 + int num_doms; 47 + struct scmi_regulator **sregv; 48 + }; 49 + 50 + static int scmi_reg_enable(struct regulator_dev *rdev) 51 + { 52 + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); 53 + const struct scmi_handle *handle = sreg->sdev->handle; 54 + 55 + return handle->voltage_ops->config_set(handle, sreg->id, 56 + SCMI_VOLTAGE_ARCH_STATE_ON); 57 + } 58 + 59 + static int scmi_reg_disable(struct regulator_dev *rdev) 60 + { 61 + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); 62 + const struct scmi_handle *handle = sreg->sdev->handle; 63 + 64 + return handle->voltage_ops->config_set(handle, sreg->id, 65 + SCMI_VOLTAGE_ARCH_STATE_OFF); 66 + } 67 + 68 + static int scmi_reg_is_enabled(struct regulator_dev *rdev) 69 + { 70 + int ret; 71 + u32 config; 72 + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); 73 + const struct scmi_handle *handle = sreg->sdev->handle; 74 + 75 + ret = handle->voltage_ops->config_get(handle, sreg->id, 76 + &config); 77 + if (ret) { 78 + dev_err(&sreg->sdev->dev, 79 + "Error %d reading regulator %s status.\n", 80 + ret, sreg->desc.name); 81 + return ret; 82 + } 83 + 84 + return config & SCMI_VOLTAGE_ARCH_STATE_ON; 85 + } 86 + 87 + static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev) 88 + { 89 + int ret; 90 + s32 volt_uV; 91 + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); 92 + const struct scmi_handle *handle = sreg->sdev->handle; 93 + 94 + ret = handle->voltage_ops->level_get(handle, sreg->id, &volt_uV); 95 + if (ret) 96 + return ret; 97 + 98 + return sreg->desc.ops->map_voltage(rdev, volt_uV, volt_uV); 99 + } 100 + 101 + static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev, 102 + unsigned int selector) 103 + { 104 + s32 volt_uV; 105 + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); 106 + const struct scmi_handle *handle = sreg->sdev->handle; 107 + 108 + volt_uV = sreg->desc.ops->list_voltage(rdev, selector); 109 + if (volt_uV <= 0) 110 + return -EINVAL; 111 + 112 + return handle->voltage_ops->level_set(handle, sreg->id, 0x0, volt_uV); 113 + } 114 + 115 + static const struct regulator_ops scmi_reg_fixed_ops = { 116 + .enable = scmi_reg_enable, 117 + .disable = scmi_reg_disable, 118 + .is_enabled = scmi_reg_is_enabled, 119 + }; 120 + 121 + static const struct regulator_ops scmi_reg_linear_ops = { 122 + .enable = scmi_reg_enable, 123 + .disable = scmi_reg_disable, 124 + .is_enabled = scmi_reg_is_enabled, 125 + .get_voltage_sel = scmi_reg_get_voltage_sel, 126 + .set_voltage_sel = scmi_reg_set_voltage_sel, 127 + .list_voltage = regulator_list_voltage_linear, 128 + .map_voltage = regulator_map_voltage_linear, 129 + }; 130 + 131 + static const struct regulator_ops scmi_reg_discrete_ops = { 132 + .enable = scmi_reg_enable, 133 + .disable = scmi_reg_disable, 134 + .is_enabled = scmi_reg_is_enabled, 135 + .get_voltage_sel = scmi_reg_get_voltage_sel, 136 + .set_voltage_sel = scmi_reg_set_voltage_sel, 137 + .list_voltage = regulator_list_voltage_table, 138 + .map_voltage = regulator_map_voltage_iterate, 139 + }; 140 + 141 + static int 142 + scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg, 143 + const struct scmi_voltage_info *vinfo) 144 + { 145 + s32 delta_uV; 146 + 147 + /* 148 + * Note that SCMI voltage domains describable by linear ranges 149 + * (segments) {low, high, step} are guaranteed to come in one single 150 + * triplet by the SCMI Voltage Domain protocol support itself. 151 + */ 152 + 153 + delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] - 154 + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]); 155 + 156 + /* Rule out buggy negative-intervals answers from fw */ 157 + if (delta_uV < 0) { 158 + dev_err(&sreg->sdev->dev, 159 + "Invalid volt-range %d-%duV for domain %d\n", 160 + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW], 161 + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH], 162 + sreg->id); 163 + return -EINVAL; 164 + } 165 + 166 + if (!delta_uV) { 167 + /* Just one fixed voltage exposed by SCMI */ 168 + sreg->desc.fixed_uV = 169 + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; 170 + sreg->desc.n_voltages = 1; 171 + sreg->desc.ops = &scmi_reg_fixed_ops; 172 + } else { 173 + /* One simple linear mapping. */ 174 + sreg->desc.min_uV = 175 + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; 176 + sreg->desc.uV_step = 177 + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP]; 178 + sreg->desc.linear_min_sel = 0; 179 + sreg->desc.n_voltages = delta_uV / sreg->desc.uV_step; 180 + sreg->desc.ops = &scmi_reg_linear_ops; 181 + } 182 + 183 + return 0; 184 + } 185 + 186 + static int 187 + scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg, 188 + const struct scmi_voltage_info *vinfo) 189 + { 190 + /* Discrete non linear levels are mapped to volt_table */ 191 + sreg->desc.n_voltages = vinfo->num_levels; 192 + 193 + if (sreg->desc.n_voltages > 1) { 194 + sreg->desc.volt_table = (const unsigned int *)vinfo->levels_uv; 195 + sreg->desc.ops = &scmi_reg_discrete_ops; 196 + } else { 197 + sreg->desc.fixed_uV = vinfo->levels_uv[0]; 198 + sreg->desc.ops = &scmi_reg_fixed_ops; 199 + } 200 + 201 + return 0; 202 + } 203 + 204 + static int scmi_regulator_common_init(struct scmi_regulator *sreg) 205 + { 206 + int ret; 207 + const struct scmi_handle *handle = sreg->sdev->handle; 208 + struct device *dev = &sreg->sdev->dev; 209 + const struct scmi_voltage_info *vinfo; 210 + 211 + vinfo = handle->voltage_ops->info_get(handle, sreg->id); 212 + if (!vinfo) { 213 + dev_warn(dev, "Failure to get voltage domain %d\n", 214 + sreg->id); 215 + return -ENODEV; 216 + } 217 + 218 + /* 219 + * Regulator framework does not fully support negative voltages 220 + * so we discard any voltage domain reported as supporting negative 221 + * voltages: as a consequence each levels_uv entry is guaranteed to 222 + * be non-negative from here on. 223 + */ 224 + if (vinfo->negative_volts_allowed) { 225 + dev_warn(dev, "Negative voltages NOT supported...skip %s\n", 226 + sreg->of_node->full_name); 227 + return -EOPNOTSUPP; 228 + } 229 + 230 + sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name); 231 + if (!sreg->desc.name) 232 + return -ENOMEM; 233 + 234 + sreg->desc.id = sreg->id; 235 + sreg->desc.type = REGULATOR_VOLTAGE; 236 + sreg->desc.owner = THIS_MODULE; 237 + sreg->desc.of_match_full_name = true; 238 + sreg->desc.of_match = sreg->of_node->full_name; 239 + sreg->desc.regulators_node = "regulators"; 240 + if (vinfo->segmented) 241 + ret = scmi_config_linear_regulator_mappings(sreg, vinfo); 242 + else 243 + ret = scmi_config_discrete_regulator_mappings(sreg, vinfo); 244 + if (ret) 245 + return ret; 246 + 247 + /* 248 + * Using the scmi device here to have DT searched from Voltage 249 + * protocol node down. 250 + */ 251 + sreg->conf.dev = dev; 252 + 253 + /* Store for later retrieval via rdev_get_drvdata() */ 254 + sreg->conf.driver_data = sreg; 255 + 256 + return 0; 257 + } 258 + 259 + static int process_scmi_regulator_of_node(struct scmi_device *sdev, 260 + struct device_node *np, 261 + struct scmi_regulator_info *rinfo) 262 + { 263 + u32 dom, ret; 264 + 265 + ret = of_property_read_u32(np, "reg", &dom); 266 + if (ret) 267 + return ret; 268 + 269 + if (dom >= rinfo->num_doms) 270 + return -ENODEV; 271 + 272 + if (rinfo->sregv[dom]) { 273 + dev_err(&sdev->dev, 274 + "SCMI Voltage Domain %d already in use. Skipping: %s\n", 275 + dom, np->full_name); 276 + return -EINVAL; 277 + } 278 + 279 + rinfo->sregv[dom] = devm_kzalloc(&sdev->dev, 280 + sizeof(struct scmi_regulator), 281 + GFP_KERNEL); 282 + if (!rinfo->sregv[dom]) 283 + return -ENOMEM; 284 + 285 + rinfo->sregv[dom]->id = dom; 286 + rinfo->sregv[dom]->sdev = sdev; 287 + 288 + /* get hold of good nodes */ 289 + of_node_get(np); 290 + rinfo->sregv[dom]->of_node = np; 291 + 292 + dev_dbg(&sdev->dev, 293 + "Found SCMI Regulator entry -- OF node [%d] -> %s\n", 294 + dom, np->full_name); 295 + 296 + return 0; 297 + } 298 + 299 + static int scmi_regulator_probe(struct scmi_device *sdev) 300 + { 301 + int d, ret, num_doms; 302 + struct device_node *np, *child; 303 + const struct scmi_handle *handle = sdev->handle; 304 + struct scmi_regulator_info *rinfo; 305 + 306 + if (!handle || !handle->voltage_ops) 307 + return -ENODEV; 308 + 309 + num_doms = handle->voltage_ops->num_domains_get(handle); 310 + if (num_doms <= 0) { 311 + if (!num_doms) { 312 + dev_err(&sdev->dev, 313 + "number of voltage domains invalid\n"); 314 + num_doms = -EINVAL; 315 + } else { 316 + dev_err(&sdev->dev, 317 + "failed to get voltage domains - err:%d\n", 318 + num_doms); 319 + } 320 + 321 + return num_doms; 322 + } 323 + 324 + rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL); 325 + if (!rinfo) 326 + return -ENOMEM; 327 + 328 + /* Allocate pointers array for all possible domains */ 329 + rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms, 330 + sizeof(void *), GFP_KERNEL); 331 + if (!rinfo->sregv) 332 + return -ENOMEM; 333 + 334 + rinfo->num_doms = num_doms; 335 + 336 + /* 337 + * Start collecting into rinfo->sregv possibly good SCMI Regulators as 338 + * described by a well-formed DT entry and associated with an existing 339 + * plausible SCMI Voltage Domain number, all belonging to this SCMI 340 + * platform instance node (handle->dev->of_node). 341 + */ 342 + np = of_find_node_by_name(handle->dev->of_node, "regulators"); 343 + for_each_child_of_node(np, child) { 344 + ret = process_scmi_regulator_of_node(sdev, child, rinfo); 345 + /* abort on any mem issue */ 346 + if (ret == -ENOMEM) 347 + return ret; 348 + } 349 + 350 + /* 351 + * Register a regulator for each valid regulator-DT-entry that we 352 + * can successfully reach via SCMI and has a valid associated voltage 353 + * domain. 354 + */ 355 + for (d = 0; d < num_doms; d++) { 356 + struct scmi_regulator *sreg = rinfo->sregv[d]; 357 + 358 + /* Skip empty slots */ 359 + if (!sreg) 360 + continue; 361 + 362 + ret = scmi_regulator_common_init(sreg); 363 + /* Skip invalid voltage domains */ 364 + if (ret) 365 + continue; 366 + 367 + sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc, 368 + &sreg->conf); 369 + if (IS_ERR(sreg->rdev)) { 370 + sreg->rdev = NULL; 371 + continue; 372 + } 373 + 374 + dev_info(&sdev->dev, 375 + "Regulator %s registered for domain [%d]\n", 376 + sreg->desc.name, sreg->id); 377 + } 378 + 379 + dev_set_drvdata(&sdev->dev, rinfo); 380 + 381 + return 0; 382 + } 383 + 384 + static void scmi_regulator_remove(struct scmi_device *sdev) 385 + { 386 + int d; 387 + struct scmi_regulator_info *rinfo; 388 + 389 + rinfo = dev_get_drvdata(&sdev->dev); 390 + if (!rinfo) 391 + return; 392 + 393 + for (d = 0; d < rinfo->num_doms; d++) { 394 + if (!rinfo->sregv[d]) 395 + continue; 396 + of_node_put(rinfo->sregv[d]->of_node); 397 + } 398 + } 399 + 400 + static const struct scmi_device_id scmi_regulator_id_table[] = { 401 + { SCMI_PROTOCOL_VOLTAGE, "regulator" }, 402 + { }, 403 + }; 404 + MODULE_DEVICE_TABLE(scmi, scmi_regulator_id_table); 405 + 406 + static struct scmi_driver scmi_drv = { 407 + .name = "scmi-regulator", 408 + .probe = scmi_regulator_probe, 409 + .remove = scmi_regulator_remove, 410 + .id_table = scmi_regulator_id_table, 411 + }; 412 + 413 + module_scmi_driver(scmi_drv); 414 + 415 + MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>"); 416 + MODULE_DESCRIPTION("ARM SCMI regulator driver"); 417 + MODULE_LICENSE("GPL v2");