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