162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Regulator driver for RICOH RC5T583 power management chip. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. 662306a36Sopenharmony_ci * Author: Laxman dewangan <ldewangan@nvidia.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * based on code 962306a36Sopenharmony_ci * Copyright (C) 2011 RICOH COMPANY,LTD 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/regulator/driver.h> 1862306a36Sopenharmony_ci#include <linux/regulator/machine.h> 1962306a36Sopenharmony_ci#include <linux/mfd/rc5t583.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct rc5t583_regulator_info { 2262306a36Sopenharmony_ci int deepsleep_id; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci /* Regulator register address.*/ 2562306a36Sopenharmony_ci uint8_t reg_disc_reg; 2662306a36Sopenharmony_ci uint8_t disc_bit; 2762306a36Sopenharmony_ci uint8_t deepsleep_reg; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci /* Regulator specific turn-on delay and voltage settling time*/ 3062306a36Sopenharmony_ci int enable_uv_per_us; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* Used by regulator core */ 3362306a36Sopenharmony_ci struct regulator_desc desc; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int rc5t583_regulator_enable_time(struct regulator_dev *rdev) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct rc5t583_regulator_info *reg_info = rdev_get_drvdata(rdev); 3962306a36Sopenharmony_ci int vsel = regulator_get_voltage_sel_regmap(rdev); 4062306a36Sopenharmony_ci int curr_uV = regulator_list_voltage_linear(rdev, vsel); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return DIV_ROUND_UP(curr_uV, reg_info->enable_uv_per_us); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const struct regulator_ops rc5t583_ops = { 4662306a36Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 4762306a36Sopenharmony_ci .enable = regulator_enable_regmap, 4862306a36Sopenharmony_ci .disable = regulator_disable_regmap, 4962306a36Sopenharmony_ci .enable_time = rc5t583_regulator_enable_time, 5062306a36Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 5162306a36Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 5262306a36Sopenharmony_ci .list_voltage = regulator_list_voltage_linear, 5362306a36Sopenharmony_ci .map_voltage = regulator_map_voltage_linear, 5462306a36Sopenharmony_ci .set_voltage_time_sel = regulator_set_voltage_time_sel, 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define RC5T583_REG(_id, _en_reg, _en_bit, _disc_reg, _disc_bit, \ 5862306a36Sopenharmony_ci _vout_mask, _min_mv, _max_mv, _step_uV, _enable_mv) \ 5962306a36Sopenharmony_ci{ \ 6062306a36Sopenharmony_ci .reg_disc_reg = RC5T583_REG_##_disc_reg, \ 6162306a36Sopenharmony_ci .disc_bit = _disc_bit, \ 6262306a36Sopenharmony_ci .deepsleep_reg = RC5T583_REG_##_id##DAC_DS, \ 6362306a36Sopenharmony_ci .enable_uv_per_us = _enable_mv * 1000, \ 6462306a36Sopenharmony_ci .deepsleep_id = RC5T583_DS_##_id, \ 6562306a36Sopenharmony_ci .desc = { \ 6662306a36Sopenharmony_ci .name = "rc5t583-regulator-"#_id, \ 6762306a36Sopenharmony_ci .id = RC5T583_REGULATOR_##_id, \ 6862306a36Sopenharmony_ci .n_voltages = (_max_mv - _min_mv) * 1000 / _step_uV + 1, \ 6962306a36Sopenharmony_ci .ops = &rc5t583_ops, \ 7062306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, \ 7162306a36Sopenharmony_ci .owner = THIS_MODULE, \ 7262306a36Sopenharmony_ci .vsel_reg = RC5T583_REG_##_id##DAC, \ 7362306a36Sopenharmony_ci .vsel_mask = _vout_mask, \ 7462306a36Sopenharmony_ci .enable_reg = RC5T583_REG_##_en_reg, \ 7562306a36Sopenharmony_ci .enable_mask = BIT(_en_bit), \ 7662306a36Sopenharmony_ci .min_uV = _min_mv * 1000, \ 7762306a36Sopenharmony_ci .uV_step = _step_uV, \ 7862306a36Sopenharmony_ci .ramp_delay = 40 * 1000, \ 7962306a36Sopenharmony_ci }, \ 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct rc5t583_regulator_info rc5t583_reg_info[RC5T583_REGULATOR_MAX] = { 8362306a36Sopenharmony_ci RC5T583_REG(DC0, DC0CTL, 0, DC0CTL, 1, 0x7F, 700, 1500, 12500, 4), 8462306a36Sopenharmony_ci RC5T583_REG(DC1, DC1CTL, 0, DC1CTL, 1, 0x7F, 700, 1500, 12500, 14), 8562306a36Sopenharmony_ci RC5T583_REG(DC2, DC2CTL, 0, DC2CTL, 1, 0x7F, 900, 2400, 12500, 14), 8662306a36Sopenharmony_ci RC5T583_REG(DC3, DC3CTL, 0, DC3CTL, 1, 0x7F, 900, 2400, 12500, 14), 8762306a36Sopenharmony_ci RC5T583_REG(LDO0, LDOEN2, 0, LDODIS2, 0, 0x7F, 900, 3400, 25000, 160), 8862306a36Sopenharmony_ci RC5T583_REG(LDO1, LDOEN2, 1, LDODIS2, 1, 0x7F, 900, 3400, 25000, 160), 8962306a36Sopenharmony_ci RC5T583_REG(LDO2, LDOEN2, 2, LDODIS2, 2, 0x7F, 900, 3400, 25000, 160), 9062306a36Sopenharmony_ci RC5T583_REG(LDO3, LDOEN2, 3, LDODIS2, 3, 0x7F, 900, 3400, 25000, 160), 9162306a36Sopenharmony_ci RC5T583_REG(LDO4, LDOEN2, 4, LDODIS2, 4, 0x3F, 750, 1500, 12500, 133), 9262306a36Sopenharmony_ci RC5T583_REG(LDO5, LDOEN2, 5, LDODIS2, 5, 0x7F, 900, 3400, 25000, 267), 9362306a36Sopenharmony_ci RC5T583_REG(LDO6, LDOEN2, 6, LDODIS2, 6, 0x7F, 900, 3400, 25000, 133), 9462306a36Sopenharmony_ci RC5T583_REG(LDO7, LDOEN2, 7, LDODIS2, 7, 0x7F, 900, 3400, 25000, 233), 9562306a36Sopenharmony_ci RC5T583_REG(LDO8, LDOEN1, 0, LDODIS1, 0, 0x7F, 900, 3400, 25000, 233), 9662306a36Sopenharmony_ci RC5T583_REG(LDO9, LDOEN1, 1, LDODIS1, 1, 0x7F, 900, 3400, 25000, 133), 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int rc5t583_regulator_probe(struct platform_device *pdev) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); 10262306a36Sopenharmony_ci struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); 10362306a36Sopenharmony_ci struct regulator_config config = { }; 10462306a36Sopenharmony_ci struct regulator_dev *rdev; 10562306a36Sopenharmony_ci struct rc5t583_regulator_info *ri; 10662306a36Sopenharmony_ci int ret; 10762306a36Sopenharmony_ci int id; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (!pdata) { 11062306a36Sopenharmony_ci dev_err(&pdev->dev, "No platform data, exiting...\n"); 11162306a36Sopenharmony_ci return -ENODEV; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) { 11562306a36Sopenharmony_ci ri = &rc5t583_reg_info[id]; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (ri->deepsleep_id == RC5T583_DS_NONE) 11862306a36Sopenharmony_ci goto skip_ext_pwr_config; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ret = rc5t583_ext_power_req_config(rc5t583->dev, 12162306a36Sopenharmony_ci ri->deepsleep_id, 12262306a36Sopenharmony_ci pdata->regulator_ext_pwr_control[id], 12362306a36Sopenharmony_ci pdata->regulator_deepsleep_slot[id]); 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * Configuring external control is not a major issue, 12662306a36Sopenharmony_ci * just give warning. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci if (ret < 0) 12962306a36Sopenharmony_ci dev_warn(&pdev->dev, 13062306a36Sopenharmony_ci "Failed to configure ext control %d\n", id); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ciskip_ext_pwr_config: 13362306a36Sopenharmony_ci config.dev = &pdev->dev; 13462306a36Sopenharmony_ci config.init_data = pdata->reg_init_data[id]; 13562306a36Sopenharmony_ci config.driver_data = ri; 13662306a36Sopenharmony_ci config.regmap = rc5t583->regmap; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config); 13962306a36Sopenharmony_ci if (IS_ERR(rdev)) { 14062306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register regulator %s\n", 14162306a36Sopenharmony_ci ri->desc.name); 14262306a36Sopenharmony_ci return PTR_ERR(rdev); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic struct platform_driver rc5t583_regulator_driver = { 14962306a36Sopenharmony_ci .driver = { 15062306a36Sopenharmony_ci .name = "rc5t583-regulator", 15162306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 15262306a36Sopenharmony_ci }, 15362306a36Sopenharmony_ci .probe = rc5t583_regulator_probe, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int __init rc5t583_regulator_init(void) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci return platform_driver_register(&rc5t583_regulator_driver); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_cisubsys_initcall(rc5t583_regulator_init); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void __exit rc5t583_regulator_exit(void) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci platform_driver_unregister(&rc5t583_regulator_driver); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_cimodule_exit(rc5t583_regulator_exit); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciMODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); 16962306a36Sopenharmony_ciMODULE_DESCRIPTION("RC5T583 regulator driver"); 17062306a36Sopenharmony_ciMODULE_ALIAS("platform:rc5t583-regulator"); 17162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 172