194e487a4SYu Chen // SPDX-License-Identifier: GPL-2.0 294e487a4SYu Chen /* 394e487a4SYu Chen * Phy provider for USB 3.0 controller on HiSilicon 3660 platform 494e487a4SYu Chen * 594e487a4SYu Chen * Copyright (C) 2017-2018 Hilisicon Electronics Co., Ltd. 694e487a4SYu Chen * http://www.huawei.com 794e487a4SYu Chen * 894e487a4SYu Chen * Authors: Yu Chen <chenyu56@huawei.com> 994e487a4SYu Chen */ 1094e487a4SYu Chen 1194e487a4SYu Chen #include <linux/kernel.h> 1294e487a4SYu Chen #include <linux/mfd/syscon.h> 1394e487a4SYu Chen #include <linux/module.h> 1494e487a4SYu Chen #include <linux/phy/phy.h> 1594e487a4SYu Chen #include <linux/platform_device.h> 1694e487a4SYu Chen #include <linux/regmap.h> 1794e487a4SYu Chen 1894e487a4SYu Chen #define PERI_CRG_CLK_EN4 0x40 1994e487a4SYu Chen #define PERI_CRG_CLK_DIS4 0x44 2094e487a4SYu Chen #define GT_CLK_USB3OTG_REF BIT(0) 2194e487a4SYu Chen #define GT_ACLK_USB3OTG BIT(1) 2294e487a4SYu Chen 2394e487a4SYu Chen #define PERI_CRG_RSTEN4 0x90 2494e487a4SYu Chen #define PERI_CRG_RSTDIS4 0x94 2594e487a4SYu Chen #define IP_RST_USB3OTGPHY_POR BIT(3) 2694e487a4SYu Chen #define IP_RST_USB3OTG BIT(5) 2794e487a4SYu Chen 2894e487a4SYu Chen #define PERI_CRG_ISODIS 0x148 2994e487a4SYu Chen #define USB_REFCLK_ISO_EN BIT(25) 3094e487a4SYu Chen 3194e487a4SYu Chen #define PCTRL_PERI_CTRL3 0x10 3294e487a4SYu Chen #define PCTRL_PERI_CTRL3_MSK_START 16 3394e487a4SYu Chen #define USB_TCXO_EN BIT(1) 3494e487a4SYu Chen 3594e487a4SYu Chen #define PCTRL_PERI_CTRL24 0x64 3694e487a4SYu Chen #define SC_CLK_USB3PHY_3MUX1_SEL BIT(25) 3794e487a4SYu Chen 3894e487a4SYu Chen #define USBOTG3_CTRL0 0x00 3994e487a4SYu Chen #define SC_USB3PHY_ABB_GT_EN BIT(15) 4094e487a4SYu Chen 4194e487a4SYu Chen #define USBOTG3_CTRL2 0x08 4294e487a4SYu Chen #define USBOTG3CTRL2_POWERDOWN_HSP BIT(0) 4394e487a4SYu Chen #define USBOTG3CTRL2_POWERDOWN_SSP BIT(1) 4494e487a4SYu Chen 4594e487a4SYu Chen #define USBOTG3_CTRL3 0x0C 4694e487a4SYu Chen #define USBOTG3_CTRL3_VBUSVLDEXT BIT(6) 4794e487a4SYu Chen #define USBOTG3_CTRL3_VBUSVLDEXTSEL BIT(5) 4894e487a4SYu Chen 4994e487a4SYu Chen #define USBOTG3_CTRL4 0x10 5094e487a4SYu Chen 5194e487a4SYu Chen #define USBOTG3_CTRL7 0x1c 5294e487a4SYu Chen #define REF_SSP_EN BIT(16) 5394e487a4SYu Chen 5494e487a4SYu Chen /* This value config the default txtune parameter of the usb 2.0 phy */ 5594e487a4SYu Chen #define HI3660_USB_DEFAULT_PHY_PARAM 0x1c466e3 5694e487a4SYu Chen 5794e487a4SYu Chen struct hi3660_priv { 5894e487a4SYu Chen struct device *dev; 5994e487a4SYu Chen struct regmap *peri_crg; 6094e487a4SYu Chen struct regmap *pctrl; 6194e487a4SYu Chen struct regmap *otg_bc; 6294e487a4SYu Chen u32 eye_diagram_param; 6394e487a4SYu Chen }; 6494e487a4SYu Chen 6594e487a4SYu Chen static int hi3660_phy_init(struct phy *phy) 6694e487a4SYu Chen { 6794e487a4SYu Chen struct hi3660_priv *priv = phy_get_drvdata(phy); 6894e487a4SYu Chen u32 val, mask; 6994e487a4SYu Chen int ret; 7094e487a4SYu Chen 7194e487a4SYu Chen /* usb refclk iso disable */ 7294e487a4SYu Chen ret = regmap_write(priv->peri_crg, PERI_CRG_ISODIS, USB_REFCLK_ISO_EN); 7394e487a4SYu Chen if (ret) 7494e487a4SYu Chen goto out; 7594e487a4SYu Chen 7694e487a4SYu Chen /* enable usb_tcxo_en */ 7794e487a4SYu Chen val = USB_TCXO_EN | (USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START); 7894e487a4SYu Chen ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3, val); 7994e487a4SYu Chen if (ret) 8094e487a4SYu Chen goto out; 8194e487a4SYu Chen 8294e487a4SYu Chen /* assert phy */ 8394e487a4SYu Chen val = IP_RST_USB3OTGPHY_POR | IP_RST_USB3OTG; 8494e487a4SYu Chen ret = regmap_write(priv->peri_crg, PERI_CRG_RSTEN4, val); 8594e487a4SYu Chen if (ret) 8694e487a4SYu Chen goto out; 8794e487a4SYu Chen 8894e487a4SYu Chen /* enable phy ref clk */ 8994e487a4SYu Chen val = SC_USB3PHY_ABB_GT_EN; 9094e487a4SYu Chen mask = val; 9194e487a4SYu Chen ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL0, mask, val); 9294e487a4SYu Chen if (ret) 9394e487a4SYu Chen goto out; 9494e487a4SYu Chen 9594e487a4SYu Chen val = REF_SSP_EN; 9694e487a4SYu Chen mask = val; 9794e487a4SYu Chen ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL7, mask, val); 9894e487a4SYu Chen if (ret) 9994e487a4SYu Chen goto out; 10094e487a4SYu Chen 10194e487a4SYu Chen /* exit from IDDQ mode */ 10294e487a4SYu Chen mask = USBOTG3CTRL2_POWERDOWN_HSP | USBOTG3CTRL2_POWERDOWN_SSP; 10394e487a4SYu Chen ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL2, mask, 0); 10494e487a4SYu Chen if (ret) 10594e487a4SYu Chen goto out; 10694e487a4SYu Chen 10794e487a4SYu Chen /* delay for exit from IDDQ mode */ 10894e487a4SYu Chen usleep_range(100, 120); 10994e487a4SYu Chen 11094e487a4SYu Chen /* deassert phy */ 11194e487a4SYu Chen val = IP_RST_USB3OTGPHY_POR | IP_RST_USB3OTG; 11294e487a4SYu Chen ret = regmap_write(priv->peri_crg, PERI_CRG_RSTDIS4, val); 11394e487a4SYu Chen if (ret) 11494e487a4SYu Chen goto out; 11594e487a4SYu Chen 11694e487a4SYu Chen /* delay for phy deasserted */ 11794e487a4SYu Chen usleep_range(10000, 15000); 11894e487a4SYu Chen 11994e487a4SYu Chen /* fake vbus valid signal */ 12094e487a4SYu Chen val = USBOTG3_CTRL3_VBUSVLDEXT | USBOTG3_CTRL3_VBUSVLDEXTSEL; 12194e487a4SYu Chen mask = val; 12294e487a4SYu Chen ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL3, mask, val); 12394e487a4SYu Chen if (ret) 12494e487a4SYu Chen goto out; 12594e487a4SYu Chen 12694e487a4SYu Chen /* delay for vbus valid */ 12794e487a4SYu Chen usleep_range(100, 120); 12894e487a4SYu Chen 12994e487a4SYu Chen ret = regmap_write(priv->otg_bc, USBOTG3_CTRL4, 13094e487a4SYu Chen priv->eye_diagram_param); 13194e487a4SYu Chen if (ret) 13294e487a4SYu Chen goto out; 13394e487a4SYu Chen 13494e487a4SYu Chen return 0; 13594e487a4SYu Chen out: 13694e487a4SYu Chen dev_err(priv->dev, "failed to init phy ret: %d\n", ret); 13794e487a4SYu Chen return ret; 13894e487a4SYu Chen } 13994e487a4SYu Chen 14094e487a4SYu Chen static int hi3660_phy_exit(struct phy *phy) 14194e487a4SYu Chen { 14294e487a4SYu Chen struct hi3660_priv *priv = phy_get_drvdata(phy); 14394e487a4SYu Chen u32 val; 14494e487a4SYu Chen int ret; 14594e487a4SYu Chen 14694e487a4SYu Chen /* assert phy */ 14794e487a4SYu Chen val = IP_RST_USB3OTGPHY_POR; 14894e487a4SYu Chen ret = regmap_write(priv->peri_crg, PERI_CRG_RSTEN4, val); 14994e487a4SYu Chen if (ret) 15094e487a4SYu Chen goto out; 15194e487a4SYu Chen 15294e487a4SYu Chen /* disable usb_tcxo_en */ 15394e487a4SYu Chen val = USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START; 15494e487a4SYu Chen ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3, val); 15594e487a4SYu Chen if (ret) 15694e487a4SYu Chen goto out; 15794e487a4SYu Chen 15894e487a4SYu Chen return 0; 15994e487a4SYu Chen out: 16094e487a4SYu Chen dev_err(priv->dev, "failed to exit phy ret: %d\n", ret); 16194e487a4SYu Chen return ret; 16294e487a4SYu Chen } 16394e487a4SYu Chen 164fdde71d3SRikard Falkeborn static const struct phy_ops hi3660_phy_ops = { 16594e487a4SYu Chen .init = hi3660_phy_init, 16694e487a4SYu Chen .exit = hi3660_phy_exit, 16794e487a4SYu Chen .owner = THIS_MODULE, 16894e487a4SYu Chen }; 16994e487a4SYu Chen 17094e487a4SYu Chen static int hi3660_phy_probe(struct platform_device *pdev) 17194e487a4SYu Chen { 17294e487a4SYu Chen struct phy_provider *phy_provider; 17394e487a4SYu Chen struct device *dev = &pdev->dev; 17494e487a4SYu Chen struct phy *phy; 17594e487a4SYu Chen struct hi3660_priv *priv; 17694e487a4SYu Chen 17794e487a4SYu Chen priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 17894e487a4SYu Chen if (!priv) 17994e487a4SYu Chen return -ENOMEM; 18094e487a4SYu Chen 18194e487a4SYu Chen priv->dev = dev; 18294e487a4SYu Chen priv->peri_crg = syscon_regmap_lookup_by_phandle(dev->of_node, 18394e487a4SYu Chen "hisilicon,pericrg-syscon"); 18494e487a4SYu Chen if (IS_ERR(priv->peri_crg)) { 18594e487a4SYu Chen dev_err(dev, "no hisilicon,pericrg-syscon\n"); 18694e487a4SYu Chen return PTR_ERR(priv->peri_crg); 18794e487a4SYu Chen } 18894e487a4SYu Chen 18994e487a4SYu Chen priv->pctrl = syscon_regmap_lookup_by_phandle(dev->of_node, 19094e487a4SYu Chen "hisilicon,pctrl-syscon"); 19194e487a4SYu Chen if (IS_ERR(priv->pctrl)) { 19294e487a4SYu Chen dev_err(dev, "no hisilicon,pctrl-syscon\n"); 19394e487a4SYu Chen return PTR_ERR(priv->pctrl); 19494e487a4SYu Chen } 19594e487a4SYu Chen 19694e487a4SYu Chen /* node of hi3660 phy is a sub-node of usb3_otg_bc */ 19794e487a4SYu Chen priv->otg_bc = syscon_node_to_regmap(dev->parent->of_node); 19894e487a4SYu Chen if (IS_ERR(priv->otg_bc)) { 19994e487a4SYu Chen dev_err(dev, "no hisilicon,usb3-otg-bc-syscon\n"); 20094e487a4SYu Chen return PTR_ERR(priv->otg_bc); 20194e487a4SYu Chen } 20294e487a4SYu Chen 20394e487a4SYu Chen if (of_property_read_u32(dev->of_node, "hisilicon,eye-diagram-param", 20494e487a4SYu Chen &(priv->eye_diagram_param))) 20594e487a4SYu Chen priv->eye_diagram_param = HI3660_USB_DEFAULT_PHY_PARAM; 20694e487a4SYu Chen 20794e487a4SYu Chen phy = devm_phy_create(dev, NULL, &hi3660_phy_ops); 20894e487a4SYu Chen if (IS_ERR(phy)) 20994e487a4SYu Chen return PTR_ERR(phy); 21094e487a4SYu Chen 21194e487a4SYu Chen phy_set_drvdata(phy, priv); 21294e487a4SYu Chen phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 21394e487a4SYu Chen return PTR_ERR_OR_ZERO(phy_provider); 21494e487a4SYu Chen } 21594e487a4SYu Chen 21694e487a4SYu Chen static const struct of_device_id hi3660_phy_of_match[] = { 21794e487a4SYu Chen {.compatible = "hisilicon,hi3660-usb-phy",}, 21894e487a4SYu Chen { } 21994e487a4SYu Chen }; 22094e487a4SYu Chen MODULE_DEVICE_TABLE(of, hi3660_phy_of_match); 22194e487a4SYu Chen 22294e487a4SYu Chen static struct platform_driver hi3660_phy_driver = { 22394e487a4SYu Chen .probe = hi3660_phy_probe, 22494e487a4SYu Chen .driver = { 22594e487a4SYu Chen .name = "hi3660-usb-phy", 22694e487a4SYu Chen .of_match_table = hi3660_phy_of_match, 22794e487a4SYu Chen } 22894e487a4SYu Chen }; 22994e487a4SYu Chen module_platform_driver(hi3660_phy_driver); 23094e487a4SYu Chen 23194e487a4SYu Chen MODULE_AUTHOR("Yu Chen <chenyu56@huawei.com>"); 23294e487a4SYu Chen MODULE_LICENSE("GPL v2"); 23394e487a4SYu Chen MODULE_DESCRIPTION("Hilisicon Hi3660 USB3 PHY Driver"); 234