162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2016 Linaro Ltd
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/ulpi/driver.h>
762306a36Sopenharmony_ci#include <linux/ulpi/regs.h>
862306a36Sopenharmony_ci#include <linux/phy/phy.h>
962306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
1062306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl-state.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/clk.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define ULPI_HSIC_CFG		0x30
1562306a36Sopenharmony_ci#define ULPI_HSIC_IO_CAL	0x33
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct qcom_usb_hsic_phy {
1862306a36Sopenharmony_ci	struct ulpi *ulpi;
1962306a36Sopenharmony_ci	struct phy *phy;
2062306a36Sopenharmony_ci	struct pinctrl *pctl;
2162306a36Sopenharmony_ci	struct clk *phy_clk;
2262306a36Sopenharmony_ci	struct clk *cal_clk;
2362306a36Sopenharmony_ci	struct clk *cal_sleep_clk;
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int qcom_usb_hsic_phy_power_on(struct phy *phy)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy);
2962306a36Sopenharmony_ci	struct ulpi *ulpi = uphy->ulpi;
3062306a36Sopenharmony_ci	struct pinctrl_state *pins_default;
3162306a36Sopenharmony_ci	int ret;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	ret = clk_prepare_enable(uphy->phy_clk);
3462306a36Sopenharmony_ci	if (ret)
3562306a36Sopenharmony_ci		return ret;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	ret = clk_prepare_enable(uphy->cal_clk);
3862306a36Sopenharmony_ci	if (ret)
3962306a36Sopenharmony_ci		goto err_cal;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	ret = clk_prepare_enable(uphy->cal_sleep_clk);
4262306a36Sopenharmony_ci	if (ret)
4362306a36Sopenharmony_ci		goto err_sleep;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */
4662306a36Sopenharmony_ci	ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff);
4762306a36Sopenharmony_ci	if (ret)
4862306a36Sopenharmony_ci		goto err_ulpi;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* Enable periodic IO calibration in HSIC_CFG register */
5162306a36Sopenharmony_ci	ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8);
5262306a36Sopenharmony_ci	if (ret)
5362306a36Sopenharmony_ci		goto err_ulpi;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* Configure pins for HSIC functionality */
5662306a36Sopenharmony_ci	pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT);
5762306a36Sopenharmony_ci	if (IS_ERR(pins_default)) {
5862306a36Sopenharmony_ci		ret = PTR_ERR(pins_default);
5962306a36Sopenharmony_ci		goto err_ulpi;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	ret = pinctrl_select_state(uphy->pctl, pins_default);
6362306a36Sopenharmony_ci	if (ret)
6462306a36Sopenharmony_ci		goto err_ulpi;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	 /* Enable HSIC mode in HSIC_CFG register */
6762306a36Sopenharmony_ci	ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01);
6862306a36Sopenharmony_ci	if (ret)
6962306a36Sopenharmony_ci		goto err_ulpi;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* Disable auto-resume */
7262306a36Sopenharmony_ci	ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL),
7362306a36Sopenharmony_ci			 ULPI_IFC_CTRL_AUTORESUME);
7462306a36Sopenharmony_ci	if (ret)
7562306a36Sopenharmony_ci		goto err_ulpi;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return ret;
7862306a36Sopenharmony_cierr_ulpi:
7962306a36Sopenharmony_ci	clk_disable_unprepare(uphy->cal_sleep_clk);
8062306a36Sopenharmony_cierr_sleep:
8162306a36Sopenharmony_ci	clk_disable_unprepare(uphy->cal_clk);
8262306a36Sopenharmony_cierr_cal:
8362306a36Sopenharmony_ci	clk_disable_unprepare(uphy->phy_clk);
8462306a36Sopenharmony_ci	return ret;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int qcom_usb_hsic_phy_power_off(struct phy *phy)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	clk_disable_unprepare(uphy->cal_sleep_clk);
9262306a36Sopenharmony_ci	clk_disable_unprepare(uphy->cal_clk);
9362306a36Sopenharmony_ci	clk_disable_unprepare(uphy->phy_clk);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic const struct phy_ops qcom_usb_hsic_phy_ops = {
9962306a36Sopenharmony_ci	.power_on = qcom_usb_hsic_phy_power_on,
10062306a36Sopenharmony_ci	.power_off = qcom_usb_hsic_phy_power_off,
10162306a36Sopenharmony_ci	.owner = THIS_MODULE,
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int qcom_usb_hsic_phy_probe(struct ulpi *ulpi)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct qcom_usb_hsic_phy *uphy;
10762306a36Sopenharmony_ci	struct phy_provider *p;
10862306a36Sopenharmony_ci	struct clk *clk;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL);
11162306a36Sopenharmony_ci	if (!uphy)
11262306a36Sopenharmony_ci		return -ENOMEM;
11362306a36Sopenharmony_ci	ulpi_set_drvdata(ulpi, uphy);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	uphy->ulpi = ulpi;
11662306a36Sopenharmony_ci	uphy->pctl = devm_pinctrl_get(&ulpi->dev);
11762306a36Sopenharmony_ci	if (IS_ERR(uphy->pctl))
11862306a36Sopenharmony_ci		return PTR_ERR(uphy->pctl);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy");
12162306a36Sopenharmony_ci	if (IS_ERR(clk))
12262306a36Sopenharmony_ci		return PTR_ERR(clk);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal");
12562306a36Sopenharmony_ci	if (IS_ERR(clk))
12662306a36Sopenharmony_ci		return PTR_ERR(clk);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep");
12962306a36Sopenharmony_ci	if (IS_ERR(clk))
13062306a36Sopenharmony_ci		return PTR_ERR(clk);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node,
13362306a36Sopenharmony_ci				    &qcom_usb_hsic_phy_ops);
13462306a36Sopenharmony_ci	if (IS_ERR(uphy->phy))
13562306a36Sopenharmony_ci		return PTR_ERR(uphy->phy);
13662306a36Sopenharmony_ci	phy_set_drvdata(uphy->phy, uphy);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate);
13962306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(p);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic const struct of_device_id qcom_usb_hsic_phy_match[] = {
14362306a36Sopenharmony_ci	{ .compatible = "qcom,usb-hsic-phy", },
14462306a36Sopenharmony_ci	{ }
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic struct ulpi_driver qcom_usb_hsic_phy_driver = {
14962306a36Sopenharmony_ci	.probe = qcom_usb_hsic_phy_probe,
15062306a36Sopenharmony_ci	.driver = {
15162306a36Sopenharmony_ci		.name = "qcom_usb_hsic_phy",
15262306a36Sopenharmony_ci		.of_match_table = qcom_usb_hsic_phy_match,
15362306a36Sopenharmony_ci	},
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_cimodule_ulpi_driver(qcom_usb_hsic_phy_driver);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm USB HSIC phy");
15862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
159