162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Qualcomm PMIC VBUS output regulator driver
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (c) 2020, The Linux Foundation. All rights reserved.
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/regulator/driver.h>
1362306a36Sopenharmony_ci#include <linux/regulator/of_regulator.h>
1462306a36Sopenharmony_ci#include <linux/regmap.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define CMD_OTG				0x40
1762306a36Sopenharmony_ci#define OTG_EN				BIT(0)
1862306a36Sopenharmony_ci#define OTG_CURRENT_LIMIT_CFG		0x52
1962306a36Sopenharmony_ci#define OTG_CURRENT_LIMIT_MASK		GENMASK(2, 0)
2062306a36Sopenharmony_ci#define OTG_CFG				0x53
2162306a36Sopenharmony_ci#define OTG_EN_SRC_CFG			BIT(1)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic const unsigned int curr_table[] = {
2462306a36Sopenharmony_ci	500000, 1000000, 1500000, 2000000, 2500000, 3000000,
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const struct regulator_ops qcom_usb_vbus_reg_ops = {
2862306a36Sopenharmony_ci	.enable = regulator_enable_regmap,
2962306a36Sopenharmony_ci	.disable = regulator_disable_regmap,
3062306a36Sopenharmony_ci	.is_enabled = regulator_is_enabled_regmap,
3162306a36Sopenharmony_ci	.get_current_limit = regulator_get_current_limit_regmap,
3262306a36Sopenharmony_ci	.set_current_limit = regulator_set_current_limit_regmap,
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic struct regulator_desc qcom_usb_vbus_rdesc = {
3662306a36Sopenharmony_ci	.name = "usb_vbus",
3762306a36Sopenharmony_ci	.ops = &qcom_usb_vbus_reg_ops,
3862306a36Sopenharmony_ci	.owner = THIS_MODULE,
3962306a36Sopenharmony_ci	.type = REGULATOR_VOLTAGE,
4062306a36Sopenharmony_ci	.curr_table = curr_table,
4162306a36Sopenharmony_ci	.n_current_limits = ARRAY_SIZE(curr_table),
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int qcom_usb_vbus_regulator_probe(struct platform_device *pdev)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
4762306a36Sopenharmony_ci	struct regulator_dev *rdev;
4862306a36Sopenharmony_ci	struct regmap *regmap;
4962306a36Sopenharmony_ci	struct regulator_config config = { };
5062306a36Sopenharmony_ci	struct regulator_init_data *init_data;
5162306a36Sopenharmony_ci	int ret;
5262306a36Sopenharmony_ci	u32 base;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	ret = of_property_read_u32(dev->of_node, "reg", &base);
5562306a36Sopenharmony_ci	if (ret < 0) {
5662306a36Sopenharmony_ci		dev_err(dev, "no base address found\n");
5762306a36Sopenharmony_ci		return ret;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	regmap = dev_get_regmap(dev->parent, NULL);
6162306a36Sopenharmony_ci	if (!regmap) {
6262306a36Sopenharmony_ci		dev_err(dev, "Failed to get regmap\n");
6362306a36Sopenharmony_ci		return -ENOENT;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	init_data = of_get_regulator_init_data(dev, dev->of_node,
6762306a36Sopenharmony_ci					       &qcom_usb_vbus_rdesc);
6862306a36Sopenharmony_ci	if (!init_data)
6962306a36Sopenharmony_ci		return -ENOMEM;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	qcom_usb_vbus_rdesc.enable_reg = base + CMD_OTG;
7262306a36Sopenharmony_ci	qcom_usb_vbus_rdesc.enable_mask = OTG_EN;
7362306a36Sopenharmony_ci	qcom_usb_vbus_rdesc.csel_reg = base + OTG_CURRENT_LIMIT_CFG;
7462306a36Sopenharmony_ci	qcom_usb_vbus_rdesc.csel_mask = OTG_CURRENT_LIMIT_MASK;
7562306a36Sopenharmony_ci	config.dev = dev;
7662306a36Sopenharmony_ci	config.init_data = init_data;
7762306a36Sopenharmony_ci	config.of_node = dev->of_node;
7862306a36Sopenharmony_ci	config.regmap = regmap;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	rdev = devm_regulator_register(dev, &qcom_usb_vbus_rdesc, &config);
8162306a36Sopenharmony_ci	if (IS_ERR(rdev)) {
8262306a36Sopenharmony_ci		ret = PTR_ERR(rdev);
8362306a36Sopenharmony_ci		dev_err(dev, "not able to register vbus reg %d\n", ret);
8462306a36Sopenharmony_ci		return ret;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* Disable HW logic for VBUS enable */
8862306a36Sopenharmony_ci	regmap_update_bits(regmap, base + OTG_CFG, OTG_EN_SRC_CFG, 0);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic const struct of_device_id qcom_usb_vbus_regulator_match[] = {
9462306a36Sopenharmony_ci	{ .compatible = "qcom,pm8150b-vbus-reg" },
9562306a36Sopenharmony_ci	{ }
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_usb_vbus_regulator_match);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic struct platform_driver qcom_usb_vbus_regulator_driver = {
10062306a36Sopenharmony_ci	.driver		= {
10162306a36Sopenharmony_ci		.name	= "qcom-usb-vbus-regulator",
10262306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
10362306a36Sopenharmony_ci		.of_match_table = qcom_usb_vbus_regulator_match,
10462306a36Sopenharmony_ci	},
10562306a36Sopenharmony_ci	.probe		= qcom_usb_vbus_regulator_probe,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_cimodule_platform_driver(qcom_usb_vbus_regulator_driver);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm USB vbus regulator driver");
11062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
111