18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Regulator driver for RICOH RC5T583 power management chip. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. 68c2ecf20Sopenharmony_ci * Author: Laxman dewangan <ldewangan@nvidia.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * based on code 98c2ecf20Sopenharmony_ci * Copyright (C) 2011 RICOH COMPANY,LTD 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 188c2ecf20Sopenharmony_ci#include <linux/regulator/machine.h> 198c2ecf20Sopenharmony_ci#include <linux/gpio.h> 208c2ecf20Sopenharmony_ci#include <linux/mfd/rc5t583.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct rc5t583_regulator_info { 238c2ecf20Sopenharmony_ci int deepsleep_id; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci /* Regulator register address.*/ 268c2ecf20Sopenharmony_ci uint8_t reg_disc_reg; 278c2ecf20Sopenharmony_ci uint8_t disc_bit; 288c2ecf20Sopenharmony_ci uint8_t deepsleep_reg; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci /* Regulator specific turn-on delay and voltage settling time*/ 318c2ecf20Sopenharmony_ci int enable_uv_per_us; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci /* Used by regulator core */ 348c2ecf20Sopenharmony_ci struct regulator_desc desc; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int rc5t583_regulator_enable_time(struct regulator_dev *rdev) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct rc5t583_regulator_info *reg_info = rdev_get_drvdata(rdev); 408c2ecf20Sopenharmony_ci int vsel = regulator_get_voltage_sel_regmap(rdev); 418c2ecf20Sopenharmony_ci int curr_uV = regulator_list_voltage_linear(rdev, vsel); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return DIV_ROUND_UP(curr_uV, reg_info->enable_uv_per_us); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic const struct regulator_ops rc5t583_ops = { 478c2ecf20Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 488c2ecf20Sopenharmony_ci .enable = regulator_enable_regmap, 498c2ecf20Sopenharmony_ci .disable = regulator_disable_regmap, 508c2ecf20Sopenharmony_ci .enable_time = rc5t583_regulator_enable_time, 518c2ecf20Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 528c2ecf20Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 538c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_linear, 548c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_linear, 558c2ecf20Sopenharmony_ci .set_voltage_time_sel = regulator_set_voltage_time_sel, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define RC5T583_REG(_id, _en_reg, _en_bit, _disc_reg, _disc_bit, \ 598c2ecf20Sopenharmony_ci _vout_mask, _min_mv, _max_mv, _step_uV, _enable_mv) \ 608c2ecf20Sopenharmony_ci{ \ 618c2ecf20Sopenharmony_ci .reg_disc_reg = RC5T583_REG_##_disc_reg, \ 628c2ecf20Sopenharmony_ci .disc_bit = _disc_bit, \ 638c2ecf20Sopenharmony_ci .deepsleep_reg = RC5T583_REG_##_id##DAC_DS, \ 648c2ecf20Sopenharmony_ci .enable_uv_per_us = _enable_mv * 1000, \ 658c2ecf20Sopenharmony_ci .deepsleep_id = RC5T583_DS_##_id, \ 668c2ecf20Sopenharmony_ci .desc = { \ 678c2ecf20Sopenharmony_ci .name = "rc5t583-regulator-"#_id, \ 688c2ecf20Sopenharmony_ci .id = RC5T583_REGULATOR_##_id, \ 698c2ecf20Sopenharmony_ci .n_voltages = (_max_mv - _min_mv) * 1000 / _step_uV + 1, \ 708c2ecf20Sopenharmony_ci .ops = &rc5t583_ops, \ 718c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, \ 728c2ecf20Sopenharmony_ci .owner = THIS_MODULE, \ 738c2ecf20Sopenharmony_ci .vsel_reg = RC5T583_REG_##_id##DAC, \ 748c2ecf20Sopenharmony_ci .vsel_mask = _vout_mask, \ 758c2ecf20Sopenharmony_ci .enable_reg = RC5T583_REG_##_en_reg, \ 768c2ecf20Sopenharmony_ci .enable_mask = BIT(_en_bit), \ 778c2ecf20Sopenharmony_ci .min_uV = _min_mv * 1000, \ 788c2ecf20Sopenharmony_ci .uV_step = _step_uV, \ 798c2ecf20Sopenharmony_ci .ramp_delay = 40 * 1000, \ 808c2ecf20Sopenharmony_ci }, \ 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic struct rc5t583_regulator_info rc5t583_reg_info[RC5T583_REGULATOR_MAX] = { 848c2ecf20Sopenharmony_ci RC5T583_REG(DC0, DC0CTL, 0, DC0CTL, 1, 0x7F, 700, 1500, 12500, 4), 858c2ecf20Sopenharmony_ci RC5T583_REG(DC1, DC1CTL, 0, DC1CTL, 1, 0x7F, 700, 1500, 12500, 14), 868c2ecf20Sopenharmony_ci RC5T583_REG(DC2, DC2CTL, 0, DC2CTL, 1, 0x7F, 900, 2400, 12500, 14), 878c2ecf20Sopenharmony_ci RC5T583_REG(DC3, DC3CTL, 0, DC3CTL, 1, 0x7F, 900, 2400, 12500, 14), 888c2ecf20Sopenharmony_ci RC5T583_REG(LDO0, LDOEN2, 0, LDODIS2, 0, 0x7F, 900, 3400, 25000, 160), 898c2ecf20Sopenharmony_ci RC5T583_REG(LDO1, LDOEN2, 1, LDODIS2, 1, 0x7F, 900, 3400, 25000, 160), 908c2ecf20Sopenharmony_ci RC5T583_REG(LDO2, LDOEN2, 2, LDODIS2, 2, 0x7F, 900, 3400, 25000, 160), 918c2ecf20Sopenharmony_ci RC5T583_REG(LDO3, LDOEN2, 3, LDODIS2, 3, 0x7F, 900, 3400, 25000, 160), 928c2ecf20Sopenharmony_ci RC5T583_REG(LDO4, LDOEN2, 4, LDODIS2, 4, 0x3F, 750, 1500, 12500, 133), 938c2ecf20Sopenharmony_ci RC5T583_REG(LDO5, LDOEN2, 5, LDODIS2, 5, 0x7F, 900, 3400, 25000, 267), 948c2ecf20Sopenharmony_ci RC5T583_REG(LDO6, LDOEN2, 6, LDODIS2, 6, 0x7F, 900, 3400, 25000, 133), 958c2ecf20Sopenharmony_ci RC5T583_REG(LDO7, LDOEN2, 7, LDODIS2, 7, 0x7F, 900, 3400, 25000, 233), 968c2ecf20Sopenharmony_ci RC5T583_REG(LDO8, LDOEN1, 0, LDODIS1, 0, 0x7F, 900, 3400, 25000, 233), 978c2ecf20Sopenharmony_ci RC5T583_REG(LDO9, LDOEN1, 1, LDODIS1, 1, 0x7F, 900, 3400, 25000, 133), 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int rc5t583_regulator_probe(struct platform_device *pdev) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); 1038c2ecf20Sopenharmony_ci struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); 1048c2ecf20Sopenharmony_ci struct regulator_config config = { }; 1058c2ecf20Sopenharmony_ci struct regulator_dev *rdev; 1068c2ecf20Sopenharmony_ci struct rc5t583_regulator_info *ri; 1078c2ecf20Sopenharmony_ci int ret; 1088c2ecf20Sopenharmony_ci int id; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!pdata) { 1118c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No platform data, exiting...\n"); 1128c2ecf20Sopenharmony_ci return -ENODEV; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) { 1168c2ecf20Sopenharmony_ci ri = &rc5t583_reg_info[id]; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (ri->deepsleep_id == RC5T583_DS_NONE) 1198c2ecf20Sopenharmony_ci goto skip_ext_pwr_config; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ret = rc5t583_ext_power_req_config(rc5t583->dev, 1228c2ecf20Sopenharmony_ci ri->deepsleep_id, 1238c2ecf20Sopenharmony_ci pdata->regulator_ext_pwr_control[id], 1248c2ecf20Sopenharmony_ci pdata->regulator_deepsleep_slot[id]); 1258c2ecf20Sopenharmony_ci /* 1268c2ecf20Sopenharmony_ci * Configuring external control is not a major issue, 1278c2ecf20Sopenharmony_ci * just give warning. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci if (ret < 0) 1308c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 1318c2ecf20Sopenharmony_ci "Failed to configure ext control %d\n", id); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ciskip_ext_pwr_config: 1348c2ecf20Sopenharmony_ci config.dev = &pdev->dev; 1358c2ecf20Sopenharmony_ci config.init_data = pdata->reg_init_data[id]; 1368c2ecf20Sopenharmony_ci config.driver_data = ri; 1378c2ecf20Sopenharmony_ci config.regmap = rc5t583->regmap; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config); 1408c2ecf20Sopenharmony_ci if (IS_ERR(rdev)) { 1418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register regulator %s\n", 1428c2ecf20Sopenharmony_ci ri->desc.name); 1438c2ecf20Sopenharmony_ci return PTR_ERR(rdev); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic struct platform_driver rc5t583_regulator_driver = { 1508c2ecf20Sopenharmony_ci .driver = { 1518c2ecf20Sopenharmony_ci .name = "rc5t583-regulator", 1528c2ecf20Sopenharmony_ci }, 1538c2ecf20Sopenharmony_ci .probe = rc5t583_regulator_probe, 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int __init rc5t583_regulator_init(void) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci return platform_driver_register(&rc5t583_regulator_driver); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_cisubsys_initcall(rc5t583_regulator_init); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void __exit rc5t583_regulator_exit(void) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci platform_driver_unregister(&rc5t583_regulator_driver); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_cimodule_exit(rc5t583_regulator_exit); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); 1698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RC5T583 regulator driver"); 1708c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:rc5t583-regulator"); 1718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 172