1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2013 Samsung Electronics Co., Ltd.
6*4882a593Smuzhiyun * Authors: 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 "phy-samsung-usb2.h"
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun /* Exynos USB PHY registers */
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun /* PHY power control */
17*4882a593Smuzhiyun #define S5PV210_UPHYPWR 0x0
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0)
20*4882a593Smuzhiyun #define S5PV210_UPHYPWR_PHY0_PWR BIT(3)
21*4882a593Smuzhiyun #define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4)
22*4882a593Smuzhiyun #define S5PV210_UPHYPWR_PHY0 ( \
23*4882a593Smuzhiyun S5PV210_UPHYPWR_PHY0_SUSPEND | \
24*4882a593Smuzhiyun S5PV210_UPHYPWR_PHY0_PWR | \
25*4882a593Smuzhiyun S5PV210_UPHYPWR_PHY0_OTG_PWR)
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6)
28*4882a593Smuzhiyun #define S5PV210_UPHYPWR_PHY1_PWR BIT(7)
29*4882a593Smuzhiyun #define S5PV210_UPHYPWR_PHY1 ( \
30*4882a593Smuzhiyun S5PV210_UPHYPWR_PHY1_SUSPEND | \
31*4882a593Smuzhiyun S5PV210_UPHYPWR_PHY1_PWR)
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun /* PHY clock control */
34*4882a593Smuzhiyun #define S5PV210_UPHYCLK 0x4
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0)
37*4882a593Smuzhiyun #define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0)
38*4882a593Smuzhiyun #define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0)
39*4882a593Smuzhiyun #define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun #define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2)
42*4882a593Smuzhiyun #define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4)
43*4882a593Smuzhiyun #define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7)
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /* PHY reset control */
46*4882a593Smuzhiyun #define S5PV210_UPHYRST 0x8
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun #define S5PV210_URSTCON_PHY0 BIT(0)
49*4882a593Smuzhiyun #define S5PV210_URSTCON_OTG_HLINK BIT(1)
50*4882a593Smuzhiyun #define S5PV210_URSTCON_OTG_PHYLINK BIT(2)
51*4882a593Smuzhiyun #define S5PV210_URSTCON_PHY1_ALL BIT(3)
52*4882a593Smuzhiyun #define S5PV210_URSTCON_HOST_LINK_ALL BIT(4)
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun /* Isolation, configured in the power management unit */
55*4882a593Smuzhiyun #define S5PV210_USB_ISOL_OFFSET 0x680c
56*4882a593Smuzhiyun #define S5PV210_USB_ISOL_DEVICE BIT(0)
57*4882a593Smuzhiyun #define S5PV210_USB_ISOL_HOST BIT(1)
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun enum s5pv210_phy_id {
61*4882a593Smuzhiyun S5PV210_DEVICE,
62*4882a593Smuzhiyun S5PV210_HOST,
63*4882a593Smuzhiyun S5PV210_NUM_PHYS,
64*4882a593Smuzhiyun };
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun /*
67*4882a593Smuzhiyun * s5pv210_rate_to_clk() converts the supplied clock rate to the value that
68*4882a593Smuzhiyun * can be written to the phy register.
69*4882a593Smuzhiyun */
s5pv210_rate_to_clk(unsigned long rate,u32 * reg)70*4882a593Smuzhiyun static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun switch (rate) {
73*4882a593Smuzhiyun case 12 * MHZ:
74*4882a593Smuzhiyun *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
75*4882a593Smuzhiyun break;
76*4882a593Smuzhiyun case 24 * MHZ:
77*4882a593Smuzhiyun *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
78*4882a593Smuzhiyun break;
79*4882a593Smuzhiyun case 48 * MHZ:
80*4882a593Smuzhiyun *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
81*4882a593Smuzhiyun break;
82*4882a593Smuzhiyun default:
83*4882a593Smuzhiyun return -EINVAL;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun return 0;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
s5pv210_isol(struct samsung_usb2_phy_instance * inst,bool on)89*4882a593Smuzhiyun static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun struct samsung_usb2_phy_driver *drv = inst->drv;
92*4882a593Smuzhiyun u32 mask;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun switch (inst->cfg->id) {
95*4882a593Smuzhiyun case S5PV210_DEVICE:
96*4882a593Smuzhiyun mask = S5PV210_USB_ISOL_DEVICE;
97*4882a593Smuzhiyun break;
98*4882a593Smuzhiyun case S5PV210_HOST:
99*4882a593Smuzhiyun mask = S5PV210_USB_ISOL_HOST;
100*4882a593Smuzhiyun break;
101*4882a593Smuzhiyun default:
102*4882a593Smuzhiyun return;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET,
106*4882a593Smuzhiyun mask, on ? 0 : mask);
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
s5pv210_phy_pwr(struct samsung_usb2_phy_instance * inst,bool on)109*4882a593Smuzhiyun static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun struct samsung_usb2_phy_driver *drv = inst->drv;
112*4882a593Smuzhiyun u32 rstbits = 0;
113*4882a593Smuzhiyun u32 phypwr = 0;
114*4882a593Smuzhiyun u32 rst;
115*4882a593Smuzhiyun u32 pwr;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun switch (inst->cfg->id) {
118*4882a593Smuzhiyun case S5PV210_DEVICE:
119*4882a593Smuzhiyun phypwr = S5PV210_UPHYPWR_PHY0;
120*4882a593Smuzhiyun rstbits = S5PV210_URSTCON_PHY0;
121*4882a593Smuzhiyun break;
122*4882a593Smuzhiyun case S5PV210_HOST:
123*4882a593Smuzhiyun phypwr = S5PV210_UPHYPWR_PHY1;
124*4882a593Smuzhiyun rstbits = S5PV210_URSTCON_PHY1_ALL |
125*4882a593Smuzhiyun S5PV210_URSTCON_HOST_LINK_ALL;
126*4882a593Smuzhiyun break;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun if (on) {
130*4882a593Smuzhiyun writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK);
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
133*4882a593Smuzhiyun pwr &= ~phypwr;
134*4882a593Smuzhiyun writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun rst = readl(drv->reg_phy + S5PV210_UPHYRST);
137*4882a593Smuzhiyun rst |= rstbits;
138*4882a593Smuzhiyun writel(rst, drv->reg_phy + S5PV210_UPHYRST);
139*4882a593Smuzhiyun udelay(10);
140*4882a593Smuzhiyun rst &= ~rstbits;
141*4882a593Smuzhiyun writel(rst, drv->reg_phy + S5PV210_UPHYRST);
142*4882a593Smuzhiyun /* The following delay is necessary for the reset sequence to be
143*4882a593Smuzhiyun * completed
144*4882a593Smuzhiyun */
145*4882a593Smuzhiyun udelay(80);
146*4882a593Smuzhiyun } else {
147*4882a593Smuzhiyun pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
148*4882a593Smuzhiyun pwr |= phypwr;
149*4882a593Smuzhiyun writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
s5pv210_power_on(struct samsung_usb2_phy_instance * inst)153*4882a593Smuzhiyun static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun s5pv210_isol(inst, 0);
156*4882a593Smuzhiyun s5pv210_phy_pwr(inst, 1);
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun return 0;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
s5pv210_power_off(struct samsung_usb2_phy_instance * inst)161*4882a593Smuzhiyun static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun s5pv210_phy_pwr(inst, 0);
164*4882a593Smuzhiyun s5pv210_isol(inst, 1);
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun return 0;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = {
170*4882a593Smuzhiyun [S5PV210_DEVICE] = {
171*4882a593Smuzhiyun .label = "device",
172*4882a593Smuzhiyun .id = S5PV210_DEVICE,
173*4882a593Smuzhiyun .power_on = s5pv210_power_on,
174*4882a593Smuzhiyun .power_off = s5pv210_power_off,
175*4882a593Smuzhiyun },
176*4882a593Smuzhiyun [S5PV210_HOST] = {
177*4882a593Smuzhiyun .label = "host",
178*4882a593Smuzhiyun .id = S5PV210_HOST,
179*4882a593Smuzhiyun .power_on = s5pv210_power_on,
180*4882a593Smuzhiyun .power_off = s5pv210_power_off,
181*4882a593Smuzhiyun },
182*4882a593Smuzhiyun };
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
185*4882a593Smuzhiyun .num_phys = ARRAY_SIZE(s5pv210_phys),
186*4882a593Smuzhiyun .phys = s5pv210_phys,
187*4882a593Smuzhiyun .rate_to_clk = s5pv210_rate_to_clk,
188*4882a593Smuzhiyun };
189