162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Copyright (c) 2019 Mantas Pucka <mantas@8devices.com>
462306a36Sopenharmony_ci// Copyright (c) 2019 Robert Marko <robert.marko@sartura.hr>
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Driver for IPQ4019 SD/MMC controller's I/O LDO voltage regulator
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/regmap.h>
1362306a36Sopenharmony_ci#include <linux/regulator/driver.h>
1462306a36Sopenharmony_ci#include <linux/regulator/machine.h>
1562306a36Sopenharmony_ci#include <linux/regulator/of_regulator.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic const unsigned int ipq4019_vmmc_voltages[] = {
1862306a36Sopenharmony_ci	1500000, 1800000, 2500000, 3000000,
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic const struct regulator_ops ipq4019_regulator_voltage_ops = {
2262306a36Sopenharmony_ci	.list_voltage = regulator_list_voltage_table,
2362306a36Sopenharmony_ci	.map_voltage = regulator_map_voltage_ascend,
2462306a36Sopenharmony_ci	.get_voltage_sel = regulator_get_voltage_sel_regmap,
2562306a36Sopenharmony_ci	.set_voltage_sel = regulator_set_voltage_sel_regmap,
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic const struct regulator_desc vmmc_regulator = {
2962306a36Sopenharmony_ci	.name		= "vmmcq",
3062306a36Sopenharmony_ci	.ops		= &ipq4019_regulator_voltage_ops,
3162306a36Sopenharmony_ci	.type		= REGULATOR_VOLTAGE,
3262306a36Sopenharmony_ci	.owner		= THIS_MODULE,
3362306a36Sopenharmony_ci	.volt_table	= ipq4019_vmmc_voltages,
3462306a36Sopenharmony_ci	.n_voltages	= ARRAY_SIZE(ipq4019_vmmc_voltages),
3562306a36Sopenharmony_ci	.vsel_reg	= 0,
3662306a36Sopenharmony_ci	.vsel_mask	= 0x3,
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const struct regmap_config ipq4019_vmmcq_regmap_config = {
4062306a36Sopenharmony_ci	.reg_bits	= 32,
4162306a36Sopenharmony_ci	.reg_stride	= 4,
4262306a36Sopenharmony_ci	.val_bits	= 32,
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int ipq4019_regulator_probe(struct platform_device *pdev)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
4862306a36Sopenharmony_ci	struct regulator_init_data *init_data;
4962306a36Sopenharmony_ci	struct regulator_config cfg = {};
5062306a36Sopenharmony_ci	struct regulator_dev *rdev;
5162306a36Sopenharmony_ci	struct regmap *rmap;
5262306a36Sopenharmony_ci	void __iomem *base;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	init_data = of_get_regulator_init_data(dev, dev->of_node,
5562306a36Sopenharmony_ci					       &vmmc_regulator);
5662306a36Sopenharmony_ci	if (!init_data)
5762306a36Sopenharmony_ci		return -EINVAL;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
6062306a36Sopenharmony_ci	if (IS_ERR(base))
6162306a36Sopenharmony_ci		return PTR_ERR(base);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	rmap = devm_regmap_init_mmio(dev, base, &ipq4019_vmmcq_regmap_config);
6462306a36Sopenharmony_ci	if (IS_ERR(rmap))
6562306a36Sopenharmony_ci		return PTR_ERR(rmap);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	cfg.dev = dev;
6862306a36Sopenharmony_ci	cfg.init_data = init_data;
6962306a36Sopenharmony_ci	cfg.of_node = dev->of_node;
7062306a36Sopenharmony_ci	cfg.regmap = rmap;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	rdev = devm_regulator_register(dev, &vmmc_regulator, &cfg);
7362306a36Sopenharmony_ci	if (IS_ERR(rdev)) {
7462306a36Sopenharmony_ci		dev_err(dev, "Failed to register regulator: %ld\n",
7562306a36Sopenharmony_ci			PTR_ERR(rdev));
7662306a36Sopenharmony_ci		return PTR_ERR(rdev);
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci	platform_set_drvdata(pdev, rdev);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic const struct of_device_id regulator_ipq4019_of_match[] = {
8462306a36Sopenharmony_ci	{ .compatible = "qcom,vqmmc-ipq4019-regulator", },
8562306a36Sopenharmony_ci	{},
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic struct platform_driver ipq4019_regulator_driver = {
8962306a36Sopenharmony_ci	.probe = ipq4019_regulator_probe,
9062306a36Sopenharmony_ci	.driver = {
9162306a36Sopenharmony_ci		.name = "vqmmc-ipq4019-regulator",
9262306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
9362306a36Sopenharmony_ci		.of_match_table = of_match_ptr(regulator_ipq4019_of_match),
9462306a36Sopenharmony_ci	},
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_cimodule_platform_driver(ipq4019_regulator_driver);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
9962306a36Sopenharmony_ciMODULE_AUTHOR("Mantas Pucka <mantas@8devices.com>");
10062306a36Sopenharmony_ciMODULE_DESCRIPTION("IPQ4019 VQMMC voltage regulator");
101