aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/phy
diff options
context:
space:
mode:
authorVivek Gautam <gautam.vivek@samsung.com>2013-01-15 01:10:25 -0500
committerFelipe Balbi <balbi@ti.com>2013-01-18 08:14:49 -0500
commit69f0946a8db75d6de377c6a5796aa2e417c3c83a (patch)
tree1bc45a05c1ead2d1706f0d8514e987cb04068816 /drivers/usb/phy
parent63a1307930867a45f86a1a69f80315b2df7b7b49 (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.c163
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
63enum samsung_cpu_type { 67enum 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 */
87struct 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 */
78struct samsung_usbphy { 105struct 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
119static 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 */
147static 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)
185static int samsung_usbphy_init(struct usb_phy *phy) 266static 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)
214static void samsung_usbphy_shutdown(struct usb_phy *phy) 302static 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
235static const struct of_device_id samsung_usbphy_dt_match[]; 330static const struct of_device_id samsung_usbphy_dt_match[];
236 331
237static inline int samsung_usbphy_get_driver_data(struct platform_device *pdev) 332static 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
249static int __devinit samsung_usbphy_probe(struct platform_device *pdev) 346static 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
420static const struct samsung_usbphy_drvdata usbphy_s3c64xx = {
421 .cpu_type = TYPE_S3C64XX,
422 .devphy_en_mask = S3C64XX_USBPHY_ENABLE,
423};
424
425static 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
312static const struct of_device_id samsung_usbphy_dt_match[] = { 431static 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);
325static struct platform_device_id samsung_usbphy_driver_ids[] = { 444static 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};