1 // SPDX-License-Identifier: GPL-2.0-only
3 * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
5 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
6 * Author: Kamil Debski <k.debski@samsung.com>
9 #include <linux/delay.h>
11 #include <linux/phy/phy.h>
12 #include <linux/regmap.h>
13 #include "phy-samsung-usb2.h"
15 /* Exynos USB PHY registers */
17 /* PHY power control */
18 #define EXYNOS_4210_UPHYPWR 0x0
20 #define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0)
21 #define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3)
22 #define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4)
23 #define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5)
24 #define EXYNOS_4210_UPHYPWR_PHY0 ( \
25 EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
26 EXYNOS_4210_UPHYPWR_PHY0_PWR | \
27 EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
28 EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
30 #define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6)
31 #define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7)
32 #define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8)
33 #define EXYNOS_4210_UPHYPWR_PHY1 ( \
34 EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
35 EXYNOS_4210_UPHYPWR_PHY1_PWR | \
36 EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
38 #define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9)
39 #define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10)
40 #define EXYNOS_4210_UPHYPWR_HSIC0 ( \
41 EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
42 EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
44 #define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11)
45 #define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12)
46 #define EXYNOS_4210_UPHYPWR_HSIC1 ( \
47 EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
48 EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
50 /* PHY clock control */
51 #define EXYNOS_4210_UPHYCLK 0x4
53 #define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0)
54 #define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0
55 #define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0)
56 #define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0)
57 #define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
59 #define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2)
60 #define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4)
61 #define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7)
63 /* PHY reset control */
64 #define EXYNOS_4210_UPHYRST 0x8
66 #define EXYNOS_4210_URSTCON_PHY0 BIT(0)
67 #define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1)
68 #define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2)
69 #define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3)
70 #define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4)
71 #define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5)
72 #define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6)
73 #define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7)
74 #define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8)
75 #define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9)
77 /* Isolation, configured in the power management unit */
78 #define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704
79 #define EXYNOS_4210_USB_ISOL_DEVICE BIT(0)
80 #define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708
81 #define EXYNOS_4210_USB_ISOL_HOST BIT(0)
83 /* USBYPHY1 Floating prevention */
84 #define EXYNOS_4210_UPHY1CON 0x34
85 #define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1
87 /* Mode switching SUB Device <-> Host */
88 #define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c
89 #define EXYNOS_4210_MODE_SWITCH_MASK 1
90 #define EXYNOS_4210_MODE_SWITCH_DEVICE 0
91 #define EXYNOS_4210_MODE_SWITCH_HOST 1
93 enum exynos4210_phy_id
{
102 * exynos4210_rate_to_clk() converts the supplied clock rate to the value that
103 * can be written to the phy register.
105 static int exynos4210_rate_to_clk(unsigned long rate
, u32
*reg
)
109 *reg
= EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ
;
112 *reg
= EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ
;
115 *reg
= EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ
;
124 static void exynos4210_isol(struct samsung_usb2_phy_instance
*inst
, bool on
)
126 struct samsung_usb2_phy_driver
*drv
= inst
->drv
;
130 switch (inst
->cfg
->id
) {
131 case EXYNOS4210_DEVICE
:
132 offset
= EXYNOS_4210_USB_ISOL_DEVICE_OFFSET
;
133 mask
= EXYNOS_4210_USB_ISOL_DEVICE
;
135 case EXYNOS4210_HOST
:
136 offset
= EXYNOS_4210_USB_ISOL_HOST_OFFSET
;
137 mask
= EXYNOS_4210_USB_ISOL_HOST
;
143 regmap_update_bits(drv
->reg_pmu
, offset
, mask
, on
? 0 : mask
);
146 static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance
*inst
, bool on
)
148 struct samsung_usb2_phy_driver
*drv
= inst
->drv
;
155 switch (inst
->cfg
->id
) {
156 case EXYNOS4210_DEVICE
:
157 phypwr
= EXYNOS_4210_UPHYPWR_PHY0
;
158 rstbits
= EXYNOS_4210_URSTCON_PHY0
;
160 case EXYNOS4210_HOST
:
161 phypwr
= EXYNOS_4210_UPHYPWR_PHY1
;
162 rstbits
= EXYNOS_4210_URSTCON_PHY1_ALL
|
163 EXYNOS_4210_URSTCON_PHY1_P0
|
164 EXYNOS_4210_URSTCON_PHY1_P1P2
|
165 EXYNOS_4210_URSTCON_HOST_LINK_ALL
|
166 EXYNOS_4210_URSTCON_HOST_LINK_P0
;
167 writel(on
, drv
->reg_phy
+ EXYNOS_4210_UPHY1CON
);
169 case EXYNOS4210_HSIC0
:
170 phypwr
= EXYNOS_4210_UPHYPWR_HSIC0
;
171 rstbits
= EXYNOS_4210_URSTCON_PHY1_P1P2
|
172 EXYNOS_4210_URSTCON_HOST_LINK_P1
;
174 case EXYNOS4210_HSIC1
:
175 phypwr
= EXYNOS_4210_UPHYPWR_HSIC1
;
176 rstbits
= EXYNOS_4210_URSTCON_PHY1_P1P2
|
177 EXYNOS_4210_URSTCON_HOST_LINK_P2
;
182 clk
= readl(drv
->reg_phy
+ EXYNOS_4210_UPHYCLK
);
183 clk
&= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK
;
184 clk
|= drv
->ref_reg_val
<< EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET
;
185 writel(clk
, drv
->reg_phy
+ EXYNOS_4210_UPHYCLK
);
187 pwr
= readl(drv
->reg_phy
+ EXYNOS_4210_UPHYPWR
);
189 writel(pwr
, drv
->reg_phy
+ EXYNOS_4210_UPHYPWR
);
191 rst
= readl(drv
->reg_phy
+ EXYNOS_4210_UPHYRST
);
193 writel(rst
, drv
->reg_phy
+ EXYNOS_4210_UPHYRST
);
196 writel(rst
, drv
->reg_phy
+ EXYNOS_4210_UPHYRST
);
197 /* The following delay is necessary for the reset sequence to be
201 pwr
= readl(drv
->reg_phy
+ EXYNOS_4210_UPHYPWR
);
203 writel(pwr
, drv
->reg_phy
+ EXYNOS_4210_UPHYPWR
);
207 static int exynos4210_power_on(struct samsung_usb2_phy_instance
*inst
)
209 /* Order of initialisation is important - first power then isolation */
210 exynos4210_phy_pwr(inst
, 1);
211 exynos4210_isol(inst
, 0);
216 static int exynos4210_power_off(struct samsung_usb2_phy_instance
*inst
)
218 exynos4210_isol(inst
, 1);
219 exynos4210_phy_pwr(inst
, 0);
225 static const struct samsung_usb2_common_phy exynos4210_phys
[] = {
228 .id
= EXYNOS4210_DEVICE
,
229 .power_on
= exynos4210_power_on
,
230 .power_off
= exynos4210_power_off
,
234 .id
= EXYNOS4210_HOST
,
235 .power_on
= exynos4210_power_on
,
236 .power_off
= exynos4210_power_off
,
240 .id
= EXYNOS4210_HSIC0
,
241 .power_on
= exynos4210_power_on
,
242 .power_off
= exynos4210_power_off
,
246 .id
= EXYNOS4210_HSIC1
,
247 .power_on
= exynos4210_power_on
,
248 .power_off
= exynos4210_power_off
,
252 const struct samsung_usb2_phy_config exynos4210_usb2_phy_config
= {
253 .has_mode_switch
= 0,
254 .num_phys
= EXYNOS4210_NUM_PHYS
,
255 .phys
= exynos4210_phys
,
256 .rate_to_clk
= exynos4210_rate_to_clk
,