15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2e443b333SAlexander Shishkin /* 3e443b333SAlexander Shishkin * core.c - ChipIdea USB IP core family device controller 4e443b333SAlexander Shishkin * 5e443b333SAlexander Shishkin * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. 6e443b333SAlexander Shishkin * 7e443b333SAlexander Shishkin * Author: David Lopo 8e443b333SAlexander Shishkin */ 9e443b333SAlexander Shishkin 10e443b333SAlexander Shishkin /* 11e443b333SAlexander Shishkin * Description: ChipIdea USB IP core family device controller 12e443b333SAlexander Shishkin * 13e443b333SAlexander Shishkin * This driver is composed of several blocks: 14e443b333SAlexander Shishkin * - HW: hardware interface 15e443b333SAlexander Shishkin * - DBG: debug facilities (optional) 16e443b333SAlexander Shishkin * - UTIL: utilities 17e443b333SAlexander Shishkin * - ISR: interrupts handling 18e443b333SAlexander Shishkin * - ENDPT: endpoint operations (Gadget API) 19e443b333SAlexander Shishkin * - GADGET: gadget operations (Gadget API) 20e443b333SAlexander Shishkin * - BUS: bus glue code, bus abstraction layer 21e443b333SAlexander Shishkin * 22e443b333SAlexander Shishkin * Compile Options 23e443b333SAlexander Shishkin * - STALL_IN: non-empty bulk-in pipes cannot be halted 24e443b333SAlexander Shishkin * if defined mass storage compliance succeeds but with warnings 25e443b333SAlexander Shishkin * => case 4: Hi > Dn 26e443b333SAlexander Shishkin * => case 5: Hi > Di 27e443b333SAlexander Shishkin * => case 8: Hi <> Do 28e443b333SAlexander Shishkin * if undefined usbtest 13 fails 29e443b333SAlexander Shishkin * - TRACE: enable function tracing (depends on DEBUG) 30e443b333SAlexander Shishkin * 31e443b333SAlexander Shishkin * Main Features 32e443b333SAlexander Shishkin * - Chapter 9 & Mass Storage Compliance with Gadget File Storage 33e443b333SAlexander Shishkin * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) 34e443b333SAlexander Shishkin * - Normal & LPM support 35e443b333SAlexander Shishkin * 36e443b333SAlexander Shishkin * USBTEST Report 37e443b333SAlexander Shishkin * - OK: 0-12, 13 (STALL_IN defined) & 14 38e443b333SAlexander Shishkin * - Not Supported: 15 & 16 (ISO) 39e443b333SAlexander Shishkin * 40e443b333SAlexander Shishkin * TODO List 41e443b333SAlexander Shishkin * - Suspend & Remote Wakeup 42e443b333SAlexander Shishkin */ 43e443b333SAlexander Shishkin #include <linux/delay.h> 44e443b333SAlexander Shishkin #include <linux/device.h> 45e443b333SAlexander Shishkin #include <linux/dma-mapping.h> 463ecb3e09SIvan T. Ivanov #include <linux/extcon.h> 471e5e2d3dSAntoine Tenart #include <linux/phy/phy.h> 48e443b333SAlexander Shishkin #include <linux/platform_device.h> 49e443b333SAlexander Shishkin #include <linux/module.h> 50fe6e125eSRichard Zhao #include <linux/idr.h> 51e443b333SAlexander Shishkin #include <linux/interrupt.h> 52e443b333SAlexander Shishkin #include <linux/io.h> 53e443b333SAlexander Shishkin #include <linux/kernel.h> 54e443b333SAlexander Shishkin #include <linux/slab.h> 55e443b333SAlexander Shishkin #include <linux/pm_runtime.h> 5616caf1faSLoic Poulain #include <linux/pinctrl/consumer.h> 57e443b333SAlexander Shishkin #include <linux/usb/ch9.h> 58e443b333SAlexander Shishkin #include <linux/usb/gadget.h> 59e443b333SAlexander Shishkin #include <linux/usb/otg.h> 60e443b333SAlexander Shishkin #include <linux/usb/chipidea.h> 6140dcd0e8SMichael Grzeschik #include <linux/usb/of.h> 624f6743d5SMichael Grzeschik #include <linux/of.h> 631542d9c3SPeter Chen #include <linux/regulator/consumer.h> 648022d3d5SPeter Chen #include <linux/usb/ehci_def.h> 65e443b333SAlexander Shishkin 66e443b333SAlexander Shishkin #include "ci.h" 67e443b333SAlexander Shishkin #include "udc.h" 68e443b333SAlexander Shishkin #include "bits.h" 69eb70e5abSAlexander Shishkin #include "host.h" 70c10b4f03SPeter Chen #include "otg.h" 714dcf720cSLi Jun #include "otg_fsm.h" 72e443b333SAlexander Shishkin 735f36e231SAlexander Shishkin /* Controller register map */ 74987e7bc3SMarc Kleine-Budde static const u8 ci_regs_nolpm[] = { 75987e7bc3SMarc Kleine-Budde [CAP_CAPLENGTH] = 0x00U, 76987e7bc3SMarc Kleine-Budde [CAP_HCCPARAMS] = 0x08U, 77987e7bc3SMarc Kleine-Budde [CAP_DCCPARAMS] = 0x24U, 78987e7bc3SMarc Kleine-Budde [CAP_TESTMODE] = 0x38U, 79987e7bc3SMarc Kleine-Budde [OP_USBCMD] = 0x00U, 80987e7bc3SMarc Kleine-Budde [OP_USBSTS] = 0x04U, 81987e7bc3SMarc Kleine-Budde [OP_USBINTR] = 0x08U, 82987e7bc3SMarc Kleine-Budde [OP_DEVICEADDR] = 0x14U, 83987e7bc3SMarc Kleine-Budde [OP_ENDPTLISTADDR] = 0x18U, 8428362673SPeter Chen [OP_TTCTRL] = 0x1CU, 8596625eadSPeter Chen [OP_BURSTSIZE] = 0x20U, 867bb7e9b1SStephen Boyd [OP_ULPI_VIEWPORT] = 0x30U, 87987e7bc3SMarc Kleine-Budde [OP_PORTSC] = 0x44U, 88987e7bc3SMarc Kleine-Budde [OP_DEVLC] = 0x84U, 89987e7bc3SMarc Kleine-Budde [OP_OTGSC] = 0x64U, 90987e7bc3SMarc Kleine-Budde [OP_USBMODE] = 0x68U, 91987e7bc3SMarc Kleine-Budde [OP_ENDPTSETUPSTAT] = 0x6CU, 92987e7bc3SMarc Kleine-Budde [OP_ENDPTPRIME] = 0x70U, 93987e7bc3SMarc Kleine-Budde [OP_ENDPTFLUSH] = 0x74U, 94987e7bc3SMarc Kleine-Budde [OP_ENDPTSTAT] = 0x78U, 95987e7bc3SMarc Kleine-Budde [OP_ENDPTCOMPLETE] = 0x7CU, 96987e7bc3SMarc Kleine-Budde [OP_ENDPTCTRL] = 0x80U, 97e443b333SAlexander Shishkin }; 98e443b333SAlexander Shishkin 99987e7bc3SMarc Kleine-Budde static const u8 ci_regs_lpm[] = { 100987e7bc3SMarc Kleine-Budde [CAP_CAPLENGTH] = 0x00U, 101987e7bc3SMarc Kleine-Budde [CAP_HCCPARAMS] = 0x08U, 102987e7bc3SMarc Kleine-Budde [CAP_DCCPARAMS] = 0x24U, 103987e7bc3SMarc Kleine-Budde [CAP_TESTMODE] = 0xFCU, 104987e7bc3SMarc Kleine-Budde [OP_USBCMD] = 0x00U, 105987e7bc3SMarc Kleine-Budde [OP_USBSTS] = 0x04U, 106987e7bc3SMarc Kleine-Budde [OP_USBINTR] = 0x08U, 107987e7bc3SMarc Kleine-Budde [OP_DEVICEADDR] = 0x14U, 108987e7bc3SMarc Kleine-Budde [OP_ENDPTLISTADDR] = 0x18U, 10928362673SPeter Chen [OP_TTCTRL] = 0x1CU, 11096625eadSPeter Chen [OP_BURSTSIZE] = 0x20U, 1117bb7e9b1SStephen Boyd [OP_ULPI_VIEWPORT] = 0x30U, 112987e7bc3SMarc Kleine-Budde [OP_PORTSC] = 0x44U, 113987e7bc3SMarc Kleine-Budde [OP_DEVLC] = 0x84U, 114987e7bc3SMarc Kleine-Budde [OP_OTGSC] = 0xC4U, 115987e7bc3SMarc Kleine-Budde [OP_USBMODE] = 0xC8U, 116987e7bc3SMarc Kleine-Budde [OP_ENDPTSETUPSTAT] = 0xD8U, 117987e7bc3SMarc Kleine-Budde [OP_ENDPTPRIME] = 0xDCU, 118987e7bc3SMarc Kleine-Budde [OP_ENDPTFLUSH] = 0xE0U, 119987e7bc3SMarc Kleine-Budde [OP_ENDPTSTAT] = 0xE4U, 120987e7bc3SMarc Kleine-Budde [OP_ENDPTCOMPLETE] = 0xE8U, 121987e7bc3SMarc Kleine-Budde [OP_ENDPTCTRL] = 0xECU, 122e443b333SAlexander Shishkin }; 123e443b333SAlexander Shishkin 124158ec071SNicholas Krause static void hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) 125e443b333SAlexander Shishkin { 126e443b333SAlexander Shishkin int i; 127e443b333SAlexander Shishkin 128e443b333SAlexander Shishkin for (i = 0; i < OP_ENDPTCTRL; i++) 1295f36e231SAlexander Shishkin ci->hw_bank.regmap[i] = 1305f36e231SAlexander Shishkin (i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) + 131e443b333SAlexander Shishkin (is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]); 132e443b333SAlexander Shishkin 133e443b333SAlexander Shishkin for (; i <= OP_LAST; i++) 1345f36e231SAlexander Shishkin ci->hw_bank.regmap[i] = ci->hw_bank.op + 135e443b333SAlexander Shishkin 4 * (i - OP_ENDPTCTRL) + 136e443b333SAlexander Shishkin (is_lpm 137e443b333SAlexander Shishkin ? ci_regs_lpm[OP_ENDPTCTRL] 138e443b333SAlexander Shishkin : ci_regs_nolpm[OP_ENDPTCTRL]); 139e443b333SAlexander Shishkin 140e443b333SAlexander Shishkin } 141e443b333SAlexander Shishkin 142cb271f3cSPeter Chen static enum ci_revision ci_get_revision(struct ci_hdrc *ci) 143cb271f3cSPeter Chen { 144cb271f3cSPeter Chen int ver = hw_read_id_reg(ci, ID_ID, VERSION) >> __ffs(VERSION); 145cb271f3cSPeter Chen enum ci_revision rev = CI_REVISION_UNKNOWN; 146cb271f3cSPeter Chen 147cb271f3cSPeter Chen if (ver == 0x2) { 148cb271f3cSPeter Chen rev = hw_read_id_reg(ci, ID_ID, REVISION) 149cb271f3cSPeter Chen >> __ffs(REVISION); 150cb271f3cSPeter Chen rev += CI_REVISION_20; 151cb271f3cSPeter Chen } else if (ver == 0x0) { 152cb271f3cSPeter Chen rev = CI_REVISION_1X; 153cb271f3cSPeter Chen } 154cb271f3cSPeter Chen 155cb271f3cSPeter Chen return rev; 156cb271f3cSPeter Chen } 157cb271f3cSPeter Chen 158e443b333SAlexander Shishkin /** 15936304b06SLi Jun * hw_read_intr_enable: returns interrupt enable register 16036304b06SLi Jun * 16119353881SPeter Chen * @ci: the controller 16219353881SPeter Chen * 16336304b06SLi Jun * This function returns register data 16436304b06SLi Jun */ 16536304b06SLi Jun u32 hw_read_intr_enable(struct ci_hdrc *ci) 16636304b06SLi Jun { 16736304b06SLi Jun return hw_read(ci, OP_USBINTR, ~0); 16836304b06SLi Jun } 16936304b06SLi Jun 17036304b06SLi Jun /** 17136304b06SLi Jun * hw_read_intr_status: returns interrupt status register 17236304b06SLi Jun * 17319353881SPeter Chen * @ci: the controller 17419353881SPeter Chen * 17536304b06SLi Jun * This function returns register data 17636304b06SLi Jun */ 17736304b06SLi Jun u32 hw_read_intr_status(struct ci_hdrc *ci) 17836304b06SLi Jun { 17936304b06SLi Jun return hw_read(ci, OP_USBSTS, ~0); 18036304b06SLi Jun } 18136304b06SLi Jun 18236304b06SLi Jun /** 183e443b333SAlexander Shishkin * hw_port_test_set: writes port test mode (execute without interruption) 184e443b333SAlexander Shishkin * @mode: new value 185e443b333SAlexander Shishkin * 186e443b333SAlexander Shishkin * This function returns an error code 187e443b333SAlexander Shishkin */ 1888e22978cSAlexander Shishkin int hw_port_test_set(struct ci_hdrc *ci, u8 mode) 189e443b333SAlexander Shishkin { 190e443b333SAlexander Shishkin const u8 TEST_MODE_MAX = 7; 191e443b333SAlexander Shishkin 192e443b333SAlexander Shishkin if (mode > TEST_MODE_MAX) 193e443b333SAlexander Shishkin return -EINVAL; 194e443b333SAlexander Shishkin 195727b4ddbSFelipe Balbi hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC)); 196e443b333SAlexander Shishkin return 0; 197e443b333SAlexander Shishkin } 198e443b333SAlexander Shishkin 199e443b333SAlexander Shishkin /** 200e443b333SAlexander Shishkin * hw_port_test_get: reads port test mode value 201e443b333SAlexander Shishkin * 20219353881SPeter Chen * @ci: the controller 20319353881SPeter Chen * 204e443b333SAlexander Shishkin * This function returns port test mode value 205e443b333SAlexander Shishkin */ 2068e22978cSAlexander Shishkin u8 hw_port_test_get(struct ci_hdrc *ci) 207e443b333SAlexander Shishkin { 208727b4ddbSFelipe Balbi return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC); 209e443b333SAlexander Shishkin } 210e443b333SAlexander Shishkin 211b82613cfSPeter Chen static void hw_wait_phy_stable(void) 212b82613cfSPeter Chen { 213b82613cfSPeter Chen /* 214b82613cfSPeter Chen * The phy needs some delay to output the stable status from low 215b82613cfSPeter Chen * power mode. And for OTGSC, the status inputs are debounced 216b82613cfSPeter Chen * using a 1 ms time constant, so, delay 2ms for controller to get 217b82613cfSPeter Chen * the stable status, like vbus and id when the phy leaves low power. 218b82613cfSPeter Chen */ 219b82613cfSPeter Chen usleep_range(2000, 2500); 220b82613cfSPeter Chen } 221b82613cfSPeter Chen 222864cf949SPeter Chen /* The PHY enters/leaves low power mode */ 223864cf949SPeter Chen static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable) 224864cf949SPeter Chen { 225864cf949SPeter Chen enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC; 226864cf949SPeter Chen bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm))); 227864cf949SPeter Chen 2286d037db6SPeter Chen if (enable && !lpm) 229864cf949SPeter Chen hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 230864cf949SPeter Chen PORTSC_PHCD(ci->hw_bank.lpm)); 2316d037db6SPeter Chen else if (!enable && lpm) 232864cf949SPeter Chen hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 233864cf949SPeter Chen 0); 234864cf949SPeter Chen } 235864cf949SPeter Chen 2368e22978cSAlexander Shishkin static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) 237e443b333SAlexander Shishkin { 238e443b333SAlexander Shishkin u32 reg; 239e443b333SAlexander Shishkin 240e443b333SAlexander Shishkin /* bank is a module variable */ 2415f36e231SAlexander Shishkin ci->hw_bank.abs = base; 242e443b333SAlexander Shishkin 2435f36e231SAlexander Shishkin ci->hw_bank.cap = ci->hw_bank.abs; 24477c4400fSRichard Zhao ci->hw_bank.cap += ci->platdata->capoffset; 245938d323fSSvetoslav Neykov ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff); 246e443b333SAlexander Shishkin 2475f36e231SAlexander Shishkin hw_alloc_regmap(ci, false); 2485f36e231SAlexander Shishkin reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >> 249727b4ddbSFelipe Balbi __ffs(HCCPARAMS_LEN); 2505f36e231SAlexander Shishkin ci->hw_bank.lpm = reg; 251aeb2c121SChris Ruehl if (reg) 2525f36e231SAlexander Shishkin hw_alloc_regmap(ci, !!reg); 2535f36e231SAlexander Shishkin ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs; 2545f36e231SAlexander Shishkin ci->hw_bank.size += OP_LAST; 2555f36e231SAlexander Shishkin ci->hw_bank.size /= sizeof(u32); 256e443b333SAlexander Shishkin 2575f36e231SAlexander Shishkin reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >> 258727b4ddbSFelipe Balbi __ffs(DCCPARAMS_DEN); 2595f36e231SAlexander Shishkin ci->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */ 260e443b333SAlexander Shishkin 26109c94e62SRichard Zhao if (ci->hw_ep_max > ENDPT_MAX) 262e443b333SAlexander Shishkin return -ENODEV; 263e443b333SAlexander Shishkin 264864cf949SPeter Chen ci_hdrc_enter_lpm(ci, false); 265864cf949SPeter Chen 266c344b518SPeter Chen /* Disable all interrupts bits */ 267c344b518SPeter Chen hw_write(ci, OP_USBINTR, 0xffffffff, 0); 268c344b518SPeter Chen 269c344b518SPeter Chen /* Clear all interrupts status bits*/ 270c344b518SPeter Chen hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff); 271c344b518SPeter Chen 272cb271f3cSPeter Chen ci->rev = ci_get_revision(ci); 273cb271f3cSPeter Chen 274cb271f3cSPeter Chen dev_dbg(ci->dev, 275cb271f3cSPeter Chen "ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n", 276cb271f3cSPeter Chen ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); 277e443b333SAlexander Shishkin 278e443b333SAlexander Shishkin /* setup lock mode ? */ 279e443b333SAlexander Shishkin 280e443b333SAlexander Shishkin /* ENDPTSETUPSTAT is '0' by default */ 281e443b333SAlexander Shishkin 282e443b333SAlexander Shishkin /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ 283e443b333SAlexander Shishkin 284e443b333SAlexander Shishkin return 0; 285e443b333SAlexander Shishkin } 286e443b333SAlexander Shishkin 2877bb7e9b1SStephen Boyd void hw_phymode_configure(struct ci_hdrc *ci) 28840dcd0e8SMichael Grzeschik { 2893b5d3e68SChris Ruehl u32 portsc, lpm, sts = 0; 29040dcd0e8SMichael Grzeschik 29140dcd0e8SMichael Grzeschik switch (ci->platdata->phy_mode) { 29240dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_UTMI: 29340dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_UTMI); 29440dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_UTMI); 29540dcd0e8SMichael Grzeschik break; 29640dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_UTMIW: 29740dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_UTMI) | PORTSC_PTW; 29840dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_UTMI) | DEVLC_PTW; 29940dcd0e8SMichael Grzeschik break; 30040dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_ULPI: 30140dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_ULPI); 30240dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_ULPI); 30340dcd0e8SMichael Grzeschik break; 30440dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_SERIAL: 30540dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_SERIAL); 30640dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_SERIAL); 30740dcd0e8SMichael Grzeschik sts = 1; 30840dcd0e8SMichael Grzeschik break; 30940dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_HSIC: 31040dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_HSIC); 31140dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_HSIC); 31240dcd0e8SMichael Grzeschik break; 31340dcd0e8SMichael Grzeschik default: 31440dcd0e8SMichael Grzeschik return; 31540dcd0e8SMichael Grzeschik } 31640dcd0e8SMichael Grzeschik 31740dcd0e8SMichael Grzeschik if (ci->hw_bank.lpm) { 31840dcd0e8SMichael Grzeschik hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm); 3193b5d3e68SChris Ruehl if (sts) 3203b5d3e68SChris Ruehl hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS); 32140dcd0e8SMichael Grzeschik } else { 32240dcd0e8SMichael Grzeschik hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc); 3233b5d3e68SChris Ruehl if (sts) 3243b5d3e68SChris Ruehl hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); 32540dcd0e8SMichael Grzeschik } 32640dcd0e8SMichael Grzeschik } 32711893daeSStephen Boyd EXPORT_SYMBOL_GPL(hw_phymode_configure); 32840dcd0e8SMichael Grzeschik 329e443b333SAlexander Shishkin /** 3301e5e2d3dSAntoine Tenart * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy 3311e5e2d3dSAntoine Tenart * interfaces 3321e5e2d3dSAntoine Tenart * @ci: the controller 3331e5e2d3dSAntoine Tenart * 3341e5e2d3dSAntoine Tenart * This function returns an error code if the phy failed to init 3351e5e2d3dSAntoine Tenart */ 3361e5e2d3dSAntoine Tenart static int _ci_usb_phy_init(struct ci_hdrc *ci) 3371e5e2d3dSAntoine Tenart { 3381e5e2d3dSAntoine Tenart int ret; 3391e5e2d3dSAntoine Tenart 3401e5e2d3dSAntoine Tenart if (ci->phy) { 3411e5e2d3dSAntoine Tenart ret = phy_init(ci->phy); 3421e5e2d3dSAntoine Tenart if (ret) 3431e5e2d3dSAntoine Tenart return ret; 3441e5e2d3dSAntoine Tenart 3451e5e2d3dSAntoine Tenart ret = phy_power_on(ci->phy); 3461e5e2d3dSAntoine Tenart if (ret) { 3471e5e2d3dSAntoine Tenart phy_exit(ci->phy); 3481e5e2d3dSAntoine Tenart return ret; 3491e5e2d3dSAntoine Tenart } 3501e5e2d3dSAntoine Tenart } else { 3511e5e2d3dSAntoine Tenart ret = usb_phy_init(ci->usb_phy); 3521e5e2d3dSAntoine Tenart } 3531e5e2d3dSAntoine Tenart 3541e5e2d3dSAntoine Tenart return ret; 3551e5e2d3dSAntoine Tenart } 3561e5e2d3dSAntoine Tenart 3571e5e2d3dSAntoine Tenart /** 3581e5e2d3dSAntoine Tenart * _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy 3591e5e2d3dSAntoine Tenart * interfaces 3601e5e2d3dSAntoine Tenart * @ci: the controller 3611e5e2d3dSAntoine Tenart */ 3621e5e2d3dSAntoine Tenart static void ci_usb_phy_exit(struct ci_hdrc *ci) 3631e5e2d3dSAntoine Tenart { 3648feb3680SStephen Boyd if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL) 3658feb3680SStephen Boyd return; 3668feb3680SStephen Boyd 3671e5e2d3dSAntoine Tenart if (ci->phy) { 3681e5e2d3dSAntoine Tenart phy_power_off(ci->phy); 3691e5e2d3dSAntoine Tenart phy_exit(ci->phy); 3701e5e2d3dSAntoine Tenart } else { 3711e5e2d3dSAntoine Tenart usb_phy_shutdown(ci->usb_phy); 3721e5e2d3dSAntoine Tenart } 3731e5e2d3dSAntoine Tenart } 3741e5e2d3dSAntoine Tenart 3751e5e2d3dSAntoine Tenart /** 376d03cccffSPeter Chen * ci_usb_phy_init: initialize phy according to different phy type 377d03cccffSPeter Chen * @ci: the controller 378d03cccffSPeter Chen * 379d03cccffSPeter Chen * This function returns an error code if usb_phy_init has failed 380d03cccffSPeter Chen */ 381d03cccffSPeter Chen static int ci_usb_phy_init(struct ci_hdrc *ci) 382d03cccffSPeter Chen { 383d03cccffSPeter Chen int ret; 384d03cccffSPeter Chen 3858feb3680SStephen Boyd if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL) 3868feb3680SStephen Boyd return 0; 3878feb3680SStephen Boyd 388d03cccffSPeter Chen switch (ci->platdata->phy_mode) { 389d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_UTMI: 390d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_UTMIW: 391d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_HSIC: 3921e5e2d3dSAntoine Tenart ret = _ci_usb_phy_init(ci); 393b82613cfSPeter Chen if (!ret) 394b82613cfSPeter Chen hw_wait_phy_stable(); 395b82613cfSPeter Chen else 396d03cccffSPeter Chen return ret; 397d03cccffSPeter Chen hw_phymode_configure(ci); 398d03cccffSPeter Chen break; 399d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_ULPI: 400d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_SERIAL: 401d03cccffSPeter Chen hw_phymode_configure(ci); 4021e5e2d3dSAntoine Tenart ret = _ci_usb_phy_init(ci); 403d03cccffSPeter Chen if (ret) 404d03cccffSPeter Chen return ret; 405d03cccffSPeter Chen break; 406d03cccffSPeter Chen default: 4071e5e2d3dSAntoine Tenart ret = _ci_usb_phy_init(ci); 408b82613cfSPeter Chen if (!ret) 409b82613cfSPeter Chen hw_wait_phy_stable(); 410d03cccffSPeter Chen } 411d03cccffSPeter Chen 412d03cccffSPeter Chen return ret; 413d03cccffSPeter Chen } 414d03cccffSPeter Chen 415bf9c85e7SPeter Chen 416bf9c85e7SPeter Chen /** 417bf9c85e7SPeter Chen * ci_platform_configure: do controller configure 418bf9c85e7SPeter Chen * @ci: the controller 419bf9c85e7SPeter Chen * 420bf9c85e7SPeter Chen */ 421bf9c85e7SPeter Chen void ci_platform_configure(struct ci_hdrc *ci) 422bf9c85e7SPeter Chen { 4238022d3d5SPeter Chen bool is_device_mode, is_host_mode; 4248022d3d5SPeter Chen 4258022d3d5SPeter Chen is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC; 4268022d3d5SPeter Chen is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC; 4278022d3d5SPeter Chen 428490b63e6SStephen Boyd if (is_device_mode) { 429490b63e6SStephen Boyd phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE); 4308022d3d5SPeter Chen 431490b63e6SStephen Boyd if (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING) 432490b63e6SStephen Boyd hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, 433490b63e6SStephen Boyd USBMODE_CI_SDIS); 434490b63e6SStephen Boyd } 435490b63e6SStephen Boyd 436490b63e6SStephen Boyd if (is_host_mode) { 437490b63e6SStephen Boyd phy_set_mode(ci->phy, PHY_MODE_USB_HOST); 438490b63e6SStephen Boyd 439490b63e6SStephen Boyd if (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING) 440490b63e6SStephen Boyd hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, 441490b63e6SStephen Boyd USBMODE_CI_SDIS); 442490b63e6SStephen Boyd } 443bf9c85e7SPeter Chen 444bf9c85e7SPeter Chen if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { 445bf9c85e7SPeter Chen if (ci->hw_bank.lpm) 446bf9c85e7SPeter Chen hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); 447bf9c85e7SPeter Chen else 448bf9c85e7SPeter Chen hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); 449bf9c85e7SPeter Chen } 450bf9c85e7SPeter Chen 451bf9c85e7SPeter Chen if (ci->platdata->flags & CI_HDRC_SET_NON_ZERO_TTHA) 452bf9c85e7SPeter Chen hw_write(ci, OP_TTCTRL, TTCTRL_TTHA_MASK, TTCTRL_TTHA); 453df96ed8dSPeter Chen 454df96ed8dSPeter Chen hw_write(ci, OP_USBCMD, 0xff0000, ci->platdata->itc_setting << 16); 455df96ed8dSPeter Chen 45665668718SPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_AHB_BURST) 45765668718SPeter Chen hw_write_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK, 45865668718SPeter Chen ci->platdata->ahb_burst_config); 45996625eadSPeter Chen 46096625eadSPeter Chen /* override burst size, take effect only when ahb_burst_config is 0 */ 46196625eadSPeter Chen if (!hw_read_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK)) { 46296625eadSPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_TX_BURST) 46396625eadSPeter Chen hw_write(ci, OP_BURSTSIZE, TX_BURST_MASK, 46496625eadSPeter Chen ci->platdata->tx_burst_size << __ffs(TX_BURST_MASK)); 46596625eadSPeter Chen 46696625eadSPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_RX_BURST) 46796625eadSPeter Chen hw_write(ci, OP_BURSTSIZE, RX_BURST_MASK, 46896625eadSPeter Chen ci->platdata->rx_burst_size); 46996625eadSPeter Chen } 470bf9c85e7SPeter Chen } 471bf9c85e7SPeter Chen 472d03cccffSPeter Chen /** 473cdd278f2SPeter Chen * hw_controller_reset: do controller reset 474cdd278f2SPeter Chen * @ci: the controller 475cdd278f2SPeter Chen * 476cdd278f2SPeter Chen * This function returns an error code 477cdd278f2SPeter Chen */ 478cdd278f2SPeter Chen static int hw_controller_reset(struct ci_hdrc *ci) 479cdd278f2SPeter Chen { 480cdd278f2SPeter Chen int count = 0; 481cdd278f2SPeter Chen 482cdd278f2SPeter Chen hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST); 483cdd278f2SPeter Chen while (hw_read(ci, OP_USBCMD, USBCMD_RST)) { 484cdd278f2SPeter Chen udelay(10); 485cdd278f2SPeter Chen if (count++ > 1000) 486cdd278f2SPeter Chen return -ETIMEDOUT; 487cdd278f2SPeter Chen } 488cdd278f2SPeter Chen 489cdd278f2SPeter Chen return 0; 490cdd278f2SPeter Chen } 491cdd278f2SPeter Chen 492cdd278f2SPeter Chen /** 493e443b333SAlexander Shishkin * hw_device_reset: resets chip (execute without interruption) 494e443b333SAlexander Shishkin * @ci: the controller 495e443b333SAlexander Shishkin * 496e443b333SAlexander Shishkin * This function returns an error code 497e443b333SAlexander Shishkin */ 4985b157300SPeter Chen int hw_device_reset(struct ci_hdrc *ci) 499e443b333SAlexander Shishkin { 500cdd278f2SPeter Chen int ret; 501cdd278f2SPeter Chen 502e443b333SAlexander Shishkin /* should flush & stop before reset */ 503e443b333SAlexander Shishkin hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); 504e443b333SAlexander Shishkin hw_write(ci, OP_USBCMD, USBCMD_RS, 0); 505e443b333SAlexander Shishkin 506cdd278f2SPeter Chen ret = hw_controller_reset(ci); 507cdd278f2SPeter Chen if (ret) { 508cdd278f2SPeter Chen dev_err(ci->dev, "error resetting controller, ret=%d\n", ret); 509cdd278f2SPeter Chen return ret; 510cdd278f2SPeter Chen } 511e443b333SAlexander Shishkin 51211893daeSStephen Boyd if (ci->platdata->notify_event) { 51311893daeSStephen Boyd ret = ci->platdata->notify_event(ci, 5148e22978cSAlexander Shishkin CI_HDRC_CONTROLLER_RESET_EVENT); 51511893daeSStephen Boyd if (ret) 51611893daeSStephen Boyd return ret; 51711893daeSStephen Boyd } 518e443b333SAlexander Shishkin 519e443b333SAlexander Shishkin /* USBMODE should be configured step by step */ 520e443b333SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); 5215b157300SPeter Chen hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC); 522e443b333SAlexander Shishkin /* HW >= 2.3 */ 523e443b333SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); 524e443b333SAlexander Shishkin 5255b157300SPeter Chen if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { 5265b157300SPeter Chen pr_err("cannot enter in %s device mode", ci_role(ci)->name); 527e443b333SAlexander Shishkin pr_err("lpm = %i", ci->hw_bank.lpm); 528e443b333SAlexander Shishkin return -ENODEV; 529e443b333SAlexander Shishkin } 530e443b333SAlexander Shishkin 531bf9c85e7SPeter Chen ci_platform_configure(ci); 532bf9c85e7SPeter Chen 533e443b333SAlexander Shishkin return 0; 534e443b333SAlexander Shishkin } 535e443b333SAlexander Shishkin 5365f36e231SAlexander Shishkin static irqreturn_t ci_irq(int irq, void *data) 5375f36e231SAlexander Shishkin { 5388e22978cSAlexander Shishkin struct ci_hdrc *ci = data; 5395f36e231SAlexander Shishkin irqreturn_t ret = IRQ_NONE; 540b183c19fSRichard Zhao u32 otgsc = 0; 5415f36e231SAlexander Shishkin 5421f874edcSPeter Chen if (ci->in_lpm) { 5431f874edcSPeter Chen disable_irq_nosync(irq); 5441f874edcSPeter Chen ci->wakeup_int = true; 5451f874edcSPeter Chen pm_runtime_get(ci->dev); 5461f874edcSPeter Chen return IRQ_HANDLED; 5471f874edcSPeter Chen } 5481f874edcSPeter Chen 5494dcf720cSLi Jun if (ci->is_otg) { 5500c33bf78SLi Jun otgsc = hw_read_otgsc(ci, ~0); 5514dcf720cSLi Jun if (ci_otg_is_fsm_mode(ci)) { 5524dcf720cSLi Jun ret = ci_otg_fsm_irq(ci); 5534dcf720cSLi Jun if (ret == IRQ_HANDLED) 5544dcf720cSLi Jun return ret; 5554dcf720cSLi Jun } 5564dcf720cSLi Jun } 5575f36e231SAlexander Shishkin 558a107f8c5SPeter Chen /* 559a107f8c5SPeter Chen * Handle id change interrupt, it indicates device/host function 560a107f8c5SPeter Chen * switch. 561a107f8c5SPeter Chen */ 562a107f8c5SPeter Chen if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { 563a107f8c5SPeter Chen ci->id_event = true; 5640c33bf78SLi Jun /* Clear ID change irq status */ 5650c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); 566be6b0c1bSPeter Chen ci_otg_queue_work(ci); 567a107f8c5SPeter Chen return IRQ_HANDLED; 5685f36e231SAlexander Shishkin } 5695f36e231SAlexander Shishkin 570a107f8c5SPeter Chen /* 571a107f8c5SPeter Chen * Handle vbus change interrupt, it indicates device connection 572a107f8c5SPeter Chen * and disconnection events. 573a107f8c5SPeter Chen */ 574a107f8c5SPeter Chen if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { 575a107f8c5SPeter Chen ci->b_sess_valid_event = true; 5760c33bf78SLi Jun /* Clear BSV irq */ 5770c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); 578be6b0c1bSPeter Chen ci_otg_queue_work(ci); 579a107f8c5SPeter Chen return IRQ_HANDLED; 580a107f8c5SPeter Chen } 581a107f8c5SPeter Chen 582a107f8c5SPeter Chen /* Handle device/host interrupt */ 583a107f8c5SPeter Chen if (ci->role != CI_ROLE_END) 584a107f8c5SPeter Chen ret = ci_role(ci)->irq(ci); 585a107f8c5SPeter Chen 586b183c19fSRichard Zhao return ret; 5875f36e231SAlexander Shishkin } 5885f36e231SAlexander Shishkin 5895cc49268SStephen Boyd static int ci_cable_notifier(struct notifier_block *nb, unsigned long event, 5903ecb3e09SIvan T. Ivanov void *ptr) 5913ecb3e09SIvan T. Ivanov { 5925cc49268SStephen Boyd struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb); 5935cc49268SStephen Boyd struct ci_hdrc *ci = cbl->ci; 5943ecb3e09SIvan T. Ivanov 5955cc49268SStephen Boyd cbl->connected = event; 5965cc49268SStephen Boyd cbl->changed = true; 5973ecb3e09SIvan T. Ivanov 5983ecb3e09SIvan T. Ivanov ci_irq(ci->irq, ci); 5993ecb3e09SIvan T. Ivanov return NOTIFY_DONE; 6003ecb3e09SIvan T. Ivanov } 6013ecb3e09SIvan T. Ivanov 6021542d9c3SPeter Chen static int ci_get_platdata(struct device *dev, 6031542d9c3SPeter Chen struct ci_hdrc_platform_data *platdata) 6041542d9c3SPeter Chen { 6053ecb3e09SIvan T. Ivanov struct extcon_dev *ext_vbus, *ext_id; 6063ecb3e09SIvan T. Ivanov struct ci_hdrc_cable *cable; 60779742351SLi Jun int ret; 60879742351SLi Jun 609c22600c3SPeter Chen if (!platdata->phy_mode) 610c22600c3SPeter Chen platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); 611c22600c3SPeter Chen 612c22600c3SPeter Chen if (!platdata->dr_mode) 61306e7114fSHeikki Krogerus platdata->dr_mode = usb_get_dr_mode(dev); 614c22600c3SPeter Chen 615c22600c3SPeter Chen if (platdata->dr_mode == USB_DR_MODE_UNKNOWN) 616c22600c3SPeter Chen platdata->dr_mode = USB_DR_MODE_OTG; 617c22600c3SPeter Chen 618c2ec3a73SPeter Chen if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) { 619c2ec3a73SPeter Chen /* Get the vbus regulator */ 620c2ec3a73SPeter Chen platdata->reg_vbus = devm_regulator_get(dev, "vbus"); 621c2ec3a73SPeter Chen if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { 622c2ec3a73SPeter Chen return -EPROBE_DEFER; 623c2ec3a73SPeter Chen } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { 6246629467bSMickael Maison /* no vbus regulator is needed */ 625c2ec3a73SPeter Chen platdata->reg_vbus = NULL; 626c2ec3a73SPeter Chen } else if (IS_ERR(platdata->reg_vbus)) { 627c2ec3a73SPeter Chen dev_err(dev, "Getting regulator error: %ld\n", 628c2ec3a73SPeter Chen PTR_ERR(platdata->reg_vbus)); 629c2ec3a73SPeter Chen return PTR_ERR(platdata->reg_vbus); 630c2ec3a73SPeter Chen } 631f6a9ff07SPeter Chen /* Get TPL support */ 632f6a9ff07SPeter Chen if (!platdata->tpl_support) 633f6a9ff07SPeter Chen platdata->tpl_support = 634f6a9ff07SPeter Chen of_usb_host_tpl_support(dev->of_node); 635c2ec3a73SPeter Chen } 636c2ec3a73SPeter Chen 63779742351SLi Jun if (platdata->dr_mode == USB_DR_MODE_OTG) { 63879742351SLi Jun /* We can support HNP and SRP of OTG 2.0 */ 63979742351SLi Jun platdata->ci_otg_caps.otg_rev = 0x0200; 64079742351SLi Jun platdata->ci_otg_caps.hnp_support = true; 64179742351SLi Jun platdata->ci_otg_caps.srp_support = true; 64279742351SLi Jun 64379742351SLi Jun /* Update otg capabilities by DT properties */ 64479742351SLi Jun ret = of_usb_update_otg_caps(dev->of_node, 64579742351SLi Jun &platdata->ci_otg_caps); 64679742351SLi Jun if (ret) 64779742351SLi Jun return ret; 64879742351SLi Jun } 64979742351SLi Jun 65063863b98SHeikki Krogerus if (usb_get_maximum_speed(dev) == USB_SPEED_FULL) 6514f6743d5SMichael Grzeschik platdata->flags |= CI_HDRC_FORCE_FULLSPEED; 6524f6743d5SMichael Grzeschik 6531fbf4628SFabio Estevam of_property_read_u32(dev->of_node, "phy-clkgate-delay-us", 6541fbf4628SFabio Estevam &platdata->phy_clkgate_delay_us); 6551fbf4628SFabio Estevam 656df96ed8dSPeter Chen platdata->itc_setting = 1; 657df96ed8dSPeter Chen 6584b19b78aSSaurabh Sengar of_property_read_u32(dev->of_node, "itc-setting", 6594b19b78aSSaurabh Sengar &platdata->itc_setting); 6604b19b78aSSaurabh Sengar 66165668718SPeter Chen ret = of_property_read_u32(dev->of_node, "ahb-burst-config", 66265668718SPeter Chen &platdata->ahb_burst_config); 6634b19b78aSSaurabh Sengar if (!ret) { 6644b19b78aSSaurabh Sengar platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST; 6654b19b78aSSaurabh Sengar } else if (ret != -EINVAL) { 6664b19b78aSSaurabh Sengar dev_err(dev, "failed to get ahb-burst-config\n"); 66765668718SPeter Chen return ret; 66865668718SPeter Chen } 66965668718SPeter Chen 67096625eadSPeter Chen ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword", 67196625eadSPeter Chen &platdata->tx_burst_size); 6724b19b78aSSaurabh Sengar if (!ret) { 67396625eadSPeter Chen platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST; 6744b19b78aSSaurabh Sengar } else if (ret != -EINVAL) { 6754b19b78aSSaurabh Sengar dev_err(dev, "failed to get tx-burst-size-dword\n"); 6764b19b78aSSaurabh Sengar return ret; 67796625eadSPeter Chen } 67896625eadSPeter Chen 67996625eadSPeter Chen ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword", 68096625eadSPeter Chen &platdata->rx_burst_size); 6814b19b78aSSaurabh Sengar if (!ret) { 68296625eadSPeter Chen platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST; 6834b19b78aSSaurabh Sengar } else if (ret != -EINVAL) { 6844b19b78aSSaurabh Sengar dev_err(dev, "failed to get rx-burst-size-dword\n"); 6854b19b78aSSaurabh Sengar return ret; 68696625eadSPeter Chen } 68796625eadSPeter Chen 688aa738187SPeter Chen if (of_find_property(dev->of_node, "non-zero-ttctrl-ttha", NULL)) 689aa738187SPeter Chen platdata->flags |= CI_HDRC_SET_NON_ZERO_TTHA; 690aa738187SPeter Chen 6913ecb3e09SIvan T. Ivanov ext_id = ERR_PTR(-ENODEV); 6923ecb3e09SIvan T. Ivanov ext_vbus = ERR_PTR(-ENODEV); 6933ecb3e09SIvan T. Ivanov if (of_property_read_bool(dev->of_node, "extcon")) { 6943ecb3e09SIvan T. Ivanov /* Each one of them is not mandatory */ 6953ecb3e09SIvan T. Ivanov ext_vbus = extcon_get_edev_by_phandle(dev, 0); 6963ecb3e09SIvan T. Ivanov if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) 6973ecb3e09SIvan T. Ivanov return PTR_ERR(ext_vbus); 6983ecb3e09SIvan T. Ivanov 6993ecb3e09SIvan T. Ivanov ext_id = extcon_get_edev_by_phandle(dev, 1); 7003ecb3e09SIvan T. Ivanov if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) 7013ecb3e09SIvan T. Ivanov return PTR_ERR(ext_id); 7023ecb3e09SIvan T. Ivanov } 7033ecb3e09SIvan T. Ivanov 7043ecb3e09SIvan T. Ivanov cable = &platdata->vbus_extcon; 7055cc49268SStephen Boyd cable->nb.notifier_call = ci_cable_notifier; 7063ecb3e09SIvan T. Ivanov cable->edev = ext_vbus; 7073ecb3e09SIvan T. Ivanov 7083ecb3e09SIvan T. Ivanov if (!IS_ERR(ext_vbus)) { 7093f991aa0SChanwoo Choi ret = extcon_get_state(cable->edev, EXTCON_USB); 7103ecb3e09SIvan T. Ivanov if (ret) 7115cc49268SStephen Boyd cable->connected = true; 7123ecb3e09SIvan T. Ivanov else 7135cc49268SStephen Boyd cable->connected = false; 7143ecb3e09SIvan T. Ivanov } 7153ecb3e09SIvan T. Ivanov 7163ecb3e09SIvan T. Ivanov cable = &platdata->id_extcon; 7175cc49268SStephen Boyd cable->nb.notifier_call = ci_cable_notifier; 7183ecb3e09SIvan T. Ivanov cable->edev = ext_id; 7193ecb3e09SIvan T. Ivanov 7203ecb3e09SIvan T. Ivanov if (!IS_ERR(ext_id)) { 7213f991aa0SChanwoo Choi ret = extcon_get_state(cable->edev, EXTCON_USB_HOST); 7223ecb3e09SIvan T. Ivanov if (ret) 7235cc49268SStephen Boyd cable->connected = true; 7243ecb3e09SIvan T. Ivanov else 7255cc49268SStephen Boyd cable->connected = false; 7263ecb3e09SIvan T. Ivanov } 72716caf1faSLoic Poulain 72816caf1faSLoic Poulain platdata->pctl = devm_pinctrl_get(dev); 72916caf1faSLoic Poulain if (!IS_ERR(platdata->pctl)) { 73016caf1faSLoic Poulain struct pinctrl_state *p; 73116caf1faSLoic Poulain 73216caf1faSLoic Poulain p = pinctrl_lookup_state(platdata->pctl, "default"); 73316caf1faSLoic Poulain if (!IS_ERR(p)) 73416caf1faSLoic Poulain platdata->pins_default = p; 73516caf1faSLoic Poulain 73616caf1faSLoic Poulain p = pinctrl_lookup_state(platdata->pctl, "host"); 73716caf1faSLoic Poulain if (!IS_ERR(p)) 73816caf1faSLoic Poulain platdata->pins_host = p; 73916caf1faSLoic Poulain 74016caf1faSLoic Poulain p = pinctrl_lookup_state(platdata->pctl, "device"); 74116caf1faSLoic Poulain if (!IS_ERR(p)) 74216caf1faSLoic Poulain platdata->pins_device = p; 74316caf1faSLoic Poulain } 74416caf1faSLoic Poulain 7451542d9c3SPeter Chen return 0; 7461542d9c3SPeter Chen } 7471542d9c3SPeter Chen 7483ecb3e09SIvan T. Ivanov static int ci_extcon_register(struct ci_hdrc *ci) 7493ecb3e09SIvan T. Ivanov { 7503ecb3e09SIvan T. Ivanov struct ci_hdrc_cable *id, *vbus; 7513ecb3e09SIvan T. Ivanov int ret; 7523ecb3e09SIvan T. Ivanov 7533ecb3e09SIvan T. Ivanov id = &ci->platdata->id_extcon; 7543ecb3e09SIvan T. Ivanov id->ci = ci; 7557c3a8b81SPeter Chen if (!IS_ERR_OR_NULL(id->edev)) { 7563f991aa0SChanwoo Choi ret = devm_extcon_register_notifier(ci->dev, id->edev, 7573f991aa0SChanwoo Choi EXTCON_USB_HOST, &id->nb); 7583ecb3e09SIvan T. Ivanov if (ret < 0) { 7593ecb3e09SIvan T. Ivanov dev_err(ci->dev, "register ID failed\n"); 7603ecb3e09SIvan T. Ivanov return ret; 7613ecb3e09SIvan T. Ivanov } 7623ecb3e09SIvan T. Ivanov } 7633ecb3e09SIvan T. Ivanov 7643ecb3e09SIvan T. Ivanov vbus = &ci->platdata->vbus_extcon; 7653ecb3e09SIvan T. Ivanov vbus->ci = ci; 7667c3a8b81SPeter Chen if (!IS_ERR_OR_NULL(vbus->edev)) { 7673f991aa0SChanwoo Choi ret = devm_extcon_register_notifier(ci->dev, vbus->edev, 7683f991aa0SChanwoo Choi EXTCON_USB, &vbus->nb); 7693ecb3e09SIvan T. Ivanov if (ret < 0) { 7703ecb3e09SIvan T. Ivanov dev_err(ci->dev, "register VBUS failed\n"); 7713ecb3e09SIvan T. Ivanov return ret; 7723ecb3e09SIvan T. Ivanov } 7733ecb3e09SIvan T. Ivanov } 7743ecb3e09SIvan T. Ivanov 7753ecb3e09SIvan T. Ivanov return 0; 7763ecb3e09SIvan T. Ivanov } 7773ecb3e09SIvan T. Ivanov 778fe6e125eSRichard Zhao static DEFINE_IDA(ci_ida); 779fe6e125eSRichard Zhao 7808e22978cSAlexander Shishkin struct platform_device *ci_hdrc_add_device(struct device *dev, 781cbc6dc2aSRichard Zhao struct resource *res, int nres, 7828e22978cSAlexander Shishkin struct ci_hdrc_platform_data *platdata) 783cbc6dc2aSRichard Zhao { 784cbc6dc2aSRichard Zhao struct platform_device *pdev; 785fe6e125eSRichard Zhao int id, ret; 786cbc6dc2aSRichard Zhao 7871542d9c3SPeter Chen ret = ci_get_platdata(dev, platdata); 7881542d9c3SPeter Chen if (ret) 7891542d9c3SPeter Chen return ERR_PTR(ret); 7901542d9c3SPeter Chen 791fe6e125eSRichard Zhao id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); 792fe6e125eSRichard Zhao if (id < 0) 793fe6e125eSRichard Zhao return ERR_PTR(id); 794fe6e125eSRichard Zhao 795fe6e125eSRichard Zhao pdev = platform_device_alloc("ci_hdrc", id); 796fe6e125eSRichard Zhao if (!pdev) { 797fe6e125eSRichard Zhao ret = -ENOMEM; 798fe6e125eSRichard Zhao goto put_id; 799fe6e125eSRichard Zhao } 800cbc6dc2aSRichard Zhao 801cbc6dc2aSRichard Zhao pdev->dev.parent = dev; 802cbc6dc2aSRichard Zhao 803cbc6dc2aSRichard Zhao ret = platform_device_add_resources(pdev, res, nres); 804cbc6dc2aSRichard Zhao if (ret) 805cbc6dc2aSRichard Zhao goto err; 806cbc6dc2aSRichard Zhao 807cbc6dc2aSRichard Zhao ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); 808cbc6dc2aSRichard Zhao if (ret) 809cbc6dc2aSRichard Zhao goto err; 810cbc6dc2aSRichard Zhao 811cbc6dc2aSRichard Zhao ret = platform_device_add(pdev); 812cbc6dc2aSRichard Zhao if (ret) 813cbc6dc2aSRichard Zhao goto err; 814cbc6dc2aSRichard Zhao 815cbc6dc2aSRichard Zhao return pdev; 816cbc6dc2aSRichard Zhao 817cbc6dc2aSRichard Zhao err: 818cbc6dc2aSRichard Zhao platform_device_put(pdev); 819fe6e125eSRichard Zhao put_id: 820fe6e125eSRichard Zhao ida_simple_remove(&ci_ida, id); 821cbc6dc2aSRichard Zhao return ERR_PTR(ret); 822cbc6dc2aSRichard Zhao } 8238e22978cSAlexander Shishkin EXPORT_SYMBOL_GPL(ci_hdrc_add_device); 824cbc6dc2aSRichard Zhao 8258e22978cSAlexander Shishkin void ci_hdrc_remove_device(struct platform_device *pdev) 826cbc6dc2aSRichard Zhao { 82798c35534SLothar Waßmann int id = pdev->id; 828cbc6dc2aSRichard Zhao platform_device_unregister(pdev); 82998c35534SLothar Waßmann ida_simple_remove(&ci_ida, id); 830cbc6dc2aSRichard Zhao } 8318e22978cSAlexander Shishkin EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); 832cbc6dc2aSRichard Zhao 8333f124d23SPeter Chen static inline void ci_role_destroy(struct ci_hdrc *ci) 8343f124d23SPeter Chen { 8353f124d23SPeter Chen ci_hdrc_gadget_destroy(ci); 8363f124d23SPeter Chen ci_hdrc_host_destroy(ci); 837c4a0bbbdSJisheng Zhang if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) 838cbec6bd5SPeter Chen ci_hdrc_otg_destroy(ci); 8393f124d23SPeter Chen } 8403f124d23SPeter Chen 841577b232fSPeter Chen static void ci_get_otg_capable(struct ci_hdrc *ci) 842577b232fSPeter Chen { 843577b232fSPeter Chen if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) 844577b232fSPeter Chen ci->is_otg = false; 845577b232fSPeter Chen else 846577b232fSPeter Chen ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, 847577b232fSPeter Chen DCCPARAMS_DC | DCCPARAMS_HC) 848577b232fSPeter Chen == (DCCPARAMS_DC | DCCPARAMS_HC)); 8492e37cfd8SPeter Chen if (ci->is_otg) { 850577b232fSPeter Chen dev_dbg(ci->dev, "It is OTG capable controller\n"); 8512e37cfd8SPeter Chen /* Disable and clear all OTG irq */ 8522e37cfd8SPeter Chen hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, 8532e37cfd8SPeter Chen OTGSC_INT_STATUS_BITS); 8542e37cfd8SPeter Chen } 855577b232fSPeter Chen } 856577b232fSPeter Chen 857ed5bd7a4SGreg Kroah-Hartman static ssize_t role_show(struct device *dev, struct device_attribute *attr, 858a932a804SPeter Chen char *buf) 859a932a804SPeter Chen { 860a932a804SPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 861a932a804SPeter Chen 862cbb22ebcSMichael Thalmeier if (ci->role != CI_ROLE_END) 863a932a804SPeter Chen return sprintf(buf, "%s\n", ci_role(ci)->name); 864cbb22ebcSMichael Thalmeier 865cbb22ebcSMichael Thalmeier return 0; 866a932a804SPeter Chen } 867a932a804SPeter Chen 868ed5bd7a4SGreg Kroah-Hartman static ssize_t role_store(struct device *dev, 869a932a804SPeter Chen struct device_attribute *attr, const char *buf, size_t n) 870a932a804SPeter Chen { 871a932a804SPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 872a932a804SPeter Chen enum ci_role role; 873a932a804SPeter Chen int ret; 874a932a804SPeter Chen 875a932a804SPeter Chen if (!(ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])) { 876a932a804SPeter Chen dev_warn(dev, "Current configuration is not dual-role, quit\n"); 877a932a804SPeter Chen return -EPERM; 878a932a804SPeter Chen } 879a932a804SPeter Chen 880a932a804SPeter Chen for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++) 881a932a804SPeter Chen if (!strncmp(buf, ci->roles[role]->name, 882a932a804SPeter Chen strlen(ci->roles[role]->name))) 883a932a804SPeter Chen break; 884a932a804SPeter Chen 885a932a804SPeter Chen if (role == CI_ROLE_END || role == ci->role) 886a932a804SPeter Chen return -EINVAL; 887a932a804SPeter Chen 888a932a804SPeter Chen pm_runtime_get_sync(dev); 889a932a804SPeter Chen disable_irq(ci->irq); 890a932a804SPeter Chen ci_role_stop(ci); 891a932a804SPeter Chen ret = ci_role_start(ci, role); 892a932a804SPeter Chen if (!ret && ci->role == CI_ROLE_GADGET) 893a932a804SPeter Chen ci_handle_vbus_change(ci); 894a932a804SPeter Chen enable_irq(ci->irq); 895a932a804SPeter Chen pm_runtime_put_sync(dev); 896a932a804SPeter Chen 897a932a804SPeter Chen return (ret == 0) ? n : ret; 898a932a804SPeter Chen } 899ed5bd7a4SGreg Kroah-Hartman static DEVICE_ATTR_RW(role); 900a932a804SPeter Chen 901a932a804SPeter Chen static struct attribute *ci_attrs[] = { 902a932a804SPeter Chen &dev_attr_role.attr, 903a932a804SPeter Chen NULL, 904a932a804SPeter Chen }; 905a932a804SPeter Chen 906a351a2bfSArvind Yadav static const struct attribute_group ci_attr_group = { 907a932a804SPeter Chen .attrs = ci_attrs, 908a932a804SPeter Chen }; 909a932a804SPeter Chen 91041ac7b3aSBill Pemberton static int ci_hdrc_probe(struct platform_device *pdev) 911e443b333SAlexander Shishkin { 912e443b333SAlexander Shishkin struct device *dev = &pdev->dev; 9138e22978cSAlexander Shishkin struct ci_hdrc *ci; 914e443b333SAlexander Shishkin struct resource *res; 915e443b333SAlexander Shishkin void __iomem *base; 916e443b333SAlexander Shishkin int ret; 917691962d1SSascha Hauer enum usb_dr_mode dr_mode; 918e443b333SAlexander Shishkin 919fad56745SJingoo Han if (!dev_get_platdata(dev)) { 920e443b333SAlexander Shishkin dev_err(dev, "platform data missing\n"); 921e443b333SAlexander Shishkin return -ENODEV; 922e443b333SAlexander Shishkin } 923e443b333SAlexander Shishkin 924e443b333SAlexander Shishkin res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 92519290816SFelipe Balbi base = devm_ioremap_resource(dev, res); 92619290816SFelipe Balbi if (IS_ERR(base)) 92719290816SFelipe Balbi return PTR_ERR(base); 928e443b333SAlexander Shishkin 9295f36e231SAlexander Shishkin ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL); 930d0f99249SFabio Estevam if (!ci) 9315f36e231SAlexander Shishkin return -ENOMEM; 932e443b333SAlexander Shishkin 933a5d906bbSPeter Chen spin_lock_init(&ci->lock); 9345f36e231SAlexander Shishkin ci->dev = dev; 935fad56745SJingoo Han ci->platdata = dev_get_platdata(dev); 936ed8f8318SPeter Chen ci->imx28_write_fix = !!(ci->platdata->flags & 937ed8f8318SPeter Chen CI_HDRC_IMX28_WRITE_FIX); 9381f874edcSPeter Chen ci->supports_runtime_pm = !!(ci->platdata->flags & 9391f874edcSPeter Chen CI_HDRC_SUPPORTS_RUNTIME_PM); 9407bb7e9b1SStephen Boyd platform_set_drvdata(pdev, ci); 9415f36e231SAlexander Shishkin 9425f36e231SAlexander Shishkin ret = hw_device_init(ci, base); 9435f36e231SAlexander Shishkin if (ret < 0) { 9445f36e231SAlexander Shishkin dev_err(dev, "can't initialize hardware\n"); 9455f36e231SAlexander Shishkin return -ENODEV; 9465f36e231SAlexander Shishkin } 9475f36e231SAlexander Shishkin 9487bb7e9b1SStephen Boyd ret = ci_ulpi_init(ci); 9497bb7e9b1SStephen Boyd if (ret) 9507bb7e9b1SStephen Boyd return ret; 9517bb7e9b1SStephen Boyd 9521e5e2d3dSAntoine Tenart if (ci->platdata->phy) { 9531e5e2d3dSAntoine Tenart ci->phy = ci->platdata->phy; 9541e5e2d3dSAntoine Tenart } else if (ci->platdata->usb_phy) { 955ef44cb42SAntoine Tenart ci->usb_phy = ci->platdata->usb_phy; 9561e5e2d3dSAntoine Tenart } else { 95721a5b579SAntoine Tenart ci->phy = devm_phy_get(dev->parent, "usb-phy"); 95821a5b579SAntoine Tenart ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2); 959c859aa65SPeter Chen 9601e5e2d3dSAntoine Tenart /* if both generic PHY and USB PHY layers aren't enabled */ 9611e5e2d3dSAntoine Tenart if (PTR_ERR(ci->phy) == -ENOSYS && 9627bb7e9b1SStephen Boyd PTR_ERR(ci->usb_phy) == -ENXIO) { 9637bb7e9b1SStephen Boyd ret = -ENXIO; 9647bb7e9b1SStephen Boyd goto ulpi_exit; 9657bb7e9b1SStephen Boyd } 966c859aa65SPeter Chen 9677bb7e9b1SStephen Boyd if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) { 9687bb7e9b1SStephen Boyd ret = -EPROBE_DEFER; 9697bb7e9b1SStephen Boyd goto ulpi_exit; 9707bb7e9b1SStephen Boyd } 9711e5e2d3dSAntoine Tenart 9721e5e2d3dSAntoine Tenart if (IS_ERR(ci->phy)) 9731e5e2d3dSAntoine Tenart ci->phy = NULL; 9741e5e2d3dSAntoine Tenart else if (IS_ERR(ci->usb_phy)) 9751e5e2d3dSAntoine Tenart ci->usb_phy = NULL; 976c859aa65SPeter Chen } 977c859aa65SPeter Chen 978d03cccffSPeter Chen ret = ci_usb_phy_init(ci); 97974475edeSPeter Chen if (ret) { 98074475edeSPeter Chen dev_err(dev, "unable to init phy: %d\n", ret); 98174475edeSPeter Chen return ret; 98274475edeSPeter Chen } 98374475edeSPeter Chen 984eb70e5abSAlexander Shishkin ci->hw_bank.phys = res->start; 985eb70e5abSAlexander Shishkin 9865f36e231SAlexander Shishkin ci->irq = platform_get_irq(pdev, 0); 9875f36e231SAlexander Shishkin if (ci->irq < 0) { 988e443b333SAlexander Shishkin dev_err(dev, "missing IRQ\n"); 98942d18212SFabio Estevam ret = ci->irq; 990c859aa65SPeter Chen goto deinit_phy; 991e443b333SAlexander Shishkin } 992e443b333SAlexander Shishkin 993577b232fSPeter Chen ci_get_otg_capable(ci); 994577b232fSPeter Chen 995691962d1SSascha Hauer dr_mode = ci->platdata->dr_mode; 9965f36e231SAlexander Shishkin /* initialize role(s) before the interrupt is requested */ 997691962d1SSascha Hauer if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { 998eb70e5abSAlexander Shishkin ret = ci_hdrc_host_init(ci); 999c4a0bbbdSJisheng Zhang if (ret) { 1000c4a0bbbdSJisheng Zhang if (ret == -ENXIO) 1001eb70e5abSAlexander Shishkin dev_info(dev, "doesn't support host\n"); 1002c4a0bbbdSJisheng Zhang else 1003c4a0bbbdSJisheng Zhang goto deinit_phy; 1004c4a0bbbdSJisheng Zhang } 1005691962d1SSascha Hauer } 1006eb70e5abSAlexander Shishkin 1007691962d1SSascha Hauer if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { 10085f36e231SAlexander Shishkin ret = ci_hdrc_gadget_init(ci); 1009c4a0bbbdSJisheng Zhang if (ret) { 1010c4a0bbbdSJisheng Zhang if (ret == -ENXIO) 10115f36e231SAlexander Shishkin dev_info(dev, "doesn't support gadget\n"); 1012c4a0bbbdSJisheng Zhang else 1013c4a0bbbdSJisheng Zhang goto deinit_host; 1014c4a0bbbdSJisheng Zhang } 1015691962d1SSascha Hauer } 10165f36e231SAlexander Shishkin 10175f36e231SAlexander Shishkin if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { 10185f36e231SAlexander Shishkin dev_err(dev, "no supported roles\n"); 101974475edeSPeter Chen ret = -ENODEV; 1020c4a0bbbdSJisheng Zhang goto deinit_gadget; 1021cbec6bd5SPeter Chen } 1022cbec6bd5SPeter Chen 102327c62c2dSPeter Chen if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) { 1024cbec6bd5SPeter Chen ret = ci_hdrc_otg_init(ci); 1025cbec6bd5SPeter Chen if (ret) { 1026cbec6bd5SPeter Chen dev_err(dev, "init otg fails, ret = %d\n", ret); 1027c4a0bbbdSJisheng Zhang goto deinit_gadget; 1028cbec6bd5SPeter Chen } 10295f36e231SAlexander Shishkin } 10305f36e231SAlexander Shishkin 10315f36e231SAlexander Shishkin if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { 1032577b232fSPeter Chen if (ci->is_otg) { 10335f36e231SAlexander Shishkin ci->role = ci_otg_role(ci); 10340c33bf78SLi Jun /* Enable ID change irq */ 10350c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); 1036577b232fSPeter Chen } else { 1037577b232fSPeter Chen /* 1038577b232fSPeter Chen * If the controller is not OTG capable, but support 1039577b232fSPeter Chen * role switch, the defalt role is gadget, and the 1040577b232fSPeter Chen * user can switch it through debugfs. 1041577b232fSPeter Chen */ 1042577b232fSPeter Chen ci->role = CI_ROLE_GADGET; 1043577b232fSPeter Chen } 10445f36e231SAlexander Shishkin } else { 10455f36e231SAlexander Shishkin ci->role = ci->roles[CI_ROLE_HOST] 10465f36e231SAlexander Shishkin ? CI_ROLE_HOST 10475f36e231SAlexander Shishkin : CI_ROLE_GADGET; 10485f36e231SAlexander Shishkin } 10495f36e231SAlexander Shishkin 1050961ea496SLi Jun if (!ci_otg_is_fsm_mode(ci)) { 10515a1e1456SPeter Chen /* only update vbus status for peripheral */ 10525a1e1456SPeter Chen if (ci->role == CI_ROLE_GADGET) 10535a1e1456SPeter Chen ci_handle_vbus_change(ci); 10545a1e1456SPeter Chen 10555f36e231SAlexander Shishkin ret = ci_role_start(ci, ci->role); 10565f36e231SAlexander Shishkin if (ret) { 10574dcf720cSLi Jun dev_err(dev, "can't start %s role\n", 10584dcf720cSLi Jun ci_role(ci)->name); 1059cbec6bd5SPeter Chen goto stop; 10605f36e231SAlexander Shishkin } 10614dcf720cSLi Jun } 10625f36e231SAlexander Shishkin 10634c503dd5SPeter Chen ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED, 10644c503dd5SPeter Chen ci->platdata->name, ci); 10655f36e231SAlexander Shishkin if (ret) 10665f36e231SAlexander Shishkin goto stop; 10675f36e231SAlexander Shishkin 10683ecb3e09SIvan T. Ivanov ret = ci_extcon_register(ci); 10693ecb3e09SIvan T. Ivanov if (ret) 10703ecb3e09SIvan T. Ivanov goto stop; 10713ecb3e09SIvan T. Ivanov 10721f874edcSPeter Chen if (ci->supports_runtime_pm) { 10731f874edcSPeter Chen pm_runtime_set_active(&pdev->dev); 10741f874edcSPeter Chen pm_runtime_enable(&pdev->dev); 10751f874edcSPeter Chen pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); 10761f874edcSPeter Chen pm_runtime_mark_last_busy(ci->dev); 10771f874edcSPeter Chen pm_runtime_use_autosuspend(&pdev->dev); 10781f874edcSPeter Chen } 10791f874edcSPeter Chen 10804dcf720cSLi Jun if (ci_otg_is_fsm_mode(ci)) 10814dcf720cSLi Jun ci_hdrc_otg_fsm_start(ci); 10824dcf720cSLi Jun 1083f8efa766SPeter Chen device_set_wakeup_capable(&pdev->dev, true); 1084a61b75d1SGreg Kroah-Hartman dbg_create_files(ci); 1085a932a804SPeter Chen 1086a932a804SPeter Chen ret = sysfs_create_group(&dev->kobj, &ci_attr_group); 1087a932a804SPeter Chen if (ret) 1088a932a804SPeter Chen goto remove_debug; 1089a932a804SPeter Chen 1090adf0f735SAlexander Shishkin return 0; 10915f36e231SAlexander Shishkin 1092a932a804SPeter Chen remove_debug: 1093a932a804SPeter Chen dbg_remove_files(ci); 10945f36e231SAlexander Shishkin stop: 1095c4a0bbbdSJisheng Zhang if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) 1096c4a0bbbdSJisheng Zhang ci_hdrc_otg_destroy(ci); 1097c4a0bbbdSJisheng Zhang deinit_gadget: 1098c4a0bbbdSJisheng Zhang ci_hdrc_gadget_destroy(ci); 1099c4a0bbbdSJisheng Zhang deinit_host: 1100c4a0bbbdSJisheng Zhang ci_hdrc_host_destroy(ci); 1101c859aa65SPeter Chen deinit_phy: 11021e5e2d3dSAntoine Tenart ci_usb_phy_exit(ci); 11037bb7e9b1SStephen Boyd ulpi_exit: 11047bb7e9b1SStephen Boyd ci_ulpi_exit(ci); 1105e443b333SAlexander Shishkin 1106e443b333SAlexander Shishkin return ret; 1107e443b333SAlexander Shishkin } 1108e443b333SAlexander Shishkin 1109fb4e98abSBill Pemberton static int ci_hdrc_remove(struct platform_device *pdev) 1110e443b333SAlexander Shishkin { 11118e22978cSAlexander Shishkin struct ci_hdrc *ci = platform_get_drvdata(pdev); 1112e443b333SAlexander Shishkin 11131f874edcSPeter Chen if (ci->supports_runtime_pm) { 11141f874edcSPeter Chen pm_runtime_get_sync(&pdev->dev); 11151f874edcSPeter Chen pm_runtime_disable(&pdev->dev); 11161f874edcSPeter Chen pm_runtime_put_noidle(&pdev->dev); 11171f874edcSPeter Chen } 11181f874edcSPeter Chen 1119adf0f735SAlexander Shishkin dbg_remove_files(ci); 1120a932a804SPeter Chen sysfs_remove_group(&ci->dev->kobj, &ci_attr_group); 11213f124d23SPeter Chen ci_role_destroy(ci); 1122864cf949SPeter Chen ci_hdrc_enter_lpm(ci, true); 11231e5e2d3dSAntoine Tenart ci_usb_phy_exit(ci); 11247bb7e9b1SStephen Boyd ci_ulpi_exit(ci); 1125e443b333SAlexander Shishkin 1126e443b333SAlexander Shishkin return 0; 1127e443b333SAlexander Shishkin } 1128e443b333SAlexander Shishkin 11291f874edcSPeter Chen #ifdef CONFIG_PM 1130961ea496SLi Jun /* Prepare wakeup by SRP before suspend */ 1131961ea496SLi Jun static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci) 1132961ea496SLi Jun { 1133961ea496SLi Jun if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && 1134961ea496SLi Jun !hw_read_otgsc(ci, OTGSC_ID)) { 1135961ea496SLi Jun hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 1136961ea496SLi Jun PORTSC_PP); 1137961ea496SLi Jun hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN, 1138961ea496SLi Jun PORTSC_WKCN); 1139961ea496SLi Jun } 1140961ea496SLi Jun } 1141961ea496SLi Jun 1142961ea496SLi Jun /* Handle SRP when wakeup by data pulse */ 1143961ea496SLi Jun static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci) 1144961ea496SLi Jun { 1145961ea496SLi Jun if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && 1146961ea496SLi Jun (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) { 1147961ea496SLi Jun if (!hw_read_otgsc(ci, OTGSC_ID)) { 1148961ea496SLi Jun ci->fsm.a_srp_det = 1; 1149961ea496SLi Jun ci->fsm.a_bus_drop = 0; 1150961ea496SLi Jun } else { 1151961ea496SLi Jun ci->fsm.id = 1; 1152961ea496SLi Jun } 1153961ea496SLi Jun ci_otg_queue_work(ci); 1154961ea496SLi Jun } 1155961ea496SLi Jun } 1156961ea496SLi Jun 11578076932fSPeter Chen static void ci_controller_suspend(struct ci_hdrc *ci) 11588076932fSPeter Chen { 11591f874edcSPeter Chen disable_irq(ci->irq); 11608076932fSPeter Chen ci_hdrc_enter_lpm(ci, true); 11611fbf4628SFabio Estevam if (ci->platdata->phy_clkgate_delay_us) 11621fbf4628SFabio Estevam usleep_range(ci->platdata->phy_clkgate_delay_us, 11631fbf4628SFabio Estevam ci->platdata->phy_clkgate_delay_us + 50); 11648076932fSPeter Chen usb_phy_set_suspend(ci->usb_phy, 1); 11651f874edcSPeter Chen ci->in_lpm = true; 11661f874edcSPeter Chen enable_irq(ci->irq); 11678076932fSPeter Chen } 11688076932fSPeter Chen 11698076932fSPeter Chen static int ci_controller_resume(struct device *dev) 11708076932fSPeter Chen { 11718076932fSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 11727bb7e9b1SStephen Boyd int ret; 11738076932fSPeter Chen 11748076932fSPeter Chen dev_dbg(dev, "at %s\n", __func__); 11758076932fSPeter Chen 11761f874edcSPeter Chen if (!ci->in_lpm) { 11771f874edcSPeter Chen WARN_ON(1); 11781f874edcSPeter Chen return 0; 11791f874edcSPeter Chen } 11808076932fSPeter Chen 11811f874edcSPeter Chen ci_hdrc_enter_lpm(ci, false); 11827bb7e9b1SStephen Boyd 11837bb7e9b1SStephen Boyd ret = ci_ulpi_resume(ci); 11847bb7e9b1SStephen Boyd if (ret) 11857bb7e9b1SStephen Boyd return ret; 11867bb7e9b1SStephen Boyd 11878076932fSPeter Chen if (ci->usb_phy) { 11888076932fSPeter Chen usb_phy_set_suspend(ci->usb_phy, 0); 11898076932fSPeter Chen usb_phy_set_wakeup(ci->usb_phy, false); 11908076932fSPeter Chen hw_wait_phy_stable(); 11918076932fSPeter Chen } 11928076932fSPeter Chen 11931f874edcSPeter Chen ci->in_lpm = false; 11941f874edcSPeter Chen if (ci->wakeup_int) { 11951f874edcSPeter Chen ci->wakeup_int = false; 11961f874edcSPeter Chen pm_runtime_mark_last_busy(ci->dev); 11971f874edcSPeter Chen pm_runtime_put_autosuspend(ci->dev); 11981f874edcSPeter Chen enable_irq(ci->irq); 1199961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1200961ea496SLi Jun ci_otg_fsm_wakeup_by_srp(ci); 12011f874edcSPeter Chen } 12021f874edcSPeter Chen 12038076932fSPeter Chen return 0; 12048076932fSPeter Chen } 12058076932fSPeter Chen 12061f874edcSPeter Chen #ifdef CONFIG_PM_SLEEP 12078076932fSPeter Chen static int ci_suspend(struct device *dev) 12088076932fSPeter Chen { 12098076932fSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 12108076932fSPeter Chen 12118076932fSPeter Chen if (ci->wq) 12128076932fSPeter Chen flush_workqueue(ci->wq); 12131f874edcSPeter Chen /* 12141f874edcSPeter Chen * Controller needs to be active during suspend, otherwise the core 12151f874edcSPeter Chen * may run resume when the parent is at suspend if other driver's 12161f874edcSPeter Chen * suspend fails, it occurs before parent's suspend has not started, 12171f874edcSPeter Chen * but the core suspend has finished. 12181f874edcSPeter Chen */ 12191f874edcSPeter Chen if (ci->in_lpm) 12201f874edcSPeter Chen pm_runtime_resume(dev); 12211f874edcSPeter Chen 12221f874edcSPeter Chen if (ci->in_lpm) { 12231f874edcSPeter Chen WARN_ON(1); 12241f874edcSPeter Chen return 0; 12251f874edcSPeter Chen } 12268076932fSPeter Chen 1227f8efa766SPeter Chen if (device_may_wakeup(dev)) { 1228961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1229961ea496SLi Jun ci_otg_fsm_suspend_for_srp(ci); 1230961ea496SLi Jun 1231f8efa766SPeter Chen usb_phy_set_wakeup(ci->usb_phy, true); 1232f8efa766SPeter Chen enable_irq_wake(ci->irq); 1233f8efa766SPeter Chen } 1234f8efa766SPeter Chen 12358076932fSPeter Chen ci_controller_suspend(ci); 12368076932fSPeter Chen 12378076932fSPeter Chen return 0; 12388076932fSPeter Chen } 12398076932fSPeter Chen 12408076932fSPeter Chen static int ci_resume(struct device *dev) 12418076932fSPeter Chen { 12421f874edcSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 12431f874edcSPeter Chen int ret; 12441f874edcSPeter Chen 1245f8efa766SPeter Chen if (device_may_wakeup(dev)) 1246f8efa766SPeter Chen disable_irq_wake(ci->irq); 1247f8efa766SPeter Chen 12481f874edcSPeter Chen ret = ci_controller_resume(dev); 12491f874edcSPeter Chen if (ret) 12501f874edcSPeter Chen return ret; 12511f874edcSPeter Chen 12521f874edcSPeter Chen if (ci->supports_runtime_pm) { 12531f874edcSPeter Chen pm_runtime_disable(dev); 12541f874edcSPeter Chen pm_runtime_set_active(dev); 12551f874edcSPeter Chen pm_runtime_enable(dev); 12561f874edcSPeter Chen } 12571f874edcSPeter Chen 12581f874edcSPeter Chen return ret; 12598076932fSPeter Chen } 12608076932fSPeter Chen #endif /* CONFIG_PM_SLEEP */ 12618076932fSPeter Chen 12621f874edcSPeter Chen static int ci_runtime_suspend(struct device *dev) 12631f874edcSPeter Chen { 12641f874edcSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 12651f874edcSPeter Chen 12661f874edcSPeter Chen dev_dbg(dev, "at %s\n", __func__); 12671f874edcSPeter Chen 12681f874edcSPeter Chen if (ci->in_lpm) { 12691f874edcSPeter Chen WARN_ON(1); 12701f874edcSPeter Chen return 0; 12711f874edcSPeter Chen } 12721f874edcSPeter Chen 1273961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1274961ea496SLi Jun ci_otg_fsm_suspend_for_srp(ci); 1275961ea496SLi Jun 12761f874edcSPeter Chen usb_phy_set_wakeup(ci->usb_phy, true); 12771f874edcSPeter Chen ci_controller_suspend(ci); 12781f874edcSPeter Chen 12791f874edcSPeter Chen return 0; 12801f874edcSPeter Chen } 12811f874edcSPeter Chen 12821f874edcSPeter Chen static int ci_runtime_resume(struct device *dev) 12831f874edcSPeter Chen { 12841f874edcSPeter Chen return ci_controller_resume(dev); 12851f874edcSPeter Chen } 12861f874edcSPeter Chen 12871f874edcSPeter Chen #endif /* CONFIG_PM */ 12888076932fSPeter Chen static const struct dev_pm_ops ci_pm_ops = { 12898076932fSPeter Chen SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume) 12901f874edcSPeter Chen SET_RUNTIME_PM_OPS(ci_runtime_suspend, ci_runtime_resume, NULL) 12918076932fSPeter Chen }; 12921f874edcSPeter Chen 12935f36e231SAlexander Shishkin static struct platform_driver ci_hdrc_driver = { 12945f36e231SAlexander Shishkin .probe = ci_hdrc_probe, 12957690417dSBill Pemberton .remove = ci_hdrc_remove, 1296e443b333SAlexander Shishkin .driver = { 12975f36e231SAlexander Shishkin .name = "ci_hdrc", 12988076932fSPeter Chen .pm = &ci_pm_ops, 1299e443b333SAlexander Shishkin }, 1300e443b333SAlexander Shishkin }; 1301e443b333SAlexander Shishkin 13022f01a33bSPeter Chen static int __init ci_hdrc_platform_register(void) 13032f01a33bSPeter Chen { 13042f01a33bSPeter Chen ci_hdrc_host_driver_init(); 13052f01a33bSPeter Chen return platform_driver_register(&ci_hdrc_driver); 13062f01a33bSPeter Chen } 13072f01a33bSPeter Chen module_init(ci_hdrc_platform_register); 13082f01a33bSPeter Chen 13092f01a33bSPeter Chen static void __exit ci_hdrc_platform_unregister(void) 13102f01a33bSPeter Chen { 13112f01a33bSPeter Chen platform_driver_unregister(&ci_hdrc_driver); 13122f01a33bSPeter Chen } 13132f01a33bSPeter Chen module_exit(ci_hdrc_platform_unregister); 1314e443b333SAlexander Shishkin 13155f36e231SAlexander Shishkin MODULE_ALIAS("platform:ci_hdrc"); 1316e443b333SAlexander Shishkin MODULE_LICENSE("GPL v2"); 1317e443b333SAlexander Shishkin MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>"); 13185f36e231SAlexander Shishkin MODULE_DESCRIPTION("ChipIdea HDRC Driver"); 1319