Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.11 540 lines 14 kB view raw
1/* linux/drivers/usb/phy/phy-samsung-usb2.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 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and 9 * OHCI-EXYNOS controllers. 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 */ 20 21#include <linux/module.h> 22#include <linux/platform_device.h> 23#include <linux/clk.h> 24#include <linux/delay.h> 25#include <linux/device.h> 26#include <linux/err.h> 27#include <linux/io.h> 28#include <linux/of.h> 29#include <linux/usb/otg.h> 30#include <linux/usb/samsung_usb_phy.h> 31#include <linux/platform_data/samsung-usbphy.h> 32 33#include "phy-samsung-usb.h" 34 35static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) 36{ 37 if (!otg) 38 return -ENODEV; 39 40 if (!otg->host) 41 otg->host = host; 42 43 return 0; 44} 45 46static bool exynos5_phyhost_is_on(void __iomem *regs) 47{ 48 u32 reg; 49 50 reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); 51 52 return !(reg & HOST_CTRL0_SIDDQ); 53} 54 55static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy) 56{ 57 void __iomem *regs = sphy->regs; 58 u32 phyclk = sphy->ref_clk_freq; 59 u32 phyhost; 60 u32 phyotg; 61 u32 phyhsic; 62 u32 ehcictrl; 63 u32 ohcictrl; 64 65 /* 66 * phy_usage helps in keeping usage count for phy 67 * so that the first consumer enabling the phy is also 68 * the last consumer to disable it. 69 */ 70 71 atomic_inc(&sphy->phy_usage); 72 73 if (exynos5_phyhost_is_on(regs)) { 74 dev_info(sphy->dev, "Already power on PHY\n"); 75 return; 76 } 77 78 /* Host configuration */ 79 phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); 80 81 /* phy reference clock configuration */ 82 phyhost &= ~HOST_CTRL0_FSEL_MASK; 83 phyhost |= HOST_CTRL0_FSEL(phyclk); 84 85 /* host phy reset */ 86 phyhost &= ~(HOST_CTRL0_PHYSWRST | 87 HOST_CTRL0_PHYSWRSTALL | 88 HOST_CTRL0_SIDDQ | 89 /* Enable normal mode of operation */ 90 HOST_CTRL0_FORCESUSPEND | 91 HOST_CTRL0_FORCESLEEP); 92 93 /* Link reset */ 94 phyhost |= (HOST_CTRL0_LINKSWRST | 95 HOST_CTRL0_UTMISWRST | 96 /* COMMON Block configuration during suspend */ 97 HOST_CTRL0_COMMONON_N); 98 writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); 99 udelay(10); 100 phyhost &= ~(HOST_CTRL0_LINKSWRST | 101 HOST_CTRL0_UTMISWRST); 102 writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); 103 104 /* OTG configuration */ 105 phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); 106 107 /* phy reference clock configuration */ 108 phyotg &= ~OTG_SYS_FSEL_MASK; 109 phyotg |= OTG_SYS_FSEL(phyclk); 110 111 /* Enable normal mode of operation */ 112 phyotg &= ~(OTG_SYS_FORCESUSPEND | 113 OTG_SYS_SIDDQ_UOTG | 114 OTG_SYS_FORCESLEEP | 115 OTG_SYS_REFCLKSEL_MASK | 116 /* COMMON Block configuration during suspend */ 117 OTG_SYS_COMMON_ON); 118 119 /* OTG phy & link reset */ 120 phyotg |= (OTG_SYS_PHY0_SWRST | 121 OTG_SYS_LINKSWRST_UOTG | 122 OTG_SYS_PHYLINK_SWRESET | 123 OTG_SYS_OTGDISABLE | 124 /* Set phy refclk */ 125 OTG_SYS_REFCLKSEL_CLKCORE); 126 127 writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); 128 udelay(10); 129 phyotg &= ~(OTG_SYS_PHY0_SWRST | 130 OTG_SYS_LINKSWRST_UOTG | 131 OTG_SYS_PHYLINK_SWRESET); 132 writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); 133 134 /* HSIC phy configuration */ 135 phyhsic = (HSIC_CTRL_REFCLKDIV_12 | 136 HSIC_CTRL_REFCLKSEL | 137 HSIC_CTRL_PHYSWRST); 138 writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); 139 writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); 140 udelay(10); 141 phyhsic &= ~HSIC_CTRL_PHYSWRST; 142 writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); 143 writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); 144 145 udelay(80); 146 147 /* enable EHCI DMA burst */ 148 ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); 149 ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | 150 HOST_EHCICTRL_ENAINCR4 | 151 HOST_EHCICTRL_ENAINCR8 | 152 HOST_EHCICTRL_ENAINCR16); 153 writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); 154 155 /* set ohci_suspend_on_n */ 156 ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); 157 ohcictrl |= HOST_OHCICTRL_SUSPLGCY; 158 writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); 159} 160 161static void samsung_usb2phy_enable(struct samsung_usbphy *sphy) 162{ 163 void __iomem *regs = sphy->regs; 164 u32 phypwr; 165 u32 phyclk; 166 u32 rstcon; 167 168 /* set clock frequency for PLL */ 169 phyclk = sphy->ref_clk_freq; 170 phypwr = readl(regs + SAMSUNG_PHYPWR); 171 rstcon = readl(regs + SAMSUNG_RSTCON); 172 173 switch (sphy->drv_data->cpu_type) { 174 case TYPE_S3C64XX: 175 phyclk &= ~PHYCLK_COMMON_ON_N; 176 phypwr &= ~PHYPWR_NORMAL_MASK; 177 rstcon |= RSTCON_SWRST; 178 break; 179 case TYPE_EXYNOS4X12: 180 phypwr &= ~(PHYPWR_NORMAL_MASK_HSIC0 | 181 PHYPWR_NORMAL_MASK_HSIC1 | 182 PHYPWR_NORMAL_MASK_PHY1); 183 rstcon |= RSTCON_HOSTPHY_SWRST; 184 case TYPE_EXYNOS4210: 185 phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; 186 rstcon |= RSTCON_SWRST; 187 default: 188 break; 189 } 190 191 writel(phyclk, regs + SAMSUNG_PHYCLK); 192 /* Configure PHY0 for normal operation*/ 193 writel(phypwr, regs + SAMSUNG_PHYPWR); 194 /* reset all ports of PHY and Link */ 195 writel(rstcon, regs + SAMSUNG_RSTCON); 196 udelay(10); 197 if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12) 198 rstcon &= ~RSTCON_HOSTPHY_SWRST; 199 rstcon &= ~RSTCON_SWRST; 200 writel(rstcon, regs + SAMSUNG_RSTCON); 201} 202 203static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy *sphy) 204{ 205 void __iomem *regs = sphy->regs; 206 u32 phyhost; 207 u32 phyotg; 208 u32 phyhsic; 209 210 if (atomic_dec_return(&sphy->phy_usage) > 0) { 211 dev_info(sphy->dev, "still being used\n"); 212 return; 213 } 214 215 phyhsic = (HSIC_CTRL_REFCLKDIV_12 | 216 HSIC_CTRL_REFCLKSEL | 217 HSIC_CTRL_SIDDQ | 218 HSIC_CTRL_FORCESLEEP | 219 HSIC_CTRL_FORCESUSPEND); 220 writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); 221 writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); 222 223 phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); 224 phyhost |= (HOST_CTRL0_SIDDQ | 225 HOST_CTRL0_FORCESUSPEND | 226 HOST_CTRL0_FORCESLEEP | 227 HOST_CTRL0_PHYSWRST | 228 HOST_CTRL0_PHYSWRSTALL); 229 writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); 230 231 phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); 232 phyotg |= (OTG_SYS_FORCESUSPEND | 233 OTG_SYS_SIDDQ_UOTG | 234 OTG_SYS_FORCESLEEP); 235 writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); 236} 237 238static void samsung_usb2phy_disable(struct samsung_usbphy *sphy) 239{ 240 void __iomem *regs = sphy->regs; 241 u32 phypwr; 242 243 phypwr = readl(regs + SAMSUNG_PHYPWR); 244 245 switch (sphy->drv_data->cpu_type) { 246 case TYPE_S3C64XX: 247 phypwr |= PHYPWR_NORMAL_MASK; 248 break; 249 case TYPE_EXYNOS4X12: 250 phypwr |= (PHYPWR_NORMAL_MASK_HSIC0 | 251 PHYPWR_NORMAL_MASK_HSIC1 | 252 PHYPWR_NORMAL_MASK_PHY1); 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_usb2phy_init(struct usb_phy *phy) 267{ 268 struct samsung_usbphy *sphy; 269 struct usb_bus *host = NULL; 270 unsigned long flags; 271 int ret = 0; 272 273 sphy = phy_to_sphy(phy); 274 275 host = phy->otg->host; 276 277 /* Enable the phy clock */ 278 ret = clk_prepare_enable(sphy->clk); 279 if (ret) { 280 dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); 281 return ret; 282 } 283 284 spin_lock_irqsave(&sphy->lock, flags); 285 286 if (host) { 287 /* setting default phy-type for USB 2.0 */ 288 if (!strstr(dev_name(host->controller), "ehci") || 289 !strstr(dev_name(host->controller), "ohci")) 290 samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); 291 } else { 292 samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); 293 } 294 295 /* Disable phy isolation */ 296 if (sphy->plat && sphy->plat->pmu_isolation) 297 sphy->plat->pmu_isolation(false); 298 else if (sphy->drv_data->set_isolation) 299 sphy->drv_data->set_isolation(sphy, false); 300 301 /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ 302 samsung_usbphy_cfg_sel(sphy); 303 304 /* Initialize usb phy registers */ 305 sphy->drv_data->phy_enable(sphy); 306 307 spin_unlock_irqrestore(&sphy->lock, flags); 308 309 /* Disable the phy clock */ 310 clk_disable_unprepare(sphy->clk); 311 312 return ret; 313} 314 315/* 316 * The function passed to the usb driver for phy shutdown 317 */ 318static void samsung_usb2phy_shutdown(struct usb_phy *phy) 319{ 320 struct samsung_usbphy *sphy; 321 struct usb_bus *host = NULL; 322 unsigned long flags; 323 324 sphy = phy_to_sphy(phy); 325 326 host = phy->otg->host; 327 328 if (clk_prepare_enable(sphy->clk)) { 329 dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); 330 return; 331 } 332 333 spin_lock_irqsave(&sphy->lock, flags); 334 335 if (host) { 336 /* setting default phy-type for USB 2.0 */ 337 if (!strstr(dev_name(host->controller), "ehci") || 338 !strstr(dev_name(host->controller), "ohci")) 339 samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); 340 } else { 341 samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); 342 } 343 344 /* De-initialize usb phy registers */ 345 sphy->drv_data->phy_disable(sphy); 346 347 /* Enable phy isolation */ 348 if (sphy->plat && sphy->plat->pmu_isolation) 349 sphy->plat->pmu_isolation(true); 350 else if (sphy->drv_data->set_isolation) 351 sphy->drv_data->set_isolation(sphy, true); 352 353 spin_unlock_irqrestore(&sphy->lock, flags); 354 355 clk_disable_unprepare(sphy->clk); 356} 357 358static int samsung_usb2phy_probe(struct platform_device *pdev) 359{ 360 struct samsung_usbphy *sphy; 361 struct usb_otg *otg; 362 struct samsung_usbphy_data *pdata = pdev->dev.platform_data; 363 const struct samsung_usbphy_drvdata *drv_data; 364 struct device *dev = &pdev->dev; 365 struct resource *phy_mem; 366 void __iomem *phy_base; 367 struct clk *clk; 368 int ret; 369 370 phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 371 phy_base = devm_ioremap_resource(dev, phy_mem); 372 if (IS_ERR(phy_base)) 373 return PTR_ERR(phy_base); 374 375 sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); 376 if (!sphy) 377 return -ENOMEM; 378 379 otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); 380 if (!otg) 381 return -ENOMEM; 382 383 drv_data = samsung_usbphy_get_driver_data(pdev); 384 385 if (drv_data->cpu_type == TYPE_EXYNOS5250) 386 clk = devm_clk_get(dev, "usbhost"); 387 else 388 clk = devm_clk_get(dev, "otg"); 389 390 if (IS_ERR(clk)) { 391 dev_err(dev, "Failed to get usbhost/otg clock\n"); 392 return PTR_ERR(clk); 393 } 394 395 sphy->dev = dev; 396 397 if (dev->of_node) { 398 ret = samsung_usbphy_parse_dt(sphy); 399 if (ret < 0) 400 return ret; 401 } else { 402 if (!pdata) { 403 dev_err(dev, "no platform data specified\n"); 404 return -EINVAL; 405 } 406 } 407 408 sphy->plat = pdata; 409 sphy->regs = phy_base; 410 sphy->clk = clk; 411 sphy->drv_data = drv_data; 412 sphy->phy.dev = sphy->dev; 413 sphy->phy.label = "samsung-usb2phy"; 414 sphy->phy.init = samsung_usb2phy_init; 415 sphy->phy.shutdown = samsung_usb2phy_shutdown; 416 417 sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); 418 if (sphy->ref_clk_freq < 0) 419 return -EINVAL; 420 421 sphy->phy.otg = otg; 422 sphy->phy.otg->phy = &sphy->phy; 423 sphy->phy.otg->set_host = samsung_usbphy_set_host; 424 425 spin_lock_init(&sphy->lock); 426 427 platform_set_drvdata(pdev, sphy); 428 429 return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); 430} 431 432static int samsung_usb2phy_remove(struct platform_device *pdev) 433{ 434 struct samsung_usbphy *sphy = platform_get_drvdata(pdev); 435 436 usb_remove_phy(&sphy->phy); 437 438 if (sphy->pmuregs) 439 iounmap(sphy->pmuregs); 440 if (sphy->sysreg) 441 iounmap(sphy->sysreg); 442 443 return 0; 444} 445 446static const struct samsung_usbphy_drvdata usb2phy_s3c64xx = { 447 .cpu_type = TYPE_S3C64XX, 448 .devphy_en_mask = S3C64XX_USBPHY_ENABLE, 449 .rate_to_clksel = samsung_usbphy_rate_to_clksel_64xx, 450 .set_isolation = NULL, /* TODO */ 451 .phy_enable = samsung_usb2phy_enable, 452 .phy_disable = samsung_usb2phy_disable, 453}; 454 455static const struct samsung_usbphy_drvdata usb2phy_exynos4 = { 456 .cpu_type = TYPE_EXYNOS4210, 457 .devphy_en_mask = EXYNOS_USBPHY_ENABLE, 458 .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, 459 .rate_to_clksel = samsung_usbphy_rate_to_clksel_64xx, 460 .set_isolation = samsung_usbphy_set_isolation_4210, 461 .phy_enable = samsung_usb2phy_enable, 462 .phy_disable = samsung_usb2phy_disable, 463}; 464 465static const struct samsung_usbphy_drvdata usb2phy_exynos4x12 = { 466 .cpu_type = TYPE_EXYNOS4X12, 467 .devphy_en_mask = EXYNOS_USBPHY_ENABLE, 468 .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, 469 .rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12, 470 .set_isolation = samsung_usbphy_set_isolation_4210, 471 .phy_enable = samsung_usb2phy_enable, 472 .phy_disable = samsung_usb2phy_disable, 473}; 474 475static struct samsung_usbphy_drvdata usb2phy_exynos5 = { 476 .cpu_type = TYPE_EXYNOS5250, 477 .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, 478 .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, 479 .rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12, 480 .set_isolation = samsung_usbphy_set_isolation_4210, 481 .phy_enable = samsung_exynos5_usb2phy_enable, 482 .phy_disable = samsung_exynos5_usb2phy_disable, 483}; 484 485#ifdef CONFIG_OF 486static const struct of_device_id samsung_usbphy_dt_match[] = { 487 { 488 .compatible = "samsung,s3c64xx-usb2phy", 489 .data = &usb2phy_s3c64xx, 490 }, { 491 .compatible = "samsung,exynos4210-usb2phy", 492 .data = &usb2phy_exynos4, 493 }, { 494 .compatible = "samsung,exynos4x12-usb2phy", 495 .data = &usb2phy_exynos4x12, 496 }, { 497 .compatible = "samsung,exynos5250-usb2phy", 498 .data = &usb2phy_exynos5 499 }, 500 {}, 501}; 502MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); 503#endif 504 505static struct platform_device_id samsung_usbphy_driver_ids[] = { 506 { 507 .name = "s3c64xx-usb2phy", 508 .driver_data = (unsigned long)&usb2phy_s3c64xx, 509 }, { 510 .name = "exynos4210-usb2phy", 511 .driver_data = (unsigned long)&usb2phy_exynos4, 512 }, { 513 .name = "exynos4x12-usb2phy", 514 .driver_data = (unsigned long)&usb2phy_exynos4x12, 515 }, { 516 .name = "exynos5250-usb2phy", 517 .driver_data = (unsigned long)&usb2phy_exynos5, 518 }, 519 {}, 520}; 521 522MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); 523 524static struct platform_driver samsung_usb2phy_driver = { 525 .probe = samsung_usb2phy_probe, 526 .remove = samsung_usb2phy_remove, 527 .id_table = samsung_usbphy_driver_ids, 528 .driver = { 529 .name = "samsung-usb2phy", 530 .owner = THIS_MODULE, 531 .of_match_table = of_match_ptr(samsung_usbphy_dt_match), 532 }, 533}; 534 535module_platform_driver(samsung_usb2phy_driver); 536 537MODULE_DESCRIPTION("Samsung USB 2.0 phy controller"); 538MODULE_AUTHOR("Praveen Paneri <p.paneri@samsung.com>"); 539MODULE_LICENSE("GPL"); 540MODULE_ALIAS("platform:samsung-usb2phy");