1eb70e5abSAlexander Shishkin /* 2eb70e5abSAlexander Shishkin * host.c - ChipIdea USB host controller driver 3eb70e5abSAlexander Shishkin * 4eb70e5abSAlexander Shishkin * Copyright (c) 2012 Intel Corporation 5eb70e5abSAlexander Shishkin * 6eb70e5abSAlexander Shishkin * Author: Alexander Shishkin 7eb70e5abSAlexander Shishkin * 8eb70e5abSAlexander Shishkin * This program is free software; you can redistribute it and/or modify 9eb70e5abSAlexander Shishkin * it under the terms of the GNU General Public License version 2 as 10eb70e5abSAlexander Shishkin * published by the Free Software Foundation. 11eb70e5abSAlexander Shishkin * 12eb70e5abSAlexander Shishkin * This program is distributed in the hope that it will be useful, 13eb70e5abSAlexander Shishkin * but WITHOUT ANY WARRANTY; without even the implied warranty of 14eb70e5abSAlexander Shishkin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15eb70e5abSAlexander Shishkin * GNU General Public License for more details. 16eb70e5abSAlexander Shishkin * 17eb70e5abSAlexander Shishkin * You should have received a copy of the GNU General Public License 18eb70e5abSAlexander Shishkin * along with this program; if not, write to the Free Software 19eb70e5abSAlexander Shishkin * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20eb70e5abSAlexander Shishkin */ 21eb70e5abSAlexander Shishkin 22eb70e5abSAlexander Shishkin #include <linux/kernel.h> 23cdb2fac7SAlan Stern #include <linux/io.h> 24eb70e5abSAlexander Shishkin #include <linux/usb.h> 25eb70e5abSAlexander Shishkin #include <linux/usb/hcd.h> 26eb70e5abSAlexander Shishkin #include <linux/usb/chipidea.h> 2740ed51a4SPeter Chen #include <linux/regulator/consumer.h> 28eb70e5abSAlexander Shishkin 2909f6ffdeSAlan Stern #include "../host/ehci.h" 30eb70e5abSAlexander Shishkin 31eb70e5abSAlexander Shishkin #include "ci.h" 32eb70e5abSAlexander Shishkin #include "bits.h" 33eb70e5abSAlexander Shishkin #include "host.h" 34eb70e5abSAlexander Shishkin 3509f6ffdeSAlan Stern static struct hc_driver __read_mostly ci_ehci_hc_driver; 3678f0357eSPeter Chen static int (*orig_bus_suspend)(struct usb_hcd *hcd); 3709f6ffdeSAlan Stern 38c8679a2fSMichael Grzeschik struct ehci_ci_priv { 39c8679a2fSMichael Grzeschik struct regulator *reg_vbus; 40c8679a2fSMichael Grzeschik }; 41c8679a2fSMichael Grzeschik 42c8679a2fSMichael Grzeschik static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) 43c8679a2fSMichael Grzeschik { 44c8679a2fSMichael Grzeschik struct ehci_hcd *ehci = hcd_to_ehci(hcd); 45c8679a2fSMichael Grzeschik struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv; 46c8679a2fSMichael Grzeschik struct device *dev = hcd->self.controller; 47c8679a2fSMichael Grzeschik struct ci_hdrc *ci = dev_get_drvdata(dev); 48c8679a2fSMichael Grzeschik int ret = 0; 49c8679a2fSMichael Grzeschik int port = HCS_N_PORTS(ehci->hcs_params); 50c8679a2fSMichael Grzeschik 51c8679a2fSMichael Grzeschik if (priv->reg_vbus && !ci_otg_is_fsm_mode(ci)) { 52c8679a2fSMichael Grzeschik if (port > 1) { 53c8679a2fSMichael Grzeschik dev_warn(dev, 54c8679a2fSMichael Grzeschik "Not support multi-port regulator control\n"); 55c8679a2fSMichael Grzeschik return 0; 56c8679a2fSMichael Grzeschik } 57c8679a2fSMichael Grzeschik if (enable) 58c8679a2fSMichael Grzeschik ret = regulator_enable(priv->reg_vbus); 59c8679a2fSMichael Grzeschik else 60c8679a2fSMichael Grzeschik ret = regulator_disable(priv->reg_vbus); 61c8679a2fSMichael Grzeschik if (ret) { 62c8679a2fSMichael Grzeschik dev_err(dev, 63c8679a2fSMichael Grzeschik "Failed to %s vbus regulator, ret=%d\n", 64c8679a2fSMichael Grzeschik enable ? "enable" : "disable", ret); 65c8679a2fSMichael Grzeschik return ret; 66c8679a2fSMichael Grzeschik } 67c8679a2fSMichael Grzeschik } 68c8679a2fSMichael Grzeschik return 0; 69c8679a2fSMichael Grzeschik }; 70c8679a2fSMichael Grzeschik 71c8679a2fSMichael Grzeschik static const struct ehci_driver_overrides ehci_ci_overrides = { 72c8679a2fSMichael Grzeschik .extra_priv_size = sizeof(struct ehci_ci_priv), 73c8679a2fSMichael Grzeschik .port_power = ehci_ci_portpower, 74c8679a2fSMichael Grzeschik }; 75c8679a2fSMichael Grzeschik 768e22978cSAlexander Shishkin static irqreturn_t host_irq(struct ci_hdrc *ci) 77eb70e5abSAlexander Shishkin { 78eb70e5abSAlexander Shishkin return usb_hcd_irq(ci->irq, ci->hcd); 79eb70e5abSAlexander Shishkin } 80eb70e5abSAlexander Shishkin 818e22978cSAlexander Shishkin static int host_start(struct ci_hdrc *ci) 82eb70e5abSAlexander Shishkin { 83eb70e5abSAlexander Shishkin struct usb_hcd *hcd; 84eb70e5abSAlexander Shishkin struct ehci_hcd *ehci; 85c8679a2fSMichael Grzeschik struct ehci_ci_priv *priv; 86eb70e5abSAlexander Shishkin int ret; 87eb70e5abSAlexander Shishkin 88eb70e5abSAlexander Shishkin if (usb_disabled()) 89eb70e5abSAlexander Shishkin return -ENODEV; 90eb70e5abSAlexander Shishkin 91eb70e5abSAlexander Shishkin hcd = usb_create_hcd(&ci_ehci_hc_driver, ci->dev, dev_name(ci->dev)); 92eb70e5abSAlexander Shishkin if (!hcd) 93eb70e5abSAlexander Shishkin return -ENOMEM; 94eb70e5abSAlexander Shishkin 9524c498dfSPeter Chen dev_set_drvdata(ci->dev, ci); 96eb70e5abSAlexander Shishkin hcd->rsrc_start = ci->hw_bank.phys; 97eb70e5abSAlexander Shishkin hcd->rsrc_len = ci->hw_bank.size; 98eb70e5abSAlexander Shishkin hcd->regs = ci->hw_bank.abs; 99eb70e5abSAlexander Shishkin hcd->has_tt = 1; 100eb70e5abSAlexander Shishkin 10177c4400fSRichard Zhao hcd->power_budget = ci->platdata->power_budget; 102f6a9ff07SPeter Chen hcd->tpl_support = ci->platdata->tpl_support; 1031e5e2d3dSAntoine Tenart if (ci->phy) 1041e5e2d3dSAntoine Tenart hcd->phy = ci->phy; 1051e5e2d3dSAntoine Tenart else 1061e5e2d3dSAntoine Tenart hcd->usb_phy = ci->usb_phy; 107bd841986SAlexander Shishkin 108eb70e5abSAlexander Shishkin ehci = hcd_to_ehci(hcd); 109eb70e5abSAlexander Shishkin ehci->caps = ci->hw_bank.cap; 110eb70e5abSAlexander Shishkin ehci->has_hostpc = ci->hw_bank.lpm; 1112cdcec4fSTuomas Tynkkynen ehci->has_tdi_phy_lpm = ci->hw_bank.lpm; 112ed8f8318SPeter Chen ehci->imx28_write_fix = ci->imx28_write_fix; 113eb70e5abSAlexander Shishkin 114c8679a2fSMichael Grzeschik priv = (struct ehci_ci_priv *)ehci->priv; 115c8679a2fSMichael Grzeschik priv->reg_vbus = NULL; 116c8679a2fSMichael Grzeschik 117c8679a2fSMichael Grzeschik if (ci->platdata->reg_vbus) 118c8679a2fSMichael Grzeschik priv->reg_vbus = ci->platdata->reg_vbus; 11940ed51a4SPeter Chen 120eb70e5abSAlexander Shishkin ret = usb_add_hcd(hcd, 0, 0); 1210698b9b3SLi Jun if (ret) { 122c8679a2fSMichael Grzeschik goto put_hcd; 1230698b9b3SLi Jun } else { 124ef44cb42SAntoine Tenart struct usb_otg *otg = &ci->otg; 1250698b9b3SLi Jun 126eb70e5abSAlexander Shishkin ci->hcd = hcd; 127ef44cb42SAntoine Tenart 128ef44cb42SAntoine Tenart if (ci_otg_is_fsm_mode(ci)) { 1290698b9b3SLi Jun otg->host = &hcd->self; 1300698b9b3SLi Jun hcd->self.otg_port = 1; 1310698b9b3SLi Jun } 1320698b9b3SLi Jun } 133eb70e5abSAlexander Shishkin 1348e22978cSAlexander Shishkin if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) 135929473eaSFabio Estevam hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); 136929473eaSFabio Estevam 137905276c4SDaniel Tang if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) 138905276c4SDaniel Tang hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); 139905276c4SDaniel Tang 140eb70e5abSAlexander Shishkin return ret; 14140ed51a4SPeter Chen 14240ed51a4SPeter Chen put_hcd: 14340ed51a4SPeter Chen usb_put_hcd(hcd); 14440ed51a4SPeter Chen 14540ed51a4SPeter Chen return ret; 146eb70e5abSAlexander Shishkin } 147eb70e5abSAlexander Shishkin 1488e22978cSAlexander Shishkin static void host_stop(struct ci_hdrc *ci) 149eb70e5abSAlexander Shishkin { 150eb70e5abSAlexander Shishkin struct usb_hcd *hcd = ci->hcd; 151eb70e5abSAlexander Shishkin 15241314feaSRussell King - ARM Linux if (hcd) { 153eb70e5abSAlexander Shishkin usb_remove_hcd(hcd); 154eb70e5abSAlexander Shishkin usb_put_hcd(hcd); 155eb70e5abSAlexander Shishkin } 156df101c53SPeter Chen } 157eb70e5abSAlexander Shishkin 1583f124d23SPeter Chen 1593f124d23SPeter Chen void ci_hdrc_host_destroy(struct ci_hdrc *ci) 1603f124d23SPeter Chen { 161df101c53SPeter Chen if (ci->role == CI_ROLE_HOST && ci->hcd) 1623f124d23SPeter Chen host_stop(ci); 1633f124d23SPeter Chen } 1643f124d23SPeter Chen 16578f0357eSPeter Chen static int ci_ehci_bus_suspend(struct usb_hcd *hcd) 16678f0357eSPeter Chen { 16778f0357eSPeter Chen struct ehci_hcd *ehci = hcd_to_ehci(hcd); 16878f0357eSPeter Chen int port; 16978f0357eSPeter Chen u32 tmp; 17078f0357eSPeter Chen 17178f0357eSPeter Chen int ret = orig_bus_suspend(hcd); 17278f0357eSPeter Chen 17378f0357eSPeter Chen if (ret) 17478f0357eSPeter Chen return ret; 17578f0357eSPeter Chen 17678f0357eSPeter Chen port = HCS_N_PORTS(ehci->hcs_params); 17778f0357eSPeter Chen while (port--) { 17878f0357eSPeter Chen u32 __iomem *reg = &ehci->regs->port_status[port]; 17978f0357eSPeter Chen u32 portsc = ehci_readl(ehci, reg); 18078f0357eSPeter Chen 18178f0357eSPeter Chen if (portsc & PORT_CONNECT) { 18278f0357eSPeter Chen /* 18378f0357eSPeter Chen * For chipidea, the resume signal will be ended 18478f0357eSPeter Chen * automatically, so for remote wakeup case, the 18578f0357eSPeter Chen * usbcmd.rs may not be set before the resume has 18678f0357eSPeter Chen * ended if other resume paths consumes too much 18778f0357eSPeter Chen * time (~24ms), in that case, the SOF will not 18878f0357eSPeter Chen * send out within 3ms after resume ends, then the 18978f0357eSPeter Chen * high speed device will enter full speed mode. 19078f0357eSPeter Chen */ 19178f0357eSPeter Chen 19278f0357eSPeter Chen tmp = ehci_readl(ehci, &ehci->regs->command); 19378f0357eSPeter Chen tmp |= CMD_RUN; 19478f0357eSPeter Chen ehci_writel(ehci, tmp, &ehci->regs->command); 19578f0357eSPeter Chen /* 19678f0357eSPeter Chen * It needs a short delay between set RS bit and PHCD. 19778f0357eSPeter Chen */ 19878f0357eSPeter Chen usleep_range(150, 200); 19978f0357eSPeter Chen break; 20078f0357eSPeter Chen } 20178f0357eSPeter Chen } 20278f0357eSPeter Chen 20378f0357eSPeter Chen return 0; 20478f0357eSPeter Chen } 20578f0357eSPeter Chen 2068e22978cSAlexander Shishkin int ci_hdrc_host_init(struct ci_hdrc *ci) 207eb70e5abSAlexander Shishkin { 208eb70e5abSAlexander Shishkin struct ci_role_driver *rdrv; 209eb70e5abSAlexander Shishkin 210eb70e5abSAlexander Shishkin if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_HC)) 211eb70e5abSAlexander Shishkin return -ENXIO; 212eb70e5abSAlexander Shishkin 213eb70e5abSAlexander Shishkin rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL); 214eb70e5abSAlexander Shishkin if (!rdrv) 215eb70e5abSAlexander Shishkin return -ENOMEM; 216eb70e5abSAlexander Shishkin 217eb70e5abSAlexander Shishkin rdrv->start = host_start; 218eb70e5abSAlexander Shishkin rdrv->stop = host_stop; 219eb70e5abSAlexander Shishkin rdrv->irq = host_irq; 220eb70e5abSAlexander Shishkin rdrv->name = "host"; 221eb70e5abSAlexander Shishkin ci->roles[CI_ROLE_HOST] = rdrv; 222eb70e5abSAlexander Shishkin 223c8679a2fSMichael Grzeschik ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides); 22478f0357eSPeter Chen orig_bus_suspend = ci_ehci_hc_driver.bus_suspend; 22578f0357eSPeter Chen ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend; 22609f6ffdeSAlan Stern 227eb70e5abSAlexander Shishkin return 0; 228eb70e5abSAlexander Shishkin } 229