162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright (c) 2017, 2019-2020, The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci// Copyright (c) 2023, Linaro Limited 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/bitfield.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/of.h> 862306a36Sopenharmony_ci#include <linux/platform_device.h> 962306a36Sopenharmony_ci#include <linux/regmap.h> 1062306a36Sopenharmony_ci#include <linux/regulator/driver.h> 1162306a36Sopenharmony_ci#include <linux/regulator/machine.h> 1262306a36Sopenharmony_ci#include <linux/regulator/of_regulator.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define REFGEN_REG_BIAS_EN 0x08 1562306a36Sopenharmony_ci#define REFGEN_BIAS_EN_MASK GENMASK(2, 0) 1662306a36Sopenharmony_ci #define REFGEN_BIAS_EN_ENABLE 0x7 1762306a36Sopenharmony_ci #define REFGEN_BIAS_EN_DISABLE 0x6 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define REFGEN_REG_BG_CTRL 0x14 2062306a36Sopenharmony_ci#define REFGEN_BG_CTRL_MASK GENMASK(2, 1) 2162306a36Sopenharmony_ci #define REFGEN_BG_CTRL_ENABLE 0x3 2262306a36Sopenharmony_ci #define REFGEN_BG_CTRL_DISABLE 0x2 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define REFGEN_REG_PWRDWN_CTRL5 0x80 2562306a36Sopenharmony_ci#define REFGEN_PWRDWN_CTRL5_MASK BIT(0) 2662306a36Sopenharmony_ci #define REFGEN_PWRDWN_CTRL5_ENABLE 0x1 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int qcom_sdm845_refgen_enable(struct regulator_dev *rdev) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci regmap_update_bits(rdev->regmap, REFGEN_REG_BG_CTRL, REFGEN_BG_CTRL_MASK, 3162306a36Sopenharmony_ci FIELD_PREP(REFGEN_BG_CTRL_MASK, REFGEN_BG_CTRL_ENABLE)); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci regmap_write(rdev->regmap, REFGEN_REG_BIAS_EN, 3462306a36Sopenharmony_ci FIELD_PREP(REFGEN_BIAS_EN_MASK, REFGEN_BIAS_EN_ENABLE)); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci return 0; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int qcom_sdm845_refgen_disable(struct regulator_dev *rdev) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci regmap_write(rdev->regmap, REFGEN_REG_BIAS_EN, 4262306a36Sopenharmony_ci FIELD_PREP(REFGEN_BIAS_EN_MASK, REFGEN_BIAS_EN_DISABLE)); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci regmap_update_bits(rdev->regmap, REFGEN_REG_BG_CTRL, REFGEN_BG_CTRL_MASK, 4562306a36Sopenharmony_ci FIELD_PREP(REFGEN_BG_CTRL_MASK, REFGEN_BG_CTRL_DISABLE)); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int qcom_sdm845_refgen_is_enabled(struct regulator_dev *rdev) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci u32 val; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci regmap_read(rdev->regmap, REFGEN_REG_BG_CTRL, &val); 5562306a36Sopenharmony_ci if (FIELD_GET(REFGEN_BG_CTRL_MASK, val) != REFGEN_BG_CTRL_ENABLE) 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci regmap_read(rdev->regmap, REFGEN_REG_BIAS_EN, &val); 5962306a36Sopenharmony_ci if (FIELD_GET(REFGEN_BIAS_EN_MASK, val) != REFGEN_BIAS_EN_ENABLE) 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return 1; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic struct regulator_desc sdm845_refgen_desc = { 6662306a36Sopenharmony_ci .enable_time = 5, 6762306a36Sopenharmony_ci .name = "refgen", 6862306a36Sopenharmony_ci .owner = THIS_MODULE, 6962306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 7062306a36Sopenharmony_ci .ops = &(const struct regulator_ops) { 7162306a36Sopenharmony_ci .enable = qcom_sdm845_refgen_enable, 7262306a36Sopenharmony_ci .disable = qcom_sdm845_refgen_disable, 7362306a36Sopenharmony_ci .is_enabled = qcom_sdm845_refgen_is_enabled, 7462306a36Sopenharmony_ci }, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct regulator_desc sm8250_refgen_desc = { 7862306a36Sopenharmony_ci .enable_reg = REFGEN_REG_PWRDWN_CTRL5, 7962306a36Sopenharmony_ci .enable_mask = REFGEN_PWRDWN_CTRL5_MASK, 8062306a36Sopenharmony_ci .enable_val = REFGEN_PWRDWN_CTRL5_ENABLE, 8162306a36Sopenharmony_ci .disable_val = 0, 8262306a36Sopenharmony_ci .enable_time = 5, 8362306a36Sopenharmony_ci .name = "refgen", 8462306a36Sopenharmony_ci .owner = THIS_MODULE, 8562306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, 8662306a36Sopenharmony_ci .ops = &(const struct regulator_ops) { 8762306a36Sopenharmony_ci .enable = regulator_enable_regmap, 8862306a36Sopenharmony_ci .disable = regulator_disable_regmap, 8962306a36Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 9062306a36Sopenharmony_ci }, 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic const struct regmap_config qcom_refgen_regmap_config = { 9462306a36Sopenharmony_ci .reg_bits = 32, 9562306a36Sopenharmony_ci .reg_stride = 4, 9662306a36Sopenharmony_ci .val_bits = 32, 9762306a36Sopenharmony_ci .fast_io = true, 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int qcom_refgen_probe(struct platform_device *pdev) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct regulator_init_data *init_data; 10362306a36Sopenharmony_ci struct regulator_config config = {}; 10462306a36Sopenharmony_ci const struct regulator_desc *rdesc; 10562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 10662306a36Sopenharmony_ci struct regulator_dev *rdev; 10762306a36Sopenharmony_ci struct regmap *regmap; 10862306a36Sopenharmony_ci void __iomem *base; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci rdesc = of_device_get_match_data(dev); 11162306a36Sopenharmony_ci if (!rdesc) 11262306a36Sopenharmony_ci return -ENODATA; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 11562306a36Sopenharmony_ci if (IS_ERR(base)) 11662306a36Sopenharmony_ci return PTR_ERR(base); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci regmap = devm_regmap_init_mmio(dev, base, &qcom_refgen_regmap_config); 11962306a36Sopenharmony_ci if (IS_ERR(regmap)) 12062306a36Sopenharmony_ci return PTR_ERR(regmap); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci init_data = of_get_regulator_init_data(dev, dev->of_node, rdesc); 12362306a36Sopenharmony_ci if (!init_data) 12462306a36Sopenharmony_ci return -ENOMEM; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci config.dev = dev; 12762306a36Sopenharmony_ci config.init_data = init_data; 12862306a36Sopenharmony_ci config.of_node = dev->of_node; 12962306a36Sopenharmony_ci config.regmap = regmap; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci rdev = devm_regulator_register(dev, rdesc, &config); 13262306a36Sopenharmony_ci if (IS_ERR(rdev)) 13362306a36Sopenharmony_ci return PTR_ERR(rdev); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic const struct of_device_id qcom_refgen_match_table[] = { 13962306a36Sopenharmony_ci { .compatible = "qcom,sdm845-refgen-regulator", .data = &sdm845_refgen_desc }, 14062306a36Sopenharmony_ci { .compatible = "qcom,sm8250-refgen-regulator", .data = &sm8250_refgen_desc }, 14162306a36Sopenharmony_ci { } 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct platform_driver qcom_refgen_driver = { 14562306a36Sopenharmony_ci .probe = qcom_refgen_probe, 14662306a36Sopenharmony_ci .driver = { 14762306a36Sopenharmony_ci .name = "qcom-refgen-regulator", 14862306a36Sopenharmony_ci .of_match_table = qcom_refgen_match_table, 14962306a36Sopenharmony_ci }, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_cimodule_platform_driver(qcom_refgen_driver); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 15462306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm REFGEN regulator driver"); 155