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