1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Phy provider for USB 3.0 controller on HiSilicon 3660 platform 4 * 5 * Copyright (C) 2017-2018 Hilisicon Electronics Co., Ltd. 6 * http://www.huawei.com 7 * 8 * Authors: Yu Chen <chenyu56@huawei.com> 9 */ 10 11 #include <linux/kernel.h> 12 #include <linux/mfd/syscon.h> 13 #include <linux/module.h> 14 #include <linux/phy/phy.h> 15 #include <linux/platform_device.h> 16 #include <linux/regmap.h> 17 18 #define PERI_CRG_CLK_EN4 0x40 19 #define PERI_CRG_CLK_DIS4 0x44 20 #define GT_CLK_USB3OTG_REF BIT(0) 21 #define GT_ACLK_USB3OTG BIT(1) 22 23 #define PERI_CRG_RSTEN4 0x90 24 #define PERI_CRG_RSTDIS4 0x94 25 #define IP_RST_USB3OTGPHY_POR BIT(3) 26 #define IP_RST_USB3OTG BIT(5) 27 28 #define PERI_CRG_ISODIS 0x148 29 #define USB_REFCLK_ISO_EN BIT(25) 30 31 #define PCTRL_PERI_CTRL3 0x10 32 #define PCTRL_PERI_CTRL3_MSK_START 16 33 #define USB_TCXO_EN BIT(1) 34 35 #define PCTRL_PERI_CTRL24 0x64 36 #define SC_CLK_USB3PHY_3MUX1_SEL BIT(25) 37 38 #define USBOTG3_CTRL0 0x00 39 #define SC_USB3PHY_ABB_GT_EN BIT(15) 40 41 #define USBOTG3_CTRL2 0x08 42 #define USBOTG3CTRL2_POWERDOWN_HSP BIT(0) 43 #define USBOTG3CTRL2_POWERDOWN_SSP BIT(1) 44 45 #define USBOTG3_CTRL3 0x0C 46 #define USBOTG3_CTRL3_VBUSVLDEXT BIT(6) 47 #define USBOTG3_CTRL3_VBUSVLDEXTSEL BIT(5) 48 49 #define USBOTG3_CTRL4 0x10 50 51 #define USBOTG3_CTRL7 0x1c 52 #define REF_SSP_EN BIT(16) 53 54 /* This value config the default txtune parameter of the usb 2.0 phy */ 55 #define HI3660_USB_DEFAULT_PHY_PARAM 0x1c466e3 56 57 struct hi3660_priv { 58 struct device *dev; 59 struct regmap *peri_crg; 60 struct regmap *pctrl; 61 struct regmap *otg_bc; 62 u32 eye_diagram_param; 63 }; 64 65 static int hi3660_phy_init(struct phy *phy) 66 { 67 struct hi3660_priv *priv = phy_get_drvdata(phy); 68 u32 val, mask; 69 int ret; 70 71 /* usb refclk iso disable */ 72 ret = regmap_write(priv->peri_crg, PERI_CRG_ISODIS, USB_REFCLK_ISO_EN); 73 if (ret) 74 goto out; 75 76 /* enable usb_tcxo_en */ 77 val = USB_TCXO_EN | (USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START); 78 ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3, val); 79 if (ret) 80 goto out; 81 82 /* assert phy */ 83 val = IP_RST_USB3OTGPHY_POR | IP_RST_USB3OTG; 84 ret = regmap_write(priv->peri_crg, PERI_CRG_RSTEN4, val); 85 if (ret) 86 goto out; 87 88 /* enable phy ref clk */ 89 val = SC_USB3PHY_ABB_GT_EN; 90 mask = val; 91 ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL0, mask, val); 92 if (ret) 93 goto out; 94 95 val = REF_SSP_EN; 96 mask = val; 97 ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL7, mask, val); 98 if (ret) 99 goto out; 100 101 /* exit from IDDQ mode */ 102 mask = USBOTG3CTRL2_POWERDOWN_HSP | USBOTG3CTRL2_POWERDOWN_SSP; 103 ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL2, mask, 0); 104 if (ret) 105 goto out; 106 107 /* delay for exit from IDDQ mode */ 108 usleep_range(100, 120); 109 110 /* deassert phy */ 111 val = IP_RST_USB3OTGPHY_POR | IP_RST_USB3OTG; 112 ret = regmap_write(priv->peri_crg, PERI_CRG_RSTDIS4, val); 113 if (ret) 114 goto out; 115 116 /* delay for phy deasserted */ 117 usleep_range(10000, 15000); 118 119 /* fake vbus valid signal */ 120 val = USBOTG3_CTRL3_VBUSVLDEXT | USBOTG3_CTRL3_VBUSVLDEXTSEL; 121 mask = val; 122 ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL3, mask, val); 123 if (ret) 124 goto out; 125 126 /* delay for vbus valid */ 127 usleep_range(100, 120); 128 129 ret = regmap_write(priv->otg_bc, USBOTG3_CTRL4, 130 priv->eye_diagram_param); 131 if (ret) 132 goto out; 133 134 return 0; 135 out: 136 dev_err(priv->dev, "failed to init phy ret: %d\n", ret); 137 return ret; 138 } 139 140 static int hi3660_phy_exit(struct phy *phy) 141 { 142 struct hi3660_priv *priv = phy_get_drvdata(phy); 143 u32 val; 144 int ret; 145 146 /* assert phy */ 147 val = IP_RST_USB3OTGPHY_POR; 148 ret = regmap_write(priv->peri_crg, PERI_CRG_RSTEN4, val); 149 if (ret) 150 goto out; 151 152 /* disable usb_tcxo_en */ 153 val = USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START; 154 ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3, val); 155 if (ret) 156 goto out; 157 158 return 0; 159 out: 160 dev_err(priv->dev, "failed to exit phy ret: %d\n", ret); 161 return ret; 162 } 163 164 static struct phy_ops hi3660_phy_ops = { 165 .init = hi3660_phy_init, 166 .exit = hi3660_phy_exit, 167 .owner = THIS_MODULE, 168 }; 169 170 static int hi3660_phy_probe(struct platform_device *pdev) 171 { 172 struct phy_provider *phy_provider; 173 struct device *dev = &pdev->dev; 174 struct phy *phy; 175 struct hi3660_priv *priv; 176 177 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 178 if (!priv) 179 return -ENOMEM; 180 181 priv->dev = dev; 182 priv->peri_crg = syscon_regmap_lookup_by_phandle(dev->of_node, 183 "hisilicon,pericrg-syscon"); 184 if (IS_ERR(priv->peri_crg)) { 185 dev_err(dev, "no hisilicon,pericrg-syscon\n"); 186 return PTR_ERR(priv->peri_crg); 187 } 188 189 priv->pctrl = syscon_regmap_lookup_by_phandle(dev->of_node, 190 "hisilicon,pctrl-syscon"); 191 if (IS_ERR(priv->pctrl)) { 192 dev_err(dev, "no hisilicon,pctrl-syscon\n"); 193 return PTR_ERR(priv->pctrl); 194 } 195 196 /* node of hi3660 phy is a sub-node of usb3_otg_bc */ 197 priv->otg_bc = syscon_node_to_regmap(dev->parent->of_node); 198 if (IS_ERR(priv->otg_bc)) { 199 dev_err(dev, "no hisilicon,usb3-otg-bc-syscon\n"); 200 return PTR_ERR(priv->otg_bc); 201 } 202 203 if (of_property_read_u32(dev->of_node, "hisilicon,eye-diagram-param", 204 &(priv->eye_diagram_param))) 205 priv->eye_diagram_param = HI3660_USB_DEFAULT_PHY_PARAM; 206 207 phy = devm_phy_create(dev, NULL, &hi3660_phy_ops); 208 if (IS_ERR(phy)) 209 return PTR_ERR(phy); 210 211 phy_set_drvdata(phy, priv); 212 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 213 return PTR_ERR_OR_ZERO(phy_provider); 214 } 215 216 static const struct of_device_id hi3660_phy_of_match[] = { 217 {.compatible = "hisilicon,hi3660-usb-phy",}, 218 { } 219 }; 220 MODULE_DEVICE_TABLE(of, hi3660_phy_of_match); 221 222 static struct platform_driver hi3660_phy_driver = { 223 .probe = hi3660_phy_probe, 224 .driver = { 225 .name = "hi3660-usb-phy", 226 .of_match_table = hi3660_phy_of_match, 227 } 228 }; 229 module_platform_driver(hi3660_phy_driver); 230 231 MODULE_AUTHOR("Yu Chen <chenyu56@huawei.com>"); 232 MODULE_LICENSE("GPL v2"); 233 MODULE_DESCRIPTION("Hilisicon Hi3660 USB3 PHY Driver"); 234