1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2013 Samsung Electronics Co., Ltd.
6*4882a593Smuzhiyun * Author: Kamil Debski <k.debski@samsung.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/delay.h>
10*4882a593Smuzhiyun #include <linux/io.h>
11*4882a593Smuzhiyun #include <linux/phy/phy.h>
12*4882a593Smuzhiyun #include <linux/regmap.h>
13*4882a593Smuzhiyun #include "phy-samsung-usb2.h"
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun /* Exynos USB PHY registers */
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun /* PHY power control */
18*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR 0x0
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0)
21*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3)
22*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4)
23*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5)
24*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_PHY0 ( \
25*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
26*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_PHY0_PWR | \
27*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
28*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6)
31*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7)
32*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8)
33*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_PHY1 ( \
34*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
35*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_PHY1_PWR | \
36*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9)
39*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10)
40*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_HSIC0 ( \
41*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
42*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11)
45*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12)
46*4882a593Smuzhiyun #define EXYNOS_4210_UPHYPWR_HSIC1 ( \
47*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
48*4882a593Smuzhiyun EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun /* PHY clock control */
51*4882a593Smuzhiyun #define EXYNOS_4210_UPHYCLK 0x4
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun #define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0)
54*4882a593Smuzhiyun #define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0
55*4882a593Smuzhiyun #define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0)
56*4882a593Smuzhiyun #define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0)
57*4882a593Smuzhiyun #define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun #define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2)
60*4882a593Smuzhiyun #define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4)
61*4882a593Smuzhiyun #define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7)
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun /* PHY reset control */
64*4882a593Smuzhiyun #define EXYNOS_4210_UPHYRST 0x8
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun #define EXYNOS_4210_URSTCON_PHY0 BIT(0)
67*4882a593Smuzhiyun #define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1)
68*4882a593Smuzhiyun #define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2)
69*4882a593Smuzhiyun #define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3)
70*4882a593Smuzhiyun #define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4)
71*4882a593Smuzhiyun #define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5)
72*4882a593Smuzhiyun #define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6)
73*4882a593Smuzhiyun #define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7)
74*4882a593Smuzhiyun #define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8)
75*4882a593Smuzhiyun #define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9)
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun /* Isolation, configured in the power management unit */
78*4882a593Smuzhiyun #define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704
79*4882a593Smuzhiyun #define EXYNOS_4210_USB_ISOL_DEVICE BIT(0)
80*4882a593Smuzhiyun #define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708
81*4882a593Smuzhiyun #define EXYNOS_4210_USB_ISOL_HOST BIT(0)
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun /* USBYPHY1 Floating prevention */
84*4882a593Smuzhiyun #define EXYNOS_4210_UPHY1CON 0x34
85*4882a593Smuzhiyun #define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun /* Mode switching SUB Device <-> Host */
88*4882a593Smuzhiyun #define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c
89*4882a593Smuzhiyun #define EXYNOS_4210_MODE_SWITCH_MASK 1
90*4882a593Smuzhiyun #define EXYNOS_4210_MODE_SWITCH_DEVICE 0
91*4882a593Smuzhiyun #define EXYNOS_4210_MODE_SWITCH_HOST 1
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun enum exynos4210_phy_id {
94*4882a593Smuzhiyun EXYNOS4210_DEVICE,
95*4882a593Smuzhiyun EXYNOS4210_HOST,
96*4882a593Smuzhiyun EXYNOS4210_HSIC0,
97*4882a593Smuzhiyun EXYNOS4210_HSIC1,
98*4882a593Smuzhiyun EXYNOS4210_NUM_PHYS,
99*4882a593Smuzhiyun };
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun /*
102*4882a593Smuzhiyun * exynos4210_rate_to_clk() converts the supplied clock rate to the value that
103*4882a593Smuzhiyun * can be written to the phy register.
104*4882a593Smuzhiyun */
exynos4210_rate_to_clk(unsigned long rate,u32 * reg)105*4882a593Smuzhiyun static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun switch (rate) {
108*4882a593Smuzhiyun case 12 * MHZ:
109*4882a593Smuzhiyun *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
110*4882a593Smuzhiyun break;
111*4882a593Smuzhiyun case 24 * MHZ:
112*4882a593Smuzhiyun *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
113*4882a593Smuzhiyun break;
114*4882a593Smuzhiyun case 48 * MHZ:
115*4882a593Smuzhiyun *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
116*4882a593Smuzhiyun break;
117*4882a593Smuzhiyun default:
118*4882a593Smuzhiyun return -EINVAL;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun return 0;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun
exynos4210_isol(struct samsung_usb2_phy_instance * inst,bool on)124*4882a593Smuzhiyun static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun struct samsung_usb2_phy_driver *drv = inst->drv;
127*4882a593Smuzhiyun u32 offset;
128*4882a593Smuzhiyun u32 mask;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun switch (inst->cfg->id) {
131*4882a593Smuzhiyun case EXYNOS4210_DEVICE:
132*4882a593Smuzhiyun offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
133*4882a593Smuzhiyun mask = EXYNOS_4210_USB_ISOL_DEVICE;
134*4882a593Smuzhiyun break;
135*4882a593Smuzhiyun case EXYNOS4210_HOST:
136*4882a593Smuzhiyun offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
137*4882a593Smuzhiyun mask = EXYNOS_4210_USB_ISOL_HOST;
138*4882a593Smuzhiyun break;
139*4882a593Smuzhiyun default:
140*4882a593Smuzhiyun return;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
exynos4210_phy_pwr(struct samsung_usb2_phy_instance * inst,bool on)146*4882a593Smuzhiyun static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun struct samsung_usb2_phy_driver *drv = inst->drv;
149*4882a593Smuzhiyun u32 rstbits = 0;
150*4882a593Smuzhiyun u32 phypwr = 0;
151*4882a593Smuzhiyun u32 rst;
152*4882a593Smuzhiyun u32 pwr;
153*4882a593Smuzhiyun u32 clk;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun switch (inst->cfg->id) {
156*4882a593Smuzhiyun case EXYNOS4210_DEVICE:
157*4882a593Smuzhiyun phypwr = EXYNOS_4210_UPHYPWR_PHY0;
158*4882a593Smuzhiyun rstbits = EXYNOS_4210_URSTCON_PHY0;
159*4882a593Smuzhiyun break;
160*4882a593Smuzhiyun case EXYNOS4210_HOST:
161*4882a593Smuzhiyun phypwr = EXYNOS_4210_UPHYPWR_PHY1;
162*4882a593Smuzhiyun rstbits = EXYNOS_4210_URSTCON_PHY1_ALL |
163*4882a593Smuzhiyun EXYNOS_4210_URSTCON_PHY1_P0 |
164*4882a593Smuzhiyun EXYNOS_4210_URSTCON_PHY1_P1P2 |
165*4882a593Smuzhiyun EXYNOS_4210_URSTCON_HOST_LINK_ALL |
166*4882a593Smuzhiyun EXYNOS_4210_URSTCON_HOST_LINK_P0;
167*4882a593Smuzhiyun writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
168*4882a593Smuzhiyun break;
169*4882a593Smuzhiyun case EXYNOS4210_HSIC0:
170*4882a593Smuzhiyun phypwr = EXYNOS_4210_UPHYPWR_HSIC0;
171*4882a593Smuzhiyun rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 |
172*4882a593Smuzhiyun EXYNOS_4210_URSTCON_HOST_LINK_P1;
173*4882a593Smuzhiyun break;
174*4882a593Smuzhiyun case EXYNOS4210_HSIC1:
175*4882a593Smuzhiyun phypwr = EXYNOS_4210_UPHYPWR_HSIC1;
176*4882a593Smuzhiyun rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 |
177*4882a593Smuzhiyun EXYNOS_4210_URSTCON_HOST_LINK_P2;
178*4882a593Smuzhiyun break;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun if (on) {
182*4882a593Smuzhiyun clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK);
183*4882a593Smuzhiyun clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK;
184*4882a593Smuzhiyun clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET;
185*4882a593Smuzhiyun writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK);
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
188*4882a593Smuzhiyun pwr &= ~phypwr;
189*4882a593Smuzhiyun writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
192*4882a593Smuzhiyun rst |= rstbits;
193*4882a593Smuzhiyun writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
194*4882a593Smuzhiyun udelay(10);
195*4882a593Smuzhiyun rst &= ~rstbits;
196*4882a593Smuzhiyun writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
197*4882a593Smuzhiyun /* The following delay is necessary for the reset sequence to be
198*4882a593Smuzhiyun * completed */
199*4882a593Smuzhiyun udelay(80);
200*4882a593Smuzhiyun } else {
201*4882a593Smuzhiyun pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
202*4882a593Smuzhiyun pwr |= phypwr;
203*4882a593Smuzhiyun writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
exynos4210_power_on(struct samsung_usb2_phy_instance * inst)207*4882a593Smuzhiyun static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun /* Order of initialisation is important - first power then isolation */
210*4882a593Smuzhiyun exynos4210_phy_pwr(inst, 1);
211*4882a593Smuzhiyun exynos4210_isol(inst, 0);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun return 0;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun
exynos4210_power_off(struct samsung_usb2_phy_instance * inst)216*4882a593Smuzhiyun static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun exynos4210_isol(inst, 1);
219*4882a593Smuzhiyun exynos4210_phy_pwr(inst, 0);
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun return 0;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun static const struct samsung_usb2_common_phy exynos4210_phys[] = {
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun .label = "device",
228*4882a593Smuzhiyun .id = EXYNOS4210_DEVICE,
229*4882a593Smuzhiyun .power_on = exynos4210_power_on,
230*4882a593Smuzhiyun .power_off = exynos4210_power_off,
231*4882a593Smuzhiyun },
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun .label = "host",
234*4882a593Smuzhiyun .id = EXYNOS4210_HOST,
235*4882a593Smuzhiyun .power_on = exynos4210_power_on,
236*4882a593Smuzhiyun .power_off = exynos4210_power_off,
237*4882a593Smuzhiyun },
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun .label = "hsic0",
240*4882a593Smuzhiyun .id = EXYNOS4210_HSIC0,
241*4882a593Smuzhiyun .power_on = exynos4210_power_on,
242*4882a593Smuzhiyun .power_off = exynos4210_power_off,
243*4882a593Smuzhiyun },
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun .label = "hsic1",
246*4882a593Smuzhiyun .id = EXYNOS4210_HSIC1,
247*4882a593Smuzhiyun .power_on = exynos4210_power_on,
248*4882a593Smuzhiyun .power_off = exynos4210_power_off,
249*4882a593Smuzhiyun },
250*4882a593Smuzhiyun };
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
253*4882a593Smuzhiyun .has_mode_switch = 0,
254*4882a593Smuzhiyun .num_phys = EXYNOS4210_NUM_PHYS,
255*4882a593Smuzhiyun .phys = exynos4210_phys,
256*4882a593Smuzhiyun .rate_to_clk = exynos4210_rate_to_clk,
257*4882a593Smuzhiyun };
258