18e22978cSAlexander Shishkin /* Copyright (c) 2010, Code Aurora Forum. All rights reserved. 28e22978cSAlexander Shishkin * 38e22978cSAlexander Shishkin * This program is free software; you can redistribute it and/or modify 48e22978cSAlexander Shishkin * it under the terms of the GNU General Public License version 2 and 58e22978cSAlexander Shishkin * only version 2 as published by the Free Software Foundation. 68e22978cSAlexander Shishkin */ 78e22978cSAlexander Shishkin 88e22978cSAlexander Shishkin #include <linux/module.h> 98e22978cSAlexander Shishkin #include <linux/platform_device.h> 108e22978cSAlexander Shishkin #include <linux/pm_runtime.h> 118e22978cSAlexander Shishkin #include <linux/usb/msm_hsusb_hw.h> 128e22978cSAlexander Shishkin #include <linux/usb/ulpi.h> 138e22978cSAlexander Shishkin #include <linux/usb/gadget.h> 148e22978cSAlexander Shishkin #include <linux/usb/chipidea.h> 158e22978cSAlexander Shishkin 168e22978cSAlexander Shishkin #include "ci.h" 178e22978cSAlexander Shishkin 188e22978cSAlexander Shishkin #define MSM_USB_BASE (ci->hw_bank.abs) 198e22978cSAlexander Shishkin 208e22978cSAlexander Shishkin static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event) 218e22978cSAlexander Shishkin { 228e22978cSAlexander Shishkin struct device *dev = ci->gadget.dev.parent; 238e22978cSAlexander Shishkin int val; 248e22978cSAlexander Shishkin 258e22978cSAlexander Shishkin switch (event) { 268e22978cSAlexander Shishkin case CI_HDRC_CONTROLLER_RESET_EVENT: 278e22978cSAlexander Shishkin dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n"); 288e22978cSAlexander Shishkin writel(0, USB_AHBBURST); 298e22978cSAlexander Shishkin writel(0, USB_AHBMODE); 308e22978cSAlexander Shishkin break; 318e22978cSAlexander Shishkin case CI_HDRC_CONTROLLER_STOPPED_EVENT: 328e22978cSAlexander Shishkin dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n"); 338e22978cSAlexander Shishkin /* 348e22978cSAlexander Shishkin * Put the transceiver in non-driving mode. Otherwise host 358e22978cSAlexander Shishkin * may not detect soft-disconnection. 368e22978cSAlexander Shishkin */ 378e22978cSAlexander Shishkin val = usb_phy_io_read(ci->transceiver, ULPI_FUNC_CTRL); 388e22978cSAlexander Shishkin val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; 398e22978cSAlexander Shishkin val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; 408e22978cSAlexander Shishkin usb_phy_io_write(ci->transceiver, val, ULPI_FUNC_CTRL); 418e22978cSAlexander Shishkin break; 428e22978cSAlexander Shishkin default: 438e22978cSAlexander Shishkin dev_dbg(dev, "unknown ci_hdrc event\n"); 448e22978cSAlexander Shishkin break; 458e22978cSAlexander Shishkin } 468e22978cSAlexander Shishkin } 478e22978cSAlexander Shishkin 488e22978cSAlexander Shishkin static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = { 498e22978cSAlexander Shishkin .name = "ci_hdrc_msm", 508e22978cSAlexander Shishkin .flags = CI_HDRC_REGS_SHARED | 518e22978cSAlexander Shishkin CI_HDRC_REQUIRE_TRANSCEIVER | 528e22978cSAlexander Shishkin CI_HDRC_DISABLE_STREAMING, 538e22978cSAlexander Shishkin 548e22978cSAlexander Shishkin .notify_event = ci_hdrc_msm_notify_event, 558e22978cSAlexander Shishkin }; 568e22978cSAlexander Shishkin 578e22978cSAlexander Shishkin static int ci_hdrc_msm_probe(struct platform_device *pdev) 588e22978cSAlexander Shishkin { 598e22978cSAlexander Shishkin struct platform_device *plat_ci; 60*2629b101SIvan T. Ivanov struct usb_phy *phy; 618e22978cSAlexander Shishkin 628e22978cSAlexander Shishkin dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n"); 638e22978cSAlexander Shishkin 64*2629b101SIvan T. Ivanov /* 65*2629b101SIvan T. Ivanov * OTG(PHY) driver takes care of PHY initialization, clock management, 66*2629b101SIvan T. Ivanov * powering up VBUS, mapping of registers address space and power 67*2629b101SIvan T. Ivanov * management. 68*2629b101SIvan T. Ivanov */ 69*2629b101SIvan T. Ivanov phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); 70*2629b101SIvan T. Ivanov if (IS_ERR(phy)) 71*2629b101SIvan T. Ivanov return PTR_ERR(phy); 72*2629b101SIvan T. Ivanov 73*2629b101SIvan T. Ivanov ci_hdrc_msm_platdata.phy = phy; 74*2629b101SIvan T. Ivanov 758e22978cSAlexander Shishkin plat_ci = ci_hdrc_add_device(&pdev->dev, 768e22978cSAlexander Shishkin pdev->resource, pdev->num_resources, 778e22978cSAlexander Shishkin &ci_hdrc_msm_platdata); 788e22978cSAlexander Shishkin if (IS_ERR(plat_ci)) { 798e22978cSAlexander Shishkin dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n"); 808e22978cSAlexander Shishkin return PTR_ERR(plat_ci); 818e22978cSAlexander Shishkin } 828e22978cSAlexander Shishkin 838e22978cSAlexander Shishkin platform_set_drvdata(pdev, plat_ci); 848e22978cSAlexander Shishkin 858e22978cSAlexander Shishkin pm_runtime_no_callbacks(&pdev->dev); 868e22978cSAlexander Shishkin pm_runtime_enable(&pdev->dev); 878e22978cSAlexander Shishkin 888e22978cSAlexander Shishkin return 0; 898e22978cSAlexander Shishkin } 908e22978cSAlexander Shishkin 918e22978cSAlexander Shishkin static int ci_hdrc_msm_remove(struct platform_device *pdev) 928e22978cSAlexander Shishkin { 938e22978cSAlexander Shishkin struct platform_device *plat_ci = platform_get_drvdata(pdev); 948e22978cSAlexander Shishkin 958e22978cSAlexander Shishkin pm_runtime_disable(&pdev->dev); 968e22978cSAlexander Shishkin ci_hdrc_remove_device(plat_ci); 978e22978cSAlexander Shishkin 988e22978cSAlexander Shishkin return 0; 998e22978cSAlexander Shishkin } 1008e22978cSAlexander Shishkin 101*2629b101SIvan T. Ivanov static const struct of_device_id msm_ci_dt_match[] = { 102*2629b101SIvan T. Ivanov { .compatible = "qcom,ci-hdrc", }, 103*2629b101SIvan T. Ivanov { } 104*2629b101SIvan T. Ivanov }; 105*2629b101SIvan T. Ivanov MODULE_DEVICE_TABLE(of, msm_ci_dt_match); 106*2629b101SIvan T. Ivanov 1078e22978cSAlexander Shishkin static struct platform_driver ci_hdrc_msm_driver = { 1088e22978cSAlexander Shishkin .probe = ci_hdrc_msm_probe, 1098e22978cSAlexander Shishkin .remove = ci_hdrc_msm_remove, 110*2629b101SIvan T. Ivanov .driver = { 111*2629b101SIvan T. Ivanov .name = "msm_hsusb", 112*2629b101SIvan T. Ivanov .of_match_table = msm_ci_dt_match, 113*2629b101SIvan T. Ivanov }, 1148e22978cSAlexander Shishkin }; 1158e22978cSAlexander Shishkin 1168e22978cSAlexander Shishkin module_platform_driver(ci_hdrc_msm_driver); 1178e22978cSAlexander Shishkin 1188e22978cSAlexander Shishkin MODULE_ALIAS("platform:msm_hsusb"); 1198e22978cSAlexander Shishkin MODULE_ALIAS("platform:ci13xxx_msm"); 1208e22978cSAlexander Shishkin MODULE_LICENSE("GPL v2"); 121