1*6a1f303cSIvaylo Ivanov // SPDX-License-Identifier: GPL-2.0-or-later 2*6a1f303cSIvaylo Ivanov // 3*6a1f303cSIvaylo Ivanov // regulator driver for Maxim MAX77838 4*6a1f303cSIvaylo Ivanov // 5*6a1f303cSIvaylo Ivanov // based on max77826-regulator.c 6*6a1f303cSIvaylo Ivanov // 7*6a1f303cSIvaylo Ivanov // Copyright (c) 2025, Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com> 8*6a1f303cSIvaylo Ivanov 9*6a1f303cSIvaylo Ivanov #include <linux/kernel.h> 10*6a1f303cSIvaylo Ivanov #include <linux/module.h> 11*6a1f303cSIvaylo Ivanov #include <linux/init.h> 12*6a1f303cSIvaylo Ivanov #include <linux/err.h> 13*6a1f303cSIvaylo Ivanov #include <linux/of.h> 14*6a1f303cSIvaylo Ivanov #include <linux/platform_device.h> 15*6a1f303cSIvaylo Ivanov #include <linux/regulator/driver.h> 16*6a1f303cSIvaylo Ivanov #include <linux/regulator/of_regulator.h> 17*6a1f303cSIvaylo Ivanov #include <linux/i2c.h> 18*6a1f303cSIvaylo Ivanov #include <linux/regmap.h> 19*6a1f303cSIvaylo Ivanov 20*6a1f303cSIvaylo Ivanov enum max77838_registers { 21*6a1f303cSIvaylo Ivanov MAX77838_REG_DEVICE_ID = 0x00, 22*6a1f303cSIvaylo Ivanov MAX77838_REG_TOPSYS_STAT, 23*6a1f303cSIvaylo Ivanov MAX77838_REG_STAT, 24*6a1f303cSIvaylo Ivanov MAX77838_REG_EN, 25*6a1f303cSIvaylo Ivanov MAX77838_REG_GPIO_PD_CTRL, 26*6a1f303cSIvaylo Ivanov MAX77838_REG_UVLO_CFG1, 27*6a1f303cSIvaylo Ivanov /* 0x06 - 0x0B: reserved */ 28*6a1f303cSIvaylo Ivanov MAX77838_REG_I2C_CFG = 0x0C, 29*6a1f303cSIvaylo Ivanov /* 0x0D - 0x0F: reserved */ 30*6a1f303cSIvaylo Ivanov MAX77838_REG_LDO1_CFG = 0x10, 31*6a1f303cSIvaylo Ivanov MAX77838_REG_LDO2_CFG, 32*6a1f303cSIvaylo Ivanov MAX77838_REG_LDO3_CFG, 33*6a1f303cSIvaylo Ivanov MAX77838_REG_LDO4_CFG, 34*6a1f303cSIvaylo Ivanov /* 0x14 - 0x1F: reserved */ 35*6a1f303cSIvaylo Ivanov MAX77838_REG_BUCK_CFG1 = 0x20, 36*6a1f303cSIvaylo Ivanov MAX77838_REG_BUCK_VOUT, 37*6a1f303cSIvaylo Ivanov }; 38*6a1f303cSIvaylo Ivanov 39*6a1f303cSIvaylo Ivanov enum max77838_regulators { 40*6a1f303cSIvaylo Ivanov MAX77838_LDO1 = 0, 41*6a1f303cSIvaylo Ivanov MAX77838_LDO2, 42*6a1f303cSIvaylo Ivanov MAX77838_LDO3, 43*6a1f303cSIvaylo Ivanov MAX77838_LDO4, 44*6a1f303cSIvaylo Ivanov MAX77838_BUCK, 45*6a1f303cSIvaylo Ivanov MAX77838_MAX_REGULATORS, 46*6a1f303cSIvaylo Ivanov }; 47*6a1f303cSIvaylo Ivanov 48*6a1f303cSIvaylo Ivanov #define MAX77838_MASK_LDO 0x7f 49*6a1f303cSIvaylo Ivanov #define MAX77838_MASK_BUCK 0xff 50*6a1f303cSIvaylo Ivanov 51*6a1f303cSIvaylo Ivanov #define MAX77838_LDO1_EN BIT(0) 52*6a1f303cSIvaylo Ivanov #define MAX77838_LDO2_EN BIT(1) 53*6a1f303cSIvaylo Ivanov #define MAX77838_LDO3_EN BIT(2) 54*6a1f303cSIvaylo Ivanov #define MAX77838_LDO4_EN BIT(3) 55*6a1f303cSIvaylo Ivanov #define MAX77838_BUCK_EN BIT(4) 56*6a1f303cSIvaylo Ivanov 57*6a1f303cSIvaylo Ivanov #define MAX77838_BUCK_AD BIT(3) 58*6a1f303cSIvaylo Ivanov #define MAX77838_LDO_AD BIT(7) 59*6a1f303cSIvaylo Ivanov 60*6a1f303cSIvaylo Ivanov #define MAX77838_LDO_VOLT_MIN 600000 61*6a1f303cSIvaylo Ivanov #define MAX77838_LDO_VOLT_MAX 3775000 62*6a1f303cSIvaylo Ivanov #define MAX77838_LDO_VOLT_STEP 25000 63*6a1f303cSIvaylo Ivanov 64*6a1f303cSIvaylo Ivanov #define MAX77838_BUCK_VOLT_MIN 500000 65*6a1f303cSIvaylo Ivanov #define MAX77838_BUCK_VOLT_MAX 2093750 66*6a1f303cSIvaylo Ivanov #define MAX77838_BUCK_VOLT_STEP 6250 67*6a1f303cSIvaylo Ivanov 68*6a1f303cSIvaylo Ivanov #define MAX77838_VOLT_RANGE(_type) \ 69*6a1f303cSIvaylo Ivanov ((MAX77838_ ## _type ## _VOLT_MAX - \ 70*6a1f303cSIvaylo Ivanov MAX77838_ ## _type ## _VOLT_MIN) / \ 71*6a1f303cSIvaylo Ivanov MAX77838_ ## _type ## _VOLT_STEP + 1) 72*6a1f303cSIvaylo Ivanov 73*6a1f303cSIvaylo Ivanov #define MAX77838_LDO(_id) \ 74*6a1f303cSIvaylo Ivanov [MAX77838_LDO ## _id] = { \ 75*6a1f303cSIvaylo Ivanov .id = MAX77838_LDO ## _id, \ 76*6a1f303cSIvaylo Ivanov .name = "ldo"#_id, \ 77*6a1f303cSIvaylo Ivanov .of_match = of_match_ptr("ldo"#_id), \ 78*6a1f303cSIvaylo Ivanov .regulators_node = "regulators", \ 79*6a1f303cSIvaylo Ivanov .ops = &max77838_regulator_ops, \ 80*6a1f303cSIvaylo Ivanov .min_uV = MAX77838_LDO_VOLT_MIN, \ 81*6a1f303cSIvaylo Ivanov .uV_step = MAX77838_LDO_VOLT_STEP, \ 82*6a1f303cSIvaylo Ivanov .n_voltages = MAX77838_VOLT_RANGE(LDO), \ 83*6a1f303cSIvaylo Ivanov .enable_reg = MAX77838_REG_EN, \ 84*6a1f303cSIvaylo Ivanov .enable_mask = MAX77838_LDO ## _id ## _EN, \ 85*6a1f303cSIvaylo Ivanov .vsel_reg = MAX77838_REG_LDO ## _id ## _CFG, \ 86*6a1f303cSIvaylo Ivanov .vsel_mask = MAX77838_MASK_LDO, \ 87*6a1f303cSIvaylo Ivanov .active_discharge_off = 0, \ 88*6a1f303cSIvaylo Ivanov .active_discharge_on = MAX77838_LDO_AD, \ 89*6a1f303cSIvaylo Ivanov .active_discharge_mask = MAX77838_LDO_AD, \ 90*6a1f303cSIvaylo Ivanov .active_discharge_reg = MAX77838_REG_LDO ## _id ## _CFG, \ 91*6a1f303cSIvaylo Ivanov .owner = THIS_MODULE, \ 92*6a1f303cSIvaylo Ivanov } 93*6a1f303cSIvaylo Ivanov 94*6a1f303cSIvaylo Ivanov #define MAX77838_BUCK_DESC \ 95*6a1f303cSIvaylo Ivanov [MAX77838_BUCK] = { \ 96*6a1f303cSIvaylo Ivanov .id = MAX77838_BUCK, \ 97*6a1f303cSIvaylo Ivanov .name = "buck", \ 98*6a1f303cSIvaylo Ivanov .of_match = of_match_ptr("buck"), \ 99*6a1f303cSIvaylo Ivanov .regulators_node = "regulators", \ 100*6a1f303cSIvaylo Ivanov .ops = &max77838_regulator_ops, \ 101*6a1f303cSIvaylo Ivanov .min_uV = MAX77838_BUCK_VOLT_MIN, \ 102*6a1f303cSIvaylo Ivanov .uV_step = MAX77838_BUCK_VOLT_STEP, \ 103*6a1f303cSIvaylo Ivanov .n_voltages = MAX77838_VOLT_RANGE(BUCK), \ 104*6a1f303cSIvaylo Ivanov .enable_reg = MAX77838_REG_EN, \ 105*6a1f303cSIvaylo Ivanov .enable_mask = MAX77838_BUCK_EN, \ 106*6a1f303cSIvaylo Ivanov .vsel_reg = MAX77838_REG_BUCK_VOUT, \ 107*6a1f303cSIvaylo Ivanov .vsel_mask = MAX77838_MASK_BUCK, \ 108*6a1f303cSIvaylo Ivanov .active_discharge_off = 0, \ 109*6a1f303cSIvaylo Ivanov .active_discharge_on = MAX77838_BUCK_AD, \ 110*6a1f303cSIvaylo Ivanov .active_discharge_mask = MAX77838_BUCK_AD, \ 111*6a1f303cSIvaylo Ivanov .active_discharge_reg = MAX77838_REG_BUCK_CFG1, \ 112*6a1f303cSIvaylo Ivanov .owner = THIS_MODULE, \ 113*6a1f303cSIvaylo Ivanov } 114*6a1f303cSIvaylo Ivanov 115*6a1f303cSIvaylo Ivanov struct max77838_regulator_info { 116*6a1f303cSIvaylo Ivanov struct regmap *regmap; 117*6a1f303cSIvaylo Ivanov }; 118*6a1f303cSIvaylo Ivanov 119*6a1f303cSIvaylo Ivanov static const struct regmap_config max77838_regmap_config = { 120*6a1f303cSIvaylo Ivanov .reg_bits = 8, 121*6a1f303cSIvaylo Ivanov .val_bits = 8, 122*6a1f303cSIvaylo Ivanov .max_register = MAX77838_REG_BUCK_VOUT, 123*6a1f303cSIvaylo Ivanov }; 124*6a1f303cSIvaylo Ivanov 125*6a1f303cSIvaylo Ivanov static const struct regulator_ops max77838_regulator_ops = { 126*6a1f303cSIvaylo Ivanov .enable = regulator_enable_regmap, 127*6a1f303cSIvaylo Ivanov .disable = regulator_disable_regmap, 128*6a1f303cSIvaylo Ivanov .is_enabled = regulator_is_enabled_regmap, 129*6a1f303cSIvaylo Ivanov .list_voltage = regulator_list_voltage_linear, 130*6a1f303cSIvaylo Ivanov .map_voltage = regulator_map_voltage_linear, 131*6a1f303cSIvaylo Ivanov .get_voltage_sel = regulator_get_voltage_sel_regmap, 132*6a1f303cSIvaylo Ivanov .set_voltage_sel = regulator_set_voltage_sel_regmap, 133*6a1f303cSIvaylo Ivanov .set_active_discharge = regulator_set_active_discharge_regmap, 134*6a1f303cSIvaylo Ivanov }; 135*6a1f303cSIvaylo Ivanov 136*6a1f303cSIvaylo Ivanov static const struct regulator_desc max77838_regulators_desc[] = { 137*6a1f303cSIvaylo Ivanov MAX77838_LDO(1), 138*6a1f303cSIvaylo Ivanov MAX77838_LDO(2), 139*6a1f303cSIvaylo Ivanov MAX77838_LDO(3), 140*6a1f303cSIvaylo Ivanov MAX77838_LDO(4), 141*6a1f303cSIvaylo Ivanov MAX77838_BUCK_DESC, 142*6a1f303cSIvaylo Ivanov }; 143*6a1f303cSIvaylo Ivanov 144*6a1f303cSIvaylo Ivanov static int max77838_read_device_id(struct regmap *regmap, struct device *dev) 145*6a1f303cSIvaylo Ivanov { 146*6a1f303cSIvaylo Ivanov unsigned int device_id; 147*6a1f303cSIvaylo Ivanov int ret; 148*6a1f303cSIvaylo Ivanov 149*6a1f303cSIvaylo Ivanov ret = regmap_read(regmap, MAX77838_REG_DEVICE_ID, &device_id); 150*6a1f303cSIvaylo Ivanov if (!ret) 151*6a1f303cSIvaylo Ivanov dev_dbg(dev, "DEVICE_ID: 0x%x\n", device_id); 152*6a1f303cSIvaylo Ivanov 153*6a1f303cSIvaylo Ivanov return ret; 154*6a1f303cSIvaylo Ivanov } 155*6a1f303cSIvaylo Ivanov 156*6a1f303cSIvaylo Ivanov static int max77838_i2c_probe(struct i2c_client *client) 157*6a1f303cSIvaylo Ivanov { 158*6a1f303cSIvaylo Ivanov struct device *dev = &client->dev; 159*6a1f303cSIvaylo Ivanov struct max77838_regulator_info *info; 160*6a1f303cSIvaylo Ivanov struct regulator_config config = {}; 161*6a1f303cSIvaylo Ivanov struct regulator_dev *rdev; 162*6a1f303cSIvaylo Ivanov struct regmap *regmap; 163*6a1f303cSIvaylo Ivanov int i; 164*6a1f303cSIvaylo Ivanov 165*6a1f303cSIvaylo Ivanov info = devm_kzalloc(dev, sizeof(struct max77838_regulator_info), 166*6a1f303cSIvaylo Ivanov GFP_KERNEL); 167*6a1f303cSIvaylo Ivanov if (!info) 168*6a1f303cSIvaylo Ivanov return -ENOMEM; 169*6a1f303cSIvaylo Ivanov 170*6a1f303cSIvaylo Ivanov regmap = devm_regmap_init_i2c(client, &max77838_regmap_config); 171*6a1f303cSIvaylo Ivanov if (IS_ERR(regmap)) { 172*6a1f303cSIvaylo Ivanov dev_err(dev, "Failed to allocate regmap!\n"); 173*6a1f303cSIvaylo Ivanov return PTR_ERR(regmap); 174*6a1f303cSIvaylo Ivanov } 175*6a1f303cSIvaylo Ivanov 176*6a1f303cSIvaylo Ivanov info->regmap = regmap; 177*6a1f303cSIvaylo Ivanov i2c_set_clientdata(client, info); 178*6a1f303cSIvaylo Ivanov 179*6a1f303cSIvaylo Ivanov config.dev = dev; 180*6a1f303cSIvaylo Ivanov config.regmap = regmap; 181*6a1f303cSIvaylo Ivanov config.driver_data = info; 182*6a1f303cSIvaylo Ivanov 183*6a1f303cSIvaylo Ivanov for (i = 0; i < MAX77838_MAX_REGULATORS; i++) { 184*6a1f303cSIvaylo Ivanov rdev = devm_regulator_register(dev, 185*6a1f303cSIvaylo Ivanov &max77838_regulators_desc[i], 186*6a1f303cSIvaylo Ivanov &config); 187*6a1f303cSIvaylo Ivanov if (IS_ERR(rdev)) { 188*6a1f303cSIvaylo Ivanov dev_err(dev, "Failed to register regulator!\n"); 189*6a1f303cSIvaylo Ivanov return PTR_ERR(rdev); 190*6a1f303cSIvaylo Ivanov } 191*6a1f303cSIvaylo Ivanov } 192*6a1f303cSIvaylo Ivanov 193*6a1f303cSIvaylo Ivanov return max77838_read_device_id(regmap, dev); 194*6a1f303cSIvaylo Ivanov } 195*6a1f303cSIvaylo Ivanov 196*6a1f303cSIvaylo Ivanov static const struct of_device_id __maybe_unused max77838_of_match[] = { 197*6a1f303cSIvaylo Ivanov { .compatible = "maxim,max77838" }, 198*6a1f303cSIvaylo Ivanov { /* sentinel */ } 199*6a1f303cSIvaylo Ivanov }; 200*6a1f303cSIvaylo Ivanov MODULE_DEVICE_TABLE(of, max77838_of_match); 201*6a1f303cSIvaylo Ivanov 202*6a1f303cSIvaylo Ivanov static const struct i2c_device_id max77838_id[] = { 203*6a1f303cSIvaylo Ivanov { "max77838-regulator" }, 204*6a1f303cSIvaylo Ivanov { /* sentinel */ } 205*6a1f303cSIvaylo Ivanov }; 206*6a1f303cSIvaylo Ivanov MODULE_DEVICE_TABLE(i2c, max77838_id); 207*6a1f303cSIvaylo Ivanov 208*6a1f303cSIvaylo Ivanov static struct i2c_driver max77838_regulator_driver = { 209*6a1f303cSIvaylo Ivanov .driver = { 210*6a1f303cSIvaylo Ivanov .name = "max77838", 211*6a1f303cSIvaylo Ivanov .probe_type = PROBE_PREFER_ASYNCHRONOUS, 212*6a1f303cSIvaylo Ivanov .of_match_table = of_match_ptr(max77838_of_match), 213*6a1f303cSIvaylo Ivanov }, 214*6a1f303cSIvaylo Ivanov .probe = max77838_i2c_probe, 215*6a1f303cSIvaylo Ivanov .id_table = max77838_id, 216*6a1f303cSIvaylo Ivanov }; 217*6a1f303cSIvaylo Ivanov module_i2c_driver(max77838_regulator_driver); 218*6a1f303cSIvaylo Ivanov 219*6a1f303cSIvaylo Ivanov MODULE_AUTHOR("Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>"); 220*6a1f303cSIvaylo Ivanov MODULE_DESCRIPTION("MAX77838 PMIC regulator driver"); 221*6a1f303cSIvaylo Ivanov MODULE_LICENSE("GPL"); 222