162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/regulator/aat2870-regulator.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2011, NVIDIA Corporation. 662306a36Sopenharmony_ci * Author: Jin Park <jinyoungp@nvidia.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/regulator/driver.h> 1662306a36Sopenharmony_ci#include <linux/regulator/machine.h> 1762306a36Sopenharmony_ci#include <linux/mfd/aat2870.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct aat2870_regulator { 2062306a36Sopenharmony_ci struct aat2870_data *aat2870; 2162306a36Sopenharmony_ci struct regulator_desc desc; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci u8 enable_addr; 2462306a36Sopenharmony_ci u8 enable_shift; 2562306a36Sopenharmony_ci u8 enable_mask; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci u8 voltage_addr; 2862306a36Sopenharmony_ci u8 voltage_shift; 2962306a36Sopenharmony_ci u8 voltage_mask; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int aat2870_ldo_set_voltage_sel(struct regulator_dev *rdev, 3362306a36Sopenharmony_ci unsigned selector) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct aat2870_regulator *ri = rdev_get_drvdata(rdev); 3662306a36Sopenharmony_ci struct aat2870_data *aat2870 = ri->aat2870; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return aat2870->update(aat2870, ri->voltage_addr, ri->voltage_mask, 3962306a36Sopenharmony_ci selector << ri->voltage_shift); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int aat2870_ldo_get_voltage_sel(struct regulator_dev *rdev) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct aat2870_regulator *ri = rdev_get_drvdata(rdev); 4562306a36Sopenharmony_ci struct aat2870_data *aat2870 = ri->aat2870; 4662306a36Sopenharmony_ci u8 val; 4762306a36Sopenharmony_ci int ret; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci ret = aat2870->read(aat2870, ri->voltage_addr, &val); 5062306a36Sopenharmony_ci if (ret) 5162306a36Sopenharmony_ci return ret; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return (val & ri->voltage_mask) >> ri->voltage_shift; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int aat2870_ldo_enable(struct regulator_dev *rdev) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct aat2870_regulator *ri = rdev_get_drvdata(rdev); 5962306a36Sopenharmony_ci struct aat2870_data *aat2870 = ri->aat2870; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, 6262306a36Sopenharmony_ci ri->enable_mask); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int aat2870_ldo_disable(struct regulator_dev *rdev) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct aat2870_regulator *ri = rdev_get_drvdata(rdev); 6862306a36Sopenharmony_ci struct aat2870_data *aat2870 = ri->aat2870; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, 0); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int aat2870_ldo_is_enabled(struct regulator_dev *rdev) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct aat2870_regulator *ri = rdev_get_drvdata(rdev); 7662306a36Sopenharmony_ci struct aat2870_data *aat2870 = ri->aat2870; 7762306a36Sopenharmony_ci u8 val; 7862306a36Sopenharmony_ci int ret; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci ret = aat2870->read(aat2870, ri->enable_addr, &val); 8162306a36Sopenharmony_ci if (ret) 8262306a36Sopenharmony_ci return ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return val & ri->enable_mask ? 1 : 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic const struct regulator_ops aat2870_ldo_ops = { 8862306a36Sopenharmony_ci .list_voltage = regulator_list_voltage_table, 8962306a36Sopenharmony_ci .map_voltage = regulator_map_voltage_ascend, 9062306a36Sopenharmony_ci .set_voltage_sel = aat2870_ldo_set_voltage_sel, 9162306a36Sopenharmony_ci .get_voltage_sel = aat2870_ldo_get_voltage_sel, 9262306a36Sopenharmony_ci .enable = aat2870_ldo_enable, 9362306a36Sopenharmony_ci .disable = aat2870_ldo_disable, 9462306a36Sopenharmony_ci .is_enabled = aat2870_ldo_is_enabled, 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic const unsigned int aat2870_ldo_voltages[] = { 9862306a36Sopenharmony_ci 1200000, 1300000, 1500000, 1600000, 9962306a36Sopenharmony_ci 1800000, 2000000, 2200000, 2500000, 10062306a36Sopenharmony_ci 2600000, 2700000, 2800000, 2900000, 10162306a36Sopenharmony_ci 3000000, 3100000, 3200000, 3300000, 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define AAT2870_LDO(ids) \ 10562306a36Sopenharmony_ci { \ 10662306a36Sopenharmony_ci .desc = { \ 10762306a36Sopenharmony_ci .name = #ids, \ 10862306a36Sopenharmony_ci .id = AAT2870_ID_##ids, \ 10962306a36Sopenharmony_ci .n_voltages = ARRAY_SIZE(aat2870_ldo_voltages), \ 11062306a36Sopenharmony_ci .volt_table = aat2870_ldo_voltages, \ 11162306a36Sopenharmony_ci .ops = &aat2870_ldo_ops, \ 11262306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, \ 11362306a36Sopenharmony_ci .owner = THIS_MODULE, \ 11462306a36Sopenharmony_ci }, \ 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic struct aat2870_regulator aat2870_regulators[] = { 11862306a36Sopenharmony_ci AAT2870_LDO(LDOA), 11962306a36Sopenharmony_ci AAT2870_LDO(LDOB), 12062306a36Sopenharmony_ci AAT2870_LDO(LDOC), 12162306a36Sopenharmony_ci AAT2870_LDO(LDOD), 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic struct aat2870_regulator *aat2870_get_regulator(int id) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct aat2870_regulator *ri = NULL; 12762306a36Sopenharmony_ci int i; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(aat2870_regulators); i++) { 13062306a36Sopenharmony_ci ri = &aat2870_regulators[i]; 13162306a36Sopenharmony_ci if (ri->desc.id == id) 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (i == ARRAY_SIZE(aat2870_regulators)) 13662306a36Sopenharmony_ci return NULL; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ri->enable_addr = AAT2870_LDO_EN; 13962306a36Sopenharmony_ci ri->enable_shift = id - AAT2870_ID_LDOA; 14062306a36Sopenharmony_ci ri->enable_mask = 0x1 << ri->enable_shift; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci ri->voltage_addr = (id - AAT2870_ID_LDOA) / 2 ? 14362306a36Sopenharmony_ci AAT2870_LDO_CD : AAT2870_LDO_AB; 14462306a36Sopenharmony_ci ri->voltage_shift = (id - AAT2870_ID_LDOA) % 2 ? 0 : 4; 14562306a36Sopenharmony_ci ri->voltage_mask = 0xF << ri->voltage_shift; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return ri; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int aat2870_regulator_probe(struct platform_device *pdev) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct aat2870_regulator *ri; 15362306a36Sopenharmony_ci struct regulator_config config = { }; 15462306a36Sopenharmony_ci struct regulator_dev *rdev; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ri = aat2870_get_regulator(pdev->id); 15762306a36Sopenharmony_ci if (!ri) { 15862306a36Sopenharmony_ci dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id); 15962306a36Sopenharmony_ci return -EINVAL; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci ri->aat2870 = dev_get_drvdata(pdev->dev.parent); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci config.dev = &pdev->dev; 16462306a36Sopenharmony_ci config.driver_data = ri; 16562306a36Sopenharmony_ci config.init_data = dev_get_platdata(&pdev->dev); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config); 16862306a36Sopenharmony_ci if (IS_ERR(rdev)) { 16962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register regulator %s\n", 17062306a36Sopenharmony_ci ri->desc.name); 17162306a36Sopenharmony_ci return PTR_ERR(rdev); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci platform_set_drvdata(pdev, rdev); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic struct platform_driver aat2870_regulator_driver = { 17962306a36Sopenharmony_ci .driver = { 18062306a36Sopenharmony_ci .name = "aat2870-regulator", 18162306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 18262306a36Sopenharmony_ci }, 18362306a36Sopenharmony_ci .probe = aat2870_regulator_probe, 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int __init aat2870_regulator_init(void) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci return platform_driver_register(&aat2870_regulator_driver); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_cisubsys_initcall(aat2870_regulator_init); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void __exit aat2870_regulator_exit(void) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci platform_driver_unregister(&aat2870_regulator_driver); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_cimodule_exit(aat2870_regulator_exit); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciMODULE_DESCRIPTION("AnalogicTech AAT2870 Regulator"); 19962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 20062306a36Sopenharmony_ciMODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>"); 20162306a36Sopenharmony_ciMODULE_ALIAS("platform:aat2870-regulator"); 202