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