xref: /linux/drivers/phy/samsung/phy-exynos4210-usb2.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
4  *
5  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
6  * Author: Kamil Debski <k.debski@samsung.com>
7  */
8 
9 #include <linux/delay.h>
10 #include <linux/io.h>
11 #include <linux/phy/phy.h>
12 #include <linux/regmap.h>
13 #include "phy-samsung-usb2.h"
14 
15 /* Exynos USB PHY registers */
16 
17 /* PHY power control */
18 #define EXYNOS_4210_UPHYPWR			0x0
19 
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)
29 
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)
37 
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)
43 
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)
49 
50 /* PHY clock control */
51 #define EXYNOS_4210_UPHYCLK			0x4
52 
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)
58 
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)
62 
63 /* PHY reset control */
64 #define EXYNOS_4210_UPHYRST			0x8
65 
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)
76 
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)
82 
83 /* USBYPHY1 Floating prevention */
84 #define EXYNOS_4210_UPHY1CON			0x34
85 #define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
86 
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
92 
93 enum exynos4210_phy_id {
94 	EXYNOS4210_DEVICE,
95 	EXYNOS4210_HOST,
96 	EXYNOS4210_HSIC0,
97 	EXYNOS4210_HSIC1,
98 	EXYNOS4210_NUM_PHYS,
99 };
100 
101 /*
102  * exynos4210_rate_to_clk() converts the supplied clock rate to the value that
103  * can be written to the phy register.
104  */
exynos4210_rate_to_clk(unsigned long rate,u32 * reg)105 static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
106 {
107 	switch (rate) {
108 	case 12 * MHZ:
109 		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
110 		break;
111 	case 24 * MHZ:
112 		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
113 		break;
114 	case 48 * MHZ:
115 		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
116 		break;
117 	default:
118 		return -EINVAL;
119 	}
120 
121 	return 0;
122 }
123 
exynos4210_isol(struct samsung_usb2_phy_instance * inst,bool on)124 static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
125 {
126 	struct samsung_usb2_phy_driver *drv = inst->drv;
127 	u32 offset;
128 	u32 mask;
129 
130 	switch (inst->cfg->id) {
131 	case EXYNOS4210_DEVICE:
132 		offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
133 		mask = EXYNOS_4210_USB_ISOL_DEVICE;
134 		break;
135 	case EXYNOS4210_HOST:
136 		offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
137 		mask = EXYNOS_4210_USB_ISOL_HOST;
138 		break;
139 	default:
140 		return;
141 	}
142 
143 	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
144 }
145 
exynos4210_phy_pwr(struct samsung_usb2_phy_instance * inst,bool on)146 static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
147 {
148 	struct samsung_usb2_phy_driver *drv = inst->drv;
149 	u32 rstbits = 0;
150 	u32 phypwr = 0;
151 	u32 rst;
152 	u32 pwr;
153 	u32 clk;
154 
155 	switch (inst->cfg->id) {
156 	case EXYNOS4210_DEVICE:
157 		phypwr =	EXYNOS_4210_UPHYPWR_PHY0;
158 		rstbits =	EXYNOS_4210_URSTCON_PHY0;
159 		break;
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);
168 		break;
169 	case EXYNOS4210_HSIC0:
170 		phypwr =	EXYNOS_4210_UPHYPWR_HSIC0;
171 		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
172 				EXYNOS_4210_URSTCON_HOST_LINK_P1;
173 		break;
174 	case EXYNOS4210_HSIC1:
175 		phypwr =	EXYNOS_4210_UPHYPWR_HSIC1;
176 		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
177 				EXYNOS_4210_URSTCON_HOST_LINK_P2;
178 		break;
179 	}
180 
181 	if (on) {
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);
186 
187 		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
188 		pwr &= ~phypwr;
189 		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
190 
191 		rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
192 		rst |= rstbits;
193 		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
194 		udelay(10);
195 		rst &= ~rstbits;
196 		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
197 		/* The following delay is necessary for the reset sequence to be
198 		 * completed */
199 		udelay(80);
200 	} else {
201 		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
202 		pwr |= phypwr;
203 		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
204 	}
205 }
206 
exynos4210_power_on(struct samsung_usb2_phy_instance * inst)207 static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
208 {
209 	/* Order of initialisation is important - first power then isolation */
210 	exynos4210_phy_pwr(inst, 1);
211 	exynos4210_isol(inst, 0);
212 
213 	return 0;
214 }
215 
exynos4210_power_off(struct samsung_usb2_phy_instance * inst)216 static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
217 {
218 	exynos4210_isol(inst, 1);
219 	exynos4210_phy_pwr(inst, 0);
220 
221 	return 0;
222 }
223 
224 
225 static const struct samsung_usb2_common_phy exynos4210_phys[] = {
226 	{
227 		.label		= "device",
228 		.id		= EXYNOS4210_DEVICE,
229 		.power_on	= exynos4210_power_on,
230 		.power_off	= exynos4210_power_off,
231 	},
232 	{
233 		.label		= "host",
234 		.id		= EXYNOS4210_HOST,
235 		.power_on	= exynos4210_power_on,
236 		.power_off	= exynos4210_power_off,
237 	},
238 	{
239 		.label		= "hsic0",
240 		.id		= EXYNOS4210_HSIC0,
241 		.power_on	= exynos4210_power_on,
242 		.power_off	= exynos4210_power_off,
243 	},
244 	{
245 		.label		= "hsic1",
246 		.id		= EXYNOS4210_HSIC1,
247 		.power_on	= exynos4210_power_on,
248 		.power_off	= exynos4210_power_off,
249 	},
250 };
251 
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,
257 };
258