18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (C) 2020 ROHM Semiconductors 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/errno.h> 58c2ecf20Sopenharmony_ci#include <linux/mfd/rohm-generic.h> 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/of.h> 88c2ecf20Sopenharmony_ci#include <linux/regmap.h> 98c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic int set_dvs_level(const struct regulator_desc *desc, 128c2ecf20Sopenharmony_ci struct device_node *np, struct regmap *regmap, 138c2ecf20Sopenharmony_ci char *prop, unsigned int reg, unsigned int mask, 148c2ecf20Sopenharmony_ci unsigned int omask, unsigned int oreg) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci int ret, i; 178c2ecf20Sopenharmony_ci uint32_t uv; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, prop, &uv); 208c2ecf20Sopenharmony_ci if (ret) { 218c2ecf20Sopenharmony_ci if (ret != -EINVAL) 228c2ecf20Sopenharmony_ci return ret; 238c2ecf20Sopenharmony_ci return 0; 248c2ecf20Sopenharmony_ci } 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (uv == 0) { 278c2ecf20Sopenharmony_ci if (omask) 288c2ecf20Sopenharmony_ci return regmap_update_bits(regmap, oreg, omask, 0); 298c2ecf20Sopenharmony_ci } 308c2ecf20Sopenharmony_ci for (i = 0; i < desc->n_voltages; i++) { 318c2ecf20Sopenharmony_ci ret = regulator_desc_list_voltage_linear_range(desc, i); 328c2ecf20Sopenharmony_ci if (ret < 0) 338c2ecf20Sopenharmony_ci continue; 348c2ecf20Sopenharmony_ci if (ret == uv) { 358c2ecf20Sopenharmony_ci i <<= ffs(desc->vsel_mask) - 1; 368c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, reg, mask, i); 378c2ecf20Sopenharmony_ci if (omask && !ret) 388c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, oreg, omask, 398c2ecf20Sopenharmony_ci omask); 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci return ret; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciint rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs, 478c2ecf20Sopenharmony_ci struct device_node *np, 488c2ecf20Sopenharmony_ci const struct regulator_desc *desc, 498c2ecf20Sopenharmony_ci struct regmap *regmap) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci int i, ret = 0; 528c2ecf20Sopenharmony_ci char *prop; 538c2ecf20Sopenharmony_ci unsigned int reg, mask, omask, oreg = desc->enable_reg; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci for (i = 0; i < ROHM_DVS_LEVEL_VALID_AMOUNT && !ret; i++) { 568c2ecf20Sopenharmony_ci int bit; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci bit = BIT(i); 598c2ecf20Sopenharmony_ci if (dvs->level_map & bit) { 608c2ecf20Sopenharmony_ci switch (bit) { 618c2ecf20Sopenharmony_ci case ROHM_DVS_LEVEL_RUN: 628c2ecf20Sopenharmony_ci prop = "rohm,dvs-run-voltage"; 638c2ecf20Sopenharmony_ci reg = dvs->run_reg; 648c2ecf20Sopenharmony_ci mask = dvs->run_mask; 658c2ecf20Sopenharmony_ci omask = dvs->run_on_mask; 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci case ROHM_DVS_LEVEL_IDLE: 688c2ecf20Sopenharmony_ci prop = "rohm,dvs-idle-voltage"; 698c2ecf20Sopenharmony_ci reg = dvs->idle_reg; 708c2ecf20Sopenharmony_ci mask = dvs->idle_mask; 718c2ecf20Sopenharmony_ci omask = dvs->idle_on_mask; 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci case ROHM_DVS_LEVEL_SUSPEND: 748c2ecf20Sopenharmony_ci prop = "rohm,dvs-suspend-voltage"; 758c2ecf20Sopenharmony_ci reg = dvs->suspend_reg; 768c2ecf20Sopenharmony_ci mask = dvs->suspend_mask; 778c2ecf20Sopenharmony_ci omask = dvs->suspend_on_mask; 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci case ROHM_DVS_LEVEL_LPSR: 808c2ecf20Sopenharmony_ci prop = "rohm,dvs-lpsr-voltage"; 818c2ecf20Sopenharmony_ci reg = dvs->lpsr_reg; 828c2ecf20Sopenharmony_ci mask = dvs->lpsr_mask; 838c2ecf20Sopenharmony_ci omask = dvs->lpsr_on_mask; 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci default: 868c2ecf20Sopenharmony_ci return -EINVAL; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci ret = set_dvs_level(desc, np, regmap, prop, reg, mask, 898c2ecf20Sopenharmony_ci omask, oreg); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci return ret; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rohm_regulator_set_dvs_levels); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 978c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); 988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers"); 99