]>
Commit | Line | Data |
---|---|---|
99d9ccd9 VS |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | /* | |
4 | * Sunplus SP7021 USB 2.0 phy driver | |
5 | * | |
6 | * Copyright (C) 2022 Sunplus Technology Inc., All rights reserved. | |
7 | * | |
8 | * Note 1 : non-posted write command for the registers accesses of | |
9 | * Sunplus SP7021. | |
10 | * | |
11 | */ | |
12 | ||
13 | #include <linux/bitfield.h> | |
14 | #include <linux/clk.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/io.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/nvmem-consumer.h> | |
7559e757 | 19 | #include <linux/of.h> |
99d9ccd9 VS |
20 | #include <linux/phy/phy.h> |
21 | #include <linux/platform_device.h> | |
22 | #include <linux/reset.h> | |
23 | ||
24 | #define HIGH_MASK_BITS GENMASK(31, 16) | |
25 | #define LOW_MASK_BITS GENMASK(15, 0) | |
26 | #define OTP_DISC_LEVEL_DEFAULT 0xd | |
27 | ||
28 | /* GROUP UPHY */ | |
29 | #define CONFIG1 0x4 | |
30 | #define J_HS_TX_PWRSAV BIT(5) | |
31 | #define CONFIG3 0xc | |
32 | #define J_FORCE_DISC_ON BIT(5) | |
33 | #define J_DEBUG_CTRL_ADDR_MACRO BIT(0) | |
34 | #define CONFIG7 0x1c | |
35 | #define J_DISC 0X1f | |
36 | #define CONFIG9 0x24 | |
37 | #define J_ECO_PATH BIT(6) | |
38 | #define CONFIG16 0x40 | |
39 | #define J_TBCWAIT_MASK GENMASK(6, 5) | |
40 | #define J_TBCWAIT_1P1_MS FIELD_PREP(J_TBCWAIT_MASK, 0) | |
41 | #define J_TVDM_SRC_DIS_MASK GENMASK(4, 3) | |
42 | #define J_TVDM_SRC_DIS_8P2_MS FIELD_PREP(J_TVDM_SRC_DIS_MASK, 3) | |
43 | #define J_TVDM_SRC_EN_MASK GENMASK(2, 1) | |
44 | #define J_TVDM_SRC_EN_1P6_MS FIELD_PREP(J_TVDM_SRC_EN_MASK, 0) | |
45 | #define J_BC_EN BIT(0) | |
46 | #define CONFIG17 0x44 | |
47 | #define IBG_TRIM0_MASK GENMASK(7, 5) | |
48 | #define IBG_TRIM0_SSLVHT FIELD_PREP(IBG_TRIM0_MASK, 4) | |
49 | #define J_VDATREE_TRIM_MASK GENMASK(4, 1) | |
50 | #define J_VDATREE_TRIM_DEFAULT FIELD_PREP(J_VDATREE_TRIM_MASK, 9) | |
51 | #define CONFIG23 0x5c | |
52 | #define PROB_MASK GENMASK(5, 3) | |
53 | #define PROB FIELD_PREP(PROB_MASK, 7) | |
54 | ||
55 | /* GROUP MOON4 */ | |
56 | #define UPHY_CONTROL0 0x0 | |
57 | #define UPHY_CONTROL1 0x4 | |
58 | #define UPHY_CONTROL2 0x8 | |
59 | #define MO1_UPHY_RX_CLK_SEL BIT(6) | |
60 | #define MASK_MO1_UPHY_RX_CLK_SEL BIT(6 + 16) | |
61 | #define UPHY_CONTROL3 0xc | |
62 | #define MO1_UPHY_PLL_POWER_OFF_SEL BIT(7) | |
63 | #define MASK_MO1_UPHY_PLL_POWER_OFF_SEL BIT(7 + 16) | |
64 | #define MO1_UPHY_PLL_POWER_OFF BIT(3) | |
65 | #define MASK_UPHY_PLL_POWER_OFF BIT(3 + 16) | |
66 | ||
67 | struct sp_usbphy { | |
68 | struct device *dev; | |
69 | struct resource *phy_res_mem; | |
70 | struct resource *moon4_res_mem; | |
71 | struct reset_control *rstc; | |
72 | struct clk *phy_clk; | |
73 | void __iomem *phy_regs; | |
74 | void __iomem *moon4_regs; | |
75 | u32 disc_vol_addr_off; | |
76 | }; | |
77 | ||
78 | static int update_disc_vol(struct sp_usbphy *usbphy) | |
79 | { | |
80 | struct nvmem_cell *cell; | |
81 | char *disc_name = "disc_vol"; | |
82 | ssize_t otp_l = 0; | |
83 | char *otp_v; | |
84 | u32 val, set; | |
85 | ||
86 | cell = nvmem_cell_get(usbphy->dev, disc_name); | |
87 | if (IS_ERR_OR_NULL(cell)) { | |
88 | if (PTR_ERR(cell) == -EPROBE_DEFER) | |
89 | return -EPROBE_DEFER; | |
90 | } | |
91 | ||
92 | otp_v = nvmem_cell_read(cell, &otp_l); | |
93 | nvmem_cell_put(cell); | |
94 | ||
677c577e | 95 | if (!IS_ERR(otp_v)) { |
99d9ccd9 VS |
96 | set = *(otp_v + 1); |
97 | set = (set << (sizeof(char) * 8)) | *otp_v; | |
98 | set = (set >> usbphy->disc_vol_addr_off) & J_DISC; | |
99 | } | |
100 | ||
677c577e | 101 | if (IS_ERR(otp_v) || set == 0) |
99d9ccd9 VS |
102 | set = OTP_DISC_LEVEL_DEFAULT; |
103 | ||
104 | val = readl(usbphy->phy_regs + CONFIG7); | |
105 | val = (val & ~J_DISC) | set; | |
106 | writel(val, usbphy->phy_regs + CONFIG7); | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
111 | static int sp_uphy_init(struct phy *phy) | |
112 | { | |
113 | struct sp_usbphy *usbphy = phy_get_drvdata(phy); | |
114 | u32 val; | |
115 | int ret; | |
116 | ||
117 | ret = clk_prepare_enable(usbphy->phy_clk); | |
118 | if (ret) | |
119 | goto err_clk; | |
120 | ||
121 | ret = reset_control_deassert(usbphy->rstc); | |
122 | if (ret) | |
123 | goto err_reset; | |
124 | ||
125 | /* Default value modification */ | |
126 | writel(HIGH_MASK_BITS | 0x4002, usbphy->moon4_regs + UPHY_CONTROL0); | |
127 | writel(HIGH_MASK_BITS | 0x8747, usbphy->moon4_regs + UPHY_CONTROL1); | |
128 | ||
129 | /* disconnect voltage */ | |
130 | ret = update_disc_vol(usbphy); | |
131 | if (ret < 0) | |
132 | return ret; | |
133 | ||
134 | /* board uphy 0 internal register modification for tid certification */ | |
135 | val = readl(usbphy->phy_regs + CONFIG9); | |
136 | val &= ~(J_ECO_PATH); | |
137 | writel(val, usbphy->phy_regs + CONFIG9); | |
138 | ||
139 | val = readl(usbphy->phy_regs + CONFIG1); | |
140 | val &= ~(J_HS_TX_PWRSAV); | |
141 | writel(val, usbphy->phy_regs + CONFIG1); | |
142 | ||
143 | val = readl(usbphy->phy_regs + CONFIG23); | |
144 | val = (val & ~PROB) | PROB; | |
145 | writel(val, usbphy->phy_regs + CONFIG23); | |
146 | ||
147 | /* port 0 uphy clk fix */ | |
148 | writel(MASK_MO1_UPHY_RX_CLK_SEL | MO1_UPHY_RX_CLK_SEL, | |
149 | usbphy->moon4_regs + UPHY_CONTROL2); | |
150 | ||
151 | /* battery charger */ | |
152 | writel(J_TBCWAIT_1P1_MS | J_TVDM_SRC_DIS_8P2_MS | J_TVDM_SRC_EN_1P6_MS | J_BC_EN, | |
153 | usbphy->phy_regs + CONFIG16); | |
154 | writel(IBG_TRIM0_SSLVHT | J_VDATREE_TRIM_DEFAULT, usbphy->phy_regs + CONFIG17); | |
155 | ||
156 | /* chirp mode */ | |
157 | writel(J_FORCE_DISC_ON | J_DEBUG_CTRL_ADDR_MACRO, usbphy->phy_regs + CONFIG3); | |
158 | ||
159 | return 0; | |
160 | ||
161 | err_reset: | |
162 | reset_control_assert(usbphy->rstc); | |
163 | err_clk: | |
164 | clk_disable_unprepare(usbphy->phy_clk); | |
165 | ||
166 | return ret; | |
167 | } | |
168 | ||
169 | static int sp_uphy_power_on(struct phy *phy) | |
170 | { | |
171 | struct sp_usbphy *usbphy = phy_get_drvdata(phy); | |
172 | u32 pll_pwr_on, pll_pwr_off; | |
173 | ||
174 | /* PLL power off/on twice */ | |
175 | pll_pwr_off = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS) | |
176 | | MO1_UPHY_PLL_POWER_OFF_SEL | MO1_UPHY_PLL_POWER_OFF; | |
177 | pll_pwr_on = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS) | |
178 | | MO1_UPHY_PLL_POWER_OFF_SEL; | |
179 | ||
180 | writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off, | |
181 | usbphy->moon4_regs + UPHY_CONTROL3); | |
182 | mdelay(1); | |
183 | writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_on, | |
184 | usbphy->moon4_regs + UPHY_CONTROL3); | |
185 | mdelay(1); | |
186 | writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off, | |
187 | usbphy->moon4_regs + UPHY_CONTROL3); | |
188 | mdelay(1); | |
189 | writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_on, | |
190 | usbphy->moon4_regs + UPHY_CONTROL3); | |
191 | mdelay(1); | |
192 | writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | 0x0, | |
193 | usbphy->moon4_regs + UPHY_CONTROL3); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | static int sp_uphy_power_off(struct phy *phy) | |
199 | { | |
200 | struct sp_usbphy *usbphy = phy_get_drvdata(phy); | |
201 | u32 pll_pwr_off; | |
202 | ||
203 | pll_pwr_off = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS) | |
204 | | MO1_UPHY_PLL_POWER_OFF_SEL | MO1_UPHY_PLL_POWER_OFF; | |
205 | ||
206 | writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off, | |
207 | usbphy->moon4_regs + UPHY_CONTROL3); | |
208 | mdelay(1); | |
209 | writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | 0x0, | |
210 | usbphy->moon4_regs + UPHY_CONTROL3); | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | static int sp_uphy_exit(struct phy *phy) | |
216 | { | |
217 | struct sp_usbphy *usbphy = phy_get_drvdata(phy); | |
218 | ||
219 | reset_control_assert(usbphy->rstc); | |
220 | clk_disable_unprepare(usbphy->phy_clk); | |
221 | ||
222 | return 0; | |
223 | } | |
224 | ||
225 | static const struct phy_ops sp_uphy_ops = { | |
226 | .init = sp_uphy_init, | |
227 | .power_on = sp_uphy_power_on, | |
228 | .power_off = sp_uphy_power_off, | |
229 | .exit = sp_uphy_exit, | |
230 | }; | |
231 | ||
232 | static const struct of_device_id sp_uphy_dt_ids[] = { | |
233 | {.compatible = "sunplus,sp7021-usb2-phy", }, | |
234 | { } | |
235 | }; | |
236 | MODULE_DEVICE_TABLE(of, sp_uphy_dt_ids); | |
237 | ||
238 | static int sp_usb_phy_probe(struct platform_device *pdev) | |
239 | { | |
240 | struct sp_usbphy *usbphy; | |
241 | struct phy_provider *phy_provider; | |
242 | struct phy *phy; | |
243 | int ret; | |
244 | ||
245 | usbphy = devm_kzalloc(&pdev->dev, sizeof(*usbphy), GFP_KERNEL); | |
246 | if (!usbphy) | |
247 | return -ENOMEM; | |
248 | ||
249 | usbphy->dev = &pdev->dev; | |
250 | ||
251 | usbphy->phy_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); | |
252 | usbphy->phy_regs = devm_ioremap_resource(&pdev->dev, usbphy->phy_res_mem); | |
253 | if (IS_ERR(usbphy->phy_regs)) | |
254 | return PTR_ERR(usbphy->phy_regs); | |
255 | ||
256 | usbphy->moon4_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "moon4"); | |
17eee264 SX |
257 | if (!usbphy->moon4_res_mem) |
258 | return -EINVAL; | |
259 | ||
99d9ccd9 VS |
260 | usbphy->moon4_regs = devm_ioremap(&pdev->dev, usbphy->moon4_res_mem->start, |
261 | resource_size(usbphy->moon4_res_mem)); | |
6e59419f PW |
262 | if (!usbphy->moon4_regs) |
263 | return -ENOMEM; | |
99d9ccd9 VS |
264 | |
265 | usbphy->phy_clk = devm_clk_get(&pdev->dev, NULL); | |
266 | if (IS_ERR(usbphy->phy_clk)) | |
267 | return PTR_ERR(usbphy->phy_clk); | |
268 | ||
269 | usbphy->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); | |
270 | if (IS_ERR(usbphy->rstc)) | |
271 | return PTR_ERR(usbphy->rstc); | |
272 | ||
273 | of_property_read_u32(pdev->dev.of_node, "sunplus,disc-vol-addr-off", | |
274 | &usbphy->disc_vol_addr_off); | |
275 | ||
276 | phy = devm_phy_create(&pdev->dev, NULL, &sp_uphy_ops); | |
277 | if (IS_ERR(phy)) { | |
2a9c7138 | 278 | ret = PTR_ERR(phy); |
99d9ccd9 VS |
279 | return ret; |
280 | } | |
281 | ||
282 | phy_set_drvdata(phy, usbphy); | |
283 | phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); | |
284 | ||
285 | return PTR_ERR_OR_ZERO(phy_provider); | |
286 | } | |
287 | ||
288 | static struct platform_driver sunplus_usb_phy_driver = { | |
289 | .probe = sp_usb_phy_probe, | |
290 | .driver = { | |
291 | .name = "sunplus-usb2-phy", | |
292 | .of_match_table = sp_uphy_dt_ids, | |
293 | }, | |
294 | }; | |
295 | module_platform_driver(sunplus_usb_phy_driver); | |
296 | ||
297 | MODULE_AUTHOR("Vincent Shih <vincent.shih@sunplus.com>"); | |
298 | MODULE_DESCRIPTION("Sunplus USB 2.0 phy driver"); | |
299 | MODULE_LICENSE("GPL"); |