diff options
author | Vivek Gautam <gautam.vivek@samsung.com> | 2013-01-15 01:10:25 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2013-01-18 08:14:49 -0500 |
commit | 69f0946a8db75d6de377c6a5796aa2e417c3c83a (patch) | |
tree | 1bc45a05c1ead2d1706f0d8514e987cb04068816 /drivers/usb/phy | |
parent | 63a1307930867a45f86a1a69f80315b2df7b7b49 (diff) |
usb: phy: samsung: Add support to set pmu isolation
Adding support to parse device node data in order to get
required properties to set pmu isolation for usb-phy.
Signed-off-by: Vivek Gautam <gautam.vivek@samsung.com>
Acked-by: Kukjin Kim <kgene.kim@samsung.com>
Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Reviewed-by: Doug Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r-- | drivers/usb/phy/samsung-usbphy.c | 163 |
1 files changed, 141 insertions, 22 deletions
diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c index 5c5e1bb5de7b..30aebb59d803 100644 --- a/drivers/usb/phy/samsung-usbphy.c +++ b/drivers/usb/phy/samsung-usbphy.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/err.h> | 24 | #include <linux/err.h> |
25 | #include <linux/io.h> | 25 | #include <linux/io.h> |
26 | #include <linux/of.h> | 26 | #include <linux/of.h> |
27 | #include <linux/of_address.h> | ||
27 | #include <linux/usb/otg.h> | 28 | #include <linux/usb/otg.h> |
28 | #include <linux/platform_data/samsung-usbphy.h> | 29 | #include <linux/platform_data/samsung-usbphy.h> |
29 | 30 | ||
@@ -60,20 +61,46 @@ | |||
60 | #define MHZ (1000*1000) | 61 | #define MHZ (1000*1000) |
61 | #endif | 62 | #endif |
62 | 63 | ||
64 | #define S3C64XX_USBPHY_ENABLE (0x1 << 16) | ||
65 | #define EXYNOS_USBPHY_ENABLE (0x1 << 0) | ||
66 | |||
63 | enum samsung_cpu_type { | 67 | enum samsung_cpu_type { |
64 | TYPE_S3C64XX, | 68 | TYPE_S3C64XX, |
65 | TYPE_EXYNOS4210, | 69 | TYPE_EXYNOS4210, |
66 | }; | 70 | }; |
67 | 71 | ||
68 | /* | 72 | /* |
73 | * struct samsung_usbphy_drvdata - driver data for various SoC variants | ||
74 | * @cpu_type: machine identifier | ||
75 | * @devphy_en_mask: device phy enable mask for PHY CONTROL register | ||
76 | * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from | ||
77 | * mapped address of system controller. | ||
78 | * | ||
79 | * Here we have a separate mask for device type phy. | ||
80 | * Having different masks for host and device type phy helps | ||
81 | * in setting independent masks in case of SoCs like S5PV210, | ||
82 | * in which PHY0 and PHY1 enable bits belong to same register | ||
83 | * placed at position 0 and 1 respectively. | ||
84 | * Although for newer SoCs like exynos these bits belong to | ||
85 | * different registers altogether placed at position 0. | ||
86 | */ | ||
87 | struct samsung_usbphy_drvdata { | ||
88 | int cpu_type; | ||
89 | int devphy_en_mask; | ||
90 | u32 devphy_reg_offset; | ||
91 | }; | ||
92 | |||
93 | /* | ||
69 | * struct samsung_usbphy - transceiver driver state | 94 | * struct samsung_usbphy - transceiver driver state |
70 | * @phy: transceiver structure | 95 | * @phy: transceiver structure |
71 | * @plat: platform data | 96 | * @plat: platform data |
72 | * @dev: The parent device supplied to the probe function | 97 | * @dev: The parent device supplied to the probe function |
73 | * @clk: usb phy clock | 98 | * @clk: usb phy clock |
74 | * @regs: usb phy register memory base | 99 | * @regs: usb phy controller registers memory base |
100 | * @pmuregs: USB device PHY_CONTROL register memory base | ||
75 | * @ref_clk_freq: reference clock frequency selection | 101 | * @ref_clk_freq: reference clock frequency selection |
76 | * @cpu_type: machine identifier | 102 | * @drv_data: driver data available for different SoCs |
103 | * @lock: lock for phy operations | ||
77 | */ | 104 | */ |
78 | struct samsung_usbphy { | 105 | struct samsung_usbphy { |
79 | struct usb_phy phy; | 106 | struct usb_phy phy; |
@@ -81,12 +108,66 @@ struct samsung_usbphy { | |||
81 | struct device *dev; | 108 | struct device *dev; |
82 | struct clk *clk; | 109 | struct clk *clk; |
83 | void __iomem *regs; | 110 | void __iomem *regs; |
111 | void __iomem *pmuregs; | ||
84 | int ref_clk_freq; | 112 | int ref_clk_freq; |
85 | int cpu_type; | 113 | const struct samsung_usbphy_drvdata *drv_data; |
114 | spinlock_t lock; | ||
86 | }; | 115 | }; |
87 | 116 | ||
88 | #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) | 117 | #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) |
89 | 118 | ||
119 | static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) | ||
120 | { | ||
121 | struct device_node *usbphy_sys; | ||
122 | |||
123 | /* Getting node for system controller interface for usb-phy */ | ||
124 | usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys"); | ||
125 | if (!usbphy_sys) { | ||
126 | dev_err(sphy->dev, "No sys-controller interface for usb-phy\n"); | ||
127 | return -ENODEV; | ||
128 | } | ||
129 | |||
130 | sphy->pmuregs = of_iomap(usbphy_sys, 0); | ||
131 | |||
132 | of_node_put(usbphy_sys); | ||
133 | |||
134 | if (sphy->pmuregs == NULL) { | ||
135 | dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); | ||
136 | return -ENODEV; | ||
137 | } | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Set isolation here for phy. | ||
144 | * Here 'on = true' would mean USB PHY block is isolated, hence | ||
145 | * de-activated and vice-versa. | ||
146 | */ | ||
147 | static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) | ||
148 | { | ||
149 | void __iomem *reg; | ||
150 | u32 reg_val; | ||
151 | u32 en_mask; | ||
152 | |||
153 | if (!sphy->pmuregs) { | ||
154 | dev_warn(sphy->dev, "Can't set pmu isolation\n"); | ||
155 | return; | ||
156 | } | ||
157 | |||
158 | reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset; | ||
159 | en_mask = sphy->drv_data->devphy_en_mask; | ||
160 | |||
161 | reg_val = readl(reg); | ||
162 | |||
163 | if (on) | ||
164 | reg_val &= ~en_mask; | ||
165 | else | ||
166 | reg_val |= en_mask; | ||
167 | |||
168 | writel(reg_val, reg); | ||
169 | } | ||
170 | |||
90 | /* | 171 | /* |
91 | * Returns reference clock frequency selection value | 172 | * Returns reference clock frequency selection value |
92 | */ | 173 | */ |
@@ -112,7 +193,7 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) | |||
112 | refclk_freq = PHYCLK_CLKSEL_48M; | 193 | refclk_freq = PHYCLK_CLKSEL_48M; |
113 | break; | 194 | break; |
114 | default: | 195 | default: |
115 | if (sphy->cpu_type == TYPE_S3C64XX) | 196 | if (sphy->drv_data->cpu_type == TYPE_S3C64XX) |
116 | refclk_freq = PHYCLK_CLKSEL_48M; | 197 | refclk_freq = PHYCLK_CLKSEL_48M; |
117 | else | 198 | else |
118 | refclk_freq = PHYCLK_CLKSEL_24M; | 199 | refclk_freq = PHYCLK_CLKSEL_24M; |
@@ -135,7 +216,7 @@ static void samsung_usbphy_enable(struct samsung_usbphy *sphy) | |||
135 | phypwr = readl(regs + SAMSUNG_PHYPWR); | 216 | phypwr = readl(regs + SAMSUNG_PHYPWR); |
136 | rstcon = readl(regs + SAMSUNG_RSTCON); | 217 | rstcon = readl(regs + SAMSUNG_RSTCON); |
137 | 218 | ||
138 | switch (sphy->cpu_type) { | 219 | switch (sphy->drv_data->cpu_type) { |
139 | case TYPE_S3C64XX: | 220 | case TYPE_S3C64XX: |
140 | phyclk &= ~PHYCLK_COMMON_ON_N; | 221 | phyclk &= ~PHYCLK_COMMON_ON_N; |
141 | phypwr &= ~PHYPWR_NORMAL_MASK; | 222 | phypwr &= ~PHYPWR_NORMAL_MASK; |
@@ -165,7 +246,7 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy) | |||
165 | 246 | ||
166 | phypwr = readl(regs + SAMSUNG_PHYPWR); | 247 | phypwr = readl(regs + SAMSUNG_PHYPWR); |
167 | 248 | ||
168 | switch (sphy->cpu_type) { | 249 | switch (sphy->drv_data->cpu_type) { |
169 | case TYPE_S3C64XX: | 250 | case TYPE_S3C64XX: |
170 | phypwr |= PHYPWR_NORMAL_MASK; | 251 | phypwr |= PHYPWR_NORMAL_MASK; |
171 | break; | 252 | break; |
@@ -185,6 +266,7 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy) | |||
185 | static int samsung_usbphy_init(struct usb_phy *phy) | 266 | static int samsung_usbphy_init(struct usb_phy *phy) |
186 | { | 267 | { |
187 | struct samsung_usbphy *sphy; | 268 | struct samsung_usbphy *sphy; |
269 | unsigned long flags; | ||
188 | int ret = 0; | 270 | int ret = 0; |
189 | 271 | ||
190 | sphy = phy_to_sphy(phy); | 272 | sphy = phy_to_sphy(phy); |
@@ -196,13 +278,19 @@ static int samsung_usbphy_init(struct usb_phy *phy) | |||
196 | return ret; | 278 | return ret; |
197 | } | 279 | } |
198 | 280 | ||
281 | spin_lock_irqsave(&sphy->lock, flags); | ||
282 | |||
199 | /* Disable phy isolation */ | 283 | /* Disable phy isolation */ |
200 | if (sphy->plat && sphy->plat->pmu_isolation) | 284 | if (sphy->plat && sphy->plat->pmu_isolation) |
201 | sphy->plat->pmu_isolation(false); | 285 | sphy->plat->pmu_isolation(false); |
286 | else | ||
287 | samsung_usbphy_set_isolation(sphy, false); | ||
202 | 288 | ||
203 | /* Initialize usb phy registers */ | 289 | /* Initialize usb phy registers */ |
204 | samsung_usbphy_enable(sphy); | 290 | samsung_usbphy_enable(sphy); |
205 | 291 | ||
292 | spin_unlock_irqrestore(&sphy->lock, flags); | ||
293 | |||
206 | /* Disable the phy clock */ | 294 | /* Disable the phy clock */ |
207 | clk_disable_unprepare(sphy->clk); | 295 | clk_disable_unprepare(sphy->clk); |
208 | return ret; | 296 | return ret; |
@@ -214,6 +302,7 @@ static int samsung_usbphy_init(struct usb_phy *phy) | |||
214 | static void samsung_usbphy_shutdown(struct usb_phy *phy) | 302 | static void samsung_usbphy_shutdown(struct usb_phy *phy) |
215 | { | 303 | { |
216 | struct samsung_usbphy *sphy; | 304 | struct samsung_usbphy *sphy; |
305 | unsigned long flags; | ||
217 | 306 | ||
218 | sphy = phy_to_sphy(phy); | 307 | sphy = phy_to_sphy(phy); |
219 | 308 | ||
@@ -222,44 +311,47 @@ static void samsung_usbphy_shutdown(struct usb_phy *phy) | |||
222 | return; | 311 | return; |
223 | } | 312 | } |
224 | 313 | ||
314 | spin_lock_irqsave(&sphy->lock, flags); | ||
315 | |||
225 | /* De-initialize usb phy registers */ | 316 | /* De-initialize usb phy registers */ |
226 | samsung_usbphy_disable(sphy); | 317 | samsung_usbphy_disable(sphy); |
227 | 318 | ||
228 | /* Enable phy isolation */ | 319 | /* Enable phy isolation */ |
229 | if (sphy->plat && sphy->plat->pmu_isolation) | 320 | if (sphy->plat && sphy->plat->pmu_isolation) |
230 | sphy->plat->pmu_isolation(true); | 321 | sphy->plat->pmu_isolation(true); |
322 | else | ||
323 | samsung_usbphy_set_isolation(sphy, true); | ||
324 | |||
325 | spin_unlock_irqrestore(&sphy->lock, flags); | ||
231 | 326 | ||
232 | clk_disable_unprepare(sphy->clk); | 327 | clk_disable_unprepare(sphy->clk); |
233 | } | 328 | } |
234 | 329 | ||
235 | static const struct of_device_id samsung_usbphy_dt_match[]; | 330 | static const struct of_device_id samsung_usbphy_dt_match[]; |
236 | 331 | ||
237 | static inline int samsung_usbphy_get_driver_data(struct platform_device *pdev) | 332 | static inline const struct samsung_usbphy_drvdata |
333 | *samsung_usbphy_get_driver_data(struct platform_device *pdev) | ||
238 | { | 334 | { |
239 | if (pdev->dev.of_node) { | 335 | if (pdev->dev.of_node) { |
240 | const struct of_device_id *match; | 336 | const struct of_device_id *match; |
241 | match = of_match_node(samsung_usbphy_dt_match, | 337 | match = of_match_node(samsung_usbphy_dt_match, |
242 | pdev->dev.of_node); | 338 | pdev->dev.of_node); |
243 | return (int) match->data; | 339 | return match->data; |
244 | } | 340 | } |
245 | 341 | ||
246 | return platform_get_device_id(pdev)->driver_data; | 342 | return (struct samsung_usbphy_drvdata *) |
343 | platform_get_device_id(pdev)->driver_data; | ||
247 | } | 344 | } |
248 | 345 | ||
249 | static int __devinit samsung_usbphy_probe(struct platform_device *pdev) | 346 | static int __devinit samsung_usbphy_probe(struct platform_device *pdev) |
250 | { | 347 | { |
251 | struct samsung_usbphy *sphy; | 348 | struct samsung_usbphy *sphy; |
252 | struct samsung_usbphy_data *pdata; | 349 | struct samsung_usbphy_data *pdata = pdev->dev.platform_data; |
253 | struct device *dev = &pdev->dev; | 350 | struct device *dev = &pdev->dev; |
254 | struct resource *phy_mem; | 351 | struct resource *phy_mem; |
255 | void __iomem *phy_base; | 352 | void __iomem *phy_base; |
256 | struct clk *clk; | 353 | struct clk *clk; |
257 | 354 | int ret; | |
258 | pdata = pdev->dev.platform_data; | ||
259 | if (!pdata) { | ||
260 | dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); | ||
261 | return -EINVAL; | ||
262 | } | ||
263 | 355 | ||
264 | phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 356 | phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
265 | if (!phy_mem) { | 357 | if (!phy_mem) { |
@@ -283,7 +375,19 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) | |||
283 | return PTR_ERR(clk); | 375 | return PTR_ERR(clk); |
284 | } | 376 | } |
285 | 377 | ||
286 | sphy->dev = &pdev->dev; | 378 | sphy->dev = dev; |
379 | |||
380 | if (dev->of_node) { | ||
381 | ret = samsung_usbphy_parse_dt(sphy); | ||
382 | if (ret < 0) | ||
383 | return ret; | ||
384 | } else { | ||
385 | if (!pdata) { | ||
386 | dev_err(dev, "no platform data specified\n"); | ||
387 | return -EINVAL; | ||
388 | } | ||
389 | } | ||
390 | |||
287 | sphy->plat = pdata; | 391 | sphy->plat = pdata; |
288 | sphy->regs = phy_base; | 392 | sphy->regs = phy_base; |
289 | sphy->clk = clk; | 393 | sphy->clk = clk; |
@@ -291,9 +395,11 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) | |||
291 | sphy->phy.label = "samsung-usbphy"; | 395 | sphy->phy.label = "samsung-usbphy"; |
292 | sphy->phy.init = samsung_usbphy_init; | 396 | sphy->phy.init = samsung_usbphy_init; |
293 | sphy->phy.shutdown = samsung_usbphy_shutdown; | 397 | sphy->phy.shutdown = samsung_usbphy_shutdown; |
294 | sphy->cpu_type = samsung_usbphy_get_driver_data(pdev); | 398 | sphy->drv_data = samsung_usbphy_get_driver_data(pdev); |
295 | sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); | 399 | sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); |
296 | 400 | ||
401 | spin_lock_init(&sphy->lock); | ||
402 | |||
297 | platform_set_drvdata(pdev, sphy); | 403 | platform_set_drvdata(pdev, sphy); |
298 | 404 | ||
299 | return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); | 405 | return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); |
@@ -305,17 +411,30 @@ static int __exit samsung_usbphy_remove(struct platform_device *pdev) | |||
305 | 411 | ||
306 | usb_remove_phy(&sphy->phy); | 412 | usb_remove_phy(&sphy->phy); |
307 | 413 | ||
414 | if (sphy->pmuregs) | ||
415 | iounmap(sphy->pmuregs); | ||
416 | |||
308 | return 0; | 417 | return 0; |
309 | } | 418 | } |
310 | 419 | ||
420 | static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { | ||
421 | .cpu_type = TYPE_S3C64XX, | ||
422 | .devphy_en_mask = S3C64XX_USBPHY_ENABLE, | ||
423 | }; | ||
424 | |||
425 | static const struct samsung_usbphy_drvdata usbphy_exynos4 = { | ||
426 | .cpu_type = TYPE_EXYNOS4210, | ||
427 | .devphy_en_mask = EXYNOS_USBPHY_ENABLE, | ||
428 | }; | ||
429 | |||
311 | #ifdef CONFIG_OF | 430 | #ifdef CONFIG_OF |
312 | static const struct of_device_id samsung_usbphy_dt_match[] = { | 431 | static const struct of_device_id samsung_usbphy_dt_match[] = { |
313 | { | 432 | { |
314 | .compatible = "samsung,s3c64xx-usbphy", | 433 | .compatible = "samsung,s3c64xx-usbphy", |
315 | .data = (void *)TYPE_S3C64XX, | 434 | .data = &usbphy_s3c64xx, |
316 | }, { | 435 | }, { |
317 | .compatible = "samsung,exynos4210-usbphy", | 436 | .compatible = "samsung,exynos4210-usbphy", |
318 | .data = (void *)TYPE_EXYNOS4210, | 437 | .data = &usbphy_exynos4, |
319 | }, | 438 | }, |
320 | {}, | 439 | {}, |
321 | }; | 440 | }; |
@@ -325,10 +444,10 @@ MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); | |||
325 | static struct platform_device_id samsung_usbphy_driver_ids[] = { | 444 | static struct platform_device_id samsung_usbphy_driver_ids[] = { |
326 | { | 445 | { |
327 | .name = "s3c64xx-usbphy", | 446 | .name = "s3c64xx-usbphy", |
328 | .driver_data = TYPE_S3C64XX, | 447 | .driver_data = (unsigned long)&usbphy_s3c64xx, |
329 | }, { | 448 | }, { |
330 | .name = "exynos4210-usbphy", | 449 | .name = "exynos4210-usbphy", |
331 | .driver_data = TYPE_EXYNOS4210, | 450 | .driver_data = (unsigned long)&usbphy_exynos4, |
332 | }, | 451 | }, |
333 | {}, | 452 | {}, |
334 | }; | 453 | }; |