1e443b333SAlexander Shishkin /* 2e443b333SAlexander Shishkin * core.c - ChipIdea USB IP core family device controller 3e443b333SAlexander Shishkin * 4e443b333SAlexander Shishkin * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. 5e443b333SAlexander Shishkin * 6e443b333SAlexander Shishkin * Author: David Lopo 7e443b333SAlexander Shishkin * 8e443b333SAlexander Shishkin * This program is free software; you can redistribute it and/or modify 9e443b333SAlexander Shishkin * it under the terms of the GNU General Public License version 2 as 10e443b333SAlexander Shishkin * published by the Free Software Foundation. 11e443b333SAlexander Shishkin */ 12e443b333SAlexander Shishkin 13e443b333SAlexander Shishkin /* 14e443b333SAlexander Shishkin * Description: ChipIdea USB IP core family device controller 15e443b333SAlexander Shishkin * 16e443b333SAlexander Shishkin * This driver is composed of several blocks: 17e443b333SAlexander Shishkin * - HW: hardware interface 18e443b333SAlexander Shishkin * - DBG: debug facilities (optional) 19e443b333SAlexander Shishkin * - UTIL: utilities 20e443b333SAlexander Shishkin * - ISR: interrupts handling 21e443b333SAlexander Shishkin * - ENDPT: endpoint operations (Gadget API) 22e443b333SAlexander Shishkin * - GADGET: gadget operations (Gadget API) 23e443b333SAlexander Shishkin * - BUS: bus glue code, bus abstraction layer 24e443b333SAlexander Shishkin * 25e443b333SAlexander Shishkin * Compile Options 26e443b333SAlexander Shishkin * - STALL_IN: non-empty bulk-in pipes cannot be halted 27e443b333SAlexander Shishkin * if defined mass storage compliance succeeds but with warnings 28e443b333SAlexander Shishkin * => case 4: Hi > Dn 29e443b333SAlexander Shishkin * => case 5: Hi > Di 30e443b333SAlexander Shishkin * => case 8: Hi <> Do 31e443b333SAlexander Shishkin * if undefined usbtest 13 fails 32e443b333SAlexander Shishkin * - TRACE: enable function tracing (depends on DEBUG) 33e443b333SAlexander Shishkin * 34e443b333SAlexander Shishkin * Main Features 35e443b333SAlexander Shishkin * - Chapter 9 & Mass Storage Compliance with Gadget File Storage 36e443b333SAlexander Shishkin * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) 37e443b333SAlexander Shishkin * - Normal & LPM support 38e443b333SAlexander Shishkin * 39e443b333SAlexander Shishkin * USBTEST Report 40e443b333SAlexander Shishkin * - OK: 0-12, 13 (STALL_IN defined) & 14 41e443b333SAlexander Shishkin * - Not Supported: 15 & 16 (ISO) 42e443b333SAlexander Shishkin * 43e443b333SAlexander Shishkin * TODO List 44e443b333SAlexander Shishkin * - Suspend & Remote Wakeup 45e443b333SAlexander Shishkin */ 46e443b333SAlexander Shishkin #include <linux/delay.h> 47e443b333SAlexander Shishkin #include <linux/device.h> 48e443b333SAlexander Shishkin #include <linux/dma-mapping.h> 493ecb3e09SIvan T. Ivanov #include <linux/extcon.h> 501e5e2d3dSAntoine Tenart #include <linux/phy/phy.h> 51e443b333SAlexander Shishkin #include <linux/platform_device.h> 52e443b333SAlexander Shishkin #include <linux/module.h> 53fe6e125eSRichard Zhao #include <linux/idr.h> 54e443b333SAlexander Shishkin #include <linux/interrupt.h> 55e443b333SAlexander Shishkin #include <linux/io.h> 56e443b333SAlexander Shishkin #include <linux/kernel.h> 57e443b333SAlexander Shishkin #include <linux/slab.h> 58e443b333SAlexander Shishkin #include <linux/pm_runtime.h> 59e443b333SAlexander Shishkin #include <linux/usb/ch9.h> 60e443b333SAlexander Shishkin #include <linux/usb/gadget.h> 61e443b333SAlexander Shishkin #include <linux/usb/otg.h> 62e443b333SAlexander Shishkin #include <linux/usb/chipidea.h> 6340dcd0e8SMichael Grzeschik #include <linux/usb/of.h> 644f6743d5SMichael Grzeschik #include <linux/of.h> 6540dcd0e8SMichael Grzeschik #include <linux/phy.h> 661542d9c3SPeter Chen #include <linux/regulator/consumer.h> 678022d3d5SPeter Chen #include <linux/usb/ehci_def.h> 68e443b333SAlexander Shishkin 69e443b333SAlexander Shishkin #include "ci.h" 70e443b333SAlexander Shishkin #include "udc.h" 71e443b333SAlexander Shishkin #include "bits.h" 72eb70e5abSAlexander Shishkin #include "host.h" 73c10b4f03SPeter Chen #include "otg.h" 744dcf720cSLi Jun #include "otg_fsm.h" 75e443b333SAlexander Shishkin 765f36e231SAlexander Shishkin /* Controller register map */ 77987e7bc3SMarc Kleine-Budde static const u8 ci_regs_nolpm[] = { 78987e7bc3SMarc Kleine-Budde [CAP_CAPLENGTH] = 0x00U, 79987e7bc3SMarc Kleine-Budde [CAP_HCCPARAMS] = 0x08U, 80987e7bc3SMarc Kleine-Budde [CAP_DCCPARAMS] = 0x24U, 81987e7bc3SMarc Kleine-Budde [CAP_TESTMODE] = 0x38U, 82987e7bc3SMarc Kleine-Budde [OP_USBCMD] = 0x00U, 83987e7bc3SMarc Kleine-Budde [OP_USBSTS] = 0x04U, 84987e7bc3SMarc Kleine-Budde [OP_USBINTR] = 0x08U, 85987e7bc3SMarc Kleine-Budde [OP_DEVICEADDR] = 0x14U, 86987e7bc3SMarc Kleine-Budde [OP_ENDPTLISTADDR] = 0x18U, 8728362673SPeter Chen [OP_TTCTRL] = 0x1CU, 8896625eadSPeter Chen [OP_BURSTSIZE] = 0x20U, 897bb7e9b1SStephen Boyd [OP_ULPI_VIEWPORT] = 0x30U, 90987e7bc3SMarc Kleine-Budde [OP_PORTSC] = 0x44U, 91987e7bc3SMarc Kleine-Budde [OP_DEVLC] = 0x84U, 92987e7bc3SMarc Kleine-Budde [OP_OTGSC] = 0x64U, 93987e7bc3SMarc Kleine-Budde [OP_USBMODE] = 0x68U, 94987e7bc3SMarc Kleine-Budde [OP_ENDPTSETUPSTAT] = 0x6CU, 95987e7bc3SMarc Kleine-Budde [OP_ENDPTPRIME] = 0x70U, 96987e7bc3SMarc Kleine-Budde [OP_ENDPTFLUSH] = 0x74U, 97987e7bc3SMarc Kleine-Budde [OP_ENDPTSTAT] = 0x78U, 98987e7bc3SMarc Kleine-Budde [OP_ENDPTCOMPLETE] = 0x7CU, 99987e7bc3SMarc Kleine-Budde [OP_ENDPTCTRL] = 0x80U, 100e443b333SAlexander Shishkin }; 101e443b333SAlexander Shishkin 102987e7bc3SMarc Kleine-Budde static const u8 ci_regs_lpm[] = { 103987e7bc3SMarc Kleine-Budde [CAP_CAPLENGTH] = 0x00U, 104987e7bc3SMarc Kleine-Budde [CAP_HCCPARAMS] = 0x08U, 105987e7bc3SMarc Kleine-Budde [CAP_DCCPARAMS] = 0x24U, 106987e7bc3SMarc Kleine-Budde [CAP_TESTMODE] = 0xFCU, 107987e7bc3SMarc Kleine-Budde [OP_USBCMD] = 0x00U, 108987e7bc3SMarc Kleine-Budde [OP_USBSTS] = 0x04U, 109987e7bc3SMarc Kleine-Budde [OP_USBINTR] = 0x08U, 110987e7bc3SMarc Kleine-Budde [OP_DEVICEADDR] = 0x14U, 111987e7bc3SMarc Kleine-Budde [OP_ENDPTLISTADDR] = 0x18U, 11228362673SPeter Chen [OP_TTCTRL] = 0x1CU, 11396625eadSPeter Chen [OP_BURSTSIZE] = 0x20U, 1147bb7e9b1SStephen Boyd [OP_ULPI_VIEWPORT] = 0x30U, 115987e7bc3SMarc Kleine-Budde [OP_PORTSC] = 0x44U, 116987e7bc3SMarc Kleine-Budde [OP_DEVLC] = 0x84U, 117987e7bc3SMarc Kleine-Budde [OP_OTGSC] = 0xC4U, 118987e7bc3SMarc Kleine-Budde [OP_USBMODE] = 0xC8U, 119987e7bc3SMarc Kleine-Budde [OP_ENDPTSETUPSTAT] = 0xD8U, 120987e7bc3SMarc Kleine-Budde [OP_ENDPTPRIME] = 0xDCU, 121987e7bc3SMarc Kleine-Budde [OP_ENDPTFLUSH] = 0xE0U, 122987e7bc3SMarc Kleine-Budde [OP_ENDPTSTAT] = 0xE4U, 123987e7bc3SMarc Kleine-Budde [OP_ENDPTCOMPLETE] = 0xE8U, 124987e7bc3SMarc Kleine-Budde [OP_ENDPTCTRL] = 0xECU, 125e443b333SAlexander Shishkin }; 126e443b333SAlexander Shishkin 127158ec071SNicholas Krause static void hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) 128e443b333SAlexander Shishkin { 129e443b333SAlexander Shishkin int i; 130e443b333SAlexander Shishkin 131e443b333SAlexander Shishkin for (i = 0; i < OP_ENDPTCTRL; i++) 1325f36e231SAlexander Shishkin ci->hw_bank.regmap[i] = 1335f36e231SAlexander Shishkin (i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) + 134e443b333SAlexander Shishkin (is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]); 135e443b333SAlexander Shishkin 136e443b333SAlexander Shishkin for (; i <= OP_LAST; i++) 1375f36e231SAlexander Shishkin ci->hw_bank.regmap[i] = ci->hw_bank.op + 138e443b333SAlexander Shishkin 4 * (i - OP_ENDPTCTRL) + 139e443b333SAlexander Shishkin (is_lpm 140e443b333SAlexander Shishkin ? ci_regs_lpm[OP_ENDPTCTRL] 141e443b333SAlexander Shishkin : ci_regs_nolpm[OP_ENDPTCTRL]); 142e443b333SAlexander Shishkin 143e443b333SAlexander Shishkin } 144e443b333SAlexander Shishkin 145cb271f3cSPeter Chen static enum ci_revision ci_get_revision(struct ci_hdrc *ci) 146cb271f3cSPeter Chen { 147cb271f3cSPeter Chen int ver = hw_read_id_reg(ci, ID_ID, VERSION) >> __ffs(VERSION); 148cb271f3cSPeter Chen enum ci_revision rev = CI_REVISION_UNKNOWN; 149cb271f3cSPeter Chen 150cb271f3cSPeter Chen if (ver == 0x2) { 151cb271f3cSPeter Chen rev = hw_read_id_reg(ci, ID_ID, REVISION) 152cb271f3cSPeter Chen >> __ffs(REVISION); 153cb271f3cSPeter Chen rev += CI_REVISION_20; 154cb271f3cSPeter Chen } else if (ver == 0x0) { 155cb271f3cSPeter Chen rev = CI_REVISION_1X; 156cb271f3cSPeter Chen } 157cb271f3cSPeter Chen 158cb271f3cSPeter Chen return rev; 159cb271f3cSPeter Chen } 160cb271f3cSPeter Chen 161e443b333SAlexander Shishkin /** 16236304b06SLi Jun * hw_read_intr_enable: returns interrupt enable register 16336304b06SLi Jun * 16419353881SPeter Chen * @ci: the controller 16519353881SPeter Chen * 16636304b06SLi Jun * This function returns register data 16736304b06SLi Jun */ 16836304b06SLi Jun u32 hw_read_intr_enable(struct ci_hdrc *ci) 16936304b06SLi Jun { 17036304b06SLi Jun return hw_read(ci, OP_USBINTR, ~0); 17136304b06SLi Jun } 17236304b06SLi Jun 17336304b06SLi Jun /** 17436304b06SLi Jun * hw_read_intr_status: returns interrupt status register 17536304b06SLi Jun * 17619353881SPeter Chen * @ci: the controller 17719353881SPeter Chen * 17836304b06SLi Jun * This function returns register data 17936304b06SLi Jun */ 18036304b06SLi Jun u32 hw_read_intr_status(struct ci_hdrc *ci) 18136304b06SLi Jun { 18236304b06SLi Jun return hw_read(ci, OP_USBSTS, ~0); 18336304b06SLi Jun } 18436304b06SLi Jun 18536304b06SLi Jun /** 186e443b333SAlexander Shishkin * hw_port_test_set: writes port test mode (execute without interruption) 187e443b333SAlexander Shishkin * @mode: new value 188e443b333SAlexander Shishkin * 189e443b333SAlexander Shishkin * This function returns an error code 190e443b333SAlexander Shishkin */ 1918e22978cSAlexander Shishkin int hw_port_test_set(struct ci_hdrc *ci, u8 mode) 192e443b333SAlexander Shishkin { 193e443b333SAlexander Shishkin const u8 TEST_MODE_MAX = 7; 194e443b333SAlexander Shishkin 195e443b333SAlexander Shishkin if (mode > TEST_MODE_MAX) 196e443b333SAlexander Shishkin return -EINVAL; 197e443b333SAlexander Shishkin 198727b4ddbSFelipe Balbi hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC)); 199e443b333SAlexander Shishkin return 0; 200e443b333SAlexander Shishkin } 201e443b333SAlexander Shishkin 202e443b333SAlexander Shishkin /** 203e443b333SAlexander Shishkin * hw_port_test_get: reads port test mode value 204e443b333SAlexander Shishkin * 20519353881SPeter Chen * @ci: the controller 20619353881SPeter Chen * 207e443b333SAlexander Shishkin * This function returns port test mode value 208e443b333SAlexander Shishkin */ 2098e22978cSAlexander Shishkin u8 hw_port_test_get(struct ci_hdrc *ci) 210e443b333SAlexander Shishkin { 211727b4ddbSFelipe Balbi return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC); 212e443b333SAlexander Shishkin } 213e443b333SAlexander Shishkin 214b82613cfSPeter Chen static void hw_wait_phy_stable(void) 215b82613cfSPeter Chen { 216b82613cfSPeter Chen /* 217b82613cfSPeter Chen * The phy needs some delay to output the stable status from low 218b82613cfSPeter Chen * power mode. And for OTGSC, the status inputs are debounced 219b82613cfSPeter Chen * using a 1 ms time constant, so, delay 2ms for controller to get 220b82613cfSPeter Chen * the stable status, like vbus and id when the phy leaves low power. 221b82613cfSPeter Chen */ 222b82613cfSPeter Chen usleep_range(2000, 2500); 223b82613cfSPeter Chen } 224b82613cfSPeter Chen 225864cf949SPeter Chen /* The PHY enters/leaves low power mode */ 226864cf949SPeter Chen static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable) 227864cf949SPeter Chen { 228864cf949SPeter Chen enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC; 229864cf949SPeter Chen bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm))); 230864cf949SPeter Chen 2316d037db6SPeter Chen if (enable && !lpm) 232864cf949SPeter Chen hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 233864cf949SPeter Chen PORTSC_PHCD(ci->hw_bank.lpm)); 2346d037db6SPeter Chen else if (!enable && lpm) 235864cf949SPeter Chen hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 236864cf949SPeter Chen 0); 237864cf949SPeter Chen } 238864cf949SPeter Chen 2398e22978cSAlexander Shishkin static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) 240e443b333SAlexander Shishkin { 241e443b333SAlexander Shishkin u32 reg; 242e443b333SAlexander Shishkin 243e443b333SAlexander Shishkin /* bank is a module variable */ 2445f36e231SAlexander Shishkin ci->hw_bank.abs = base; 245e443b333SAlexander Shishkin 2465f36e231SAlexander Shishkin ci->hw_bank.cap = ci->hw_bank.abs; 24777c4400fSRichard Zhao ci->hw_bank.cap += ci->platdata->capoffset; 248938d323fSSvetoslav Neykov ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff); 249e443b333SAlexander Shishkin 2505f36e231SAlexander Shishkin hw_alloc_regmap(ci, false); 2515f36e231SAlexander Shishkin reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >> 252727b4ddbSFelipe Balbi __ffs(HCCPARAMS_LEN); 2535f36e231SAlexander Shishkin ci->hw_bank.lpm = reg; 254aeb2c121SChris Ruehl if (reg) 2555f36e231SAlexander Shishkin hw_alloc_regmap(ci, !!reg); 2565f36e231SAlexander Shishkin ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs; 2575f36e231SAlexander Shishkin ci->hw_bank.size += OP_LAST; 2585f36e231SAlexander Shishkin ci->hw_bank.size /= sizeof(u32); 259e443b333SAlexander Shishkin 2605f36e231SAlexander Shishkin reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >> 261727b4ddbSFelipe Balbi __ffs(DCCPARAMS_DEN); 2625f36e231SAlexander Shishkin ci->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */ 263e443b333SAlexander Shishkin 26409c94e62SRichard Zhao if (ci->hw_ep_max > ENDPT_MAX) 265e443b333SAlexander Shishkin return -ENODEV; 266e443b333SAlexander Shishkin 267864cf949SPeter Chen ci_hdrc_enter_lpm(ci, false); 268864cf949SPeter Chen 269c344b518SPeter Chen /* Disable all interrupts bits */ 270c344b518SPeter Chen hw_write(ci, OP_USBINTR, 0xffffffff, 0); 271c344b518SPeter Chen 272c344b518SPeter Chen /* Clear all interrupts status bits*/ 273c344b518SPeter Chen hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff); 274c344b518SPeter Chen 275cb271f3cSPeter Chen ci->rev = ci_get_revision(ci); 276cb271f3cSPeter Chen 277cb271f3cSPeter Chen dev_dbg(ci->dev, 278cb271f3cSPeter Chen "ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n", 279cb271f3cSPeter Chen ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); 280e443b333SAlexander Shishkin 281e443b333SAlexander Shishkin /* setup lock mode ? */ 282e443b333SAlexander Shishkin 283e443b333SAlexander Shishkin /* ENDPTSETUPSTAT is '0' by default */ 284e443b333SAlexander Shishkin 285e443b333SAlexander Shishkin /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ 286e443b333SAlexander Shishkin 287e443b333SAlexander Shishkin return 0; 288e443b333SAlexander Shishkin } 289e443b333SAlexander Shishkin 2907bb7e9b1SStephen Boyd void hw_phymode_configure(struct ci_hdrc *ci) 29140dcd0e8SMichael Grzeschik { 2923b5d3e68SChris Ruehl u32 portsc, lpm, sts = 0; 29340dcd0e8SMichael Grzeschik 29440dcd0e8SMichael Grzeschik switch (ci->platdata->phy_mode) { 29540dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_UTMI: 29640dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_UTMI); 29740dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_UTMI); 29840dcd0e8SMichael Grzeschik break; 29940dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_UTMIW: 30040dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_UTMI) | PORTSC_PTW; 30140dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_UTMI) | DEVLC_PTW; 30240dcd0e8SMichael Grzeschik break; 30340dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_ULPI: 30440dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_ULPI); 30540dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_ULPI); 30640dcd0e8SMichael Grzeschik break; 30740dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_SERIAL: 30840dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_SERIAL); 30940dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_SERIAL); 31040dcd0e8SMichael Grzeschik sts = 1; 31140dcd0e8SMichael Grzeschik break; 31240dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_HSIC: 31340dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_HSIC); 31440dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_HSIC); 31540dcd0e8SMichael Grzeschik break; 31640dcd0e8SMichael Grzeschik default: 31740dcd0e8SMichael Grzeschik return; 31840dcd0e8SMichael Grzeschik } 31940dcd0e8SMichael Grzeschik 32040dcd0e8SMichael Grzeschik if (ci->hw_bank.lpm) { 32140dcd0e8SMichael Grzeschik hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm); 3223b5d3e68SChris Ruehl if (sts) 3233b5d3e68SChris Ruehl hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS); 32440dcd0e8SMichael Grzeschik } else { 32540dcd0e8SMichael Grzeschik hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc); 3263b5d3e68SChris Ruehl if (sts) 3273b5d3e68SChris Ruehl hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); 32840dcd0e8SMichael Grzeschik } 32940dcd0e8SMichael Grzeschik } 33040dcd0e8SMichael Grzeschik 331e443b333SAlexander Shishkin /** 3321e5e2d3dSAntoine Tenart * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy 3331e5e2d3dSAntoine Tenart * interfaces 3341e5e2d3dSAntoine Tenart * @ci: the controller 3351e5e2d3dSAntoine Tenart * 3361e5e2d3dSAntoine Tenart * This function returns an error code if the phy failed to init 3371e5e2d3dSAntoine Tenart */ 3381e5e2d3dSAntoine Tenart static int _ci_usb_phy_init(struct ci_hdrc *ci) 3391e5e2d3dSAntoine Tenart { 3401e5e2d3dSAntoine Tenart int ret; 3411e5e2d3dSAntoine Tenart 3421e5e2d3dSAntoine Tenart if (ci->phy) { 3431e5e2d3dSAntoine Tenart ret = phy_init(ci->phy); 3441e5e2d3dSAntoine Tenart if (ret) 3451e5e2d3dSAntoine Tenart return ret; 3461e5e2d3dSAntoine Tenart 3471e5e2d3dSAntoine Tenart ret = phy_power_on(ci->phy); 3481e5e2d3dSAntoine Tenart if (ret) { 3491e5e2d3dSAntoine Tenart phy_exit(ci->phy); 3501e5e2d3dSAntoine Tenart return ret; 3511e5e2d3dSAntoine Tenart } 3521e5e2d3dSAntoine Tenart } else { 3531e5e2d3dSAntoine Tenart ret = usb_phy_init(ci->usb_phy); 3541e5e2d3dSAntoine Tenart } 3551e5e2d3dSAntoine Tenart 3561e5e2d3dSAntoine Tenart return ret; 3571e5e2d3dSAntoine Tenart } 3581e5e2d3dSAntoine Tenart 3591e5e2d3dSAntoine Tenart /** 3601e5e2d3dSAntoine Tenart * _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy 3611e5e2d3dSAntoine Tenart * interfaces 3621e5e2d3dSAntoine Tenart * @ci: the controller 3631e5e2d3dSAntoine Tenart */ 3641e5e2d3dSAntoine Tenart static void ci_usb_phy_exit(struct ci_hdrc *ci) 3651e5e2d3dSAntoine Tenart { 3668feb3680SStephen Boyd if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL) 3678feb3680SStephen Boyd return; 3688feb3680SStephen Boyd 3691e5e2d3dSAntoine Tenart if (ci->phy) { 3701e5e2d3dSAntoine Tenart phy_power_off(ci->phy); 3711e5e2d3dSAntoine Tenart phy_exit(ci->phy); 3721e5e2d3dSAntoine Tenart } else { 3731e5e2d3dSAntoine Tenart usb_phy_shutdown(ci->usb_phy); 3741e5e2d3dSAntoine Tenart } 3751e5e2d3dSAntoine Tenart } 3761e5e2d3dSAntoine Tenart 3771e5e2d3dSAntoine Tenart /** 378d03cccffSPeter Chen * ci_usb_phy_init: initialize phy according to different phy type 379d03cccffSPeter Chen * @ci: the controller 380d03cccffSPeter Chen * 381d03cccffSPeter Chen * This function returns an error code if usb_phy_init has failed 382d03cccffSPeter Chen */ 383d03cccffSPeter Chen static int ci_usb_phy_init(struct ci_hdrc *ci) 384d03cccffSPeter Chen { 385d03cccffSPeter Chen int ret; 386d03cccffSPeter Chen 3878feb3680SStephen Boyd if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL) 3888feb3680SStephen Boyd return 0; 3898feb3680SStephen Boyd 390d03cccffSPeter Chen switch (ci->platdata->phy_mode) { 391d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_UTMI: 392d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_UTMIW: 393d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_HSIC: 3941e5e2d3dSAntoine Tenart ret = _ci_usb_phy_init(ci); 395b82613cfSPeter Chen if (!ret) 396b82613cfSPeter Chen hw_wait_phy_stable(); 397b82613cfSPeter Chen else 398d03cccffSPeter Chen return ret; 399d03cccffSPeter Chen hw_phymode_configure(ci); 400d03cccffSPeter Chen break; 401d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_ULPI: 402d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_SERIAL: 403d03cccffSPeter Chen hw_phymode_configure(ci); 4041e5e2d3dSAntoine Tenart ret = _ci_usb_phy_init(ci); 405d03cccffSPeter Chen if (ret) 406d03cccffSPeter Chen return ret; 407d03cccffSPeter Chen break; 408d03cccffSPeter Chen default: 4091e5e2d3dSAntoine Tenart ret = _ci_usb_phy_init(ci); 410b82613cfSPeter Chen if (!ret) 411b82613cfSPeter Chen hw_wait_phy_stable(); 412d03cccffSPeter Chen } 413d03cccffSPeter Chen 414d03cccffSPeter Chen return ret; 415d03cccffSPeter Chen } 416d03cccffSPeter Chen 417bf9c85e7SPeter Chen 418bf9c85e7SPeter Chen /** 419bf9c85e7SPeter Chen * ci_platform_configure: do controller configure 420bf9c85e7SPeter Chen * @ci: the controller 421bf9c85e7SPeter Chen * 422bf9c85e7SPeter Chen */ 423bf9c85e7SPeter Chen void ci_platform_configure(struct ci_hdrc *ci) 424bf9c85e7SPeter Chen { 4258022d3d5SPeter Chen bool is_device_mode, is_host_mode; 4268022d3d5SPeter Chen 4278022d3d5SPeter Chen is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC; 4288022d3d5SPeter Chen is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC; 4298022d3d5SPeter Chen 4308022d3d5SPeter Chen if (is_device_mode && 4318022d3d5SPeter Chen (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)) 4328022d3d5SPeter Chen hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); 4338022d3d5SPeter Chen 4348022d3d5SPeter Chen if (is_host_mode && 4358022d3d5SPeter Chen (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING)) 436bf9c85e7SPeter Chen hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); 437bf9c85e7SPeter Chen 438bf9c85e7SPeter Chen if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { 439bf9c85e7SPeter Chen if (ci->hw_bank.lpm) 440bf9c85e7SPeter Chen hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); 441bf9c85e7SPeter Chen else 442bf9c85e7SPeter Chen hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); 443bf9c85e7SPeter Chen } 444bf9c85e7SPeter Chen 445bf9c85e7SPeter Chen if (ci->platdata->flags & CI_HDRC_SET_NON_ZERO_TTHA) 446bf9c85e7SPeter Chen hw_write(ci, OP_TTCTRL, TTCTRL_TTHA_MASK, TTCTRL_TTHA); 447df96ed8dSPeter Chen 448df96ed8dSPeter Chen hw_write(ci, OP_USBCMD, 0xff0000, ci->platdata->itc_setting << 16); 449df96ed8dSPeter Chen 45065668718SPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_AHB_BURST) 45165668718SPeter Chen hw_write_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK, 45265668718SPeter Chen ci->platdata->ahb_burst_config); 45396625eadSPeter Chen 45496625eadSPeter Chen /* override burst size, take effect only when ahb_burst_config is 0 */ 45596625eadSPeter Chen if (!hw_read_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK)) { 45696625eadSPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_TX_BURST) 45796625eadSPeter Chen hw_write(ci, OP_BURSTSIZE, TX_BURST_MASK, 45896625eadSPeter Chen ci->platdata->tx_burst_size << __ffs(TX_BURST_MASK)); 45996625eadSPeter Chen 46096625eadSPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_RX_BURST) 46196625eadSPeter Chen hw_write(ci, OP_BURSTSIZE, RX_BURST_MASK, 46296625eadSPeter Chen ci->platdata->rx_burst_size); 46396625eadSPeter Chen } 464bf9c85e7SPeter Chen } 465bf9c85e7SPeter Chen 466d03cccffSPeter Chen /** 467cdd278f2SPeter Chen * hw_controller_reset: do controller reset 468cdd278f2SPeter Chen * @ci: the controller 469cdd278f2SPeter Chen * 470cdd278f2SPeter Chen * This function returns an error code 471cdd278f2SPeter Chen */ 472cdd278f2SPeter Chen static int hw_controller_reset(struct ci_hdrc *ci) 473cdd278f2SPeter Chen { 474cdd278f2SPeter Chen int count = 0; 475cdd278f2SPeter Chen 476cdd278f2SPeter Chen hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST); 477cdd278f2SPeter Chen while (hw_read(ci, OP_USBCMD, USBCMD_RST)) { 478cdd278f2SPeter Chen udelay(10); 479cdd278f2SPeter Chen if (count++ > 1000) 480cdd278f2SPeter Chen return -ETIMEDOUT; 481cdd278f2SPeter Chen } 482cdd278f2SPeter Chen 483cdd278f2SPeter Chen return 0; 484cdd278f2SPeter Chen } 485cdd278f2SPeter Chen 486cdd278f2SPeter Chen /** 487e443b333SAlexander Shishkin * hw_device_reset: resets chip (execute without interruption) 488e443b333SAlexander Shishkin * @ci: the controller 489e443b333SAlexander Shishkin * 490e443b333SAlexander Shishkin * This function returns an error code 491e443b333SAlexander Shishkin */ 4925b157300SPeter Chen int hw_device_reset(struct ci_hdrc *ci) 493e443b333SAlexander Shishkin { 494cdd278f2SPeter Chen int ret; 495cdd278f2SPeter Chen 496e443b333SAlexander Shishkin /* should flush & stop before reset */ 497e443b333SAlexander Shishkin hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); 498e443b333SAlexander Shishkin hw_write(ci, OP_USBCMD, USBCMD_RS, 0); 499e443b333SAlexander Shishkin 500cdd278f2SPeter Chen ret = hw_controller_reset(ci); 501cdd278f2SPeter Chen if (ret) { 502cdd278f2SPeter Chen dev_err(ci->dev, "error resetting controller, ret=%d\n", ret); 503cdd278f2SPeter Chen return ret; 504cdd278f2SPeter Chen } 505e443b333SAlexander Shishkin 50677c4400fSRichard Zhao if (ci->platdata->notify_event) 50777c4400fSRichard Zhao ci->platdata->notify_event(ci, 5088e22978cSAlexander Shishkin CI_HDRC_CONTROLLER_RESET_EVENT); 509e443b333SAlexander Shishkin 510e443b333SAlexander Shishkin /* USBMODE should be configured step by step */ 511e443b333SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); 5125b157300SPeter Chen hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC); 513e443b333SAlexander Shishkin /* HW >= 2.3 */ 514e443b333SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); 515e443b333SAlexander Shishkin 5165b157300SPeter Chen if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { 5175b157300SPeter Chen pr_err("cannot enter in %s device mode", ci_role(ci)->name); 518e443b333SAlexander Shishkin pr_err("lpm = %i", ci->hw_bank.lpm); 519e443b333SAlexander Shishkin return -ENODEV; 520e443b333SAlexander Shishkin } 521e443b333SAlexander Shishkin 522bf9c85e7SPeter Chen ci_platform_configure(ci); 523bf9c85e7SPeter Chen 524e443b333SAlexander Shishkin return 0; 525e443b333SAlexander Shishkin } 526e443b333SAlexander Shishkin 5275f36e231SAlexander Shishkin static irqreturn_t ci_irq(int irq, void *data) 5285f36e231SAlexander Shishkin { 5298e22978cSAlexander Shishkin struct ci_hdrc *ci = data; 5305f36e231SAlexander Shishkin irqreturn_t ret = IRQ_NONE; 531b183c19fSRichard Zhao u32 otgsc = 0; 5325f36e231SAlexander Shishkin 5331f874edcSPeter Chen if (ci->in_lpm) { 5341f874edcSPeter Chen disable_irq_nosync(irq); 5351f874edcSPeter Chen ci->wakeup_int = true; 5361f874edcSPeter Chen pm_runtime_get(ci->dev); 5371f874edcSPeter Chen return IRQ_HANDLED; 5381f874edcSPeter Chen } 5391f874edcSPeter Chen 5404dcf720cSLi Jun if (ci->is_otg) { 5410c33bf78SLi Jun otgsc = hw_read_otgsc(ci, ~0); 5424dcf720cSLi Jun if (ci_otg_is_fsm_mode(ci)) { 5434dcf720cSLi Jun ret = ci_otg_fsm_irq(ci); 5444dcf720cSLi Jun if (ret == IRQ_HANDLED) 5454dcf720cSLi Jun return ret; 5464dcf720cSLi Jun } 5474dcf720cSLi Jun } 5485f36e231SAlexander Shishkin 549a107f8c5SPeter Chen /* 550a107f8c5SPeter Chen * Handle id change interrupt, it indicates device/host function 551a107f8c5SPeter Chen * switch. 552a107f8c5SPeter Chen */ 553a107f8c5SPeter Chen if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { 554a107f8c5SPeter Chen ci->id_event = true; 5550c33bf78SLi Jun /* Clear ID change irq status */ 5560c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); 557be6b0c1bSPeter Chen ci_otg_queue_work(ci); 558a107f8c5SPeter Chen return IRQ_HANDLED; 5595f36e231SAlexander Shishkin } 5605f36e231SAlexander Shishkin 561a107f8c5SPeter Chen /* 562a107f8c5SPeter Chen * Handle vbus change interrupt, it indicates device connection 563a107f8c5SPeter Chen * and disconnection events. 564a107f8c5SPeter Chen */ 565a107f8c5SPeter Chen if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { 566a107f8c5SPeter Chen ci->b_sess_valid_event = true; 5670c33bf78SLi Jun /* Clear BSV irq */ 5680c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); 569be6b0c1bSPeter Chen ci_otg_queue_work(ci); 570a107f8c5SPeter Chen return IRQ_HANDLED; 571a107f8c5SPeter Chen } 572a107f8c5SPeter Chen 573a107f8c5SPeter Chen /* Handle device/host interrupt */ 574a107f8c5SPeter Chen if (ci->role != CI_ROLE_END) 575a107f8c5SPeter Chen ret = ci_role(ci)->irq(ci); 576a107f8c5SPeter Chen 577b183c19fSRichard Zhao return ret; 5785f36e231SAlexander Shishkin } 5795f36e231SAlexander Shishkin 5805cc49268SStephen Boyd static int ci_cable_notifier(struct notifier_block *nb, unsigned long event, 5813ecb3e09SIvan T. Ivanov void *ptr) 5823ecb3e09SIvan T. Ivanov { 5835cc49268SStephen Boyd struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb); 5845cc49268SStephen Boyd struct ci_hdrc *ci = cbl->ci; 5853ecb3e09SIvan T. Ivanov 5865cc49268SStephen Boyd cbl->connected = event; 5875cc49268SStephen Boyd cbl->changed = true; 5883ecb3e09SIvan T. Ivanov 5893ecb3e09SIvan T. Ivanov ci_irq(ci->irq, ci); 5903ecb3e09SIvan T. Ivanov return NOTIFY_DONE; 5913ecb3e09SIvan T. Ivanov } 5923ecb3e09SIvan T. Ivanov 5931542d9c3SPeter Chen static int ci_get_platdata(struct device *dev, 5941542d9c3SPeter Chen struct ci_hdrc_platform_data *platdata) 5951542d9c3SPeter Chen { 5963ecb3e09SIvan T. Ivanov struct extcon_dev *ext_vbus, *ext_id; 5973ecb3e09SIvan T. Ivanov struct ci_hdrc_cable *cable; 59879742351SLi Jun int ret; 59979742351SLi Jun 600c22600c3SPeter Chen if (!platdata->phy_mode) 601c22600c3SPeter Chen platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); 602c22600c3SPeter Chen 603c22600c3SPeter Chen if (!platdata->dr_mode) 60406e7114fSHeikki Krogerus platdata->dr_mode = usb_get_dr_mode(dev); 605c22600c3SPeter Chen 606c22600c3SPeter Chen if (platdata->dr_mode == USB_DR_MODE_UNKNOWN) 607c22600c3SPeter Chen platdata->dr_mode = USB_DR_MODE_OTG; 608c22600c3SPeter Chen 609c2ec3a73SPeter Chen if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) { 610c2ec3a73SPeter Chen /* Get the vbus regulator */ 611c2ec3a73SPeter Chen platdata->reg_vbus = devm_regulator_get(dev, "vbus"); 612c2ec3a73SPeter Chen if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { 613c2ec3a73SPeter Chen return -EPROBE_DEFER; 614c2ec3a73SPeter Chen } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { 6156629467bSMickael Maison /* no vbus regulator is needed */ 616c2ec3a73SPeter Chen platdata->reg_vbus = NULL; 617c2ec3a73SPeter Chen } else if (IS_ERR(platdata->reg_vbus)) { 618c2ec3a73SPeter Chen dev_err(dev, "Getting regulator error: %ld\n", 619c2ec3a73SPeter Chen PTR_ERR(platdata->reg_vbus)); 620c2ec3a73SPeter Chen return PTR_ERR(platdata->reg_vbus); 621c2ec3a73SPeter Chen } 622f6a9ff07SPeter Chen /* Get TPL support */ 623f6a9ff07SPeter Chen if (!platdata->tpl_support) 624f6a9ff07SPeter Chen platdata->tpl_support = 625f6a9ff07SPeter Chen of_usb_host_tpl_support(dev->of_node); 626c2ec3a73SPeter Chen } 627c2ec3a73SPeter Chen 62879742351SLi Jun if (platdata->dr_mode == USB_DR_MODE_OTG) { 62979742351SLi Jun /* We can support HNP and SRP of OTG 2.0 */ 63079742351SLi Jun platdata->ci_otg_caps.otg_rev = 0x0200; 63179742351SLi Jun platdata->ci_otg_caps.hnp_support = true; 63279742351SLi Jun platdata->ci_otg_caps.srp_support = true; 63379742351SLi Jun 63479742351SLi Jun /* Update otg capabilities by DT properties */ 63579742351SLi Jun ret = of_usb_update_otg_caps(dev->of_node, 63679742351SLi Jun &platdata->ci_otg_caps); 63779742351SLi Jun if (ret) 63879742351SLi Jun return ret; 63979742351SLi Jun } 64079742351SLi Jun 64163863b98SHeikki Krogerus if (usb_get_maximum_speed(dev) == USB_SPEED_FULL) 6424f6743d5SMichael Grzeschik platdata->flags |= CI_HDRC_FORCE_FULLSPEED; 6434f6743d5SMichael Grzeschik 6441fbf4628SFabio Estevam of_property_read_u32(dev->of_node, "phy-clkgate-delay-us", 6451fbf4628SFabio Estevam &platdata->phy_clkgate_delay_us); 6461fbf4628SFabio Estevam 647df96ed8dSPeter Chen platdata->itc_setting = 1; 648df96ed8dSPeter Chen 6494b19b78aSSaurabh Sengar of_property_read_u32(dev->of_node, "itc-setting", 6504b19b78aSSaurabh Sengar &platdata->itc_setting); 6514b19b78aSSaurabh Sengar 65265668718SPeter Chen ret = of_property_read_u32(dev->of_node, "ahb-burst-config", 65365668718SPeter Chen &platdata->ahb_burst_config); 6544b19b78aSSaurabh Sengar if (!ret) { 6554b19b78aSSaurabh Sengar platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST; 6564b19b78aSSaurabh Sengar } else if (ret != -EINVAL) { 6574b19b78aSSaurabh Sengar dev_err(dev, "failed to get ahb-burst-config\n"); 65865668718SPeter Chen return ret; 65965668718SPeter Chen } 66065668718SPeter Chen 66196625eadSPeter Chen ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword", 66296625eadSPeter Chen &platdata->tx_burst_size); 6634b19b78aSSaurabh Sengar if (!ret) { 66496625eadSPeter Chen platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST; 6654b19b78aSSaurabh Sengar } else if (ret != -EINVAL) { 6664b19b78aSSaurabh Sengar dev_err(dev, "failed to get tx-burst-size-dword\n"); 6674b19b78aSSaurabh Sengar return ret; 66896625eadSPeter Chen } 66996625eadSPeter Chen 67096625eadSPeter Chen ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword", 67196625eadSPeter Chen &platdata->rx_burst_size); 6724b19b78aSSaurabh Sengar if (!ret) { 67396625eadSPeter Chen platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST; 6744b19b78aSSaurabh Sengar } else if (ret != -EINVAL) { 6754b19b78aSSaurabh Sengar dev_err(dev, "failed to get rx-burst-size-dword\n"); 6764b19b78aSSaurabh Sengar return ret; 67796625eadSPeter Chen } 67896625eadSPeter Chen 679aa738187SPeter Chen if (of_find_property(dev->of_node, "non-zero-ttctrl-ttha", NULL)) 680aa738187SPeter Chen platdata->flags |= CI_HDRC_SET_NON_ZERO_TTHA; 681aa738187SPeter Chen 6823ecb3e09SIvan T. Ivanov ext_id = ERR_PTR(-ENODEV); 6833ecb3e09SIvan T. Ivanov ext_vbus = ERR_PTR(-ENODEV); 6843ecb3e09SIvan T. Ivanov if (of_property_read_bool(dev->of_node, "extcon")) { 6853ecb3e09SIvan T. Ivanov /* Each one of them is not mandatory */ 6863ecb3e09SIvan T. Ivanov ext_vbus = extcon_get_edev_by_phandle(dev, 0); 6873ecb3e09SIvan T. Ivanov if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) 6883ecb3e09SIvan T. Ivanov return PTR_ERR(ext_vbus); 6893ecb3e09SIvan T. Ivanov 6903ecb3e09SIvan T. Ivanov ext_id = extcon_get_edev_by_phandle(dev, 1); 6913ecb3e09SIvan T. Ivanov if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) 6923ecb3e09SIvan T. Ivanov return PTR_ERR(ext_id); 6933ecb3e09SIvan T. Ivanov } 6943ecb3e09SIvan T. Ivanov 6953ecb3e09SIvan T. Ivanov cable = &platdata->vbus_extcon; 6965cc49268SStephen Boyd cable->nb.notifier_call = ci_cable_notifier; 6973ecb3e09SIvan T. Ivanov cable->edev = ext_vbus; 6983ecb3e09SIvan T. Ivanov 6993ecb3e09SIvan T. Ivanov if (!IS_ERR(ext_vbus)) { 7003f991aa0SChanwoo Choi ret = extcon_get_state(cable->edev, EXTCON_USB); 7013ecb3e09SIvan T. Ivanov if (ret) 7025cc49268SStephen Boyd cable->connected = true; 7033ecb3e09SIvan T. Ivanov else 7045cc49268SStephen Boyd cable->connected = false; 7053ecb3e09SIvan T. Ivanov } 7063ecb3e09SIvan T. Ivanov 7073ecb3e09SIvan T. Ivanov cable = &platdata->id_extcon; 7085cc49268SStephen Boyd cable->nb.notifier_call = ci_cable_notifier; 7093ecb3e09SIvan T. Ivanov cable->edev = ext_id; 7103ecb3e09SIvan T. Ivanov 7113ecb3e09SIvan T. Ivanov if (!IS_ERR(ext_id)) { 7123f991aa0SChanwoo Choi ret = extcon_get_state(cable->edev, EXTCON_USB_HOST); 7133ecb3e09SIvan T. Ivanov if (ret) 7145cc49268SStephen Boyd cable->connected = true; 7153ecb3e09SIvan T. Ivanov else 7165cc49268SStephen Boyd cable->connected = false; 7173ecb3e09SIvan T. Ivanov } 7181542d9c3SPeter Chen return 0; 7191542d9c3SPeter Chen } 7201542d9c3SPeter Chen 7213ecb3e09SIvan T. Ivanov static int ci_extcon_register(struct ci_hdrc *ci) 7223ecb3e09SIvan T. Ivanov { 7233ecb3e09SIvan T. Ivanov struct ci_hdrc_cable *id, *vbus; 7243ecb3e09SIvan T. Ivanov int ret; 7253ecb3e09SIvan T. Ivanov 7263ecb3e09SIvan T. Ivanov id = &ci->platdata->id_extcon; 7273ecb3e09SIvan T. Ivanov id->ci = ci; 7283ecb3e09SIvan T. Ivanov if (!IS_ERR(id->edev)) { 7293f991aa0SChanwoo Choi ret = devm_extcon_register_notifier(ci->dev, id->edev, 7303f991aa0SChanwoo Choi EXTCON_USB_HOST, &id->nb); 7313ecb3e09SIvan T. Ivanov if (ret < 0) { 7323ecb3e09SIvan T. Ivanov dev_err(ci->dev, "register ID failed\n"); 7333ecb3e09SIvan T. Ivanov return ret; 7343ecb3e09SIvan T. Ivanov } 7353ecb3e09SIvan T. Ivanov } 7363ecb3e09SIvan T. Ivanov 7373ecb3e09SIvan T. Ivanov vbus = &ci->platdata->vbus_extcon; 7383ecb3e09SIvan T. Ivanov vbus->ci = ci; 7393ecb3e09SIvan T. Ivanov if (!IS_ERR(vbus->edev)) { 7403f991aa0SChanwoo Choi ret = devm_extcon_register_notifier(ci->dev, vbus->edev, 7413f991aa0SChanwoo Choi EXTCON_USB, &vbus->nb); 7423ecb3e09SIvan T. Ivanov if (ret < 0) { 7433ecb3e09SIvan T. Ivanov dev_err(ci->dev, "register VBUS failed\n"); 7443ecb3e09SIvan T. Ivanov return ret; 7453ecb3e09SIvan T. Ivanov } 7463ecb3e09SIvan T. Ivanov } 7473ecb3e09SIvan T. Ivanov 7483ecb3e09SIvan T. Ivanov return 0; 7493ecb3e09SIvan T. Ivanov } 7503ecb3e09SIvan T. Ivanov 751fe6e125eSRichard Zhao static DEFINE_IDA(ci_ida); 752fe6e125eSRichard Zhao 7538e22978cSAlexander Shishkin struct platform_device *ci_hdrc_add_device(struct device *dev, 754cbc6dc2aSRichard Zhao struct resource *res, int nres, 7558e22978cSAlexander Shishkin struct ci_hdrc_platform_data *platdata) 756cbc6dc2aSRichard Zhao { 757cbc6dc2aSRichard Zhao struct platform_device *pdev; 758fe6e125eSRichard Zhao int id, ret; 759cbc6dc2aSRichard Zhao 7601542d9c3SPeter Chen ret = ci_get_platdata(dev, platdata); 7611542d9c3SPeter Chen if (ret) 7621542d9c3SPeter Chen return ERR_PTR(ret); 7631542d9c3SPeter Chen 764fe6e125eSRichard Zhao id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); 765fe6e125eSRichard Zhao if (id < 0) 766fe6e125eSRichard Zhao return ERR_PTR(id); 767fe6e125eSRichard Zhao 768fe6e125eSRichard Zhao pdev = platform_device_alloc("ci_hdrc", id); 769fe6e125eSRichard Zhao if (!pdev) { 770fe6e125eSRichard Zhao ret = -ENOMEM; 771fe6e125eSRichard Zhao goto put_id; 772fe6e125eSRichard Zhao } 773cbc6dc2aSRichard Zhao 774cbc6dc2aSRichard Zhao pdev->dev.parent = dev; 775cbc6dc2aSRichard Zhao pdev->dev.dma_mask = dev->dma_mask; 776cbc6dc2aSRichard Zhao pdev->dev.dma_parms = dev->dma_parms; 777cbc6dc2aSRichard Zhao dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask); 778cbc6dc2aSRichard Zhao 779cbc6dc2aSRichard Zhao ret = platform_device_add_resources(pdev, res, nres); 780cbc6dc2aSRichard Zhao if (ret) 781cbc6dc2aSRichard Zhao goto err; 782cbc6dc2aSRichard Zhao 783cbc6dc2aSRichard Zhao ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); 784cbc6dc2aSRichard Zhao if (ret) 785cbc6dc2aSRichard Zhao goto err; 786cbc6dc2aSRichard Zhao 787cbc6dc2aSRichard Zhao ret = platform_device_add(pdev); 788cbc6dc2aSRichard Zhao if (ret) 789cbc6dc2aSRichard Zhao goto err; 790cbc6dc2aSRichard Zhao 791cbc6dc2aSRichard Zhao return pdev; 792cbc6dc2aSRichard Zhao 793cbc6dc2aSRichard Zhao err: 794cbc6dc2aSRichard Zhao platform_device_put(pdev); 795fe6e125eSRichard Zhao put_id: 796fe6e125eSRichard Zhao ida_simple_remove(&ci_ida, id); 797cbc6dc2aSRichard Zhao return ERR_PTR(ret); 798cbc6dc2aSRichard Zhao } 7998e22978cSAlexander Shishkin EXPORT_SYMBOL_GPL(ci_hdrc_add_device); 800cbc6dc2aSRichard Zhao 8018e22978cSAlexander Shishkin void ci_hdrc_remove_device(struct platform_device *pdev) 802cbc6dc2aSRichard Zhao { 80398c35534SLothar Waßmann int id = pdev->id; 804cbc6dc2aSRichard Zhao platform_device_unregister(pdev); 80598c35534SLothar Waßmann ida_simple_remove(&ci_ida, id); 806cbc6dc2aSRichard Zhao } 8078e22978cSAlexander Shishkin EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); 808cbc6dc2aSRichard Zhao 8093f124d23SPeter Chen static inline void ci_role_destroy(struct ci_hdrc *ci) 8103f124d23SPeter Chen { 8113f124d23SPeter Chen ci_hdrc_gadget_destroy(ci); 8123f124d23SPeter Chen ci_hdrc_host_destroy(ci); 813cbec6bd5SPeter Chen if (ci->is_otg) 814cbec6bd5SPeter Chen ci_hdrc_otg_destroy(ci); 8153f124d23SPeter Chen } 8163f124d23SPeter Chen 817577b232fSPeter Chen static void ci_get_otg_capable(struct ci_hdrc *ci) 818577b232fSPeter Chen { 819577b232fSPeter Chen if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) 820577b232fSPeter Chen ci->is_otg = false; 821577b232fSPeter Chen else 822577b232fSPeter Chen ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, 823577b232fSPeter Chen DCCPARAMS_DC | DCCPARAMS_HC) 824577b232fSPeter Chen == (DCCPARAMS_DC | DCCPARAMS_HC)); 8252e37cfd8SPeter Chen if (ci->is_otg) { 826577b232fSPeter Chen dev_dbg(ci->dev, "It is OTG capable controller\n"); 8272e37cfd8SPeter Chen /* Disable and clear all OTG irq */ 8282e37cfd8SPeter Chen hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, 8292e37cfd8SPeter Chen OTGSC_INT_STATUS_BITS); 8302e37cfd8SPeter Chen } 831577b232fSPeter Chen } 832577b232fSPeter Chen 83341ac7b3aSBill Pemberton static int ci_hdrc_probe(struct platform_device *pdev) 834e443b333SAlexander Shishkin { 835e443b333SAlexander Shishkin struct device *dev = &pdev->dev; 8368e22978cSAlexander Shishkin struct ci_hdrc *ci; 837e443b333SAlexander Shishkin struct resource *res; 838e443b333SAlexander Shishkin void __iomem *base; 839e443b333SAlexander Shishkin int ret; 840691962d1SSascha Hauer enum usb_dr_mode dr_mode; 841e443b333SAlexander Shishkin 842fad56745SJingoo Han if (!dev_get_platdata(dev)) { 843e443b333SAlexander Shishkin dev_err(dev, "platform data missing\n"); 844e443b333SAlexander Shishkin return -ENODEV; 845e443b333SAlexander Shishkin } 846e443b333SAlexander Shishkin 847e443b333SAlexander Shishkin res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 84819290816SFelipe Balbi base = devm_ioremap_resource(dev, res); 84919290816SFelipe Balbi if (IS_ERR(base)) 85019290816SFelipe Balbi return PTR_ERR(base); 851e443b333SAlexander Shishkin 8525f36e231SAlexander Shishkin ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL); 853d0f99249SFabio Estevam if (!ci) 8545f36e231SAlexander Shishkin return -ENOMEM; 855e443b333SAlexander Shishkin 856a5d906bbSPeter Chen spin_lock_init(&ci->lock); 8575f36e231SAlexander Shishkin ci->dev = dev; 858fad56745SJingoo Han ci->platdata = dev_get_platdata(dev); 859ed8f8318SPeter Chen ci->imx28_write_fix = !!(ci->platdata->flags & 860ed8f8318SPeter Chen CI_HDRC_IMX28_WRITE_FIX); 8611f874edcSPeter Chen ci->supports_runtime_pm = !!(ci->platdata->flags & 8621f874edcSPeter Chen CI_HDRC_SUPPORTS_RUNTIME_PM); 8637bb7e9b1SStephen Boyd platform_set_drvdata(pdev, ci); 8645f36e231SAlexander Shishkin 8655f36e231SAlexander Shishkin ret = hw_device_init(ci, base); 8665f36e231SAlexander Shishkin if (ret < 0) { 8675f36e231SAlexander Shishkin dev_err(dev, "can't initialize hardware\n"); 8685f36e231SAlexander Shishkin return -ENODEV; 8695f36e231SAlexander Shishkin } 8705f36e231SAlexander Shishkin 8717bb7e9b1SStephen Boyd ret = ci_ulpi_init(ci); 8727bb7e9b1SStephen Boyd if (ret) 8737bb7e9b1SStephen Boyd return ret; 8747bb7e9b1SStephen Boyd 8751e5e2d3dSAntoine Tenart if (ci->platdata->phy) { 8761e5e2d3dSAntoine Tenart ci->phy = ci->platdata->phy; 8771e5e2d3dSAntoine Tenart } else if (ci->platdata->usb_phy) { 878ef44cb42SAntoine Tenart ci->usb_phy = ci->platdata->usb_phy; 8791e5e2d3dSAntoine Tenart } else { 88021a5b579SAntoine Tenart ci->phy = devm_phy_get(dev->parent, "usb-phy"); 88121a5b579SAntoine Tenart ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2); 882c859aa65SPeter Chen 8831e5e2d3dSAntoine Tenart /* if both generic PHY and USB PHY layers aren't enabled */ 8841e5e2d3dSAntoine Tenart if (PTR_ERR(ci->phy) == -ENOSYS && 8857bb7e9b1SStephen Boyd PTR_ERR(ci->usb_phy) == -ENXIO) { 8867bb7e9b1SStephen Boyd ret = -ENXIO; 8877bb7e9b1SStephen Boyd goto ulpi_exit; 8887bb7e9b1SStephen Boyd } 889c859aa65SPeter Chen 8907bb7e9b1SStephen Boyd if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) { 8917bb7e9b1SStephen Boyd ret = -EPROBE_DEFER; 8927bb7e9b1SStephen Boyd goto ulpi_exit; 8937bb7e9b1SStephen Boyd } 8941e5e2d3dSAntoine Tenart 8951e5e2d3dSAntoine Tenart if (IS_ERR(ci->phy)) 8961e5e2d3dSAntoine Tenart ci->phy = NULL; 8971e5e2d3dSAntoine Tenart else if (IS_ERR(ci->usb_phy)) 8981e5e2d3dSAntoine Tenart ci->usb_phy = NULL; 899c859aa65SPeter Chen } 900c859aa65SPeter Chen 901d03cccffSPeter Chen ret = ci_usb_phy_init(ci); 90274475edeSPeter Chen if (ret) { 90374475edeSPeter Chen dev_err(dev, "unable to init phy: %d\n", ret); 90474475edeSPeter Chen return ret; 90574475edeSPeter Chen } 90674475edeSPeter Chen 907eb70e5abSAlexander Shishkin ci->hw_bank.phys = res->start; 908eb70e5abSAlexander Shishkin 9095f36e231SAlexander Shishkin ci->irq = platform_get_irq(pdev, 0); 9105f36e231SAlexander Shishkin if (ci->irq < 0) { 911e443b333SAlexander Shishkin dev_err(dev, "missing IRQ\n"); 91242d18212SFabio Estevam ret = ci->irq; 913c859aa65SPeter Chen goto deinit_phy; 914e443b333SAlexander Shishkin } 915e443b333SAlexander Shishkin 916577b232fSPeter Chen ci_get_otg_capable(ci); 917577b232fSPeter Chen 918691962d1SSascha Hauer dr_mode = ci->platdata->dr_mode; 9195f36e231SAlexander Shishkin /* initialize role(s) before the interrupt is requested */ 920691962d1SSascha Hauer if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { 921eb70e5abSAlexander Shishkin ret = ci_hdrc_host_init(ci); 922eb70e5abSAlexander Shishkin if (ret) 923eb70e5abSAlexander Shishkin dev_info(dev, "doesn't support host\n"); 924691962d1SSascha Hauer } 925eb70e5abSAlexander Shishkin 926691962d1SSascha Hauer if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { 9275f36e231SAlexander Shishkin ret = ci_hdrc_gadget_init(ci); 928e443b333SAlexander Shishkin if (ret) 9295f36e231SAlexander Shishkin dev_info(dev, "doesn't support gadget\n"); 930691962d1SSascha Hauer } 9315f36e231SAlexander Shishkin 9325f36e231SAlexander Shishkin if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { 9335f36e231SAlexander Shishkin dev_err(dev, "no supported roles\n"); 93474475edeSPeter Chen ret = -ENODEV; 935c859aa65SPeter Chen goto deinit_phy; 936cbec6bd5SPeter Chen } 937cbec6bd5SPeter Chen 93827c62c2dSPeter Chen if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) { 939cbec6bd5SPeter Chen ret = ci_hdrc_otg_init(ci); 940cbec6bd5SPeter Chen if (ret) { 941cbec6bd5SPeter Chen dev_err(dev, "init otg fails, ret = %d\n", ret); 942cbec6bd5SPeter Chen goto stop; 943cbec6bd5SPeter Chen } 9445f36e231SAlexander Shishkin } 9455f36e231SAlexander Shishkin 9465f36e231SAlexander Shishkin if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { 947577b232fSPeter Chen if (ci->is_otg) { 9485f36e231SAlexander Shishkin ci->role = ci_otg_role(ci); 9490c33bf78SLi Jun /* Enable ID change irq */ 9500c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); 951577b232fSPeter Chen } else { 952577b232fSPeter Chen /* 953577b232fSPeter Chen * If the controller is not OTG capable, but support 954577b232fSPeter Chen * role switch, the defalt role is gadget, and the 955577b232fSPeter Chen * user can switch it through debugfs. 956577b232fSPeter Chen */ 957577b232fSPeter Chen ci->role = CI_ROLE_GADGET; 958577b232fSPeter Chen } 9595f36e231SAlexander Shishkin } else { 9605f36e231SAlexander Shishkin ci->role = ci->roles[CI_ROLE_HOST] 9615f36e231SAlexander Shishkin ? CI_ROLE_HOST 9625f36e231SAlexander Shishkin : CI_ROLE_GADGET; 9635f36e231SAlexander Shishkin } 9645f36e231SAlexander Shishkin 965961ea496SLi Jun if (!ci_otg_is_fsm_mode(ci)) { 9665a1e1456SPeter Chen /* only update vbus status for peripheral */ 9675a1e1456SPeter Chen if (ci->role == CI_ROLE_GADGET) 9685a1e1456SPeter Chen ci_handle_vbus_change(ci); 9695a1e1456SPeter Chen 9705f36e231SAlexander Shishkin ret = ci_role_start(ci, ci->role); 9715f36e231SAlexander Shishkin if (ret) { 9724dcf720cSLi Jun dev_err(dev, "can't start %s role\n", 9734dcf720cSLi Jun ci_role(ci)->name); 974cbec6bd5SPeter Chen goto stop; 9755f36e231SAlexander Shishkin } 9764dcf720cSLi Jun } 9775f36e231SAlexander Shishkin 9784c503dd5SPeter Chen ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED, 9794c503dd5SPeter Chen ci->platdata->name, ci); 9805f36e231SAlexander Shishkin if (ret) 9815f36e231SAlexander Shishkin goto stop; 9825f36e231SAlexander Shishkin 9833ecb3e09SIvan T. Ivanov ret = ci_extcon_register(ci); 9843ecb3e09SIvan T. Ivanov if (ret) 9853ecb3e09SIvan T. Ivanov goto stop; 9863ecb3e09SIvan T. Ivanov 9871f874edcSPeter Chen if (ci->supports_runtime_pm) { 9881f874edcSPeter Chen pm_runtime_set_active(&pdev->dev); 9891f874edcSPeter Chen pm_runtime_enable(&pdev->dev); 9901f874edcSPeter Chen pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); 9911f874edcSPeter Chen pm_runtime_mark_last_busy(ci->dev); 9921f874edcSPeter Chen pm_runtime_use_autosuspend(&pdev->dev); 9931f874edcSPeter Chen } 9941f874edcSPeter Chen 9954dcf720cSLi Jun if (ci_otg_is_fsm_mode(ci)) 9964dcf720cSLi Jun ci_hdrc_otg_fsm_start(ci); 9974dcf720cSLi Jun 998f8efa766SPeter Chen device_set_wakeup_capable(&pdev->dev, true); 999f8efa766SPeter Chen 1000adf0f735SAlexander Shishkin ret = dbg_create_files(ci); 1001adf0f735SAlexander Shishkin if (!ret) 1002adf0f735SAlexander Shishkin return 0; 10035f36e231SAlexander Shishkin 10045f36e231SAlexander Shishkin stop: 10053f124d23SPeter Chen ci_role_destroy(ci); 1006c859aa65SPeter Chen deinit_phy: 10071e5e2d3dSAntoine Tenart ci_usb_phy_exit(ci); 10087bb7e9b1SStephen Boyd ulpi_exit: 10097bb7e9b1SStephen Boyd ci_ulpi_exit(ci); 1010e443b333SAlexander Shishkin 1011e443b333SAlexander Shishkin return ret; 1012e443b333SAlexander Shishkin } 1013e443b333SAlexander Shishkin 1014fb4e98abSBill Pemberton static int ci_hdrc_remove(struct platform_device *pdev) 1015e443b333SAlexander Shishkin { 10168e22978cSAlexander Shishkin struct ci_hdrc *ci = platform_get_drvdata(pdev); 1017e443b333SAlexander Shishkin 10181f874edcSPeter Chen if (ci->supports_runtime_pm) { 10191f874edcSPeter Chen pm_runtime_get_sync(&pdev->dev); 10201f874edcSPeter Chen pm_runtime_disable(&pdev->dev); 10211f874edcSPeter Chen pm_runtime_put_noidle(&pdev->dev); 10221f874edcSPeter Chen } 10231f874edcSPeter Chen 1024adf0f735SAlexander Shishkin dbg_remove_files(ci); 10253f124d23SPeter Chen ci_role_destroy(ci); 1026864cf949SPeter Chen ci_hdrc_enter_lpm(ci, true); 10271e5e2d3dSAntoine Tenart ci_usb_phy_exit(ci); 10287bb7e9b1SStephen Boyd ci_ulpi_exit(ci); 1029e443b333SAlexander Shishkin 1030e443b333SAlexander Shishkin return 0; 1031e443b333SAlexander Shishkin } 1032e443b333SAlexander Shishkin 10331f874edcSPeter Chen #ifdef CONFIG_PM 1034961ea496SLi Jun /* Prepare wakeup by SRP before suspend */ 1035961ea496SLi Jun static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci) 1036961ea496SLi Jun { 1037961ea496SLi Jun if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && 1038961ea496SLi Jun !hw_read_otgsc(ci, OTGSC_ID)) { 1039961ea496SLi Jun hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 1040961ea496SLi Jun PORTSC_PP); 1041961ea496SLi Jun hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN, 1042961ea496SLi Jun PORTSC_WKCN); 1043961ea496SLi Jun } 1044961ea496SLi Jun } 1045961ea496SLi Jun 1046961ea496SLi Jun /* Handle SRP when wakeup by data pulse */ 1047961ea496SLi Jun static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci) 1048961ea496SLi Jun { 1049961ea496SLi Jun if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && 1050961ea496SLi Jun (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) { 1051961ea496SLi Jun if (!hw_read_otgsc(ci, OTGSC_ID)) { 1052961ea496SLi Jun ci->fsm.a_srp_det = 1; 1053961ea496SLi Jun ci->fsm.a_bus_drop = 0; 1054961ea496SLi Jun } else { 1055961ea496SLi Jun ci->fsm.id = 1; 1056961ea496SLi Jun } 1057961ea496SLi Jun ci_otg_queue_work(ci); 1058961ea496SLi Jun } 1059961ea496SLi Jun } 1060961ea496SLi Jun 10618076932fSPeter Chen static void ci_controller_suspend(struct ci_hdrc *ci) 10628076932fSPeter Chen { 10631f874edcSPeter Chen disable_irq(ci->irq); 10648076932fSPeter Chen ci_hdrc_enter_lpm(ci, true); 10651fbf4628SFabio Estevam if (ci->platdata->phy_clkgate_delay_us) 10661fbf4628SFabio Estevam usleep_range(ci->platdata->phy_clkgate_delay_us, 10671fbf4628SFabio Estevam ci->platdata->phy_clkgate_delay_us + 50); 10688076932fSPeter Chen usb_phy_set_suspend(ci->usb_phy, 1); 10691f874edcSPeter Chen ci->in_lpm = true; 10701f874edcSPeter Chen enable_irq(ci->irq); 10718076932fSPeter Chen } 10728076932fSPeter Chen 10738076932fSPeter Chen static int ci_controller_resume(struct device *dev) 10748076932fSPeter Chen { 10758076932fSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 10767bb7e9b1SStephen Boyd int ret; 10778076932fSPeter Chen 10788076932fSPeter Chen dev_dbg(dev, "at %s\n", __func__); 10798076932fSPeter Chen 10801f874edcSPeter Chen if (!ci->in_lpm) { 10811f874edcSPeter Chen WARN_ON(1); 10821f874edcSPeter Chen return 0; 10831f874edcSPeter Chen } 10848076932fSPeter Chen 10851f874edcSPeter Chen ci_hdrc_enter_lpm(ci, false); 10867bb7e9b1SStephen Boyd 10877bb7e9b1SStephen Boyd ret = ci_ulpi_resume(ci); 10887bb7e9b1SStephen Boyd if (ret) 10897bb7e9b1SStephen Boyd return ret; 10907bb7e9b1SStephen Boyd 10918076932fSPeter Chen if (ci->usb_phy) { 10928076932fSPeter Chen usb_phy_set_suspend(ci->usb_phy, 0); 10938076932fSPeter Chen usb_phy_set_wakeup(ci->usb_phy, false); 10948076932fSPeter Chen hw_wait_phy_stable(); 10958076932fSPeter Chen } 10968076932fSPeter Chen 10971f874edcSPeter Chen ci->in_lpm = false; 10981f874edcSPeter Chen if (ci->wakeup_int) { 10991f874edcSPeter Chen ci->wakeup_int = false; 11001f874edcSPeter Chen pm_runtime_mark_last_busy(ci->dev); 11011f874edcSPeter Chen pm_runtime_put_autosuspend(ci->dev); 11021f874edcSPeter Chen enable_irq(ci->irq); 1103961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1104961ea496SLi Jun ci_otg_fsm_wakeup_by_srp(ci); 11051f874edcSPeter Chen } 11061f874edcSPeter Chen 11078076932fSPeter Chen return 0; 11088076932fSPeter Chen } 11098076932fSPeter Chen 11101f874edcSPeter Chen #ifdef CONFIG_PM_SLEEP 11118076932fSPeter Chen static int ci_suspend(struct device *dev) 11128076932fSPeter Chen { 11138076932fSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 11148076932fSPeter Chen 11158076932fSPeter Chen if (ci->wq) 11168076932fSPeter Chen flush_workqueue(ci->wq); 11171f874edcSPeter Chen /* 11181f874edcSPeter Chen * Controller needs to be active during suspend, otherwise the core 11191f874edcSPeter Chen * may run resume when the parent is at suspend if other driver's 11201f874edcSPeter Chen * suspend fails, it occurs before parent's suspend has not started, 11211f874edcSPeter Chen * but the core suspend has finished. 11221f874edcSPeter Chen */ 11231f874edcSPeter Chen if (ci->in_lpm) 11241f874edcSPeter Chen pm_runtime_resume(dev); 11251f874edcSPeter Chen 11261f874edcSPeter Chen if (ci->in_lpm) { 11271f874edcSPeter Chen WARN_ON(1); 11281f874edcSPeter Chen return 0; 11291f874edcSPeter Chen } 11308076932fSPeter Chen 1131f8efa766SPeter Chen if (device_may_wakeup(dev)) { 1132961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1133961ea496SLi Jun ci_otg_fsm_suspend_for_srp(ci); 1134961ea496SLi Jun 1135f8efa766SPeter Chen usb_phy_set_wakeup(ci->usb_phy, true); 1136f8efa766SPeter Chen enable_irq_wake(ci->irq); 1137f8efa766SPeter Chen } 1138f8efa766SPeter Chen 11398076932fSPeter Chen ci_controller_suspend(ci); 11408076932fSPeter Chen 11418076932fSPeter Chen return 0; 11428076932fSPeter Chen } 11438076932fSPeter Chen 11448076932fSPeter Chen static int ci_resume(struct device *dev) 11458076932fSPeter Chen { 11461f874edcSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 11471f874edcSPeter Chen int ret; 11481f874edcSPeter Chen 1149f8efa766SPeter Chen if (device_may_wakeup(dev)) 1150f8efa766SPeter Chen disable_irq_wake(ci->irq); 1151f8efa766SPeter Chen 11521f874edcSPeter Chen ret = ci_controller_resume(dev); 11531f874edcSPeter Chen if (ret) 11541f874edcSPeter Chen return ret; 11551f874edcSPeter Chen 11561f874edcSPeter Chen if (ci->supports_runtime_pm) { 11571f874edcSPeter Chen pm_runtime_disable(dev); 11581f874edcSPeter Chen pm_runtime_set_active(dev); 11591f874edcSPeter Chen pm_runtime_enable(dev); 11601f874edcSPeter Chen } 11611f874edcSPeter Chen 11621f874edcSPeter Chen return ret; 11638076932fSPeter Chen } 11648076932fSPeter Chen #endif /* CONFIG_PM_SLEEP */ 11658076932fSPeter Chen 11661f874edcSPeter Chen static int ci_runtime_suspend(struct device *dev) 11671f874edcSPeter Chen { 11681f874edcSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 11691f874edcSPeter Chen 11701f874edcSPeter Chen dev_dbg(dev, "at %s\n", __func__); 11711f874edcSPeter Chen 11721f874edcSPeter Chen if (ci->in_lpm) { 11731f874edcSPeter Chen WARN_ON(1); 11741f874edcSPeter Chen return 0; 11751f874edcSPeter Chen } 11761f874edcSPeter Chen 1177961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1178961ea496SLi Jun ci_otg_fsm_suspend_for_srp(ci); 1179961ea496SLi Jun 11801f874edcSPeter Chen usb_phy_set_wakeup(ci->usb_phy, true); 11811f874edcSPeter Chen ci_controller_suspend(ci); 11821f874edcSPeter Chen 11831f874edcSPeter Chen return 0; 11841f874edcSPeter Chen } 11851f874edcSPeter Chen 11861f874edcSPeter Chen static int ci_runtime_resume(struct device *dev) 11871f874edcSPeter Chen { 11881f874edcSPeter Chen return ci_controller_resume(dev); 11891f874edcSPeter Chen } 11901f874edcSPeter Chen 11911f874edcSPeter Chen #endif /* CONFIG_PM */ 11928076932fSPeter Chen static const struct dev_pm_ops ci_pm_ops = { 11938076932fSPeter Chen SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume) 11941f874edcSPeter Chen SET_RUNTIME_PM_OPS(ci_runtime_suspend, ci_runtime_resume, NULL) 11958076932fSPeter Chen }; 11961f874edcSPeter Chen 11975f36e231SAlexander Shishkin static struct platform_driver ci_hdrc_driver = { 11985f36e231SAlexander Shishkin .probe = ci_hdrc_probe, 11997690417dSBill Pemberton .remove = ci_hdrc_remove, 1200e443b333SAlexander Shishkin .driver = { 12015f36e231SAlexander Shishkin .name = "ci_hdrc", 12028076932fSPeter Chen .pm = &ci_pm_ops, 1203e443b333SAlexander Shishkin }, 1204e443b333SAlexander Shishkin }; 1205e443b333SAlexander Shishkin 12062f01a33bSPeter Chen static int __init ci_hdrc_platform_register(void) 12072f01a33bSPeter Chen { 12082f01a33bSPeter Chen ci_hdrc_host_driver_init(); 12092f01a33bSPeter Chen return platform_driver_register(&ci_hdrc_driver); 12102f01a33bSPeter Chen } 12112f01a33bSPeter Chen module_init(ci_hdrc_platform_register); 12122f01a33bSPeter Chen 12132f01a33bSPeter Chen static void __exit ci_hdrc_platform_unregister(void) 12142f01a33bSPeter Chen { 12152f01a33bSPeter Chen platform_driver_unregister(&ci_hdrc_driver); 12162f01a33bSPeter Chen } 12172f01a33bSPeter Chen module_exit(ci_hdrc_platform_unregister); 1218e443b333SAlexander Shishkin 12195f36e231SAlexander Shishkin MODULE_ALIAS("platform:ci_hdrc"); 1220e443b333SAlexander Shishkin MODULE_LICENSE("GPL v2"); 1221e443b333SAlexander Shishkin MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>"); 12225f36e231SAlexander Shishkin MODULE_DESCRIPTION("ChipIdea HDRC Driver"); 1223