phy-qcom-usb-hsic.c (3790B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2016 Linaro Ltd 4 */ 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 0x30 15#define ULPI_HSIC_IO_CAL 0x33 16 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 return PTR_ERR(pins_default); 59 60 ret = pinctrl_select_state(uphy->pctl, pins_default); 61 if (ret) 62 goto err_ulpi; 63 64 /* Enable HSIC mode in HSIC_CFG register */ 65 ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01); 66 if (ret) 67 goto err_ulpi; 68 69 /* Disable auto-resume */ 70 ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL), 71 ULPI_IFC_CTRL_AUTORESUME); 72 if (ret) 73 goto err_ulpi; 74 75 return ret; 76err_ulpi: 77 clk_disable_unprepare(uphy->cal_sleep_clk); 78err_sleep: 79 clk_disable_unprepare(uphy->cal_clk); 80err_cal: 81 clk_disable_unprepare(uphy->phy_clk); 82 return ret; 83} 84 85static int qcom_usb_hsic_phy_power_off(struct phy *phy) 86{ 87 struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); 88 89 clk_disable_unprepare(uphy->cal_sleep_clk); 90 clk_disable_unprepare(uphy->cal_clk); 91 clk_disable_unprepare(uphy->phy_clk); 92 93 return 0; 94} 95 96static const struct phy_ops qcom_usb_hsic_phy_ops = { 97 .power_on = qcom_usb_hsic_phy_power_on, 98 .power_off = qcom_usb_hsic_phy_power_off, 99 .owner = THIS_MODULE, 100}; 101 102static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi) 103{ 104 struct qcom_usb_hsic_phy *uphy; 105 struct phy_provider *p; 106 struct clk *clk; 107 108 uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); 109 if (!uphy) 110 return -ENOMEM; 111 ulpi_set_drvdata(ulpi, uphy); 112 113 uphy->ulpi = ulpi; 114 uphy->pctl = devm_pinctrl_get(&ulpi->dev); 115 if (IS_ERR(uphy->pctl)) 116 return PTR_ERR(uphy->pctl); 117 118 uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy"); 119 if (IS_ERR(clk)) 120 return PTR_ERR(clk); 121 122 uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal"); 123 if (IS_ERR(clk)) 124 return PTR_ERR(clk); 125 126 uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep"); 127 if (IS_ERR(clk)) 128 return PTR_ERR(clk); 129 130 uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, 131 &qcom_usb_hsic_phy_ops); 132 if (IS_ERR(uphy->phy)) 133 return PTR_ERR(uphy->phy); 134 phy_set_drvdata(uphy->phy, uphy); 135 136 p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); 137 return PTR_ERR_OR_ZERO(p); 138} 139 140static const struct of_device_id qcom_usb_hsic_phy_match[] = { 141 { .compatible = "qcom,usb-hsic-phy", }, 142 { } 143}; 144MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match); 145 146static struct ulpi_driver qcom_usb_hsic_phy_driver = { 147 .probe = qcom_usb_hsic_phy_probe, 148 .driver = { 149 .name = "qcom_usb_hsic_phy", 150 .of_match_table = qcom_usb_hsic_phy_match, 151 }, 152}; 153module_ulpi_driver(qcom_usb_hsic_phy_driver); 154 155MODULE_DESCRIPTION("Qualcomm USB HSIC phy"); 156MODULE_LICENSE("GPL v2");