162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Copyright (c) 2021 MediaTek Inc.
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/of.h>
762306a36Sopenharmony_ci#include <linux/regmap.h>
862306a36Sopenharmony_ci#include <linux/regulator/driver.h>
962306a36Sopenharmony_ci#include <linux/regulator/machine.h>
1062306a36Sopenharmony_ci#include <linux/regulator/mt6315-regulator.h>
1162306a36Sopenharmony_ci#include <linux/regulator/of_regulator.h>
1262306a36Sopenharmony_ci#include <linux/spmi.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define MT6315_BUCK_MODE_AUTO		0
1562306a36Sopenharmony_ci#define MT6315_BUCK_MODE_FORCE_PWM	1
1662306a36Sopenharmony_ci#define MT6315_BUCK_MODE_LP		2
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct mt6315_regulator_info {
1962306a36Sopenharmony_ci	struct regulator_desc desc;
2062306a36Sopenharmony_ci	u32 status_reg;
2162306a36Sopenharmony_ci	u32 lp_mode_mask;
2262306a36Sopenharmony_ci	u32 lp_mode_shift;
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct mt_regulator_init_data {
2662306a36Sopenharmony_ci	u32 modeset_mask[MT6315_VBUCK_MAX];
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct mt6315_chip {
3062306a36Sopenharmony_ci	struct device *dev;
3162306a36Sopenharmony_ci	struct regmap *regmap;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define MT_BUCK(_name, _bid, _vsel)				\
3562306a36Sopenharmony_ci[_bid] = {							\
3662306a36Sopenharmony_ci	.desc = {						\
3762306a36Sopenharmony_ci		.name = _name,					\
3862306a36Sopenharmony_ci		.of_match = of_match_ptr(_name),		\
3962306a36Sopenharmony_ci		.regulators_node = "regulators",		\
4062306a36Sopenharmony_ci		.ops = &mt6315_volt_range_ops,			\
4162306a36Sopenharmony_ci		.type = REGULATOR_VOLTAGE,			\
4262306a36Sopenharmony_ci		.id = _bid,					\
4362306a36Sopenharmony_ci		.owner = THIS_MODULE,				\
4462306a36Sopenharmony_ci		.n_voltages = 0xc0,				\
4562306a36Sopenharmony_ci		.linear_ranges = mt_volt_range1,		\
4662306a36Sopenharmony_ci		.n_linear_ranges = ARRAY_SIZE(mt_volt_range1),	\
4762306a36Sopenharmony_ci		.vsel_reg = _vsel,				\
4862306a36Sopenharmony_ci		.vsel_mask = 0xff,				\
4962306a36Sopenharmony_ci		.enable_reg = MT6315_BUCK_TOP_CON0,		\
5062306a36Sopenharmony_ci		.enable_mask = BIT(_bid),			\
5162306a36Sopenharmony_ci		.of_map_mode = mt6315_map_mode,			\
5262306a36Sopenharmony_ci	},							\
5362306a36Sopenharmony_ci	.status_reg = _bid##_DBG4,				\
5462306a36Sopenharmony_ci	.lp_mode_mask = BIT(_bid),				\
5562306a36Sopenharmony_ci	.lp_mode_shift = _bid,					\
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic const struct linear_range mt_volt_range1[] = {
5962306a36Sopenharmony_ci	REGULATOR_LINEAR_RANGE(0, 0, 0xbf, 6250),
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic unsigned int mt6315_map_mode(unsigned int mode)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	switch (mode) {
6562306a36Sopenharmony_ci	case MT6315_BUCK_MODE_AUTO:
6662306a36Sopenharmony_ci		return REGULATOR_MODE_NORMAL;
6762306a36Sopenharmony_ci	case MT6315_BUCK_MODE_FORCE_PWM:
6862306a36Sopenharmony_ci		return REGULATOR_MODE_FAST;
6962306a36Sopenharmony_ci	case MT6315_BUCK_MODE_LP:
7062306a36Sopenharmony_ci		return REGULATOR_MODE_IDLE;
7162306a36Sopenharmony_ci	default:
7262306a36Sopenharmony_ci		return REGULATOR_MODE_INVALID;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic unsigned int mt6315_regulator_get_mode(struct regulator_dev *rdev)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct mt_regulator_init_data *init = rdev_get_drvdata(rdev);
7962306a36Sopenharmony_ci	const struct mt6315_regulator_info *info;
8062306a36Sopenharmony_ci	int ret, regval;
8162306a36Sopenharmony_ci	u32 modeset_mask;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	info = container_of(rdev->desc, struct mt6315_regulator_info, desc);
8462306a36Sopenharmony_ci	modeset_mask = init->modeset_mask[rdev_get_id(rdev)];
8562306a36Sopenharmony_ci	ret = regmap_read(rdev->regmap, MT6315_BUCK_TOP_4PHASE_ANA_CON42, &regval);
8662306a36Sopenharmony_ci	if (ret != 0) {
8762306a36Sopenharmony_ci		dev_err(&rdev->dev, "Failed to get mode: %d\n", ret);
8862306a36Sopenharmony_ci		return ret;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if ((regval & modeset_mask) == modeset_mask)
9262306a36Sopenharmony_ci		return REGULATOR_MODE_FAST;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	ret = regmap_read(rdev->regmap, MT6315_BUCK_TOP_CON1, &regval);
9562306a36Sopenharmony_ci	if (ret != 0) {
9662306a36Sopenharmony_ci		dev_err(&rdev->dev, "Failed to get lp mode: %d\n", ret);
9762306a36Sopenharmony_ci		return ret;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (regval & info->lp_mode_mask)
10162306a36Sopenharmony_ci		return REGULATOR_MODE_IDLE;
10262306a36Sopenharmony_ci	else
10362306a36Sopenharmony_ci		return REGULATOR_MODE_NORMAL;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int mt6315_regulator_set_mode(struct regulator_dev *rdev,
10762306a36Sopenharmony_ci				     u32 mode)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct mt_regulator_init_data *init = rdev_get_drvdata(rdev);
11062306a36Sopenharmony_ci	const struct mt6315_regulator_info *info;
11162306a36Sopenharmony_ci	int ret, val, curr_mode;
11262306a36Sopenharmony_ci	u32 modeset_mask;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	info = container_of(rdev->desc, struct mt6315_regulator_info, desc);
11562306a36Sopenharmony_ci	modeset_mask = init->modeset_mask[rdev_get_id(rdev)];
11662306a36Sopenharmony_ci	curr_mode = mt6315_regulator_get_mode(rdev);
11762306a36Sopenharmony_ci	switch (mode) {
11862306a36Sopenharmony_ci	case REGULATOR_MODE_FAST:
11962306a36Sopenharmony_ci		ret = regmap_update_bits(rdev->regmap,
12062306a36Sopenharmony_ci					 MT6315_BUCK_TOP_4PHASE_ANA_CON42,
12162306a36Sopenharmony_ci					 modeset_mask,
12262306a36Sopenharmony_ci					 modeset_mask);
12362306a36Sopenharmony_ci		break;
12462306a36Sopenharmony_ci	case REGULATOR_MODE_NORMAL:
12562306a36Sopenharmony_ci		if (curr_mode == REGULATOR_MODE_FAST) {
12662306a36Sopenharmony_ci			ret = regmap_update_bits(rdev->regmap,
12762306a36Sopenharmony_ci						 MT6315_BUCK_TOP_4PHASE_ANA_CON42,
12862306a36Sopenharmony_ci						 modeset_mask,
12962306a36Sopenharmony_ci						 0);
13062306a36Sopenharmony_ci		} else if (curr_mode == REGULATOR_MODE_IDLE) {
13162306a36Sopenharmony_ci			ret = regmap_update_bits(rdev->regmap,
13262306a36Sopenharmony_ci						 MT6315_BUCK_TOP_CON1,
13362306a36Sopenharmony_ci						 info->lp_mode_mask,
13462306a36Sopenharmony_ci						 0);
13562306a36Sopenharmony_ci			usleep_range(100, 110);
13662306a36Sopenharmony_ci		} else {
13762306a36Sopenharmony_ci			ret = -EINVAL;
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci		break;
14062306a36Sopenharmony_ci	case REGULATOR_MODE_IDLE:
14162306a36Sopenharmony_ci		val = MT6315_BUCK_MODE_LP >> 1;
14262306a36Sopenharmony_ci		val <<= info->lp_mode_shift;
14362306a36Sopenharmony_ci		ret = regmap_update_bits(rdev->regmap,
14462306a36Sopenharmony_ci					 MT6315_BUCK_TOP_CON1,
14562306a36Sopenharmony_ci					 info->lp_mode_mask,
14662306a36Sopenharmony_ci					 val);
14762306a36Sopenharmony_ci		break;
14862306a36Sopenharmony_ci	default:
14962306a36Sopenharmony_ci		ret = -EINVAL;
15062306a36Sopenharmony_ci		dev_err(&rdev->dev, "Unsupported mode: %d\n", mode);
15162306a36Sopenharmony_ci		break;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (ret != 0) {
15562306a36Sopenharmony_ci		dev_err(&rdev->dev, "Failed to set mode: %d\n", ret);
15662306a36Sopenharmony_ci		return ret;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic int mt6315_get_status(struct regulator_dev *rdev)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	const struct mt6315_regulator_info *info;
16562306a36Sopenharmony_ci	int ret;
16662306a36Sopenharmony_ci	u32 regval;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	info = container_of(rdev->desc, struct mt6315_regulator_info, desc);
16962306a36Sopenharmony_ci	ret = regmap_read(rdev->regmap, info->status_reg, &regval);
17062306a36Sopenharmony_ci	if (ret < 0) {
17162306a36Sopenharmony_ci		dev_err(&rdev->dev, "Failed to get enable reg: %d\n", ret);
17262306a36Sopenharmony_ci		return ret;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return (regval & BIT(0)) ? REGULATOR_STATUS_ON : REGULATOR_STATUS_OFF;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic const struct regulator_ops mt6315_volt_range_ops = {
17962306a36Sopenharmony_ci	.list_voltage = regulator_list_voltage_linear_range,
18062306a36Sopenharmony_ci	.map_voltage = regulator_map_voltage_linear_range,
18162306a36Sopenharmony_ci	.set_voltage_sel = regulator_set_voltage_sel_regmap,
18262306a36Sopenharmony_ci	.get_voltage_sel = regulator_get_voltage_sel_regmap,
18362306a36Sopenharmony_ci	.set_voltage_time_sel = regulator_set_voltage_time_sel,
18462306a36Sopenharmony_ci	.enable = regulator_enable_regmap,
18562306a36Sopenharmony_ci	.disable = regulator_disable_regmap,
18662306a36Sopenharmony_ci	.is_enabled = regulator_is_enabled_regmap,
18762306a36Sopenharmony_ci	.get_status = mt6315_get_status,
18862306a36Sopenharmony_ci	.set_mode = mt6315_regulator_set_mode,
18962306a36Sopenharmony_ci	.get_mode = mt6315_regulator_get_mode,
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic const struct mt6315_regulator_info mt6315_regulators[MT6315_VBUCK_MAX] = {
19362306a36Sopenharmony_ci	MT_BUCK("vbuck1", MT6315_VBUCK1, MT6315_BUCK_TOP_ELR0),
19462306a36Sopenharmony_ci	MT_BUCK("vbuck2", MT6315_VBUCK2, MT6315_BUCK_TOP_ELR2),
19562306a36Sopenharmony_ci	MT_BUCK("vbuck3", MT6315_VBUCK3, MT6315_BUCK_TOP_ELR4),
19662306a36Sopenharmony_ci	MT_BUCK("vbuck4", MT6315_VBUCK4, MT6315_BUCK_TOP_ELR6),
19762306a36Sopenharmony_ci};
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic const struct regmap_config mt6315_regmap_config = {
20062306a36Sopenharmony_ci	.reg_bits	= 16,
20162306a36Sopenharmony_ci	.val_bits	= 8,
20262306a36Sopenharmony_ci	.max_register	= 0x16d0,
20362306a36Sopenharmony_ci	.fast_io	= true,
20462306a36Sopenharmony_ci};
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic const struct of_device_id mt6315_of_match[] = {
20762306a36Sopenharmony_ci	{
20862306a36Sopenharmony_ci		.compatible = "mediatek,mt6315-regulator",
20962306a36Sopenharmony_ci	}, {
21062306a36Sopenharmony_ci		/* sentinel */
21162306a36Sopenharmony_ci	},
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mt6315_of_match);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int mt6315_regulator_probe(struct spmi_device *pdev)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
21862306a36Sopenharmony_ci	struct regmap *regmap;
21962306a36Sopenharmony_ci	struct mt6315_chip *chip;
22062306a36Sopenharmony_ci	struct mt_regulator_init_data *init_data;
22162306a36Sopenharmony_ci	struct regulator_config config = {};
22262306a36Sopenharmony_ci	struct regulator_dev *rdev;
22362306a36Sopenharmony_ci	int i;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	regmap = devm_regmap_init_spmi_ext(pdev, &mt6315_regmap_config);
22662306a36Sopenharmony_ci	if (IS_ERR(regmap))
22762306a36Sopenharmony_ci		return PTR_ERR(regmap);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	chip = devm_kzalloc(dev, sizeof(struct mt6315_chip), GFP_KERNEL);
23062306a36Sopenharmony_ci	if (!chip)
23162306a36Sopenharmony_ci		return -ENOMEM;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	init_data = devm_kzalloc(dev, sizeof(struct mt_regulator_init_data), GFP_KERNEL);
23462306a36Sopenharmony_ci	if (!init_data)
23562306a36Sopenharmony_ci		return -ENOMEM;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	switch (pdev->usid) {
23862306a36Sopenharmony_ci	case MT6315_PP:
23962306a36Sopenharmony_ci		init_data->modeset_mask[MT6315_VBUCK1] = BIT(MT6315_VBUCK1) | BIT(MT6315_VBUCK2) |
24062306a36Sopenharmony_ci							 BIT(MT6315_VBUCK4);
24162306a36Sopenharmony_ci		break;
24262306a36Sopenharmony_ci	case MT6315_SP:
24362306a36Sopenharmony_ci	case MT6315_RP:
24462306a36Sopenharmony_ci		init_data->modeset_mask[MT6315_VBUCK1] = BIT(MT6315_VBUCK1) | BIT(MT6315_VBUCK2);
24562306a36Sopenharmony_ci		break;
24662306a36Sopenharmony_ci	default:
24762306a36Sopenharmony_ci		init_data->modeset_mask[MT6315_VBUCK1] = BIT(MT6315_VBUCK1);
24862306a36Sopenharmony_ci		break;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	for (i = MT6315_VBUCK2; i < MT6315_VBUCK_MAX; i++)
25162306a36Sopenharmony_ci		init_data->modeset_mask[i] = BIT(i);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	chip->dev = dev;
25462306a36Sopenharmony_ci	chip->regmap = regmap;
25562306a36Sopenharmony_ci	dev_set_drvdata(dev, chip);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	config.dev = dev;
25862306a36Sopenharmony_ci	config.regmap = regmap;
25962306a36Sopenharmony_ci	for (i = MT6315_VBUCK1; i < MT6315_VBUCK_MAX; i++) {
26062306a36Sopenharmony_ci		config.driver_data = init_data;
26162306a36Sopenharmony_ci		rdev = devm_regulator_register(dev, &mt6315_regulators[i].desc, &config);
26262306a36Sopenharmony_ci		if (IS_ERR(rdev)) {
26362306a36Sopenharmony_ci			dev_err(dev, "Failed to register %s\n",
26462306a36Sopenharmony_ci				mt6315_regulators[i].desc.name);
26562306a36Sopenharmony_ci			return PTR_ERR(rdev);
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void mt6315_regulator_shutdown(struct spmi_device *pdev)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct mt6315_chip *chip = dev_get_drvdata(&pdev->dev);
27562306a36Sopenharmony_ci	int ret = 0;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY_H, PROTECTION_KEY_H);
27862306a36Sopenharmony_ci	ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY, PROTECTION_KEY);
27962306a36Sopenharmony_ci	ret |= regmap_update_bits(chip->regmap, MT6315_TOP2_ELR7, 1, 1);
28062306a36Sopenharmony_ci	ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY, 0);
28162306a36Sopenharmony_ci	ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY_H, 0);
28262306a36Sopenharmony_ci	if (ret < 0)
28362306a36Sopenharmony_ci		dev_err(&pdev->dev, "[%#x] Failed to enable power off sequence. %d\n",
28462306a36Sopenharmony_ci			   pdev->usid, ret);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic struct spmi_driver mt6315_regulator_driver = {
28862306a36Sopenharmony_ci	.driver		= {
28962306a36Sopenharmony_ci		.name	= "mt6315-regulator",
29062306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
29162306a36Sopenharmony_ci		.of_match_table = mt6315_of_match,
29262306a36Sopenharmony_ci	},
29362306a36Sopenharmony_ci	.probe = mt6315_regulator_probe,
29462306a36Sopenharmony_ci	.shutdown = mt6315_regulator_shutdown,
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cimodule_spmi_driver(mt6315_regulator_driver);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ciMODULE_AUTHOR("Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>");
30062306a36Sopenharmony_ciMODULE_DESCRIPTION("Regulator Driver for MediaTek MT6315 PMIC");
30162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
302