1 // SPDX-License-Identifier: GPL-2.0
3 * Phy provider for USB 3.0 controller on HiSilicon 3660 platform
5 * Copyright (C) 2017-2018 Hilisicon Electronics Co., Ltd.
6 * http://www.huawei.com
8 * Authors: Yu Chen <chenyu56@huawei.com>
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>
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)
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)
28 #define PERI_CRG_ISODIS 0x148
29 #define USB_REFCLK_ISO_EN BIT(25)
31 #define PCTRL_PERI_CTRL3 0x10
32 #define PCTRL_PERI_CTRL3_MSK_START 16
33 #define USB_TCXO_EN BIT(1)
35 #define PCTRL_PERI_CTRL24 0x64
36 #define SC_CLK_USB3PHY_3MUX1_SEL BIT(25)
38 #define USBOTG3_CTRL0 0x00
39 #define SC_USB3PHY_ABB_GT_EN BIT(15)
41 #define USBOTG3_CTRL2 0x08
42 #define USBOTG3CTRL2_POWERDOWN_HSP BIT(0)
43 #define USBOTG3CTRL2_POWERDOWN_SSP BIT(1)
45 #define USBOTG3_CTRL3 0x0C
46 #define USBOTG3_CTRL3_VBUSVLDEXT BIT(6)
47 #define USBOTG3_CTRL3_VBUSVLDEXTSEL BIT(5)
49 #define USBOTG3_CTRL4 0x10
51 #define USBOTG3_CTRL7 0x1c
52 #define REF_SSP_EN BIT(16)
54 /* This value config the default txtune parameter of the usb 2.0 phy */
55 #define HI3660_USB_DEFAULT_PHY_PARAM 0x1c466e3
59 struct regmap
*peri_crg
;
61 struct regmap
*otg_bc
;
62 u32 eye_diagram_param
;
65 static int hi3660_phy_init(struct phy
*phy
)
67 struct hi3660_priv
*priv
= phy_get_drvdata(phy
);
71 /* usb refclk iso disable */
72 ret
= regmap_write(priv
->peri_crg
, PERI_CRG_ISODIS
, USB_REFCLK_ISO_EN
);
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
);
83 val
= IP_RST_USB3OTGPHY_POR
| IP_RST_USB3OTG
;
84 ret
= regmap_write(priv
->peri_crg
, PERI_CRG_RSTEN4
, val
);
88 /* enable phy ref clk */
89 val
= SC_USB3PHY_ABB_GT_EN
;
91 ret
= regmap_update_bits(priv
->otg_bc
, USBOTG3_CTRL0
, mask
, val
);
97 ret
= regmap_update_bits(priv
->otg_bc
, USBOTG3_CTRL7
, mask
, val
);
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);
107 /* delay for exit from IDDQ mode */
108 usleep_range(100, 120);
111 val
= IP_RST_USB3OTGPHY_POR
| IP_RST_USB3OTG
;
112 ret
= regmap_write(priv
->peri_crg
, PERI_CRG_RSTDIS4
, val
);
116 /* delay for phy deasserted */
117 usleep_range(10000, 15000);
119 /* fake vbus valid signal */
120 val
= USBOTG3_CTRL3_VBUSVLDEXT
| USBOTG3_CTRL3_VBUSVLDEXTSEL
;
122 ret
= regmap_update_bits(priv
->otg_bc
, USBOTG3_CTRL3
, mask
, val
);
126 /* delay for vbus valid */
127 usleep_range(100, 120);
129 ret
= regmap_write(priv
->otg_bc
, USBOTG3_CTRL4
,
130 priv
->eye_diagram_param
);
136 dev_err(priv
->dev
, "failed to init phy ret: %d\n", ret
);
140 static int hi3660_phy_exit(struct phy
*phy
)
142 struct hi3660_priv
*priv
= phy_get_drvdata(phy
);
147 val
= IP_RST_USB3OTGPHY_POR
;
148 ret
= regmap_write(priv
->peri_crg
, PERI_CRG_RSTEN4
, val
);
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
);
160 dev_err(priv
->dev
, "failed to exit phy ret: %d\n", ret
);
164 static struct phy_ops hi3660_phy_ops
= {
165 .init
= hi3660_phy_init
,
166 .exit
= hi3660_phy_exit
,
167 .owner
= THIS_MODULE
,
170 static int hi3660_phy_probe(struct platform_device
*pdev
)
172 struct phy_provider
*phy_provider
;
173 struct device
*dev
= &pdev
->dev
;
175 struct hi3660_priv
*priv
;
177 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
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
);
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
);
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
);
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
;
207 phy
= devm_phy_create(dev
, NULL
, &hi3660_phy_ops
);
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
);
216 static const struct of_device_id hi3660_phy_of_match
[] = {
217 {.compatible
= "hisilicon,hi3660-usb-phy",},
220 MODULE_DEVICE_TABLE(of
, hi3660_phy_of_match
);
222 static struct platform_driver hi3660_phy_driver
= {
223 .probe
= hi3660_phy_probe
,
225 .name
= "hi3660-usb-phy",
226 .of_match_table
= hi3660_phy_of_match
,
229 module_platform_driver(hi3660_phy_driver
);
231 MODULE_AUTHOR("Yu Chen <chenyu56@huawei.com>");
232 MODULE_LICENSE("GPL v2");
233 MODULE_DESCRIPTION("Hilisicon Hi3660 USB3 PHY Driver");