18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2014,2017 The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci * Copyright (c) 2018-2020, Linaro Limited
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/err.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/phy/phy.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
178c2ecf20Sopenharmony_ci#include <linux/reset.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define PHY_CTRL0			0x6C
218c2ecf20Sopenharmony_ci#define PHY_CTRL1			0x70
228c2ecf20Sopenharmony_ci#define PHY_CTRL2			0x74
238c2ecf20Sopenharmony_ci#define PHY_CTRL4			0x7C
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* PHY_CTRL bits */
268c2ecf20Sopenharmony_ci#define REF_PHY_EN			BIT(0)
278c2ecf20Sopenharmony_ci#define LANE0_PWR_ON			BIT(2)
288c2ecf20Sopenharmony_ci#define SWI_PCS_CLK_SEL			BIT(4)
298c2ecf20Sopenharmony_ci#define TST_PWR_DOWN			BIT(4)
308c2ecf20Sopenharmony_ci#define PHY_RESET			BIT(7)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define NUM_BULK_CLKS			3
338c2ecf20Sopenharmony_ci#define NUM_BULK_REGS			2
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct ssphy_priv {
368c2ecf20Sopenharmony_ci	void __iomem *base;
378c2ecf20Sopenharmony_ci	struct device *dev;
388c2ecf20Sopenharmony_ci	struct reset_control *reset_com;
398c2ecf20Sopenharmony_ci	struct reset_control *reset_phy;
408c2ecf20Sopenharmony_ci	struct regulator_bulk_data regs[NUM_BULK_REGS];
418c2ecf20Sopenharmony_ci	struct clk_bulk_data clks[NUM_BULK_CLKS];
428c2ecf20Sopenharmony_ci	enum phy_mode mode;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic inline void qcom_ssphy_updatel(void __iomem *addr, u32 mask, u32 val)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	writel((readl(addr) & ~mask) | val, addr);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int qcom_ssphy_do_reset(struct ssphy_priv *priv)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	int ret;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (!priv->reset_com) {
558c2ecf20Sopenharmony_ci		qcom_ssphy_updatel(priv->base + PHY_CTRL1, PHY_RESET,
568c2ecf20Sopenharmony_ci				   PHY_RESET);
578c2ecf20Sopenharmony_ci		usleep_range(10, 20);
588c2ecf20Sopenharmony_ci		qcom_ssphy_updatel(priv->base + PHY_CTRL1, PHY_RESET, 0);
598c2ecf20Sopenharmony_ci	} else {
608c2ecf20Sopenharmony_ci		ret = reset_control_assert(priv->reset_com);
618c2ecf20Sopenharmony_ci		if (ret) {
628c2ecf20Sopenharmony_ci			dev_err(priv->dev, "Failed to assert reset com\n");
638c2ecf20Sopenharmony_ci			return ret;
648c2ecf20Sopenharmony_ci		}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		ret = reset_control_assert(priv->reset_phy);
678c2ecf20Sopenharmony_ci		if (ret) {
688c2ecf20Sopenharmony_ci			dev_err(priv->dev, "Failed to assert reset phy\n");
698c2ecf20Sopenharmony_ci			return ret;
708c2ecf20Sopenharmony_ci		}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		usleep_range(10, 20);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		ret = reset_control_deassert(priv->reset_com);
758c2ecf20Sopenharmony_ci		if (ret) {
768c2ecf20Sopenharmony_ci			dev_err(priv->dev, "Failed to deassert reset com\n");
778c2ecf20Sopenharmony_ci			return ret;
788c2ecf20Sopenharmony_ci		}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		ret = reset_control_deassert(priv->reset_phy);
818c2ecf20Sopenharmony_ci		if (ret) {
828c2ecf20Sopenharmony_ci			dev_err(priv->dev, "Failed to deassert reset phy\n");
838c2ecf20Sopenharmony_ci			return ret;
848c2ecf20Sopenharmony_ci		}
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int qcom_ssphy_power_on(struct phy *phy)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct ssphy_priv *priv = phy_get_drvdata(phy);
938c2ecf20Sopenharmony_ci	int ret;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	ret = regulator_bulk_enable(NUM_BULK_REGS, priv->regs);
968c2ecf20Sopenharmony_ci	if (ret)
978c2ecf20Sopenharmony_ci		return ret;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	ret = clk_bulk_prepare_enable(NUM_BULK_CLKS, priv->clks);
1008c2ecf20Sopenharmony_ci	if (ret)
1018c2ecf20Sopenharmony_ci		goto err_disable_regulator;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	ret = qcom_ssphy_do_reset(priv);
1048c2ecf20Sopenharmony_ci	if (ret)
1058c2ecf20Sopenharmony_ci		goto err_disable_clock;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	writeb(SWI_PCS_CLK_SEL, priv->base + PHY_CTRL0);
1088c2ecf20Sopenharmony_ci	qcom_ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, LANE0_PWR_ON);
1098c2ecf20Sopenharmony_ci	qcom_ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, REF_PHY_EN);
1108c2ecf20Sopenharmony_ci	qcom_ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, 0);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return 0;
1138c2ecf20Sopenharmony_cierr_disable_clock:
1148c2ecf20Sopenharmony_ci	clk_bulk_disable_unprepare(NUM_BULK_CLKS, priv->clks);
1158c2ecf20Sopenharmony_cierr_disable_regulator:
1168c2ecf20Sopenharmony_ci	regulator_bulk_disable(NUM_BULK_REGS, priv->regs);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return ret;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int qcom_ssphy_power_off(struct phy *phy)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct ssphy_priv *priv = phy_get_drvdata(phy);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	qcom_ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, 0);
1268c2ecf20Sopenharmony_ci	qcom_ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, 0);
1278c2ecf20Sopenharmony_ci	qcom_ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, TST_PWR_DOWN);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	clk_bulk_disable_unprepare(NUM_BULK_CLKS, priv->clks);
1308c2ecf20Sopenharmony_ci	regulator_bulk_disable(NUM_BULK_REGS, priv->regs);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int qcom_ssphy_init_clock(struct ssphy_priv *priv)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	priv->clks[0].id = "ref";
1388c2ecf20Sopenharmony_ci	priv->clks[1].id = "ahb";
1398c2ecf20Sopenharmony_ci	priv->clks[2].id = "pipe";
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return devm_clk_bulk_get(priv->dev, NUM_BULK_CLKS, priv->clks);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int qcom_ssphy_init_regulator(struct ssphy_priv *priv)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	int ret;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	priv->regs[0].supply = "vdd";
1498c2ecf20Sopenharmony_ci	priv->regs[1].supply = "vdda1p8";
1508c2ecf20Sopenharmony_ci	ret = devm_regulator_bulk_get(priv->dev, NUM_BULK_REGS, priv->regs);
1518c2ecf20Sopenharmony_ci	if (ret) {
1528c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
1538c2ecf20Sopenharmony_ci			dev_err(priv->dev, "Failed to get regulators\n");
1548c2ecf20Sopenharmony_ci		return ret;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return ret;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int qcom_ssphy_init_reset(struct ssphy_priv *priv)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	priv->reset_com = devm_reset_control_get_optional_exclusive(priv->dev, "com");
1638c2ecf20Sopenharmony_ci	if (IS_ERR(priv->reset_com)) {
1648c2ecf20Sopenharmony_ci		dev_err(priv->dev, "Failed to get reset control com\n");
1658c2ecf20Sopenharmony_ci		return PTR_ERR(priv->reset_com);
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (priv->reset_com) {
1698c2ecf20Sopenharmony_ci		/* if reset_com is present, reset_phy is no longer optional */
1708c2ecf20Sopenharmony_ci		priv->reset_phy = devm_reset_control_get_exclusive(priv->dev, "phy");
1718c2ecf20Sopenharmony_ci		if (IS_ERR(priv->reset_phy)) {
1728c2ecf20Sopenharmony_ci			dev_err(priv->dev, "Failed to get reset control phy\n");
1738c2ecf20Sopenharmony_ci			return PTR_ERR(priv->reset_phy);
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return 0;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic const struct phy_ops qcom_ssphy_ops = {
1818c2ecf20Sopenharmony_ci	.power_off = qcom_ssphy_power_off,
1828c2ecf20Sopenharmony_ci	.power_on = qcom_ssphy_power_on,
1838c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1848c2ecf20Sopenharmony_ci};
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int qcom_ssphy_probe(struct platform_device *pdev)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1898c2ecf20Sopenharmony_ci	struct phy_provider *provider;
1908c2ecf20Sopenharmony_ci	struct ssphy_priv *priv;
1918c2ecf20Sopenharmony_ci	struct phy *phy;
1928c2ecf20Sopenharmony_ci	int ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(struct ssphy_priv), GFP_KERNEL);
1958c2ecf20Sopenharmony_ci	if (!priv)
1968c2ecf20Sopenharmony_ci		return -ENOMEM;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	priv->dev = dev;
1998c2ecf20Sopenharmony_ci	priv->mode = PHY_MODE_INVALID;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	priv->base = devm_platform_ioremap_resource(pdev, 0);
2028c2ecf20Sopenharmony_ci	if (IS_ERR(priv->base))
2038c2ecf20Sopenharmony_ci		return PTR_ERR(priv->base);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	ret = qcom_ssphy_init_clock(priv);
2068c2ecf20Sopenharmony_ci	if (ret)
2078c2ecf20Sopenharmony_ci		return ret;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	ret = qcom_ssphy_init_reset(priv);
2108c2ecf20Sopenharmony_ci	if (ret)
2118c2ecf20Sopenharmony_ci		return ret;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	ret = qcom_ssphy_init_regulator(priv);
2148c2ecf20Sopenharmony_ci	if (ret)
2158c2ecf20Sopenharmony_ci		return ret;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	phy = devm_phy_create(dev, dev->of_node, &qcom_ssphy_ops);
2188c2ecf20Sopenharmony_ci	if (IS_ERR(phy)) {
2198c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to create the SS phy\n");
2208c2ecf20Sopenharmony_ci		return PTR_ERR(phy);
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	phy_set_drvdata(phy, priv);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(provider);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_ssphy_match[] = {
2318c2ecf20Sopenharmony_ci	{ .compatible = "qcom,usb-ss-28nm-phy", },
2328c2ecf20Sopenharmony_ci	{ },
2338c2ecf20Sopenharmony_ci};
2348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_ssphy_match);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic struct platform_driver qcom_ssphy_driver = {
2378c2ecf20Sopenharmony_ci	.probe		= qcom_ssphy_probe,
2388c2ecf20Sopenharmony_ci	.driver = {
2398c2ecf20Sopenharmony_ci		.name	= "qcom-usb-ssphy",
2408c2ecf20Sopenharmony_ci		.of_match_table = qcom_ssphy_match,
2418c2ecf20Sopenharmony_ci	},
2428c2ecf20Sopenharmony_ci};
2438c2ecf20Sopenharmony_cimodule_platform_driver(qcom_ssphy_driver);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm SuperSpeed USB PHY driver");
2468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
247