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 17 struct 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 26 static 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; 78 err_ulpi: 79 clk_disable_unprepare(uphy->cal_sleep_clk); 80 err_sleep: 81 clk_disable_unprepare(uphy->cal_clk); 82 err_cal: 83 clk_disable_unprepare(uphy->phy_clk); 84 return ret; 85 } 86 87 static 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 98 static 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 104 static 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 142 static const struct of_device_id qcom_usb_hsic_phy_match[] = { 143 { .compatible = "qcom,usb-hsic-phy", }, 144 { } 145 }; 146 MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match); 147 148 static 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 }; 155 module_ulpi_driver(qcom_usb_hsic_phy_driver); 156 157 MODULE_DESCRIPTION("Qualcomm USB HSIC phy"); 158 MODULE_LICENSE("GPL v2"); 159