Commit | Line | Data |
---|---|---|
dc2377d0 | 1 | /* linux/drivers/usb/phy/phy-samsung-usb.c |
337dc3a7 PP |
2 | * |
3 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | |
4 | * http://www.samsung.com | |
5 | * | |
6 | * Author: Praveen Paneri <p.paneri@samsung.com> | |
7 | * | |
dc2377d0 VG |
8 | * Samsung USB-PHY helper driver with common function calls; |
9 | * interacts with Samsung USB 2.0 PHY controller driver and later | |
10 | * with Samsung USB 3.0 PHY driver. | |
337dc3a7 PP |
11 | * |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License version 2 as | |
14 | * published by the Free Software Foundation. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | */ | |
21 | ||
22 | #include <linux/module.h> | |
23 | #include <linux/platform_device.h> | |
24 | #include <linux/clk.h> | |
8c1b3e16 | 25 | #include <linux/device.h> |
337dc3a7 PP |
26 | #include <linux/err.h> |
27 | #include <linux/io.h> | |
28 | #include <linux/of.h> | |
69f0946a | 29 | #include <linux/of_address.h> |
8c1b3e16 | 30 | #include <linux/usb/samsung_usb_phy.h> |
337dc3a7 | 31 | |
dc2377d0 | 32 | #include "phy-samsung-usb.h" |
337dc3a7 | 33 | |
dc2377d0 | 34 | int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) |
69f0946a VG |
35 | { |
36 | struct device_node *usbphy_sys; | |
37 | ||
38 | /* Getting node for system controller interface for usb-phy */ | |
39 | usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys"); | |
40 | if (!usbphy_sys) { | |
41 | dev_err(sphy->dev, "No sys-controller interface for usb-phy\n"); | |
42 | return -ENODEV; | |
43 | } | |
44 | ||
45 | sphy->pmuregs = of_iomap(usbphy_sys, 0); | |
46 | ||
69f0946a VG |
47 | if (sphy->pmuregs == NULL) { |
48 | dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); | |
8c1b3e16 | 49 | goto err0; |
69f0946a VG |
50 | } |
51 | ||
8c1b3e16 VG |
52 | sphy->sysreg = of_iomap(usbphy_sys, 1); |
53 | ||
54 | /* | |
55 | * Not returning error code here, since this situation is not fatal. | |
56 | * Few SoCs may not have this switch available | |
57 | */ | |
58 | if (sphy->sysreg == NULL) | |
59 | dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n"); | |
60 | ||
61 | of_node_put(usbphy_sys); | |
62 | ||
69f0946a | 63 | return 0; |
8c1b3e16 VG |
64 | |
65 | err0: | |
66 | of_node_put(usbphy_sys); | |
67 | return -ENXIO; | |
69f0946a | 68 | } |
dc2377d0 | 69 | EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt); |
69f0946a VG |
70 | |
71 | /* | |
72 | * Set isolation here for phy. | |
73 | * Here 'on = true' would mean USB PHY block is isolated, hence | |
74 | * de-activated and vice-versa. | |
75 | */ | |
3f339074 | 76 | void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy, bool on) |
69f0946a | 77 | { |
8c1b3e16 | 78 | void __iomem *reg = NULL; |
69f0946a | 79 | u32 reg_val; |
8c1b3e16 | 80 | u32 en_mask = 0; |
69f0946a VG |
81 | |
82 | if (!sphy->pmuregs) { | |
83 | dev_warn(sphy->dev, "Can't set pmu isolation\n"); | |
84 | return; | |
85 | } | |
86 | ||
3f339074 TF |
87 | if (sphy->phy_type == USB_PHY_TYPE_DEVICE) { |
88 | reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset; | |
89 | en_mask = sphy->drv_data->devphy_en_mask; | |
90 | } else if (sphy->phy_type == USB_PHY_TYPE_HOST) { | |
91 | reg = sphy->pmuregs + sphy->drv_data->hostphy_reg_offset; | |
92 | en_mask = sphy->drv_data->hostphy_en_mask; | |
8c1b3e16 | 93 | } |
69f0946a VG |
94 | |
95 | reg_val = readl(reg); | |
96 | ||
97 | if (on) | |
98 | reg_val &= ~en_mask; | |
99 | else | |
100 | reg_val |= en_mask; | |
101 | ||
102 | writel(reg_val, reg); | |
6d3d61f8 DK |
103 | |
104 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12) { | |
105 | writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL0); | |
106 | writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL1); | |
107 | } | |
69f0946a | 108 | } |
3f339074 | 109 | EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation_4210); |
69f0946a | 110 | |
8c1b3e16 VG |
111 | /* |
112 | * Configure the mode of working of usb-phy here: HOST/DEVICE. | |
113 | */ | |
dc2377d0 | 114 | void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) |
8c1b3e16 VG |
115 | { |
116 | u32 reg; | |
117 | ||
118 | if (!sphy->sysreg) { | |
119 | dev_warn(sphy->dev, "Can't configure specified phy mode\n"); | |
120 | return; | |
121 | } | |
122 | ||
123 | reg = readl(sphy->sysreg); | |
124 | ||
125 | if (sphy->phy_type == USB_PHY_TYPE_DEVICE) | |
126 | reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK; | |
127 | else if (sphy->phy_type == USB_PHY_TYPE_HOST) | |
128 | reg |= EXYNOS_USB20PHY_CFG_HOST_LINK; | |
129 | ||
130 | writel(reg, sphy->sysreg); | |
131 | } | |
dc2377d0 | 132 | EXPORT_SYMBOL_GPL(samsung_usbphy_cfg_sel); |
8c1b3e16 VG |
133 | |
134 | /* | |
135 | * PHYs are different for USB Device and USB Host. | |
136 | * This make sure that correct PHY type is selected before | |
137 | * any operation on PHY. | |
138 | */ | |
dc2377d0 | 139 | int samsung_usbphy_set_type(struct usb_phy *phy, |
8c1b3e16 VG |
140 | enum samsung_usb_phy_type phy_type) |
141 | { | |
142 | struct samsung_usbphy *sphy = phy_to_sphy(phy); | |
143 | ||
144 | sphy->phy_type = phy_type; | |
145 | ||
146 | return 0; | |
147 | } | |
dc2377d0 | 148 | EXPORT_SYMBOL_GPL(samsung_usbphy_set_type); |
8c1b3e16 | 149 | |
0aa823a2 TF |
150 | int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy, |
151 | unsigned long rate) | |
152 | { | |
153 | unsigned int clksel; | |
154 | ||
155 | switch (rate) { | |
156 | case 12 * MHZ: | |
157 | clksel = PHYCLK_CLKSEL_12M; | |
158 | break; | |
159 | case 24 * MHZ: | |
160 | clksel = PHYCLK_CLKSEL_24M; | |
161 | break; | |
162 | case 48 * MHZ: | |
163 | clksel = PHYCLK_CLKSEL_48M; | |
164 | break; | |
165 | default: | |
166 | dev_err(sphy->dev, | |
167 | "Invalid reference clock frequency: %lu\n", rate); | |
168 | return -EINVAL; | |
169 | } | |
170 | ||
171 | return clksel; | |
172 | } | |
173 | EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_64xx); | |
174 | ||
175 | int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy, | |
176 | unsigned long rate) | |
177 | { | |
178 | unsigned int clksel; | |
179 | ||
180 | switch (rate) { | |
181 | case 9600 * KHZ: | |
182 | clksel = FSEL_CLKSEL_9600K; | |
183 | break; | |
184 | case 10 * MHZ: | |
185 | clksel = FSEL_CLKSEL_10M; | |
186 | break; | |
187 | case 12 * MHZ: | |
188 | clksel = FSEL_CLKSEL_12M; | |
189 | break; | |
190 | case 19200 * KHZ: | |
191 | clksel = FSEL_CLKSEL_19200K; | |
192 | break; | |
193 | case 20 * MHZ: | |
194 | clksel = FSEL_CLKSEL_20M; | |
195 | break; | |
196 | case 24 * MHZ: | |
197 | clksel = FSEL_CLKSEL_24M; | |
198 | break; | |
199 | case 50 * MHZ: | |
200 | clksel = FSEL_CLKSEL_50M; | |
201 | break; | |
202 | default: | |
203 | dev_err(sphy->dev, | |
204 | "Invalid reference clock frequency: %lu\n", rate); | |
205 | return -EINVAL; | |
206 | } | |
207 | ||
208 | return clksel; | |
209 | } | |
210 | EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_4x12); | |
211 | ||
337dc3a7 PP |
212 | /* |
213 | * Returns reference clock frequency selection value | |
214 | */ | |
dc2377d0 | 215 | int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) |
337dc3a7 PP |
216 | { |
217 | struct clk *ref_clk; | |
0aa823a2 TF |
218 | unsigned long rate; |
219 | int refclk_freq; | |
337dc3a7 | 220 | |
8c1b3e16 VG |
221 | /* |
222 | * In exynos5250 USB host and device PHY use | |
223 | * external crystal clock XXTI | |
224 | */ | |
225 | if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) | |
87331b06 | 226 | ref_clk = clk_get(sphy->dev, "ext_xtal"); |
8c1b3e16 | 227 | else |
87331b06 | 228 | ref_clk = clk_get(sphy->dev, "xusbxti"); |
337dc3a7 PP |
229 | if (IS_ERR(ref_clk)) { |
230 | dev_err(sphy->dev, "Failed to get reference clock\n"); | |
231 | return PTR_ERR(ref_clk); | |
232 | } | |
233 | ||
0aa823a2 TF |
234 | rate = clk_get_rate(ref_clk); |
235 | refclk_freq = sphy->drv_data->rate_to_clksel(sphy, rate); | |
236 | ||
337dc3a7 PP |
237 | clk_put(ref_clk); |
238 | ||
239 | return refclk_freq; | |
240 | } | |
dc2377d0 | 241 | EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq); |