159 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2016 Linaro Ltd4 */5#include <linux/module.h>6#include <linux/ulpi/driver.h>7#include <linux/ulpi/regs.h>8#include <linux/phy/phy.h>9#include <linux/pinctrl/consumer.h>10#include <linux/pinctrl/pinctrl-state.h>11#include <linux/delay.h>12#include <linux/clk.h>13 14#define ULPI_HSIC_CFG 0x3015#define ULPI_HSIC_IO_CAL 0x3316 17struct qcom_usb_hsic_phy {18 struct ulpi *ulpi;19 struct phy *phy;20 struct pinctrl *pctl;21 struct clk *phy_clk;22 struct clk *cal_clk;23 struct clk *cal_sleep_clk;24};25 26static int qcom_usb_hsic_phy_power_on(struct phy *phy)27{28 struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy);29 struct ulpi *ulpi = uphy->ulpi;30 struct pinctrl_state *pins_default;31 int ret;32 33 ret = clk_prepare_enable(uphy->phy_clk);34 if (ret)35 return ret;36 37 ret = clk_prepare_enable(uphy->cal_clk);38 if (ret)39 goto err_cal;40 41 ret = clk_prepare_enable(uphy->cal_sleep_clk);42 if (ret)43 goto err_sleep;44 45 /* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */46 ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff);47 if (ret)48 goto err_ulpi;49 50 /* Enable periodic IO calibration in HSIC_CFG register */51 ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8);52 if (ret)53 goto err_ulpi;54 55 /* Configure pins for HSIC functionality */56 pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT);57 if (IS_ERR(pins_default)) {58 ret = PTR_ERR(pins_default);59 goto err_ulpi;60 }61 62 ret = pinctrl_select_state(uphy->pctl, pins_default);63 if (ret)64 goto err_ulpi;65 66 /* Enable HSIC mode in HSIC_CFG register */67 ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01);68 if (ret)69 goto err_ulpi;70 71 /* Disable auto-resume */72 ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL),73 ULPI_IFC_CTRL_AUTORESUME);74 if (ret)75 goto err_ulpi;76 77 return ret;78err_ulpi:79 clk_disable_unprepare(uphy->cal_sleep_clk);80err_sleep:81 clk_disable_unprepare(uphy->cal_clk);82err_cal:83 clk_disable_unprepare(uphy->phy_clk);84 return ret;85}86 87static int qcom_usb_hsic_phy_power_off(struct phy *phy)88{89 struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy);90 91 clk_disable_unprepare(uphy->cal_sleep_clk);92 clk_disable_unprepare(uphy->cal_clk);93 clk_disable_unprepare(uphy->phy_clk);94 95 return 0;96}97 98static const struct phy_ops qcom_usb_hsic_phy_ops = {99 .power_on = qcom_usb_hsic_phy_power_on,100 .power_off = qcom_usb_hsic_phy_power_off,101 .owner = THIS_MODULE,102};103 104static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi)105{106 struct qcom_usb_hsic_phy *uphy;107 struct phy_provider *p;108 struct clk *clk;109 110 uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL);111 if (!uphy)112 return -ENOMEM;113 ulpi_set_drvdata(ulpi, uphy);114 115 uphy->ulpi = ulpi;116 uphy->pctl = devm_pinctrl_get(&ulpi->dev);117 if (IS_ERR(uphy->pctl))118 return PTR_ERR(uphy->pctl);119 120 uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy");121 if (IS_ERR(clk))122 return PTR_ERR(clk);123 124 uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal");125 if (IS_ERR(clk))126 return PTR_ERR(clk);127 128 uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep");129 if (IS_ERR(clk))130 return PTR_ERR(clk);131 132 uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node,133 &qcom_usb_hsic_phy_ops);134 if (IS_ERR(uphy->phy))135 return PTR_ERR(uphy->phy);136 phy_set_drvdata(uphy->phy, uphy);137 138 p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate);139 return PTR_ERR_OR_ZERO(p);140}141 142static const struct of_device_id qcom_usb_hsic_phy_match[] = {143 { .compatible = "qcom,usb-hsic-phy", },144 { }145};146MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match);147 148static struct ulpi_driver qcom_usb_hsic_phy_driver = {149 .probe = qcom_usb_hsic_phy_probe,150 .driver = {151 .name = "qcom_usb_hsic_phy",152 .of_match_table = qcom_usb_hsic_phy_match,153 },154};155module_ulpi_driver(qcom_usb_hsic_phy_driver);156 157MODULE_DESCRIPTION("Qualcomm USB HSIC phy");158MODULE_LICENSE("GPL v2");159