18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// max77826-regulator.c - regulator driver for Maxim MAX77826 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Author: Iskren Chernev <iskren.chernev@gmail.com> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 158c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h> 168c2ecf20Sopenharmony_ci#include <linux/i2c.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cienum max77826_registers { 208c2ecf20Sopenharmony_ci MAX77826_REG_INT_SRC = 0x00, 218c2ecf20Sopenharmony_ci MAX77826_REG_SYS_INT, 228c2ecf20Sopenharmony_ci MAX77826_REG_INT1, 238c2ecf20Sopenharmony_ci MAX77826_REG_INT2, 248c2ecf20Sopenharmony_ci MAX77826_REG_BB_INT, 258c2ecf20Sopenharmony_ci MAX77826_REG_INT_SRC_M, 268c2ecf20Sopenharmony_ci MAX77826_REG_TOPSYS_INT_M, 278c2ecf20Sopenharmony_ci MAX77826_REG_INT1_M, 288c2ecf20Sopenharmony_ci MAX77826_REG_INT2_M, 298c2ecf20Sopenharmony_ci MAX77826_REG_BB_INT_M, 308c2ecf20Sopenharmony_ci MAX77826_REG_TOPSYS_STAT, 318c2ecf20Sopenharmony_ci MAX77826_REG_STAT1, 328c2ecf20Sopenharmony_ci MAX77826_REG_STAT2, 338c2ecf20Sopenharmony_ci MAX77826_REG_BB_STAT, 348c2ecf20Sopenharmony_ci /* 0x0E - 0x0F: Reserved */ 358c2ecf20Sopenharmony_ci MAX77826_REG_LDO_OPMD1 = 0x10, 368c2ecf20Sopenharmony_ci MAX77826_REG_LDO_OPMD2, 378c2ecf20Sopenharmony_ci MAX77826_REG_LDO_OPMD3, 388c2ecf20Sopenharmony_ci MAX77826_REG_LDO_OPMD4, 398c2ecf20Sopenharmony_ci MAX77826_REG_B_BB_OPMD, 408c2ecf20Sopenharmony_ci /* 0x15 - 0x1F: Reserved */ 418c2ecf20Sopenharmony_ci MAX77826_REG_LDO1_CFG = 0x20, 428c2ecf20Sopenharmony_ci MAX77826_REG_LDO2_CFG, 438c2ecf20Sopenharmony_ci MAX77826_REG_LDO3_CFG, 448c2ecf20Sopenharmony_ci MAX77826_REG_LDO4_CFG, 458c2ecf20Sopenharmony_ci MAX77826_REG_LDO5_CFG, 468c2ecf20Sopenharmony_ci MAX77826_REG_LDO6_CFG, 478c2ecf20Sopenharmony_ci MAX77826_REG_LDO7_CFG, 488c2ecf20Sopenharmony_ci MAX77826_REG_LDO8_CFG, 498c2ecf20Sopenharmony_ci MAX77826_REG_LDO9_CFG, 508c2ecf20Sopenharmony_ci MAX77826_REG_LDO10_CFG, 518c2ecf20Sopenharmony_ci MAX77826_REG_LDO11_CFG, 528c2ecf20Sopenharmony_ci MAX77826_REG_LDO12_CFG, 538c2ecf20Sopenharmony_ci MAX77826_REG_LDO13_CFG, 548c2ecf20Sopenharmony_ci MAX77826_REG_LDO14_CFG, 558c2ecf20Sopenharmony_ci MAX77826_REG_LDO15_CFG, 568c2ecf20Sopenharmony_ci /* 0x2F: Reserved */ 578c2ecf20Sopenharmony_ci MAX77826_REG_BUCK_CFG = 0x30, 588c2ecf20Sopenharmony_ci MAX77826_REG_BUCK_VOUT, 598c2ecf20Sopenharmony_ci MAX77826_REG_BB_CFG, 608c2ecf20Sopenharmony_ci MAX77826_REG_BB_VOUT, 618c2ecf20Sopenharmony_ci /* 0x34 - 0x3F: Reserved */ 628c2ecf20Sopenharmony_ci MAX77826_REG_BUCK_SS_FREQ = 0x40, 638c2ecf20Sopenharmony_ci MAX77826_REG_UVLO_FALL, 648c2ecf20Sopenharmony_ci /* 0x42 - 0xCE: Reserved */ 658c2ecf20Sopenharmony_ci MAX77826_REG_DEVICE_ID = 0xCF, 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cienum max77826_regulators { 698c2ecf20Sopenharmony_ci MAX77826_LDO1 = 0, 708c2ecf20Sopenharmony_ci MAX77826_LDO2, 718c2ecf20Sopenharmony_ci MAX77826_LDO3, 728c2ecf20Sopenharmony_ci MAX77826_LDO4, 738c2ecf20Sopenharmony_ci MAX77826_LDO5, 748c2ecf20Sopenharmony_ci MAX77826_LDO6, 758c2ecf20Sopenharmony_ci MAX77826_LDO7, 768c2ecf20Sopenharmony_ci MAX77826_LDO8, 778c2ecf20Sopenharmony_ci MAX77826_LDO9, 788c2ecf20Sopenharmony_ci MAX77826_LDO10, 798c2ecf20Sopenharmony_ci MAX77826_LDO11, 808c2ecf20Sopenharmony_ci MAX77826_LDO12, 818c2ecf20Sopenharmony_ci MAX77826_LDO13, 828c2ecf20Sopenharmony_ci MAX77826_LDO14, 838c2ecf20Sopenharmony_ci MAX77826_LDO15, 848c2ecf20Sopenharmony_ci MAX77826_BUCK, 858c2ecf20Sopenharmony_ci MAX77826_BUCKBOOST, 868c2ecf20Sopenharmony_ci MAX77826_MAX_REGULATORS, 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define MAX77826_MASK_LDO 0x7f 908c2ecf20Sopenharmony_ci#define MAX77826_MASK_BUCK 0xff 918c2ecf20Sopenharmony_ci#define MAX77826_MASK_BUCKBOOST 0x7f 928c2ecf20Sopenharmony_ci#define MAX77826_BUCK_RAMP_DELAY 12500 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* values in mV */ 958c2ecf20Sopenharmony_ci/* for LDO1-3 */ 968c2ecf20Sopenharmony_ci#define MAX77826_NMOS_LDO_VOLT_MIN 600000 978c2ecf20Sopenharmony_ci#define MAX77826_NMOS_LDO_VOLT_MAX 2187500 988c2ecf20Sopenharmony_ci#define MAX77826_NMOS_LDO_VOLT_STEP 12500 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* for LDO4-15 */ 1018c2ecf20Sopenharmony_ci#define MAX77826_PMOS_LDO_VOLT_MIN 800000 1028c2ecf20Sopenharmony_ci#define MAX77826_PMOS_LDO_VOLT_MAX 3975000 1038c2ecf20Sopenharmony_ci#define MAX77826_PMOS_LDO_VOLT_STEP 25000 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* for BUCK */ 1068c2ecf20Sopenharmony_ci#define MAX77826_BUCK_VOLT_MIN 500000 1078c2ecf20Sopenharmony_ci#define MAX77826_BUCK_VOLT_MAX 1800000 1088c2ecf20Sopenharmony_ci#define MAX77826_BUCK_VOLT_STEP 6250 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* for BUCKBOOST */ 1118c2ecf20Sopenharmony_ci#define MAX77826_BUCKBOOST_VOLT_MIN 2600000 1128c2ecf20Sopenharmony_ci#define MAX77826_BUCKBOOST_VOLT_MAX 4187500 1138c2ecf20Sopenharmony_ci#define MAX77826_BUCKBOOST_VOLT_STEP 12500 1148c2ecf20Sopenharmony_ci#define MAX77826_VOLT_RANGE(_type) \ 1158c2ecf20Sopenharmony_ci ((MAX77826_ ## _type ## _VOLT_MAX - \ 1168c2ecf20Sopenharmony_ci MAX77826_ ## _type ## _VOLT_MIN) / \ 1178c2ecf20Sopenharmony_ci MAX77826_ ## _type ## _VOLT_STEP + 1) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define MAX77826_LDO(_id, _type) \ 1208c2ecf20Sopenharmony_ci [MAX77826_LDO ## _id] = { \ 1218c2ecf20Sopenharmony_ci .id = MAX77826_LDO ## _id, \ 1228c2ecf20Sopenharmony_ci .name = "LDO"#_id, \ 1238c2ecf20Sopenharmony_ci .of_match = of_match_ptr("LDO"#_id), \ 1248c2ecf20Sopenharmony_ci .regulators_node = "regulators", \ 1258c2ecf20Sopenharmony_ci .ops = &max77826_most_ops, \ 1268c2ecf20Sopenharmony_ci .min_uV = MAX77826_ ## _type ## _LDO_VOLT_MIN, \ 1278c2ecf20Sopenharmony_ci .uV_step = MAX77826_ ## _type ## _LDO_VOLT_STEP, \ 1288c2ecf20Sopenharmony_ci .n_voltages = MAX77826_VOLT_RANGE(_type ## _LDO), \ 1298c2ecf20Sopenharmony_ci .enable_reg = MAX77826_REG_LDO_OPMD1 + (_id - 1) / 4, \ 1308c2ecf20Sopenharmony_ci .enable_mask = BIT(((_id - 1) % 4) * 2 + 1), \ 1318c2ecf20Sopenharmony_ci .vsel_reg = MAX77826_REG_LDO1_CFG + (_id - 1), \ 1328c2ecf20Sopenharmony_ci .vsel_mask = MAX77826_MASK_LDO, \ 1338c2ecf20Sopenharmony_ci .owner = THIS_MODULE, \ 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define MAX77826_BUCK(_idx, _id, _ops) \ 1378c2ecf20Sopenharmony_ci [MAX77826_ ## _id] = { \ 1388c2ecf20Sopenharmony_ci .id = MAX77826_ ## _id, \ 1398c2ecf20Sopenharmony_ci .name = #_id, \ 1408c2ecf20Sopenharmony_ci .of_match = of_match_ptr(#_id), \ 1418c2ecf20Sopenharmony_ci .regulators_node = "regulators", \ 1428c2ecf20Sopenharmony_ci .ops = &_ops, \ 1438c2ecf20Sopenharmony_ci .min_uV = MAX77826_ ## _id ## _VOLT_MIN, \ 1448c2ecf20Sopenharmony_ci .uV_step = MAX77826_ ## _id ## _VOLT_STEP, \ 1458c2ecf20Sopenharmony_ci .n_voltages = MAX77826_VOLT_RANGE(_id), \ 1468c2ecf20Sopenharmony_ci .enable_reg = MAX77826_REG_B_BB_OPMD, \ 1478c2ecf20Sopenharmony_ci .enable_mask = BIT(_idx * 2 + 1), \ 1488c2ecf20Sopenharmony_ci .vsel_reg = MAX77826_REG_BUCK_VOUT + _idx * 2, \ 1498c2ecf20Sopenharmony_ci .vsel_mask = MAX77826_MASK_ ## _id, \ 1508c2ecf20Sopenharmony_ci .owner = THIS_MODULE, \ 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistruct max77826_regulator_info { 1568c2ecf20Sopenharmony_ci struct regmap *regmap; 1578c2ecf20Sopenharmony_ci struct regulator_desc *rdesc; 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct regmap_config max77826_regmap_config = { 1618c2ecf20Sopenharmony_ci .reg_bits = 8, 1628c2ecf20Sopenharmony_ci .val_bits = 8, 1638c2ecf20Sopenharmony_ci .max_register = MAX77826_REG_DEVICE_ID, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int max77826_set_voltage_time_sel(struct regulator_dev *, 1678c2ecf20Sopenharmony_ci unsigned int old_selector, 1688c2ecf20Sopenharmony_ci unsigned int new_selector); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const struct regulator_ops max77826_most_ops = { 1718c2ecf20Sopenharmony_ci .enable = regulator_enable_regmap, 1728c2ecf20Sopenharmony_ci .disable = regulator_disable_regmap, 1738c2ecf20Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 1748c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_linear, 1758c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_linear, 1768c2ecf20Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 1778c2ecf20Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic const struct regulator_ops max77826_buck_ops = { 1818c2ecf20Sopenharmony_ci .enable = regulator_enable_regmap, 1828c2ecf20Sopenharmony_ci .disable = regulator_disable_regmap, 1838c2ecf20Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 1848c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_linear, 1858c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_linear, 1868c2ecf20Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 1878c2ecf20Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 1888c2ecf20Sopenharmony_ci .set_voltage_time_sel = max77826_set_voltage_time_sel, 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic struct regulator_desc max77826_regulators_desc[] = { 1928c2ecf20Sopenharmony_ci MAX77826_LDO(1, NMOS), 1938c2ecf20Sopenharmony_ci MAX77826_LDO(2, NMOS), 1948c2ecf20Sopenharmony_ci MAX77826_LDO(3, NMOS), 1958c2ecf20Sopenharmony_ci MAX77826_LDO(4, PMOS), 1968c2ecf20Sopenharmony_ci MAX77826_LDO(5, PMOS), 1978c2ecf20Sopenharmony_ci MAX77826_LDO(6, PMOS), 1988c2ecf20Sopenharmony_ci MAX77826_LDO(7, PMOS), 1998c2ecf20Sopenharmony_ci MAX77826_LDO(8, PMOS), 2008c2ecf20Sopenharmony_ci MAX77826_LDO(9, PMOS), 2018c2ecf20Sopenharmony_ci MAX77826_LDO(10, PMOS), 2028c2ecf20Sopenharmony_ci MAX77826_LDO(11, PMOS), 2038c2ecf20Sopenharmony_ci MAX77826_LDO(12, PMOS), 2048c2ecf20Sopenharmony_ci MAX77826_LDO(13, PMOS), 2058c2ecf20Sopenharmony_ci MAX77826_LDO(14, PMOS), 2068c2ecf20Sopenharmony_ci MAX77826_LDO(15, PMOS), 2078c2ecf20Sopenharmony_ci MAX77826_BUCK(0, BUCK, max77826_buck_ops), 2088c2ecf20Sopenharmony_ci MAX77826_BUCK(1, BUCKBOOST, max77826_most_ops), 2098c2ecf20Sopenharmony_ci}; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int max77826_set_voltage_time_sel(struct regulator_dev *rdev, 2128c2ecf20Sopenharmony_ci unsigned int old_selector, 2138c2ecf20Sopenharmony_ci unsigned int new_selector) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci if (new_selector > old_selector) { 2168c2ecf20Sopenharmony_ci return DIV_ROUND_UP(MAX77826_BUCK_VOLT_STEP * 2178c2ecf20Sopenharmony_ci (new_selector - old_selector), 2188c2ecf20Sopenharmony_ci MAX77826_BUCK_RAMP_DELAY); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int max77826_read_device_id(struct regmap *regmap, struct device *dev) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci unsigned int device_id; 2278c2ecf20Sopenharmony_ci int res; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci res = regmap_read(regmap, MAX77826_REG_DEVICE_ID, &device_id); 2308c2ecf20Sopenharmony_ci if (!res) 2318c2ecf20Sopenharmony_ci dev_dbg(dev, "DEVICE_ID: 0x%x\n", device_id); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return res; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int max77826_i2c_probe(struct i2c_client *client) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 2398c2ecf20Sopenharmony_ci struct max77826_regulator_info *info; 2408c2ecf20Sopenharmony_ci struct regulator_config config = {}; 2418c2ecf20Sopenharmony_ci struct regulator_dev *rdev; 2428c2ecf20Sopenharmony_ci struct regmap *regmap; 2438c2ecf20Sopenharmony_ci int i; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci info = devm_kzalloc(dev, sizeof(struct max77826_regulator_info), 2468c2ecf20Sopenharmony_ci GFP_KERNEL); 2478c2ecf20Sopenharmony_ci if (!info) 2488c2ecf20Sopenharmony_ci return -ENOMEM; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci info->rdesc = max77826_regulators_desc; 2518c2ecf20Sopenharmony_ci regmap = devm_regmap_init_i2c(client, &max77826_regmap_config); 2528c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 2538c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate regmap!\n"); 2548c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci info->regmap = regmap; 2588c2ecf20Sopenharmony_ci i2c_set_clientdata(client, info); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci config.dev = dev; 2618c2ecf20Sopenharmony_ci config.regmap = regmap; 2628c2ecf20Sopenharmony_ci config.driver_data = info; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci for (i = 0; i < MAX77826_MAX_REGULATORS; i++) { 2658c2ecf20Sopenharmony_ci rdev = devm_regulator_register(dev, 2668c2ecf20Sopenharmony_ci &max77826_regulators_desc[i], 2678c2ecf20Sopenharmony_ci &config); 2688c2ecf20Sopenharmony_ci if (IS_ERR(rdev)) { 2698c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register regulator!\n"); 2708c2ecf20Sopenharmony_ci return PTR_ERR(rdev); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return max77826_read_device_id(regmap, dev); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic const struct of_device_id __maybe_unused max77826_of_match[] = { 2788c2ecf20Sopenharmony_ci { .compatible = "maxim,max77826" }, 2798c2ecf20Sopenharmony_ci { /* sentinel */ } 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, max77826_of_match); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic const struct i2c_device_id max77826_id[] = { 2848c2ecf20Sopenharmony_ci { "max77826-regulator" }, 2858c2ecf20Sopenharmony_ci { /* sentinel */ } 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max77826_id); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic struct i2c_driver max77826_regulator_driver = { 2908c2ecf20Sopenharmony_ci .driver = { 2918c2ecf20Sopenharmony_ci .name = "max77826", 2928c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(max77826_of_match), 2938c2ecf20Sopenharmony_ci }, 2948c2ecf20Sopenharmony_ci .probe_new = max77826_i2c_probe, 2958c2ecf20Sopenharmony_ci .id_table = max77826_id, 2968c2ecf20Sopenharmony_ci}; 2978c2ecf20Sopenharmony_cimodule_i2c_driver(max77826_regulator_driver); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Iskren Chernev <iskren.chernev@gmail.com>"); 3008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAX77826 PMIC regulator driver"); 3018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 302