18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// max77693.c - Regulator driver for the Maxim 77693 and 77843 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2013-2015 Samsung Electronics 68c2ecf20Sopenharmony_ci// Jonghwa Lee <jonghwa3.lee@samsung.com> 78c2ecf20Sopenharmony_ci// Krzysztof Kozlowski <krzk@kernel.org> 88c2ecf20Sopenharmony_ci// 98c2ecf20Sopenharmony_ci// This driver is based on max77686.c 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/export.h> 168c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 178c2ecf20Sopenharmony_ci#include <linux/regulator/machine.h> 188c2ecf20Sopenharmony_ci#include <linux/mfd/max77693.h> 198c2ecf20Sopenharmony_ci#include <linux/mfd/max77693-common.h> 208c2ecf20Sopenharmony_ci#include <linux/mfd/max77693-private.h> 218c2ecf20Sopenharmony_ci#include <linux/mfd/max77843-private.h> 228c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h> 238c2ecf20Sopenharmony_ci#include <linux/regmap.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * ID for MAX77843 regulators. 278c2ecf20Sopenharmony_ci * There is no need for such for MAX77693. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cienum max77843_regulator_type { 308c2ecf20Sopenharmony_ci MAX77843_SAFEOUT1 = 0, 318c2ecf20Sopenharmony_ci MAX77843_SAFEOUT2, 328c2ecf20Sopenharmony_ci MAX77843_CHARGER, 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci MAX77843_NUM, 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Register differences between chargers: MAX77693 and MAX77843 */ 388c2ecf20Sopenharmony_cistruct chg_reg_data { 398c2ecf20Sopenharmony_ci unsigned int linear_reg; 408c2ecf20Sopenharmony_ci unsigned int linear_mask; 418c2ecf20Sopenharmony_ci unsigned int uA_step; 428c2ecf20Sopenharmony_ci unsigned int min_sel; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * MAX77693 CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA 478c2ecf20Sopenharmony_ci * 0x00, 0x01, 0x2, 0x03 = 60 mA 488c2ecf20Sopenharmony_ci * 0x04 ~ 0x7E = (60 + (X - 3) * 20) mA 498c2ecf20Sopenharmony_ci * Actually for MAX77693 the driver manipulates the maximum input current, 508c2ecf20Sopenharmony_ci * not the fast charge current (output). This should be fixed. 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * On MAX77843 the calculation formula is the same (except values). 538c2ecf20Sopenharmony_ci * Fortunately it properly manipulates the fast charge current. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_cistatic int max77693_chg_get_current_limit(struct regulator_dev *rdev) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci const struct chg_reg_data *reg_data = rdev_get_drvdata(rdev); 588c2ecf20Sopenharmony_ci unsigned int chg_min_uA = rdev->constraints->min_uA; 598c2ecf20Sopenharmony_ci unsigned int chg_max_uA = rdev->constraints->max_uA; 608c2ecf20Sopenharmony_ci unsigned int reg, sel; 618c2ecf20Sopenharmony_ci unsigned int val; 628c2ecf20Sopenharmony_ci int ret; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci ret = regmap_read(rdev->regmap, reg_data->linear_reg, ®); 658c2ecf20Sopenharmony_ci if (ret < 0) 668c2ecf20Sopenharmony_ci return ret; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci sel = reg & reg_data->linear_mask; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* the first four codes for charger current are all 60mA */ 718c2ecf20Sopenharmony_ci if (sel <= reg_data->min_sel) 728c2ecf20Sopenharmony_ci sel = 0; 738c2ecf20Sopenharmony_ci else 748c2ecf20Sopenharmony_ci sel -= reg_data->min_sel; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci val = chg_min_uA + reg_data->uA_step * sel; 778c2ecf20Sopenharmony_ci if (val > chg_max_uA) 788c2ecf20Sopenharmony_ci return -EINVAL; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return val; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int max77693_chg_set_current_limit(struct regulator_dev *rdev, 848c2ecf20Sopenharmony_ci int min_uA, int max_uA) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci const struct chg_reg_data *reg_data = rdev_get_drvdata(rdev); 878c2ecf20Sopenharmony_ci unsigned int chg_min_uA = rdev->constraints->min_uA; 888c2ecf20Sopenharmony_ci int sel = 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci while (chg_min_uA + reg_data->uA_step * sel < min_uA) 918c2ecf20Sopenharmony_ci sel++; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (chg_min_uA + reg_data->uA_step * sel > max_uA) 948c2ecf20Sopenharmony_ci return -EINVAL; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* the first four codes for charger current are all 60mA */ 978c2ecf20Sopenharmony_ci sel += reg_data->min_sel; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return regmap_write(rdev->regmap, reg_data->linear_reg, sel); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci/* end of CHARGER regulator ops */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Returns regmap suitable for given regulator on chosen device */ 1048c2ecf20Sopenharmony_cistatic struct regmap *max77693_get_regmap(enum max77693_types type, 1058c2ecf20Sopenharmony_ci struct max77693_dev *max77693, 1068c2ecf20Sopenharmony_ci int reg_id) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci if (type == TYPE_MAX77693) 1098c2ecf20Sopenharmony_ci return max77693->regmap; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Else: TYPE_MAX77843 */ 1128c2ecf20Sopenharmony_ci switch (reg_id) { 1138c2ecf20Sopenharmony_ci case MAX77843_SAFEOUT1: 1148c2ecf20Sopenharmony_ci case MAX77843_SAFEOUT2: 1158c2ecf20Sopenharmony_ci return max77693->regmap; 1168c2ecf20Sopenharmony_ci case MAX77843_CHARGER: 1178c2ecf20Sopenharmony_ci return max77693->regmap_chg; 1188c2ecf20Sopenharmony_ci default: 1198c2ecf20Sopenharmony_ci return max77693->regmap; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic const unsigned int max77693_safeout_table[] = { 1248c2ecf20Sopenharmony_ci 4850000, 1258c2ecf20Sopenharmony_ci 4900000, 1268c2ecf20Sopenharmony_ci 4950000, 1278c2ecf20Sopenharmony_ci 3300000, 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const struct regulator_ops max77693_safeout_ops = { 1318c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_table, 1328c2ecf20Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 1338c2ecf20Sopenharmony_ci .enable = regulator_enable_regmap, 1348c2ecf20Sopenharmony_ci .disable = regulator_disable_regmap, 1358c2ecf20Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 1368c2ecf20Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic const struct regulator_ops max77693_charger_ops = { 1408c2ecf20Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 1418c2ecf20Sopenharmony_ci .enable = regulator_enable_regmap, 1428c2ecf20Sopenharmony_ci .disable = regulator_disable_regmap, 1438c2ecf20Sopenharmony_ci .get_current_limit = max77693_chg_get_current_limit, 1448c2ecf20Sopenharmony_ci .set_current_limit = max77693_chg_set_current_limit, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define max77693_regulator_desc_esafeout(_num) { \ 1488c2ecf20Sopenharmony_ci .name = "ESAFEOUT"#_num, \ 1498c2ecf20Sopenharmony_ci .id = MAX77693_ESAFEOUT##_num, \ 1508c2ecf20Sopenharmony_ci .of_match = of_match_ptr("ESAFEOUT"#_num), \ 1518c2ecf20Sopenharmony_ci .regulators_node = of_match_ptr("regulators"), \ 1528c2ecf20Sopenharmony_ci .n_voltages = 4, \ 1538c2ecf20Sopenharmony_ci .ops = &max77693_safeout_ops, \ 1548c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, \ 1558c2ecf20Sopenharmony_ci .owner = THIS_MODULE, \ 1568c2ecf20Sopenharmony_ci .volt_table = max77693_safeout_table, \ 1578c2ecf20Sopenharmony_ci .vsel_reg = MAX77693_CHG_REG_SAFEOUT_CTRL, \ 1588c2ecf20Sopenharmony_ci .vsel_mask = SAFEOUT_CTRL_SAFEOUT##_num##_MASK, \ 1598c2ecf20Sopenharmony_ci .enable_reg = MAX77693_CHG_REG_SAFEOUT_CTRL, \ 1608c2ecf20Sopenharmony_ci .enable_mask = SAFEOUT_CTRL_ENSAFEOUT##_num##_MASK , \ 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic const struct regulator_desc max77693_supported_regulators[] = { 1648c2ecf20Sopenharmony_ci max77693_regulator_desc_esafeout(1), 1658c2ecf20Sopenharmony_ci max77693_regulator_desc_esafeout(2), 1668c2ecf20Sopenharmony_ci { 1678c2ecf20Sopenharmony_ci .name = "CHARGER", 1688c2ecf20Sopenharmony_ci .id = MAX77693_CHARGER, 1698c2ecf20Sopenharmony_ci .of_match = of_match_ptr("CHARGER"), 1708c2ecf20Sopenharmony_ci .regulators_node = of_match_ptr("regulators"), 1718c2ecf20Sopenharmony_ci .ops = &max77693_charger_ops, 1728c2ecf20Sopenharmony_ci .type = REGULATOR_CURRENT, 1738c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1748c2ecf20Sopenharmony_ci .enable_reg = MAX77693_CHG_REG_CHG_CNFG_00, 1758c2ecf20Sopenharmony_ci .enable_mask = CHG_CNFG_00_CHG_MASK | 1768c2ecf20Sopenharmony_ci CHG_CNFG_00_BUCK_MASK, 1778c2ecf20Sopenharmony_ci .enable_val = CHG_CNFG_00_CHG_MASK | CHG_CNFG_00_BUCK_MASK, 1788c2ecf20Sopenharmony_ci }, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic const struct chg_reg_data max77693_chg_reg_data = { 1828c2ecf20Sopenharmony_ci .linear_reg = MAX77693_CHG_REG_CHG_CNFG_09, 1838c2ecf20Sopenharmony_ci .linear_mask = CHG_CNFG_09_CHGIN_ILIM_MASK, 1848c2ecf20Sopenharmony_ci .uA_step = 20000, 1858c2ecf20Sopenharmony_ci .min_sel = 3, 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#define max77843_regulator_desc_esafeout(num) { \ 1898c2ecf20Sopenharmony_ci .name = "SAFEOUT" # num, \ 1908c2ecf20Sopenharmony_ci .id = MAX77843_SAFEOUT ## num, \ 1918c2ecf20Sopenharmony_ci .ops = &max77693_safeout_ops, \ 1928c2ecf20Sopenharmony_ci .of_match = of_match_ptr("SAFEOUT" # num), \ 1938c2ecf20Sopenharmony_ci .regulators_node = of_match_ptr("regulators"), \ 1948c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, \ 1958c2ecf20Sopenharmony_ci .owner = THIS_MODULE, \ 1968c2ecf20Sopenharmony_ci .n_voltages = ARRAY_SIZE(max77693_safeout_table), \ 1978c2ecf20Sopenharmony_ci .volt_table = max77693_safeout_table, \ 1988c2ecf20Sopenharmony_ci .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \ 1998c2ecf20Sopenharmony_ci .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT ## num, \ 2008c2ecf20Sopenharmony_ci .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \ 2018c2ecf20Sopenharmony_ci .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT ## num ## _MASK, \ 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic const struct regulator_desc max77843_supported_regulators[] = { 2058c2ecf20Sopenharmony_ci [MAX77843_SAFEOUT1] = max77843_regulator_desc_esafeout(1), 2068c2ecf20Sopenharmony_ci [MAX77843_SAFEOUT2] = max77843_regulator_desc_esafeout(2), 2078c2ecf20Sopenharmony_ci [MAX77843_CHARGER] = { 2088c2ecf20Sopenharmony_ci .name = "CHARGER", 2098c2ecf20Sopenharmony_ci .id = MAX77843_CHARGER, 2108c2ecf20Sopenharmony_ci .ops = &max77693_charger_ops, 2118c2ecf20Sopenharmony_ci .of_match = of_match_ptr("CHARGER"), 2128c2ecf20Sopenharmony_ci .regulators_node = of_match_ptr("regulators"), 2138c2ecf20Sopenharmony_ci .type = REGULATOR_CURRENT, 2148c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2158c2ecf20Sopenharmony_ci .enable_reg = MAX77843_CHG_REG_CHG_CNFG_00, 2168c2ecf20Sopenharmony_ci .enable_mask = MAX77843_CHG_MASK, 2178c2ecf20Sopenharmony_ci .enable_val = MAX77843_CHG_MASK, 2188c2ecf20Sopenharmony_ci }, 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic const struct chg_reg_data max77843_chg_reg_data = { 2228c2ecf20Sopenharmony_ci .linear_reg = MAX77843_CHG_REG_CHG_CNFG_02, 2238c2ecf20Sopenharmony_ci .linear_mask = MAX77843_CHG_FAST_CHG_CURRENT_MASK, 2248c2ecf20Sopenharmony_ci .uA_step = MAX77843_CHG_FAST_CHG_CURRENT_STEP, 2258c2ecf20Sopenharmony_ci .min_sel = 2, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int max77693_pmic_probe(struct platform_device *pdev) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci enum max77693_types type = platform_get_device_id(pdev)->driver_data; 2318c2ecf20Sopenharmony_ci struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent); 2328c2ecf20Sopenharmony_ci const struct regulator_desc *regulators; 2338c2ecf20Sopenharmony_ci unsigned int regulators_size; 2348c2ecf20Sopenharmony_ci int i; 2358c2ecf20Sopenharmony_ci struct regulator_config config = { }; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci config.dev = iodev->dev; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci switch (type) { 2408c2ecf20Sopenharmony_ci case TYPE_MAX77693: 2418c2ecf20Sopenharmony_ci regulators = max77693_supported_regulators; 2428c2ecf20Sopenharmony_ci regulators_size = ARRAY_SIZE(max77693_supported_regulators); 2438c2ecf20Sopenharmony_ci config.driver_data = (void *)&max77693_chg_reg_data; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci case TYPE_MAX77843: 2468c2ecf20Sopenharmony_ci regulators = max77843_supported_regulators; 2478c2ecf20Sopenharmony_ci regulators_size = ARRAY_SIZE(max77843_supported_regulators); 2488c2ecf20Sopenharmony_ci config.driver_data = (void *)&max77843_chg_reg_data; 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci default: 2518c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unsupported device type: %u\n", type); 2528c2ecf20Sopenharmony_ci return -ENODEV; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci for (i = 0; i < regulators_size; i++) { 2568c2ecf20Sopenharmony_ci struct regulator_dev *rdev; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci config.regmap = max77693_get_regmap(type, iodev, 2598c2ecf20Sopenharmony_ci regulators[i].id); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci rdev = devm_regulator_register(&pdev->dev, 2628c2ecf20Sopenharmony_ci ®ulators[i], &config); 2638c2ecf20Sopenharmony_ci if (IS_ERR(rdev)) { 2648c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2658c2ecf20Sopenharmony_ci "Failed to initialize regulator-%d\n", i); 2668c2ecf20Sopenharmony_ci return PTR_ERR(rdev); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic const struct platform_device_id max77693_pmic_id[] = { 2748c2ecf20Sopenharmony_ci { "max77693-pmic", TYPE_MAX77693 }, 2758c2ecf20Sopenharmony_ci { "max77843-regulator", TYPE_MAX77843 }, 2768c2ecf20Sopenharmony_ci {}, 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, max77693_pmic_id); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic struct platform_driver max77693_pmic_driver = { 2828c2ecf20Sopenharmony_ci .driver = { 2838c2ecf20Sopenharmony_ci .name = "max77693-pmic", 2848c2ecf20Sopenharmony_ci }, 2858c2ecf20Sopenharmony_ci .probe = max77693_pmic_probe, 2868c2ecf20Sopenharmony_ci .id_table = max77693_pmic_id, 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int __init max77693_pmic_init(void) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci return platform_driver_register(&max77693_pmic_driver); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_cisubsys_initcall(max77693_pmic_init); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic void __exit max77693_pmic_cleanup(void) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci platform_driver_unregister(&max77693_pmic_driver); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_cimodule_exit(max77693_pmic_cleanup); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAXIM 77693/77843 regulator driver"); 3028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); 3038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); 3048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 305