18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright (c) 2019 Mantas Pucka <mantas@8devices.com>
48c2ecf20Sopenharmony_ci// Copyright (c) 2019 Robert Marko <robert.marko@sartura.hr>
58c2ecf20Sopenharmony_ci//
68c2ecf20Sopenharmony_ci// Driver for IPQ4019 SD/MMC controller's I/O LDO voltage regulator
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/of.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/regmap.h>
138c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h>
148c2ecf20Sopenharmony_ci#include <linux/regulator/machine.h>
158c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic const unsigned int ipq4019_vmmc_voltages[] = {
188c2ecf20Sopenharmony_ci	1500000, 1800000, 2500000, 3000000,
198c2ecf20Sopenharmony_ci};
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic const struct regulator_ops ipq4019_regulator_voltage_ops = {
228c2ecf20Sopenharmony_ci	.list_voltage = regulator_list_voltage_table,
238c2ecf20Sopenharmony_ci	.map_voltage = regulator_map_voltage_ascend,
248c2ecf20Sopenharmony_ci	.get_voltage_sel = regulator_get_voltage_sel_regmap,
258c2ecf20Sopenharmony_ci	.set_voltage_sel = regulator_set_voltage_sel_regmap,
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic const struct regulator_desc vmmc_regulator = {
298c2ecf20Sopenharmony_ci	.name		= "vmmcq",
308c2ecf20Sopenharmony_ci	.ops		= &ipq4019_regulator_voltage_ops,
318c2ecf20Sopenharmony_ci	.type		= REGULATOR_VOLTAGE,
328c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
338c2ecf20Sopenharmony_ci	.volt_table	= ipq4019_vmmc_voltages,
348c2ecf20Sopenharmony_ci	.n_voltages	= ARRAY_SIZE(ipq4019_vmmc_voltages),
358c2ecf20Sopenharmony_ci	.vsel_reg	= 0,
368c2ecf20Sopenharmony_ci	.vsel_mask	= 0x3,
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic const struct regmap_config ipq4019_vmmcq_regmap_config = {
408c2ecf20Sopenharmony_ci	.reg_bits	= 32,
418c2ecf20Sopenharmony_ci	.reg_stride	= 4,
428c2ecf20Sopenharmony_ci	.val_bits	= 32,
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int ipq4019_regulator_probe(struct platform_device *pdev)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
488c2ecf20Sopenharmony_ci	struct regulator_init_data *init_data;
498c2ecf20Sopenharmony_ci	struct regulator_config cfg = {};
508c2ecf20Sopenharmony_ci	struct regulator_dev *rdev;
518c2ecf20Sopenharmony_ci	struct resource *res;
528c2ecf20Sopenharmony_ci	struct regmap *rmap;
538c2ecf20Sopenharmony_ci	void __iomem *base;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	init_data = of_get_regulator_init_data(dev, dev->of_node,
568c2ecf20Sopenharmony_ci					       &vmmc_regulator);
578c2ecf20Sopenharmony_ci	if (!init_data)
588c2ecf20Sopenharmony_ci		return -EINVAL;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
618c2ecf20Sopenharmony_ci	base = devm_ioremap_resource(dev, res);
628c2ecf20Sopenharmony_ci	if (IS_ERR(base))
638c2ecf20Sopenharmony_ci		return PTR_ERR(base);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	rmap = devm_regmap_init_mmio(dev, base, &ipq4019_vmmcq_regmap_config);
668c2ecf20Sopenharmony_ci	if (IS_ERR(rmap))
678c2ecf20Sopenharmony_ci		return PTR_ERR(rmap);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	cfg.dev = dev;
708c2ecf20Sopenharmony_ci	cfg.init_data = init_data;
718c2ecf20Sopenharmony_ci	cfg.of_node = dev->of_node;
728c2ecf20Sopenharmony_ci	cfg.regmap = rmap;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	rdev = devm_regulator_register(dev, &vmmc_regulator, &cfg);
758c2ecf20Sopenharmony_ci	if (IS_ERR(rdev)) {
768c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to register regulator: %ld\n",
778c2ecf20Sopenharmony_ci			PTR_ERR(rdev));
788c2ecf20Sopenharmony_ci		return PTR_ERR(rdev);
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, rdev);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic const struct of_device_id regulator_ipq4019_of_match[] = {
868c2ecf20Sopenharmony_ci	{ .compatible = "qcom,vqmmc-ipq4019-regulator", },
878c2ecf20Sopenharmony_ci	{},
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic struct platform_driver ipq4019_regulator_driver = {
918c2ecf20Sopenharmony_ci	.probe = ipq4019_regulator_probe,
928c2ecf20Sopenharmony_ci	.driver = {
938c2ecf20Sopenharmony_ci		.name = "vqmmc-ipq4019-regulator",
948c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(regulator_ipq4019_of_match),
958c2ecf20Sopenharmony_ci	},
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_cimodule_platform_driver(ipq4019_regulator_driver);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mantas Pucka <mantas@8devices.com>");
1018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IPQ4019 VQMMC voltage regulator");
102