blob: 30aebb59d8032ca94c3fa0b055cfbed0165d0b40 [file] [log] [blame]
Praveen Paneri337dc3a2012-11-23 16:03:06 +05301/* linux/drivers/usb/phy/samsung-usbphy.c
2 *
3 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
5 *
6 * Author: Praveen Paneri <p.paneri@samsung.com>
7 *
8 * Samsung USB2.0 High-speed OTG transceiver, talks to S3C HS OTG controller
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/clk.h>
23#include <linux/delay.h>
24#include <linux/err.h>
25#include <linux/io.h>
26#include <linux/of.h>
Vivek Gautam69f09462013-01-15 11:40:25 +053027#include <linux/of_address.h>
Praveen Paneri337dc3a2012-11-23 16:03:06 +053028#include <linux/usb/otg.h>
29#include <linux/platform_data/samsung-usbphy.h>
30
31/* Register definitions */
32
33#define SAMSUNG_PHYPWR (0x00)
34
35#define PHYPWR_NORMAL_MASK (0x19 << 0)
36#define PHYPWR_OTG_DISABLE (0x1 << 4)
37#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3)
38#define PHYPWR_FORCE_SUSPEND (0x1 << 1)
39/* For Exynos4 */
40#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0)
41#define PHYPWR_SLEEP_PHY0 (0x1 << 5)
42
43#define SAMSUNG_PHYCLK (0x04)
44
45#define PHYCLK_MODE_USB11 (0x1 << 6)
46#define PHYCLK_EXT_OSC (0x1 << 5)
47#define PHYCLK_COMMON_ON_N (0x1 << 4)
48#define PHYCLK_ID_PULL (0x1 << 2)
49#define PHYCLK_CLKSEL_MASK (0x3 << 0)
50#define PHYCLK_CLKSEL_48M (0x0 << 0)
51#define PHYCLK_CLKSEL_12M (0x2 << 0)
52#define PHYCLK_CLKSEL_24M (0x3 << 0)
53
54#define SAMSUNG_RSTCON (0x08)
55
56#define RSTCON_PHYLINK_SWRST (0x1 << 2)
57#define RSTCON_HLINK_SWRST (0x1 << 1)
58#define RSTCON_SWRST (0x1 << 0)
59
60#ifndef MHZ
61#define MHZ (1000*1000)
62#endif
63
Vivek Gautam69f09462013-01-15 11:40:25 +053064#define S3C64XX_USBPHY_ENABLE (0x1 << 16)
65#define EXYNOS_USBPHY_ENABLE (0x1 << 0)
66
Praveen Paneri337dc3a2012-11-23 16:03:06 +053067enum samsung_cpu_type {
68 TYPE_S3C64XX,
69 TYPE_EXYNOS4210,
70};
71
72/*
Vivek Gautam69f09462013-01-15 11:40:25 +053073 * 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/*
Praveen Paneri337dc3a2012-11-23 16:03:06 +053094 * struct samsung_usbphy - transceiver driver state
95 * @phy: transceiver structure
96 * @plat: platform data
97 * @dev: The parent device supplied to the probe function
98 * @clk: usb phy clock
Vivek Gautam69f09462013-01-15 11:40:25 +053099 * @regs: usb phy controller registers memory base
100 * @pmuregs: USB device PHY_CONTROL register memory base
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530101 * @ref_clk_freq: reference clock frequency selection
Vivek Gautam69f09462013-01-15 11:40:25 +0530102 * @drv_data: driver data available for different SoCs
103 * @lock: lock for phy operations
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530104 */
105struct samsung_usbphy {
106 struct usb_phy phy;
107 struct samsung_usbphy_data *plat;
108 struct device *dev;
109 struct clk *clk;
110 void __iomem *regs;
Vivek Gautam69f09462013-01-15 11:40:25 +0530111 void __iomem *pmuregs;
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530112 int ref_clk_freq;
Vivek Gautam69f09462013-01-15 11:40:25 +0530113 const struct samsung_usbphy_drvdata *drv_data;
114 spinlock_t lock;
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530115};
116
117#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy)
118
Vivek Gautam69f09462013-01-15 11:40:25 +0530119static 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
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530171/*
172 * Returns reference clock frequency selection value
173 */
174static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy)
175{
176 struct clk *ref_clk;
177 int refclk_freq = 0;
178
179 ref_clk = clk_get(sphy->dev, "xusbxti");
180 if (IS_ERR(ref_clk)) {
181 dev_err(sphy->dev, "Failed to get reference clock\n");
182 return PTR_ERR(ref_clk);
183 }
184
185 switch (clk_get_rate(ref_clk)) {
186 case 12 * MHZ:
187 refclk_freq = PHYCLK_CLKSEL_12M;
188 break;
189 case 24 * MHZ:
190 refclk_freq = PHYCLK_CLKSEL_24M;
191 break;
192 case 48 * MHZ:
193 refclk_freq = PHYCLK_CLKSEL_48M;
194 break;
195 default:
Vivek Gautam69f09462013-01-15 11:40:25 +0530196 if (sphy->drv_data->cpu_type == TYPE_S3C64XX)
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530197 refclk_freq = PHYCLK_CLKSEL_48M;
198 else
199 refclk_freq = PHYCLK_CLKSEL_24M;
200 break;
201 }
202 clk_put(ref_clk);
203
204 return refclk_freq;
205}
206
207static void samsung_usbphy_enable(struct samsung_usbphy *sphy)
208{
209 void __iomem *regs = sphy->regs;
210 u32 phypwr;
211 u32 phyclk;
212 u32 rstcon;
213
214 /* set clock frequency for PLL */
215 phyclk = sphy->ref_clk_freq;
216 phypwr = readl(regs + SAMSUNG_PHYPWR);
217 rstcon = readl(regs + SAMSUNG_RSTCON);
218
Vivek Gautam69f09462013-01-15 11:40:25 +0530219 switch (sphy->drv_data->cpu_type) {
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530220 case TYPE_S3C64XX:
221 phyclk &= ~PHYCLK_COMMON_ON_N;
222 phypwr &= ~PHYPWR_NORMAL_MASK;
223 rstcon |= RSTCON_SWRST;
224 break;
225 case TYPE_EXYNOS4210:
226 phypwr &= ~PHYPWR_NORMAL_MASK_PHY0;
227 rstcon |= RSTCON_SWRST;
228 default:
229 break;
230 }
231
232 writel(phyclk, regs + SAMSUNG_PHYCLK);
233 /* Configure PHY0 for normal operation*/
234 writel(phypwr, regs + SAMSUNG_PHYPWR);
235 /* reset all ports of PHY and Link */
236 writel(rstcon, regs + SAMSUNG_RSTCON);
237 udelay(10);
238 rstcon &= ~RSTCON_SWRST;
239 writel(rstcon, regs + SAMSUNG_RSTCON);
240}
241
242static void samsung_usbphy_disable(struct samsung_usbphy *sphy)
243{
244 void __iomem *regs = sphy->regs;
245 u32 phypwr;
246
247 phypwr = readl(regs + SAMSUNG_PHYPWR);
248
Vivek Gautam69f09462013-01-15 11:40:25 +0530249 switch (sphy->drv_data->cpu_type) {
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530250 case TYPE_S3C64XX:
251 phypwr |= PHYPWR_NORMAL_MASK;
252 break;
253 case TYPE_EXYNOS4210:
254 phypwr |= PHYPWR_NORMAL_MASK_PHY0;
255 default:
256 break;
257 }
258
259 /* Disable analog and otg block power */
260 writel(phypwr, regs + SAMSUNG_PHYPWR);
261}
262
263/*
264 * The function passed to the usb driver for phy initialization
265 */
266static int samsung_usbphy_init(struct usb_phy *phy)
267{
268 struct samsung_usbphy *sphy;
Vivek Gautam69f09462013-01-15 11:40:25 +0530269 unsigned long flags;
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530270 int ret = 0;
271
272 sphy = phy_to_sphy(phy);
273
274 /* Enable the phy clock */
275 ret = clk_prepare_enable(sphy->clk);
276 if (ret) {
277 dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
278 return ret;
279 }
280
Vivek Gautam69f09462013-01-15 11:40:25 +0530281 spin_lock_irqsave(&sphy->lock, flags);
282
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530283 /* Disable phy isolation */
284 if (sphy->plat && sphy->plat->pmu_isolation)
285 sphy->plat->pmu_isolation(false);
Vivek Gautam69f09462013-01-15 11:40:25 +0530286 else
287 samsung_usbphy_set_isolation(sphy, false);
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530288
289 /* Initialize usb phy registers */
290 samsung_usbphy_enable(sphy);
291
Vivek Gautam69f09462013-01-15 11:40:25 +0530292 spin_unlock_irqrestore(&sphy->lock, flags);
293
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530294 /* Disable the phy clock */
295 clk_disable_unprepare(sphy->clk);
296 return ret;
297}
298
299/*
300 * The function passed to the usb driver for phy shutdown
301 */
302static void samsung_usbphy_shutdown(struct usb_phy *phy)
303{
304 struct samsung_usbphy *sphy;
Vivek Gautam69f09462013-01-15 11:40:25 +0530305 unsigned long flags;
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530306
307 sphy = phy_to_sphy(phy);
308
309 if (clk_prepare_enable(sphy->clk)) {
310 dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
311 return;
312 }
313
Vivek Gautam69f09462013-01-15 11:40:25 +0530314 spin_lock_irqsave(&sphy->lock, flags);
315
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530316 /* De-initialize usb phy registers */
317 samsung_usbphy_disable(sphy);
318
319 /* Enable phy isolation */
320 if (sphy->plat && sphy->plat->pmu_isolation)
321 sphy->plat->pmu_isolation(true);
Vivek Gautam69f09462013-01-15 11:40:25 +0530322 else
323 samsung_usbphy_set_isolation(sphy, true);
324
325 spin_unlock_irqrestore(&sphy->lock, flags);
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530326
327 clk_disable_unprepare(sphy->clk);
328}
329
330static const struct of_device_id samsung_usbphy_dt_match[];
331
Vivek Gautam69f09462013-01-15 11:40:25 +0530332static inline const struct samsung_usbphy_drvdata
333*samsung_usbphy_get_driver_data(struct platform_device *pdev)
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530334{
335 if (pdev->dev.of_node) {
336 const struct of_device_id *match;
337 match = of_match_node(samsung_usbphy_dt_match,
338 pdev->dev.of_node);
Vivek Gautam69f09462013-01-15 11:40:25 +0530339 return match->data;
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530340 }
341
Vivek Gautam69f09462013-01-15 11:40:25 +0530342 return (struct samsung_usbphy_drvdata *)
343 platform_get_device_id(pdev)->driver_data;
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530344}
345
346static int __devinit samsung_usbphy_probe(struct platform_device *pdev)
347{
348 struct samsung_usbphy *sphy;
Vivek Gautam69f09462013-01-15 11:40:25 +0530349 struct samsung_usbphy_data *pdata = pdev->dev.platform_data;
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530350 struct device *dev = &pdev->dev;
351 struct resource *phy_mem;
352 void __iomem *phy_base;
353 struct clk *clk;
Vivek Gautam69f09462013-01-15 11:40:25 +0530354 int ret;
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530355
356 phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
357 if (!phy_mem) {
358 dev_err(dev, "%s: missing mem resource\n", __func__);
359 return -ENODEV;
360 }
361
362 phy_base = devm_request_and_ioremap(dev, phy_mem);
363 if (!phy_base) {
364 dev_err(dev, "%s: register mapping failed\n", __func__);
365 return -ENXIO;
366 }
367
368 sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);
369 if (!sphy)
370 return -ENOMEM;
371
372 clk = devm_clk_get(dev, "otg");
373 if (IS_ERR(clk)) {
374 dev_err(dev, "Failed to get otg clock\n");
375 return PTR_ERR(clk);
376 }
377
Vivek Gautam69f09462013-01-15 11:40:25 +0530378 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
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530391 sphy->plat = pdata;
392 sphy->regs = phy_base;
393 sphy->clk = clk;
394 sphy->phy.dev = sphy->dev;
395 sphy->phy.label = "samsung-usbphy";
396 sphy->phy.init = samsung_usbphy_init;
397 sphy->phy.shutdown = samsung_usbphy_shutdown;
Vivek Gautam69f09462013-01-15 11:40:25 +0530398 sphy->drv_data = samsung_usbphy_get_driver_data(pdev);
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530399 sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy);
400
Vivek Gautam69f09462013-01-15 11:40:25 +0530401 spin_lock_init(&sphy->lock);
402
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530403 platform_set_drvdata(pdev, sphy);
404
405 return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2);
406}
407
408static int __exit samsung_usbphy_remove(struct platform_device *pdev)
409{
410 struct samsung_usbphy *sphy = platform_get_drvdata(pdev);
411
412 usb_remove_phy(&sphy->phy);
413
Vivek Gautam69f09462013-01-15 11:40:25 +0530414 if (sphy->pmuregs)
415 iounmap(sphy->pmuregs);
416
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530417 return 0;
418}
419
Vivek Gautam69f09462013-01-15 11:40:25 +0530420static 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
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530430#ifdef CONFIG_OF
431static const struct of_device_id samsung_usbphy_dt_match[] = {
432 {
433 .compatible = "samsung,s3c64xx-usbphy",
Vivek Gautam69f09462013-01-15 11:40:25 +0530434 .data = &usbphy_s3c64xx,
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530435 }, {
436 .compatible = "samsung,exynos4210-usbphy",
Vivek Gautam69f09462013-01-15 11:40:25 +0530437 .data = &usbphy_exynos4,
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530438 },
439 {},
440};
441MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match);
442#endif
443
444static struct platform_device_id samsung_usbphy_driver_ids[] = {
445 {
446 .name = "s3c64xx-usbphy",
Vivek Gautam69f09462013-01-15 11:40:25 +0530447 .driver_data = (unsigned long)&usbphy_s3c64xx,
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530448 }, {
449 .name = "exynos4210-usbphy",
Vivek Gautam69f09462013-01-15 11:40:25 +0530450 .driver_data = (unsigned long)&usbphy_exynos4,
Praveen Paneri337dc3a2012-11-23 16:03:06 +0530451 },
452 {},
453};
454
455MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids);
456
457static struct platform_driver samsung_usbphy_driver = {
458 .probe = samsung_usbphy_probe,
459 .remove = __devexit_p(samsung_usbphy_remove),
460 .id_table = samsung_usbphy_driver_ids,
461 .driver = {
462 .name = "samsung-usbphy",
463 .owner = THIS_MODULE,
464 .of_match_table = of_match_ptr(samsung_usbphy_dt_match),
465 },
466};
467
468module_platform_driver(samsung_usbphy_driver);
469
470MODULE_DESCRIPTION("Samsung USB phy controller");
471MODULE_AUTHOR("Praveen Paneri <p.paneri@samsung.com>");
472MODULE_LICENSE("GPL");
473MODULE_ALIAS("platform:samsung-usbphy");