Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy: Add support for Qualcomm's USB HSIC phy

The HSIC USB controller on qcom SoCs has an integrated all
digital phy controlled via the ULPI viewport.

Cc: Kishon Vijay Abraham I <kishon@ti.com>
Acked-by: Rob Herring <robh@kernel.org>
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>

authored by

Stephen Boyd and committed by
Kishon Vijay Abraham I
605b8652 a8df2768

+233
+65
Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt
··· 1 + Qualcomm's USB HSIC PHY 2 + 3 + PROPERTIES 4 + 5 + - compatible: 6 + Usage: required 7 + Value type: <string> 8 + Definition: Should contain "qcom,usb-hsic-phy" and more specifically one of the 9 + following: 10 + 11 + "qcom,usb-hsic-phy-mdm9615" 12 + "qcom,usb-hsic-phy-msm8974" 13 + 14 + - #phy-cells: 15 + Usage: required 16 + Value type: <u32> 17 + Definition: Should contain 0 18 + 19 + - clocks: 20 + Usage: required 21 + Value type: <prop-encoded-array> 22 + Definition: Should contain clock specifier for phy, calibration and 23 + a calibration sleep clock 24 + 25 + - clock-names: 26 + Usage: required 27 + Value type: <stringlist> 28 + Definition: Should contain "phy, "cal" and "cal_sleep" 29 + 30 + - pinctrl-names: 31 + Usage: required 32 + Value type: <stringlist> 33 + Definition: Should contain "init" and "default" in that order 34 + 35 + - pinctrl-0: 36 + Usage: required 37 + Value type: <prop-encoded-array> 38 + Definition: List of pinctrl settings to apply to keep HSIC pins in a glitch 39 + free state 40 + 41 + - pinctrl-1: 42 + Usage: required 43 + Value type: <prop-encoded-array> 44 + Definition: List of pinctrl settings to apply to mux out the HSIC pins 45 + 46 + EXAMPLE 47 + 48 + usb-controller { 49 + ulpi { 50 + phy { 51 + compatible = "qcom,usb-hsic-phy-msm8974", 52 + "qcom,usb-hsic-phy"; 53 + #phy-cells = <0>; 54 + pinctrl-names = "init", "default"; 55 + pinctrl-0 = <&hsic_sleep>; 56 + pinctrl-1 = <&hsic_default>; 57 + clocks = <&gcc GCC_USB_HSIC_CLK>, 58 + <&gcc GCC_USB_HSIC_IO_CAL_CLK>, 59 + <&gcc GCC_USB_HSIC_IO_CAL_SLEEP_CLK>; 60 + clock-names = "phy", "cal", "cal_sleep"; 61 + assigned-clocks = <&gcc GCC_USB_HSIC_IO_CAL_CLK>; 62 + assigned-clock-rates = <960000>; 63 + }; 64 + }; 65 + };
+7
drivers/phy/Kconfig
··· 437 437 help 438 438 Support for UFS PHY on QCOM chipsets. 439 439 440 + config PHY_QCOM_USB_HSIC 441 + tristate "Qualcomm USB HSIC ULPI PHY module" 442 + depends on USB_ULPI_BUS 443 + select GENERIC_PHY 444 + help 445 + Support for the USB HSIC ULPI compliant PHY on QCOM chipsets. 446 + 440 447 config PHY_TUSB1210 441 448 tristate "TI TUSB1210 ULPI PHY module" 442 449 depends on USB_ULPI_BUS
+1
drivers/phy/Makefile
··· 52 52 obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o 53 53 obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o 54 54 obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o 55 + obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o 55 56 obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o 56 57 obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o 57 58 obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
+160
drivers/phy/phy-qcom-usb-hsic.c
··· 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/pinctrl/consumer.h> 12 + #include <linux/pinctrl/pinctrl-state.h> 13 + #include <linux/delay.h> 14 + #include <linux/clk.h> 15 + 16 + #include "ulpi_phy.h" 17 + 18 + #define ULPI_HSIC_CFG 0x30 19 + #define ULPI_HSIC_IO_CAL 0x33 20 + 21 + struct qcom_usb_hsic_phy { 22 + struct ulpi *ulpi; 23 + struct phy *phy; 24 + struct pinctrl *pctl; 25 + struct clk *phy_clk; 26 + struct clk *cal_clk; 27 + struct clk *cal_sleep_clk; 28 + }; 29 + 30 + static int qcom_usb_hsic_phy_power_on(struct phy *phy) 31 + { 32 + struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); 33 + struct ulpi *ulpi = uphy->ulpi; 34 + struct pinctrl_state *pins_default; 35 + int ret; 36 + 37 + ret = clk_prepare_enable(uphy->phy_clk); 38 + if (ret) 39 + return ret; 40 + 41 + ret = clk_prepare_enable(uphy->cal_clk); 42 + if (ret) 43 + goto err_cal; 44 + 45 + ret = clk_prepare_enable(uphy->cal_sleep_clk); 46 + if (ret) 47 + goto err_sleep; 48 + 49 + /* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ 50 + ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff); 51 + if (ret) 52 + goto err_ulpi; 53 + 54 + /* Enable periodic IO calibration in HSIC_CFG register */ 55 + ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8); 56 + if (ret) 57 + goto err_ulpi; 58 + 59 + /* Configure pins for HSIC functionality */ 60 + pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT); 61 + if (IS_ERR(pins_default)) 62 + return PTR_ERR(pins_default); 63 + 64 + ret = pinctrl_select_state(uphy->pctl, pins_default); 65 + if (ret) 66 + goto err_ulpi; 67 + 68 + /* Enable HSIC mode in HSIC_CFG register */ 69 + ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01); 70 + if (ret) 71 + goto err_ulpi; 72 + 73 + /* Disable auto-resume */ 74 + ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL), 75 + ULPI_IFC_CTRL_AUTORESUME); 76 + if (ret) 77 + goto err_ulpi; 78 + 79 + return ret; 80 + err_ulpi: 81 + clk_disable_unprepare(uphy->cal_sleep_clk); 82 + err_sleep: 83 + clk_disable_unprepare(uphy->cal_clk); 84 + err_cal: 85 + clk_disable_unprepare(uphy->phy_clk); 86 + return ret; 87 + } 88 + 89 + static int qcom_usb_hsic_phy_power_off(struct phy *phy) 90 + { 91 + struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); 92 + 93 + clk_disable_unprepare(uphy->cal_sleep_clk); 94 + clk_disable_unprepare(uphy->cal_clk); 95 + clk_disable_unprepare(uphy->phy_clk); 96 + 97 + return 0; 98 + } 99 + 100 + static const struct phy_ops qcom_usb_hsic_phy_ops = { 101 + .power_on = qcom_usb_hsic_phy_power_on, 102 + .power_off = qcom_usb_hsic_phy_power_off, 103 + .owner = THIS_MODULE, 104 + }; 105 + 106 + static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi) 107 + { 108 + struct qcom_usb_hsic_phy *uphy; 109 + struct phy_provider *p; 110 + struct clk *clk; 111 + 112 + uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); 113 + if (!uphy) 114 + return -ENOMEM; 115 + ulpi_set_drvdata(ulpi, uphy); 116 + 117 + uphy->ulpi = ulpi; 118 + uphy->pctl = devm_pinctrl_get(&ulpi->dev); 119 + if (IS_ERR(uphy->pctl)) 120 + return PTR_ERR(uphy->pctl); 121 + 122 + uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy"); 123 + if (IS_ERR(clk)) 124 + return PTR_ERR(clk); 125 + 126 + uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal"); 127 + if (IS_ERR(clk)) 128 + return PTR_ERR(clk); 129 + 130 + uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep"); 131 + if (IS_ERR(clk)) 132 + return PTR_ERR(clk); 133 + 134 + uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, 135 + &qcom_usb_hsic_phy_ops); 136 + if (IS_ERR(uphy->phy)) 137 + return PTR_ERR(uphy->phy); 138 + phy_set_drvdata(uphy->phy, uphy); 139 + 140 + p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); 141 + return PTR_ERR_OR_ZERO(p); 142 + } 143 + 144 + static const struct of_device_id qcom_usb_hsic_phy_match[] = { 145 + { .compatible = "qcom,usb-hsic-phy", }, 146 + { } 147 + }; 148 + MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match); 149 + 150 + static struct ulpi_driver qcom_usb_hsic_phy_driver = { 151 + .probe = qcom_usb_hsic_phy_probe, 152 + .driver = { 153 + .name = "qcom_usb_hsic_phy", 154 + .of_match_table = qcom_usb_hsic_phy_match, 155 + }, 156 + }; 157 + module_ulpi_driver(qcom_usb_hsic_phy_driver); 158 + 159 + MODULE_DESCRIPTION("Qualcomm USB HSIC phy"); 160 + MODULE_LICENSE("GPL v2");