18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * pbias-regulator.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com/
58c2ecf20Sopenharmony_ci * Author: Balaji T K <balajitk@ti.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
88c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as
98c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any
128c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty
138c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
148c2ecf20Sopenharmony_ci * GNU General Public License for more details.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/err.h>
188c2ecf20Sopenharmony_ci#include <linux/io.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
218c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
228c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h>
238c2ecf20Sopenharmony_ci#include <linux/regulator/machine.h>
248c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h>
258c2ecf20Sopenharmony_ci#include <linux/regmap.h>
268c2ecf20Sopenharmony_ci#include <linux/slab.h>
278c2ecf20Sopenharmony_ci#include <linux/of.h>
288c2ecf20Sopenharmony_ci#include <linux/of_device.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistruct pbias_reg_info {
318c2ecf20Sopenharmony_ci	u32 enable;
328c2ecf20Sopenharmony_ci	u32 enable_mask;
338c2ecf20Sopenharmony_ci	u32 disable_val;
348c2ecf20Sopenharmony_ci	u32 vmode;
358c2ecf20Sopenharmony_ci	unsigned int enable_time;
368c2ecf20Sopenharmony_ci	char *name;
378c2ecf20Sopenharmony_ci	const unsigned int *pbias_volt_table;
388c2ecf20Sopenharmony_ci	int n_voltages;
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct pbias_of_data {
428c2ecf20Sopenharmony_ci	unsigned int offset;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic const unsigned int pbias_volt_table_3_0V[] = {
468c2ecf20Sopenharmony_ci	1800000,
478c2ecf20Sopenharmony_ci	3000000
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic const unsigned int pbias_volt_table_3_3V[] = {
518c2ecf20Sopenharmony_ci	1800000,
528c2ecf20Sopenharmony_ci	3300000
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic const struct regulator_ops pbias_regulator_voltage_ops = {
568c2ecf20Sopenharmony_ci	.list_voltage = regulator_list_voltage_table,
578c2ecf20Sopenharmony_ci	.get_voltage_sel = regulator_get_voltage_sel_regmap,
588c2ecf20Sopenharmony_ci	.set_voltage_sel = regulator_set_voltage_sel_regmap,
598c2ecf20Sopenharmony_ci	.enable = regulator_enable_regmap,
608c2ecf20Sopenharmony_ci	.disable = regulator_disable_regmap,
618c2ecf20Sopenharmony_ci	.is_enabled = regulator_is_enabled_regmap,
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const struct pbias_reg_info pbias_mmc_omap2430 = {
658c2ecf20Sopenharmony_ci	.enable = BIT(1),
668c2ecf20Sopenharmony_ci	.enable_mask = BIT(1),
678c2ecf20Sopenharmony_ci	.vmode = BIT(0),
688c2ecf20Sopenharmony_ci	.disable_val = 0,
698c2ecf20Sopenharmony_ci	.enable_time = 100,
708c2ecf20Sopenharmony_ci	.pbias_volt_table = pbias_volt_table_3_0V,
718c2ecf20Sopenharmony_ci	.n_voltages = 2,
728c2ecf20Sopenharmony_ci	.name = "pbias_mmc_omap2430"
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic const struct pbias_reg_info pbias_sim_omap3 = {
768c2ecf20Sopenharmony_ci	.enable = BIT(9),
778c2ecf20Sopenharmony_ci	.enable_mask = BIT(9),
788c2ecf20Sopenharmony_ci	.vmode = BIT(8),
798c2ecf20Sopenharmony_ci	.enable_time = 100,
808c2ecf20Sopenharmony_ci	.pbias_volt_table = pbias_volt_table_3_0V,
818c2ecf20Sopenharmony_ci	.n_voltages = 2,
828c2ecf20Sopenharmony_ci	.name = "pbias_sim_omap3"
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic const struct pbias_reg_info pbias_mmc_omap4 = {
868c2ecf20Sopenharmony_ci	.enable = BIT(26) | BIT(22),
878c2ecf20Sopenharmony_ci	.enable_mask = BIT(26) | BIT(25) | BIT(22),
888c2ecf20Sopenharmony_ci	.disable_val = BIT(25),
898c2ecf20Sopenharmony_ci	.vmode = BIT(21),
908c2ecf20Sopenharmony_ci	.enable_time = 100,
918c2ecf20Sopenharmony_ci	.pbias_volt_table = pbias_volt_table_3_0V,
928c2ecf20Sopenharmony_ci	.n_voltages = 2,
938c2ecf20Sopenharmony_ci	.name = "pbias_mmc_omap4"
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic const struct pbias_reg_info pbias_mmc_omap5 = {
978c2ecf20Sopenharmony_ci	.enable = BIT(27) | BIT(26),
988c2ecf20Sopenharmony_ci	.enable_mask = BIT(27) | BIT(25) | BIT(26),
998c2ecf20Sopenharmony_ci	.disable_val = BIT(25),
1008c2ecf20Sopenharmony_ci	.vmode = BIT(21),
1018c2ecf20Sopenharmony_ci	.enable_time = 100,
1028c2ecf20Sopenharmony_ci	.pbias_volt_table = pbias_volt_table_3_3V,
1038c2ecf20Sopenharmony_ci	.n_voltages = 2,
1048c2ecf20Sopenharmony_ci	.name = "pbias_mmc_omap5"
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic struct of_regulator_match pbias_matches[] = {
1088c2ecf20Sopenharmony_ci	{ .name = "pbias_mmc_omap2430", .driver_data = (void *)&pbias_mmc_omap2430},
1098c2ecf20Sopenharmony_ci	{ .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3},
1108c2ecf20Sopenharmony_ci	{ .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4},
1118c2ecf20Sopenharmony_ci	{ .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5},
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci#define PBIAS_NUM_REGS	ARRAY_SIZE(pbias_matches)
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* Offset from SCM general area (and syscon) base */
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic const struct pbias_of_data pbias_of_data_omap2 = {
1188c2ecf20Sopenharmony_ci	.offset = 0x230,
1198c2ecf20Sopenharmony_ci};
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic const struct pbias_of_data pbias_of_data_omap3 = {
1228c2ecf20Sopenharmony_ci	.offset = 0x2b0,
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic const struct pbias_of_data pbias_of_data_omap4 = {
1268c2ecf20Sopenharmony_ci	.offset = 0x60,
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic const struct pbias_of_data pbias_of_data_omap5 = {
1308c2ecf20Sopenharmony_ci	.offset = 0x60,
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic const struct pbias_of_data pbias_of_data_dra7 = {
1348c2ecf20Sopenharmony_ci	.offset = 0xe00,
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic const struct of_device_id pbias_of_match[] = {
1388c2ecf20Sopenharmony_ci	{ .compatible = "ti,pbias-omap", },
1398c2ecf20Sopenharmony_ci	{ .compatible = "ti,pbias-omap2", .data = &pbias_of_data_omap2, },
1408c2ecf20Sopenharmony_ci	{ .compatible = "ti,pbias-omap3", .data = &pbias_of_data_omap3, },
1418c2ecf20Sopenharmony_ci	{ .compatible = "ti,pbias-omap4", .data = &pbias_of_data_omap4, },
1428c2ecf20Sopenharmony_ci	{ .compatible = "ti,pbias-omap5", .data = &pbias_of_data_omap5, },
1438c2ecf20Sopenharmony_ci	{ .compatible = "ti,pbias-dra7", .data = &pbias_of_data_dra7, },
1448c2ecf20Sopenharmony_ci	{},
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pbias_of_match);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int pbias_regulator_probe(struct platform_device *pdev)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
1518c2ecf20Sopenharmony_ci	struct resource *res;
1528c2ecf20Sopenharmony_ci	struct regulator_config cfg = { };
1538c2ecf20Sopenharmony_ci	struct regulator_desc *desc;
1548c2ecf20Sopenharmony_ci	struct regulator_dev *rdev;
1558c2ecf20Sopenharmony_ci	struct regmap *syscon;
1568c2ecf20Sopenharmony_ci	const struct pbias_reg_info *info;
1578c2ecf20Sopenharmony_ci	int ret, count, idx;
1588c2ecf20Sopenharmony_ci	const struct pbias_of_data *data;
1598c2ecf20Sopenharmony_ci	unsigned int offset;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	count = of_regulator_match(&pdev->dev, np, pbias_matches,
1628c2ecf20Sopenharmony_ci						PBIAS_NUM_REGS);
1638c2ecf20Sopenharmony_ci	if (count < 0)
1648c2ecf20Sopenharmony_ci		return count;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	desc = devm_kcalloc(&pdev->dev, count, sizeof(*desc), GFP_KERNEL);
1678c2ecf20Sopenharmony_ci	if (!desc)
1688c2ecf20Sopenharmony_ci		return -ENOMEM;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
1718c2ecf20Sopenharmony_ci	if (IS_ERR(syscon))
1728c2ecf20Sopenharmony_ci		return PTR_ERR(syscon);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	data = of_device_get_match_data(&pdev->dev);
1758c2ecf20Sopenharmony_ci	if (data) {
1768c2ecf20Sopenharmony_ci		offset = data->offset;
1778c2ecf20Sopenharmony_ci	} else {
1788c2ecf20Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1798c2ecf20Sopenharmony_ci		if (!res)
1808c2ecf20Sopenharmony_ci			return -EINVAL;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		offset = res->start;
1838c2ecf20Sopenharmony_ci		dev_WARN(&pdev->dev,
1848c2ecf20Sopenharmony_ci			 "using legacy dt data for pbias offset\n");
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	cfg.regmap = syscon;
1888c2ecf20Sopenharmony_ci	cfg.dev = &pdev->dev;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	for (idx = 0; idx < PBIAS_NUM_REGS && count; idx++) {
1918c2ecf20Sopenharmony_ci		if (!pbias_matches[idx].init_data ||
1928c2ecf20Sopenharmony_ci			!pbias_matches[idx].of_node)
1938c2ecf20Sopenharmony_ci			continue;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		info = pbias_matches[idx].driver_data;
1968c2ecf20Sopenharmony_ci		if (!info)
1978c2ecf20Sopenharmony_ci			return -ENODEV;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		desc->name = info->name;
2008c2ecf20Sopenharmony_ci		desc->owner = THIS_MODULE;
2018c2ecf20Sopenharmony_ci		desc->type = REGULATOR_VOLTAGE;
2028c2ecf20Sopenharmony_ci		desc->ops = &pbias_regulator_voltage_ops;
2038c2ecf20Sopenharmony_ci		desc->volt_table = info->pbias_volt_table;
2048c2ecf20Sopenharmony_ci		desc->n_voltages = info->n_voltages;
2058c2ecf20Sopenharmony_ci		desc->enable_time = info->enable_time;
2068c2ecf20Sopenharmony_ci		desc->vsel_reg = offset;
2078c2ecf20Sopenharmony_ci		desc->vsel_mask = info->vmode;
2088c2ecf20Sopenharmony_ci		desc->enable_reg = offset;
2098c2ecf20Sopenharmony_ci		desc->enable_mask = info->enable_mask;
2108c2ecf20Sopenharmony_ci		desc->enable_val = info->enable;
2118c2ecf20Sopenharmony_ci		desc->disable_val = info->disable_val;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		cfg.init_data = pbias_matches[idx].init_data;
2148c2ecf20Sopenharmony_ci		cfg.of_node = pbias_matches[idx].of_node;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		rdev = devm_regulator_register(&pdev->dev, desc, &cfg);
2178c2ecf20Sopenharmony_ci		if (IS_ERR(rdev)) {
2188c2ecf20Sopenharmony_ci			ret = PTR_ERR(rdev);
2198c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
2208c2ecf20Sopenharmony_ci				"Failed to register regulator: %d\n", ret);
2218c2ecf20Sopenharmony_ci			return ret;
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci		desc++;
2248c2ecf20Sopenharmony_ci		count--;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic struct platform_driver pbias_regulator_driver = {
2318c2ecf20Sopenharmony_ci	.probe		= pbias_regulator_probe,
2328c2ecf20Sopenharmony_ci	.driver		= {
2338c2ecf20Sopenharmony_ci		.name		= "pbias-regulator",
2348c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(pbias_of_match),
2358c2ecf20Sopenharmony_ci	},
2368c2ecf20Sopenharmony_ci};
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cimodule_platform_driver(pbias_regulator_driver);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Balaji T K <balajitk@ti.com>");
2418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("pbias voltage regulator");
2428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2438c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:pbias-regulator");
244