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> 651542d9c3SPeter Chen #include <linux/regulator/consumer.h> 668022d3d5SPeter Chen #include <linux/usb/ehci_def.h> 67e443b333SAlexander Shishkin 68e443b333SAlexander Shishkin #include "ci.h" 69e443b333SAlexander Shishkin #include "udc.h" 70e443b333SAlexander Shishkin #include "bits.h" 71eb70e5abSAlexander Shishkin #include "host.h" 72c10b4f03SPeter Chen #include "otg.h" 734dcf720cSLi Jun #include "otg_fsm.h" 74e443b333SAlexander Shishkin 755f36e231SAlexander Shishkin /* Controller register map */ 76987e7bc3SMarc Kleine-Budde static const u8 ci_regs_nolpm[] = { 77987e7bc3SMarc Kleine-Budde [CAP_CAPLENGTH] = 0x00U, 78987e7bc3SMarc Kleine-Budde [CAP_HCCPARAMS] = 0x08U, 79987e7bc3SMarc Kleine-Budde [CAP_DCCPARAMS] = 0x24U, 80987e7bc3SMarc Kleine-Budde [CAP_TESTMODE] = 0x38U, 81987e7bc3SMarc Kleine-Budde [OP_USBCMD] = 0x00U, 82987e7bc3SMarc Kleine-Budde [OP_USBSTS] = 0x04U, 83987e7bc3SMarc Kleine-Budde [OP_USBINTR] = 0x08U, 84987e7bc3SMarc Kleine-Budde [OP_DEVICEADDR] = 0x14U, 85987e7bc3SMarc Kleine-Budde [OP_ENDPTLISTADDR] = 0x18U, 8628362673SPeter Chen [OP_TTCTRL] = 0x1CU, 8796625eadSPeter Chen [OP_BURSTSIZE] = 0x20U, 887bb7e9b1SStephen Boyd [OP_ULPI_VIEWPORT] = 0x30U, 89987e7bc3SMarc Kleine-Budde [OP_PORTSC] = 0x44U, 90987e7bc3SMarc Kleine-Budde [OP_DEVLC] = 0x84U, 91987e7bc3SMarc Kleine-Budde [OP_OTGSC] = 0x64U, 92987e7bc3SMarc Kleine-Budde [OP_USBMODE] = 0x68U, 93987e7bc3SMarc Kleine-Budde [OP_ENDPTSETUPSTAT] = 0x6CU, 94987e7bc3SMarc Kleine-Budde [OP_ENDPTPRIME] = 0x70U, 95987e7bc3SMarc Kleine-Budde [OP_ENDPTFLUSH] = 0x74U, 96987e7bc3SMarc Kleine-Budde [OP_ENDPTSTAT] = 0x78U, 97987e7bc3SMarc Kleine-Budde [OP_ENDPTCOMPLETE] = 0x7CU, 98987e7bc3SMarc Kleine-Budde [OP_ENDPTCTRL] = 0x80U, 99e443b333SAlexander Shishkin }; 100e443b333SAlexander Shishkin 101987e7bc3SMarc Kleine-Budde static const u8 ci_regs_lpm[] = { 102987e7bc3SMarc Kleine-Budde [CAP_CAPLENGTH] = 0x00U, 103987e7bc3SMarc Kleine-Budde [CAP_HCCPARAMS] = 0x08U, 104987e7bc3SMarc Kleine-Budde [CAP_DCCPARAMS] = 0x24U, 105987e7bc3SMarc Kleine-Budde [CAP_TESTMODE] = 0xFCU, 106987e7bc3SMarc Kleine-Budde [OP_USBCMD] = 0x00U, 107987e7bc3SMarc Kleine-Budde [OP_USBSTS] = 0x04U, 108987e7bc3SMarc Kleine-Budde [OP_USBINTR] = 0x08U, 109987e7bc3SMarc Kleine-Budde [OP_DEVICEADDR] = 0x14U, 110987e7bc3SMarc Kleine-Budde [OP_ENDPTLISTADDR] = 0x18U, 11128362673SPeter Chen [OP_TTCTRL] = 0x1CU, 11296625eadSPeter Chen [OP_BURSTSIZE] = 0x20U, 1137bb7e9b1SStephen Boyd [OP_ULPI_VIEWPORT] = 0x30U, 114987e7bc3SMarc Kleine-Budde [OP_PORTSC] = 0x44U, 115987e7bc3SMarc Kleine-Budde [OP_DEVLC] = 0x84U, 116987e7bc3SMarc Kleine-Budde [OP_OTGSC] = 0xC4U, 117987e7bc3SMarc Kleine-Budde [OP_USBMODE] = 0xC8U, 118987e7bc3SMarc Kleine-Budde [OP_ENDPTSETUPSTAT] = 0xD8U, 119987e7bc3SMarc Kleine-Budde [OP_ENDPTPRIME] = 0xDCU, 120987e7bc3SMarc Kleine-Budde [OP_ENDPTFLUSH] = 0xE0U, 121987e7bc3SMarc Kleine-Budde [OP_ENDPTSTAT] = 0xE4U, 122987e7bc3SMarc Kleine-Budde [OP_ENDPTCOMPLETE] = 0xE8U, 123987e7bc3SMarc Kleine-Budde [OP_ENDPTCTRL] = 0xECU, 124e443b333SAlexander Shishkin }; 125e443b333SAlexander Shishkin 126158ec071SNicholas Krause static void hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) 127e443b333SAlexander Shishkin { 128e443b333SAlexander Shishkin int i; 129e443b333SAlexander Shishkin 130e443b333SAlexander Shishkin for (i = 0; i < OP_ENDPTCTRL; i++) 1315f36e231SAlexander Shishkin ci->hw_bank.regmap[i] = 1325f36e231SAlexander Shishkin (i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) + 133e443b333SAlexander Shishkin (is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]); 134e443b333SAlexander Shishkin 135e443b333SAlexander Shishkin for (; i <= OP_LAST; i++) 1365f36e231SAlexander Shishkin ci->hw_bank.regmap[i] = ci->hw_bank.op + 137e443b333SAlexander Shishkin 4 * (i - OP_ENDPTCTRL) + 138e443b333SAlexander Shishkin (is_lpm 139e443b333SAlexander Shishkin ? ci_regs_lpm[OP_ENDPTCTRL] 140e443b333SAlexander Shishkin : ci_regs_nolpm[OP_ENDPTCTRL]); 141e443b333SAlexander Shishkin 142e443b333SAlexander Shishkin } 143e443b333SAlexander Shishkin 144cb271f3cSPeter Chen static enum ci_revision ci_get_revision(struct ci_hdrc *ci) 145cb271f3cSPeter Chen { 146cb271f3cSPeter Chen int ver = hw_read_id_reg(ci, ID_ID, VERSION) >> __ffs(VERSION); 147cb271f3cSPeter Chen enum ci_revision rev = CI_REVISION_UNKNOWN; 148cb271f3cSPeter Chen 149cb271f3cSPeter Chen if (ver == 0x2) { 150cb271f3cSPeter Chen rev = hw_read_id_reg(ci, ID_ID, REVISION) 151cb271f3cSPeter Chen >> __ffs(REVISION); 152cb271f3cSPeter Chen rev += CI_REVISION_20; 153cb271f3cSPeter Chen } else if (ver == 0x0) { 154cb271f3cSPeter Chen rev = CI_REVISION_1X; 155cb271f3cSPeter Chen } 156cb271f3cSPeter Chen 157cb271f3cSPeter Chen return rev; 158cb271f3cSPeter Chen } 159cb271f3cSPeter Chen 160e443b333SAlexander Shishkin /** 16136304b06SLi Jun * hw_read_intr_enable: returns interrupt enable register 16236304b06SLi Jun * 16319353881SPeter Chen * @ci: the controller 16419353881SPeter Chen * 16536304b06SLi Jun * This function returns register data 16636304b06SLi Jun */ 16736304b06SLi Jun u32 hw_read_intr_enable(struct ci_hdrc *ci) 16836304b06SLi Jun { 16936304b06SLi Jun return hw_read(ci, OP_USBINTR, ~0); 17036304b06SLi Jun } 17136304b06SLi Jun 17236304b06SLi Jun /** 17336304b06SLi Jun * hw_read_intr_status: returns interrupt status register 17436304b06SLi Jun * 17519353881SPeter Chen * @ci: the controller 17619353881SPeter Chen * 17736304b06SLi Jun * This function returns register data 17836304b06SLi Jun */ 17936304b06SLi Jun u32 hw_read_intr_status(struct ci_hdrc *ci) 18036304b06SLi Jun { 18136304b06SLi Jun return hw_read(ci, OP_USBSTS, ~0); 18236304b06SLi Jun } 18336304b06SLi Jun 18436304b06SLi Jun /** 185e443b333SAlexander Shishkin * hw_port_test_set: writes port test mode (execute without interruption) 186e443b333SAlexander Shishkin * @mode: new value 187e443b333SAlexander Shishkin * 188e443b333SAlexander Shishkin * This function returns an error code 189e443b333SAlexander Shishkin */ 1908e22978cSAlexander Shishkin int hw_port_test_set(struct ci_hdrc *ci, u8 mode) 191e443b333SAlexander Shishkin { 192e443b333SAlexander Shishkin const u8 TEST_MODE_MAX = 7; 193e443b333SAlexander Shishkin 194e443b333SAlexander Shishkin if (mode > TEST_MODE_MAX) 195e443b333SAlexander Shishkin return -EINVAL; 196e443b333SAlexander Shishkin 197727b4ddbSFelipe Balbi hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC)); 198e443b333SAlexander Shishkin return 0; 199e443b333SAlexander Shishkin } 200e443b333SAlexander Shishkin 201e443b333SAlexander Shishkin /** 202e443b333SAlexander Shishkin * hw_port_test_get: reads port test mode value 203e443b333SAlexander Shishkin * 20419353881SPeter Chen * @ci: the controller 20519353881SPeter Chen * 206e443b333SAlexander Shishkin * This function returns port test mode value 207e443b333SAlexander Shishkin */ 2088e22978cSAlexander Shishkin u8 hw_port_test_get(struct ci_hdrc *ci) 209e443b333SAlexander Shishkin { 210727b4ddbSFelipe Balbi return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC); 211e443b333SAlexander Shishkin } 212e443b333SAlexander Shishkin 213b82613cfSPeter Chen static void hw_wait_phy_stable(void) 214b82613cfSPeter Chen { 215b82613cfSPeter Chen /* 216b82613cfSPeter Chen * The phy needs some delay to output the stable status from low 217b82613cfSPeter Chen * power mode. And for OTGSC, the status inputs are debounced 218b82613cfSPeter Chen * using a 1 ms time constant, so, delay 2ms for controller to get 219b82613cfSPeter Chen * the stable status, like vbus and id when the phy leaves low power. 220b82613cfSPeter Chen */ 221b82613cfSPeter Chen usleep_range(2000, 2500); 222b82613cfSPeter Chen } 223b82613cfSPeter Chen 224864cf949SPeter Chen /* The PHY enters/leaves low power mode */ 225864cf949SPeter Chen static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable) 226864cf949SPeter Chen { 227864cf949SPeter Chen enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC; 228864cf949SPeter Chen bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm))); 229864cf949SPeter Chen 2306d037db6SPeter Chen if (enable && !lpm) 231864cf949SPeter Chen hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 232864cf949SPeter Chen PORTSC_PHCD(ci->hw_bank.lpm)); 2336d037db6SPeter Chen else if (!enable && lpm) 234864cf949SPeter Chen hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 235864cf949SPeter Chen 0); 236864cf949SPeter Chen } 237864cf949SPeter Chen 2388e22978cSAlexander Shishkin static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) 239e443b333SAlexander Shishkin { 240e443b333SAlexander Shishkin u32 reg; 241e443b333SAlexander Shishkin 242e443b333SAlexander Shishkin /* bank is a module variable */ 2435f36e231SAlexander Shishkin ci->hw_bank.abs = base; 244e443b333SAlexander Shishkin 2455f36e231SAlexander Shishkin ci->hw_bank.cap = ci->hw_bank.abs; 24677c4400fSRichard Zhao ci->hw_bank.cap += ci->platdata->capoffset; 247938d323fSSvetoslav Neykov ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff); 248e443b333SAlexander Shishkin 2495f36e231SAlexander Shishkin hw_alloc_regmap(ci, false); 2505f36e231SAlexander Shishkin reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >> 251727b4ddbSFelipe Balbi __ffs(HCCPARAMS_LEN); 2525f36e231SAlexander Shishkin ci->hw_bank.lpm = reg; 253aeb2c121SChris Ruehl if (reg) 2545f36e231SAlexander Shishkin hw_alloc_regmap(ci, !!reg); 2555f36e231SAlexander Shishkin ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs; 2565f36e231SAlexander Shishkin ci->hw_bank.size += OP_LAST; 2575f36e231SAlexander Shishkin ci->hw_bank.size /= sizeof(u32); 258e443b333SAlexander Shishkin 2595f36e231SAlexander Shishkin reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >> 260727b4ddbSFelipe Balbi __ffs(DCCPARAMS_DEN); 2615f36e231SAlexander Shishkin ci->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */ 262e443b333SAlexander Shishkin 26309c94e62SRichard Zhao if (ci->hw_ep_max > ENDPT_MAX) 264e443b333SAlexander Shishkin return -ENODEV; 265e443b333SAlexander Shishkin 266864cf949SPeter Chen ci_hdrc_enter_lpm(ci, false); 267864cf949SPeter Chen 268c344b518SPeter Chen /* Disable all interrupts bits */ 269c344b518SPeter Chen hw_write(ci, OP_USBINTR, 0xffffffff, 0); 270c344b518SPeter Chen 271c344b518SPeter Chen /* Clear all interrupts status bits*/ 272c344b518SPeter Chen hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff); 273c344b518SPeter Chen 274cb271f3cSPeter Chen ci->rev = ci_get_revision(ci); 275cb271f3cSPeter Chen 276cb271f3cSPeter Chen dev_dbg(ci->dev, 277cb271f3cSPeter Chen "ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n", 278cb271f3cSPeter Chen ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); 279e443b333SAlexander Shishkin 280e443b333SAlexander Shishkin /* setup lock mode ? */ 281e443b333SAlexander Shishkin 282e443b333SAlexander Shishkin /* ENDPTSETUPSTAT is '0' by default */ 283e443b333SAlexander Shishkin 284e443b333SAlexander Shishkin /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ 285e443b333SAlexander Shishkin 286e443b333SAlexander Shishkin return 0; 287e443b333SAlexander Shishkin } 288e443b333SAlexander Shishkin 2897bb7e9b1SStephen Boyd void hw_phymode_configure(struct ci_hdrc *ci) 29040dcd0e8SMichael Grzeschik { 2913b5d3e68SChris Ruehl u32 portsc, lpm, sts = 0; 29240dcd0e8SMichael Grzeschik 29340dcd0e8SMichael Grzeschik switch (ci->platdata->phy_mode) { 29440dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_UTMI: 29540dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_UTMI); 29640dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_UTMI); 29740dcd0e8SMichael Grzeschik break; 29840dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_UTMIW: 29940dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_UTMI) | PORTSC_PTW; 30040dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_UTMI) | DEVLC_PTW; 30140dcd0e8SMichael Grzeschik break; 30240dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_ULPI: 30340dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_ULPI); 30440dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_ULPI); 30540dcd0e8SMichael Grzeschik break; 30640dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_SERIAL: 30740dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_SERIAL); 30840dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_SERIAL); 30940dcd0e8SMichael Grzeschik sts = 1; 31040dcd0e8SMichael Grzeschik break; 31140dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_HSIC: 31240dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_HSIC); 31340dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_HSIC); 31440dcd0e8SMichael Grzeschik break; 31540dcd0e8SMichael Grzeschik default: 31640dcd0e8SMichael Grzeschik return; 31740dcd0e8SMichael Grzeschik } 31840dcd0e8SMichael Grzeschik 31940dcd0e8SMichael Grzeschik if (ci->hw_bank.lpm) { 32040dcd0e8SMichael Grzeschik hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm); 3213b5d3e68SChris Ruehl if (sts) 3223b5d3e68SChris Ruehl hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS); 32340dcd0e8SMichael Grzeschik } else { 32440dcd0e8SMichael Grzeschik hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc); 3253b5d3e68SChris Ruehl if (sts) 3263b5d3e68SChris Ruehl hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); 32740dcd0e8SMichael Grzeschik } 32840dcd0e8SMichael Grzeschik } 32911893daeSStephen Boyd EXPORT_SYMBOL_GPL(hw_phymode_configure); 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 430490b63e6SStephen Boyd if (is_device_mode) { 431490b63e6SStephen Boyd phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE); 4328022d3d5SPeter Chen 433490b63e6SStephen Boyd if (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING) 434490b63e6SStephen Boyd hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, 435490b63e6SStephen Boyd USBMODE_CI_SDIS); 436490b63e6SStephen Boyd } 437490b63e6SStephen Boyd 438490b63e6SStephen Boyd if (is_host_mode) { 439490b63e6SStephen Boyd phy_set_mode(ci->phy, PHY_MODE_USB_HOST); 440490b63e6SStephen Boyd 441490b63e6SStephen Boyd if (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING) 442490b63e6SStephen Boyd hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, 443490b63e6SStephen Boyd USBMODE_CI_SDIS); 444490b63e6SStephen Boyd } 445bf9c85e7SPeter Chen 446bf9c85e7SPeter Chen if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { 447bf9c85e7SPeter Chen if (ci->hw_bank.lpm) 448bf9c85e7SPeter Chen hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); 449bf9c85e7SPeter Chen else 450bf9c85e7SPeter Chen hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); 451bf9c85e7SPeter Chen } 452bf9c85e7SPeter Chen 453bf9c85e7SPeter Chen if (ci->platdata->flags & CI_HDRC_SET_NON_ZERO_TTHA) 454bf9c85e7SPeter Chen hw_write(ci, OP_TTCTRL, TTCTRL_TTHA_MASK, TTCTRL_TTHA); 455df96ed8dSPeter Chen 456df96ed8dSPeter Chen hw_write(ci, OP_USBCMD, 0xff0000, ci->platdata->itc_setting << 16); 457df96ed8dSPeter Chen 45865668718SPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_AHB_BURST) 45965668718SPeter Chen hw_write_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK, 46065668718SPeter Chen ci->platdata->ahb_burst_config); 46196625eadSPeter Chen 46296625eadSPeter Chen /* override burst size, take effect only when ahb_burst_config is 0 */ 46396625eadSPeter Chen if (!hw_read_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK)) { 46496625eadSPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_TX_BURST) 46596625eadSPeter Chen hw_write(ci, OP_BURSTSIZE, TX_BURST_MASK, 46696625eadSPeter Chen ci->platdata->tx_burst_size << __ffs(TX_BURST_MASK)); 46796625eadSPeter Chen 46896625eadSPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_RX_BURST) 46996625eadSPeter Chen hw_write(ci, OP_BURSTSIZE, RX_BURST_MASK, 47096625eadSPeter Chen ci->platdata->rx_burst_size); 47196625eadSPeter Chen } 472bf9c85e7SPeter Chen } 473bf9c85e7SPeter Chen 474d03cccffSPeter Chen /** 475cdd278f2SPeter Chen * hw_controller_reset: do controller reset 476cdd278f2SPeter Chen * @ci: the controller 477cdd278f2SPeter Chen * 478cdd278f2SPeter Chen * This function returns an error code 479cdd278f2SPeter Chen */ 480cdd278f2SPeter Chen static int hw_controller_reset(struct ci_hdrc *ci) 481cdd278f2SPeter Chen { 482cdd278f2SPeter Chen int count = 0; 483cdd278f2SPeter Chen 484cdd278f2SPeter Chen hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST); 485cdd278f2SPeter Chen while (hw_read(ci, OP_USBCMD, USBCMD_RST)) { 486cdd278f2SPeter Chen udelay(10); 487cdd278f2SPeter Chen if (count++ > 1000) 488cdd278f2SPeter Chen return -ETIMEDOUT; 489cdd278f2SPeter Chen } 490cdd278f2SPeter Chen 491cdd278f2SPeter Chen return 0; 492cdd278f2SPeter Chen } 493cdd278f2SPeter Chen 494cdd278f2SPeter Chen /** 495e443b333SAlexander Shishkin * hw_device_reset: resets chip (execute without interruption) 496e443b333SAlexander Shishkin * @ci: the controller 497e443b333SAlexander Shishkin * 498e443b333SAlexander Shishkin * This function returns an error code 499e443b333SAlexander Shishkin */ 5005b157300SPeter Chen int hw_device_reset(struct ci_hdrc *ci) 501e443b333SAlexander Shishkin { 502cdd278f2SPeter Chen int ret; 503cdd278f2SPeter Chen 504e443b333SAlexander Shishkin /* should flush & stop before reset */ 505e443b333SAlexander Shishkin hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); 506e443b333SAlexander Shishkin hw_write(ci, OP_USBCMD, USBCMD_RS, 0); 507e443b333SAlexander Shishkin 508cdd278f2SPeter Chen ret = hw_controller_reset(ci); 509cdd278f2SPeter Chen if (ret) { 510cdd278f2SPeter Chen dev_err(ci->dev, "error resetting controller, ret=%d\n", ret); 511cdd278f2SPeter Chen return ret; 512cdd278f2SPeter Chen } 513e443b333SAlexander Shishkin 51411893daeSStephen Boyd if (ci->platdata->notify_event) { 51511893daeSStephen Boyd ret = ci->platdata->notify_event(ci, 5168e22978cSAlexander Shishkin CI_HDRC_CONTROLLER_RESET_EVENT); 51711893daeSStephen Boyd if (ret) 51811893daeSStephen Boyd return ret; 51911893daeSStephen Boyd } 520e443b333SAlexander Shishkin 521e443b333SAlexander Shishkin /* USBMODE should be configured step by step */ 522e443b333SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); 5235b157300SPeter Chen hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC); 524e443b333SAlexander Shishkin /* HW >= 2.3 */ 525e443b333SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); 526e443b333SAlexander Shishkin 5275b157300SPeter Chen if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { 5285b157300SPeter Chen pr_err("cannot enter in %s device mode", ci_role(ci)->name); 529e443b333SAlexander Shishkin pr_err("lpm = %i", ci->hw_bank.lpm); 530e443b333SAlexander Shishkin return -ENODEV; 531e443b333SAlexander Shishkin } 532e443b333SAlexander Shishkin 533bf9c85e7SPeter Chen ci_platform_configure(ci); 534bf9c85e7SPeter Chen 535e443b333SAlexander Shishkin return 0; 536e443b333SAlexander Shishkin } 537e443b333SAlexander Shishkin 5385f36e231SAlexander Shishkin static irqreturn_t ci_irq(int irq, void *data) 5395f36e231SAlexander Shishkin { 5408e22978cSAlexander Shishkin struct ci_hdrc *ci = data; 5415f36e231SAlexander Shishkin irqreturn_t ret = IRQ_NONE; 542b183c19fSRichard Zhao u32 otgsc = 0; 5435f36e231SAlexander Shishkin 5441f874edcSPeter Chen if (ci->in_lpm) { 5451f874edcSPeter Chen disable_irq_nosync(irq); 5461f874edcSPeter Chen ci->wakeup_int = true; 5471f874edcSPeter Chen pm_runtime_get(ci->dev); 5481f874edcSPeter Chen return IRQ_HANDLED; 5491f874edcSPeter Chen } 5501f874edcSPeter Chen 5514dcf720cSLi Jun if (ci->is_otg) { 5520c33bf78SLi Jun otgsc = hw_read_otgsc(ci, ~0); 5534dcf720cSLi Jun if (ci_otg_is_fsm_mode(ci)) { 5544dcf720cSLi Jun ret = ci_otg_fsm_irq(ci); 5554dcf720cSLi Jun if (ret == IRQ_HANDLED) 5564dcf720cSLi Jun return ret; 5574dcf720cSLi Jun } 5584dcf720cSLi Jun } 5595f36e231SAlexander Shishkin 560a107f8c5SPeter Chen /* 561a107f8c5SPeter Chen * Handle id change interrupt, it indicates device/host function 562a107f8c5SPeter Chen * switch. 563a107f8c5SPeter Chen */ 564a107f8c5SPeter Chen if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { 565a107f8c5SPeter Chen ci->id_event = true; 5660c33bf78SLi Jun /* Clear ID change irq status */ 5670c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); 568be6b0c1bSPeter Chen ci_otg_queue_work(ci); 569a107f8c5SPeter Chen return IRQ_HANDLED; 5705f36e231SAlexander Shishkin } 5715f36e231SAlexander Shishkin 572a107f8c5SPeter Chen /* 573a107f8c5SPeter Chen * Handle vbus change interrupt, it indicates device connection 574a107f8c5SPeter Chen * and disconnection events. 575a107f8c5SPeter Chen */ 576a107f8c5SPeter Chen if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { 577a107f8c5SPeter Chen ci->b_sess_valid_event = true; 5780c33bf78SLi Jun /* Clear BSV irq */ 5790c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); 580be6b0c1bSPeter Chen ci_otg_queue_work(ci); 581a107f8c5SPeter Chen return IRQ_HANDLED; 582a107f8c5SPeter Chen } 583a107f8c5SPeter Chen 584a107f8c5SPeter Chen /* Handle device/host interrupt */ 585a107f8c5SPeter Chen if (ci->role != CI_ROLE_END) 586a107f8c5SPeter Chen ret = ci_role(ci)->irq(ci); 587a107f8c5SPeter Chen 588b183c19fSRichard Zhao return ret; 5895f36e231SAlexander Shishkin } 5905f36e231SAlexander Shishkin 5915cc49268SStephen Boyd static int ci_cable_notifier(struct notifier_block *nb, unsigned long event, 5923ecb3e09SIvan T. Ivanov void *ptr) 5933ecb3e09SIvan T. Ivanov { 5945cc49268SStephen Boyd struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb); 5955cc49268SStephen Boyd struct ci_hdrc *ci = cbl->ci; 5963ecb3e09SIvan T. Ivanov 5975cc49268SStephen Boyd cbl->connected = event; 5985cc49268SStephen Boyd cbl->changed = true; 5993ecb3e09SIvan T. Ivanov 6003ecb3e09SIvan T. Ivanov ci_irq(ci->irq, ci); 6013ecb3e09SIvan T. Ivanov return NOTIFY_DONE; 6023ecb3e09SIvan T. Ivanov } 6033ecb3e09SIvan T. Ivanov 6041542d9c3SPeter Chen static int ci_get_platdata(struct device *dev, 6051542d9c3SPeter Chen struct ci_hdrc_platform_data *platdata) 6061542d9c3SPeter Chen { 6073ecb3e09SIvan T. Ivanov struct extcon_dev *ext_vbus, *ext_id; 6083ecb3e09SIvan T. Ivanov struct ci_hdrc_cable *cable; 60979742351SLi Jun int ret; 61079742351SLi Jun 611c22600c3SPeter Chen if (!platdata->phy_mode) 612c22600c3SPeter Chen platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); 613c22600c3SPeter Chen 614c22600c3SPeter Chen if (!platdata->dr_mode) 61506e7114fSHeikki Krogerus platdata->dr_mode = usb_get_dr_mode(dev); 616c22600c3SPeter Chen 617c22600c3SPeter Chen if (platdata->dr_mode == USB_DR_MODE_UNKNOWN) 618c22600c3SPeter Chen platdata->dr_mode = USB_DR_MODE_OTG; 619c22600c3SPeter Chen 620c2ec3a73SPeter Chen if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) { 621c2ec3a73SPeter Chen /* Get the vbus regulator */ 622c2ec3a73SPeter Chen platdata->reg_vbus = devm_regulator_get(dev, "vbus"); 623c2ec3a73SPeter Chen if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { 624c2ec3a73SPeter Chen return -EPROBE_DEFER; 625c2ec3a73SPeter Chen } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { 6266629467bSMickael Maison /* no vbus regulator is needed */ 627c2ec3a73SPeter Chen platdata->reg_vbus = NULL; 628c2ec3a73SPeter Chen } else if (IS_ERR(platdata->reg_vbus)) { 629c2ec3a73SPeter Chen dev_err(dev, "Getting regulator error: %ld\n", 630c2ec3a73SPeter Chen PTR_ERR(platdata->reg_vbus)); 631c2ec3a73SPeter Chen return PTR_ERR(platdata->reg_vbus); 632c2ec3a73SPeter Chen } 633f6a9ff07SPeter Chen /* Get TPL support */ 634f6a9ff07SPeter Chen if (!platdata->tpl_support) 635f6a9ff07SPeter Chen platdata->tpl_support = 636f6a9ff07SPeter Chen of_usb_host_tpl_support(dev->of_node); 637c2ec3a73SPeter Chen } 638c2ec3a73SPeter Chen 63979742351SLi Jun if (platdata->dr_mode == USB_DR_MODE_OTG) { 64079742351SLi Jun /* We can support HNP and SRP of OTG 2.0 */ 64179742351SLi Jun platdata->ci_otg_caps.otg_rev = 0x0200; 64279742351SLi Jun platdata->ci_otg_caps.hnp_support = true; 64379742351SLi Jun platdata->ci_otg_caps.srp_support = true; 64479742351SLi Jun 64579742351SLi Jun /* Update otg capabilities by DT properties */ 64679742351SLi Jun ret = of_usb_update_otg_caps(dev->of_node, 64779742351SLi Jun &platdata->ci_otg_caps); 64879742351SLi Jun if (ret) 64979742351SLi Jun return ret; 65079742351SLi Jun } 65179742351SLi Jun 65263863b98SHeikki Krogerus if (usb_get_maximum_speed(dev) == USB_SPEED_FULL) 6534f6743d5SMichael Grzeschik platdata->flags |= CI_HDRC_FORCE_FULLSPEED; 6544f6743d5SMichael Grzeschik 6551fbf4628SFabio Estevam of_property_read_u32(dev->of_node, "phy-clkgate-delay-us", 6561fbf4628SFabio Estevam &platdata->phy_clkgate_delay_us); 6571fbf4628SFabio Estevam 658df96ed8dSPeter Chen platdata->itc_setting = 1; 659df96ed8dSPeter Chen 6604b19b78aSSaurabh Sengar of_property_read_u32(dev->of_node, "itc-setting", 6614b19b78aSSaurabh Sengar &platdata->itc_setting); 6624b19b78aSSaurabh Sengar 66365668718SPeter Chen ret = of_property_read_u32(dev->of_node, "ahb-burst-config", 66465668718SPeter Chen &platdata->ahb_burst_config); 6654b19b78aSSaurabh Sengar if (!ret) { 6664b19b78aSSaurabh Sengar platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST; 6674b19b78aSSaurabh Sengar } else if (ret != -EINVAL) { 6684b19b78aSSaurabh Sengar dev_err(dev, "failed to get ahb-burst-config\n"); 66965668718SPeter Chen return ret; 67065668718SPeter Chen } 67165668718SPeter Chen 67296625eadSPeter Chen ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword", 67396625eadSPeter Chen &platdata->tx_burst_size); 6744b19b78aSSaurabh Sengar if (!ret) { 67596625eadSPeter Chen platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST; 6764b19b78aSSaurabh Sengar } else if (ret != -EINVAL) { 6774b19b78aSSaurabh Sengar dev_err(dev, "failed to get tx-burst-size-dword\n"); 6784b19b78aSSaurabh Sengar return ret; 67996625eadSPeter Chen } 68096625eadSPeter Chen 68196625eadSPeter Chen ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword", 68296625eadSPeter Chen &platdata->rx_burst_size); 6834b19b78aSSaurabh Sengar if (!ret) { 68496625eadSPeter Chen platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST; 6854b19b78aSSaurabh Sengar } else if (ret != -EINVAL) { 6864b19b78aSSaurabh Sengar dev_err(dev, "failed to get rx-burst-size-dword\n"); 6874b19b78aSSaurabh Sengar return ret; 68896625eadSPeter Chen } 68996625eadSPeter Chen 690aa738187SPeter Chen if (of_find_property(dev->of_node, "non-zero-ttctrl-ttha", NULL)) 691aa738187SPeter Chen platdata->flags |= CI_HDRC_SET_NON_ZERO_TTHA; 692aa738187SPeter Chen 6933ecb3e09SIvan T. Ivanov ext_id = ERR_PTR(-ENODEV); 6943ecb3e09SIvan T. Ivanov ext_vbus = ERR_PTR(-ENODEV); 6953ecb3e09SIvan T. Ivanov if (of_property_read_bool(dev->of_node, "extcon")) { 6963ecb3e09SIvan T. Ivanov /* Each one of them is not mandatory */ 6973ecb3e09SIvan T. Ivanov ext_vbus = extcon_get_edev_by_phandle(dev, 0); 6983ecb3e09SIvan T. Ivanov if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) 6993ecb3e09SIvan T. Ivanov return PTR_ERR(ext_vbus); 7003ecb3e09SIvan T. Ivanov 7013ecb3e09SIvan T. Ivanov ext_id = extcon_get_edev_by_phandle(dev, 1); 7023ecb3e09SIvan T. Ivanov if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) 7033ecb3e09SIvan T. Ivanov return PTR_ERR(ext_id); 7043ecb3e09SIvan T. Ivanov } 7053ecb3e09SIvan T. Ivanov 7063ecb3e09SIvan T. Ivanov cable = &platdata->vbus_extcon; 7075cc49268SStephen Boyd cable->nb.notifier_call = ci_cable_notifier; 7083ecb3e09SIvan T. Ivanov cable->edev = ext_vbus; 7093ecb3e09SIvan T. Ivanov 7103ecb3e09SIvan T. Ivanov if (!IS_ERR(ext_vbus)) { 7113f991aa0SChanwoo Choi ret = extcon_get_state(cable->edev, EXTCON_USB); 7123ecb3e09SIvan T. Ivanov if (ret) 7135cc49268SStephen Boyd cable->connected = true; 7143ecb3e09SIvan T. Ivanov else 7155cc49268SStephen Boyd cable->connected = false; 7163ecb3e09SIvan T. Ivanov } 7173ecb3e09SIvan T. Ivanov 7183ecb3e09SIvan T. Ivanov cable = &platdata->id_extcon; 7195cc49268SStephen Boyd cable->nb.notifier_call = ci_cable_notifier; 7203ecb3e09SIvan T. Ivanov cable->edev = ext_id; 7213ecb3e09SIvan T. Ivanov 7223ecb3e09SIvan T. Ivanov if (!IS_ERR(ext_id)) { 7233f991aa0SChanwoo Choi ret = extcon_get_state(cable->edev, EXTCON_USB_HOST); 7243ecb3e09SIvan T. Ivanov if (ret) 7255cc49268SStephen Boyd cable->connected = true; 7263ecb3e09SIvan T. Ivanov else 7275cc49268SStephen Boyd cable->connected = false; 7283ecb3e09SIvan T. Ivanov } 7291542d9c3SPeter Chen return 0; 7301542d9c3SPeter Chen } 7311542d9c3SPeter Chen 7323ecb3e09SIvan T. Ivanov static int ci_extcon_register(struct ci_hdrc *ci) 7333ecb3e09SIvan T. Ivanov { 7343ecb3e09SIvan T. Ivanov struct ci_hdrc_cable *id, *vbus; 7353ecb3e09SIvan T. Ivanov int ret; 7363ecb3e09SIvan T. Ivanov 7373ecb3e09SIvan T. Ivanov id = &ci->platdata->id_extcon; 7383ecb3e09SIvan T. Ivanov id->ci = ci; 7393ecb3e09SIvan T. Ivanov if (!IS_ERR(id->edev)) { 7403f991aa0SChanwoo Choi ret = devm_extcon_register_notifier(ci->dev, id->edev, 7413f991aa0SChanwoo Choi EXTCON_USB_HOST, &id->nb); 7423ecb3e09SIvan T. Ivanov if (ret < 0) { 7433ecb3e09SIvan T. Ivanov dev_err(ci->dev, "register ID failed\n"); 7443ecb3e09SIvan T. Ivanov return ret; 7453ecb3e09SIvan T. Ivanov } 7463ecb3e09SIvan T. Ivanov } 7473ecb3e09SIvan T. Ivanov 7483ecb3e09SIvan T. Ivanov vbus = &ci->platdata->vbus_extcon; 7493ecb3e09SIvan T. Ivanov vbus->ci = ci; 7503ecb3e09SIvan T. Ivanov if (!IS_ERR(vbus->edev)) { 7513f991aa0SChanwoo Choi ret = devm_extcon_register_notifier(ci->dev, vbus->edev, 7523f991aa0SChanwoo Choi EXTCON_USB, &vbus->nb); 7533ecb3e09SIvan T. Ivanov if (ret < 0) { 7543ecb3e09SIvan T. Ivanov dev_err(ci->dev, "register VBUS failed\n"); 7553ecb3e09SIvan T. Ivanov return ret; 7563ecb3e09SIvan T. Ivanov } 7573ecb3e09SIvan T. Ivanov } 7583ecb3e09SIvan T. Ivanov 7593ecb3e09SIvan T. Ivanov return 0; 7603ecb3e09SIvan T. Ivanov } 7613ecb3e09SIvan T. Ivanov 762fe6e125eSRichard Zhao static DEFINE_IDA(ci_ida); 763fe6e125eSRichard Zhao 7648e22978cSAlexander Shishkin struct platform_device *ci_hdrc_add_device(struct device *dev, 765cbc6dc2aSRichard Zhao struct resource *res, int nres, 7668e22978cSAlexander Shishkin struct ci_hdrc_platform_data *platdata) 767cbc6dc2aSRichard Zhao { 768cbc6dc2aSRichard Zhao struct platform_device *pdev; 769fe6e125eSRichard Zhao int id, ret; 770cbc6dc2aSRichard Zhao 7711542d9c3SPeter Chen ret = ci_get_platdata(dev, platdata); 7721542d9c3SPeter Chen if (ret) 7731542d9c3SPeter Chen return ERR_PTR(ret); 7741542d9c3SPeter Chen 775fe6e125eSRichard Zhao id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); 776fe6e125eSRichard Zhao if (id < 0) 777fe6e125eSRichard Zhao return ERR_PTR(id); 778fe6e125eSRichard Zhao 779fe6e125eSRichard Zhao pdev = platform_device_alloc("ci_hdrc", id); 780fe6e125eSRichard Zhao if (!pdev) { 781fe6e125eSRichard Zhao ret = -ENOMEM; 782fe6e125eSRichard Zhao goto put_id; 783fe6e125eSRichard Zhao } 784cbc6dc2aSRichard Zhao 785cbc6dc2aSRichard Zhao pdev->dev.parent = dev; 786cbc6dc2aSRichard Zhao 787cbc6dc2aSRichard Zhao ret = platform_device_add_resources(pdev, res, nres); 788cbc6dc2aSRichard Zhao if (ret) 789cbc6dc2aSRichard Zhao goto err; 790cbc6dc2aSRichard Zhao 791cbc6dc2aSRichard Zhao ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); 792cbc6dc2aSRichard Zhao if (ret) 793cbc6dc2aSRichard Zhao goto err; 794cbc6dc2aSRichard Zhao 795cbc6dc2aSRichard Zhao ret = platform_device_add(pdev); 796cbc6dc2aSRichard Zhao if (ret) 797cbc6dc2aSRichard Zhao goto err; 798cbc6dc2aSRichard Zhao 799cbc6dc2aSRichard Zhao return pdev; 800cbc6dc2aSRichard Zhao 801cbc6dc2aSRichard Zhao err: 802cbc6dc2aSRichard Zhao platform_device_put(pdev); 803fe6e125eSRichard Zhao put_id: 804fe6e125eSRichard Zhao ida_simple_remove(&ci_ida, id); 805cbc6dc2aSRichard Zhao return ERR_PTR(ret); 806cbc6dc2aSRichard Zhao } 8078e22978cSAlexander Shishkin EXPORT_SYMBOL_GPL(ci_hdrc_add_device); 808cbc6dc2aSRichard Zhao 8098e22978cSAlexander Shishkin void ci_hdrc_remove_device(struct platform_device *pdev) 810cbc6dc2aSRichard Zhao { 81198c35534SLothar Waßmann int id = pdev->id; 812cbc6dc2aSRichard Zhao platform_device_unregister(pdev); 81398c35534SLothar Waßmann ida_simple_remove(&ci_ida, id); 814cbc6dc2aSRichard Zhao } 8158e22978cSAlexander Shishkin EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); 816cbc6dc2aSRichard Zhao 8173f124d23SPeter Chen static inline void ci_role_destroy(struct ci_hdrc *ci) 8183f124d23SPeter Chen { 8193f124d23SPeter Chen ci_hdrc_gadget_destroy(ci); 8203f124d23SPeter Chen ci_hdrc_host_destroy(ci); 821cbec6bd5SPeter Chen if (ci->is_otg) 822cbec6bd5SPeter Chen ci_hdrc_otg_destroy(ci); 8233f124d23SPeter Chen } 8243f124d23SPeter Chen 825577b232fSPeter Chen static void ci_get_otg_capable(struct ci_hdrc *ci) 826577b232fSPeter Chen { 827577b232fSPeter Chen if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) 828577b232fSPeter Chen ci->is_otg = false; 829577b232fSPeter Chen else 830577b232fSPeter Chen ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, 831577b232fSPeter Chen DCCPARAMS_DC | DCCPARAMS_HC) 832577b232fSPeter Chen == (DCCPARAMS_DC | DCCPARAMS_HC)); 8332e37cfd8SPeter Chen if (ci->is_otg) { 834577b232fSPeter Chen dev_dbg(ci->dev, "It is OTG capable controller\n"); 8352e37cfd8SPeter Chen /* Disable and clear all OTG irq */ 8362e37cfd8SPeter Chen hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, 8372e37cfd8SPeter Chen OTGSC_INT_STATUS_BITS); 8382e37cfd8SPeter Chen } 839577b232fSPeter Chen } 840577b232fSPeter Chen 841a932a804SPeter Chen static ssize_t ci_role_show(struct device *dev, struct device_attribute *attr, 842a932a804SPeter Chen char *buf) 843a932a804SPeter Chen { 844a932a804SPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 845a932a804SPeter Chen 846cbb22ebcSMichael Thalmeier if (ci->role != CI_ROLE_END) 847a932a804SPeter Chen return sprintf(buf, "%s\n", ci_role(ci)->name); 848cbb22ebcSMichael Thalmeier 849cbb22ebcSMichael Thalmeier return 0; 850a932a804SPeter Chen } 851a932a804SPeter Chen 852a932a804SPeter Chen static ssize_t ci_role_store(struct device *dev, 853a932a804SPeter Chen struct device_attribute *attr, const char *buf, size_t n) 854a932a804SPeter Chen { 855a932a804SPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 856a932a804SPeter Chen enum ci_role role; 857a932a804SPeter Chen int ret; 858a932a804SPeter Chen 859a932a804SPeter Chen if (!(ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])) { 860a932a804SPeter Chen dev_warn(dev, "Current configuration is not dual-role, quit\n"); 861a932a804SPeter Chen return -EPERM; 862a932a804SPeter Chen } 863a932a804SPeter Chen 864a932a804SPeter Chen for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++) 865a932a804SPeter Chen if (!strncmp(buf, ci->roles[role]->name, 866a932a804SPeter Chen strlen(ci->roles[role]->name))) 867a932a804SPeter Chen break; 868a932a804SPeter Chen 869a932a804SPeter Chen if (role == CI_ROLE_END || role == ci->role) 870a932a804SPeter Chen return -EINVAL; 871a932a804SPeter Chen 872a932a804SPeter Chen pm_runtime_get_sync(dev); 873a932a804SPeter Chen disable_irq(ci->irq); 874a932a804SPeter Chen ci_role_stop(ci); 875a932a804SPeter Chen ret = ci_role_start(ci, role); 876a932a804SPeter Chen if (!ret && ci->role == CI_ROLE_GADGET) 877a932a804SPeter Chen ci_handle_vbus_change(ci); 878a932a804SPeter Chen enable_irq(ci->irq); 879a932a804SPeter Chen pm_runtime_put_sync(dev); 880a932a804SPeter Chen 881a932a804SPeter Chen return (ret == 0) ? n : ret; 882a932a804SPeter Chen } 883a932a804SPeter Chen static DEVICE_ATTR(role, 0644, ci_role_show, ci_role_store); 884a932a804SPeter Chen 885a932a804SPeter Chen static struct attribute *ci_attrs[] = { 886a932a804SPeter Chen &dev_attr_role.attr, 887a932a804SPeter Chen NULL, 888a932a804SPeter Chen }; 889a932a804SPeter Chen 890a932a804SPeter Chen static struct attribute_group ci_attr_group = { 891a932a804SPeter Chen .attrs = ci_attrs, 892a932a804SPeter Chen }; 893a932a804SPeter Chen 89441ac7b3aSBill Pemberton static int ci_hdrc_probe(struct platform_device *pdev) 895e443b333SAlexander Shishkin { 896e443b333SAlexander Shishkin struct device *dev = &pdev->dev; 8978e22978cSAlexander Shishkin struct ci_hdrc *ci; 898e443b333SAlexander Shishkin struct resource *res; 899e443b333SAlexander Shishkin void __iomem *base; 900e443b333SAlexander Shishkin int ret; 901691962d1SSascha Hauer enum usb_dr_mode dr_mode; 902e443b333SAlexander Shishkin 903fad56745SJingoo Han if (!dev_get_platdata(dev)) { 904e443b333SAlexander Shishkin dev_err(dev, "platform data missing\n"); 905e443b333SAlexander Shishkin return -ENODEV; 906e443b333SAlexander Shishkin } 907e443b333SAlexander Shishkin 908e443b333SAlexander Shishkin res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 90919290816SFelipe Balbi base = devm_ioremap_resource(dev, res); 91019290816SFelipe Balbi if (IS_ERR(base)) 91119290816SFelipe Balbi return PTR_ERR(base); 912e443b333SAlexander Shishkin 9135f36e231SAlexander Shishkin ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL); 914d0f99249SFabio Estevam if (!ci) 9155f36e231SAlexander Shishkin return -ENOMEM; 916e443b333SAlexander Shishkin 917a5d906bbSPeter Chen spin_lock_init(&ci->lock); 9185f36e231SAlexander Shishkin ci->dev = dev; 919fad56745SJingoo Han ci->platdata = dev_get_platdata(dev); 920ed8f8318SPeter Chen ci->imx28_write_fix = !!(ci->platdata->flags & 921ed8f8318SPeter Chen CI_HDRC_IMX28_WRITE_FIX); 9221f874edcSPeter Chen ci->supports_runtime_pm = !!(ci->platdata->flags & 9231f874edcSPeter Chen CI_HDRC_SUPPORTS_RUNTIME_PM); 9247bb7e9b1SStephen Boyd platform_set_drvdata(pdev, ci); 9255f36e231SAlexander Shishkin 9265f36e231SAlexander Shishkin ret = hw_device_init(ci, base); 9275f36e231SAlexander Shishkin if (ret < 0) { 9285f36e231SAlexander Shishkin dev_err(dev, "can't initialize hardware\n"); 9295f36e231SAlexander Shishkin return -ENODEV; 9305f36e231SAlexander Shishkin } 9315f36e231SAlexander Shishkin 9327bb7e9b1SStephen Boyd ret = ci_ulpi_init(ci); 9337bb7e9b1SStephen Boyd if (ret) 9347bb7e9b1SStephen Boyd return ret; 9357bb7e9b1SStephen Boyd 9361e5e2d3dSAntoine Tenart if (ci->platdata->phy) { 9371e5e2d3dSAntoine Tenart ci->phy = ci->platdata->phy; 9381e5e2d3dSAntoine Tenart } else if (ci->platdata->usb_phy) { 939ef44cb42SAntoine Tenart ci->usb_phy = ci->platdata->usb_phy; 9401e5e2d3dSAntoine Tenart } else { 94121a5b579SAntoine Tenart ci->phy = devm_phy_get(dev->parent, "usb-phy"); 94221a5b579SAntoine Tenart ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2); 943c859aa65SPeter Chen 9441e5e2d3dSAntoine Tenart /* if both generic PHY and USB PHY layers aren't enabled */ 9451e5e2d3dSAntoine Tenart if (PTR_ERR(ci->phy) == -ENOSYS && 9467bb7e9b1SStephen Boyd PTR_ERR(ci->usb_phy) == -ENXIO) { 9477bb7e9b1SStephen Boyd ret = -ENXIO; 9487bb7e9b1SStephen Boyd goto ulpi_exit; 9497bb7e9b1SStephen Boyd } 950c859aa65SPeter Chen 9517bb7e9b1SStephen Boyd if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) { 9527bb7e9b1SStephen Boyd ret = -EPROBE_DEFER; 9537bb7e9b1SStephen Boyd goto ulpi_exit; 9547bb7e9b1SStephen Boyd } 9551e5e2d3dSAntoine Tenart 9561e5e2d3dSAntoine Tenart if (IS_ERR(ci->phy)) 9571e5e2d3dSAntoine Tenart ci->phy = NULL; 9581e5e2d3dSAntoine Tenart else if (IS_ERR(ci->usb_phy)) 9591e5e2d3dSAntoine Tenart ci->usb_phy = NULL; 960c859aa65SPeter Chen } 961c859aa65SPeter Chen 962d03cccffSPeter Chen ret = ci_usb_phy_init(ci); 96374475edeSPeter Chen if (ret) { 96474475edeSPeter Chen dev_err(dev, "unable to init phy: %d\n", ret); 96574475edeSPeter Chen return ret; 96674475edeSPeter Chen } 96774475edeSPeter Chen 968eb70e5abSAlexander Shishkin ci->hw_bank.phys = res->start; 969eb70e5abSAlexander Shishkin 9705f36e231SAlexander Shishkin ci->irq = platform_get_irq(pdev, 0); 9715f36e231SAlexander Shishkin if (ci->irq < 0) { 972e443b333SAlexander Shishkin dev_err(dev, "missing IRQ\n"); 97342d18212SFabio Estevam ret = ci->irq; 974c859aa65SPeter Chen goto deinit_phy; 975e443b333SAlexander Shishkin } 976e443b333SAlexander Shishkin 977577b232fSPeter Chen ci_get_otg_capable(ci); 978577b232fSPeter Chen 979691962d1SSascha Hauer dr_mode = ci->platdata->dr_mode; 9805f36e231SAlexander Shishkin /* initialize role(s) before the interrupt is requested */ 981691962d1SSascha Hauer if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { 982eb70e5abSAlexander Shishkin ret = ci_hdrc_host_init(ci); 983eb70e5abSAlexander Shishkin if (ret) 984eb70e5abSAlexander Shishkin dev_info(dev, "doesn't support host\n"); 985691962d1SSascha Hauer } 986eb70e5abSAlexander Shishkin 987691962d1SSascha Hauer if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { 9885f36e231SAlexander Shishkin ret = ci_hdrc_gadget_init(ci); 989e443b333SAlexander Shishkin if (ret) 9905f36e231SAlexander Shishkin dev_info(dev, "doesn't support gadget\n"); 991691962d1SSascha Hauer } 9925f36e231SAlexander Shishkin 9935f36e231SAlexander Shishkin if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { 9945f36e231SAlexander Shishkin dev_err(dev, "no supported roles\n"); 99574475edeSPeter Chen ret = -ENODEV; 996c859aa65SPeter Chen goto deinit_phy; 997cbec6bd5SPeter Chen } 998cbec6bd5SPeter Chen 99927c62c2dSPeter Chen if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) { 1000cbec6bd5SPeter Chen ret = ci_hdrc_otg_init(ci); 1001cbec6bd5SPeter Chen if (ret) { 1002cbec6bd5SPeter Chen dev_err(dev, "init otg fails, ret = %d\n", ret); 1003cbec6bd5SPeter Chen goto stop; 1004cbec6bd5SPeter Chen } 10055f36e231SAlexander Shishkin } 10065f36e231SAlexander Shishkin 10075f36e231SAlexander Shishkin if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { 1008577b232fSPeter Chen if (ci->is_otg) { 10095f36e231SAlexander Shishkin ci->role = ci_otg_role(ci); 10100c33bf78SLi Jun /* Enable ID change irq */ 10110c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); 1012577b232fSPeter Chen } else { 1013577b232fSPeter Chen /* 1014577b232fSPeter Chen * If the controller is not OTG capable, but support 1015577b232fSPeter Chen * role switch, the defalt role is gadget, and the 1016577b232fSPeter Chen * user can switch it through debugfs. 1017577b232fSPeter Chen */ 1018577b232fSPeter Chen ci->role = CI_ROLE_GADGET; 1019577b232fSPeter Chen } 10205f36e231SAlexander Shishkin } else { 10215f36e231SAlexander Shishkin ci->role = ci->roles[CI_ROLE_HOST] 10225f36e231SAlexander Shishkin ? CI_ROLE_HOST 10235f36e231SAlexander Shishkin : CI_ROLE_GADGET; 10245f36e231SAlexander Shishkin } 10255f36e231SAlexander Shishkin 1026961ea496SLi Jun if (!ci_otg_is_fsm_mode(ci)) { 10275a1e1456SPeter Chen /* only update vbus status for peripheral */ 10285a1e1456SPeter Chen if (ci->role == CI_ROLE_GADGET) 10295a1e1456SPeter Chen ci_handle_vbus_change(ci); 10305a1e1456SPeter Chen 10315f36e231SAlexander Shishkin ret = ci_role_start(ci, ci->role); 10325f36e231SAlexander Shishkin if (ret) { 10334dcf720cSLi Jun dev_err(dev, "can't start %s role\n", 10344dcf720cSLi Jun ci_role(ci)->name); 1035cbec6bd5SPeter Chen goto stop; 10365f36e231SAlexander Shishkin } 10374dcf720cSLi Jun } 10385f36e231SAlexander Shishkin 10394c503dd5SPeter Chen ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED, 10404c503dd5SPeter Chen ci->platdata->name, ci); 10415f36e231SAlexander Shishkin if (ret) 10425f36e231SAlexander Shishkin goto stop; 10435f36e231SAlexander Shishkin 10443ecb3e09SIvan T. Ivanov ret = ci_extcon_register(ci); 10453ecb3e09SIvan T. Ivanov if (ret) 10463ecb3e09SIvan T. Ivanov goto stop; 10473ecb3e09SIvan T. Ivanov 10481f874edcSPeter Chen if (ci->supports_runtime_pm) { 10491f874edcSPeter Chen pm_runtime_set_active(&pdev->dev); 10501f874edcSPeter Chen pm_runtime_enable(&pdev->dev); 10511f874edcSPeter Chen pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); 10521f874edcSPeter Chen pm_runtime_mark_last_busy(ci->dev); 10531f874edcSPeter Chen pm_runtime_use_autosuspend(&pdev->dev); 10541f874edcSPeter Chen } 10551f874edcSPeter Chen 10564dcf720cSLi Jun if (ci_otg_is_fsm_mode(ci)) 10574dcf720cSLi Jun ci_hdrc_otg_fsm_start(ci); 10584dcf720cSLi Jun 1059f8efa766SPeter Chen device_set_wakeup_capable(&pdev->dev, true); 1060adf0f735SAlexander Shishkin ret = dbg_create_files(ci); 1061a932a804SPeter Chen if (ret) 1062a932a804SPeter Chen goto stop; 1063a932a804SPeter Chen 1064a932a804SPeter Chen ret = sysfs_create_group(&dev->kobj, &ci_attr_group); 1065a932a804SPeter Chen if (ret) 1066a932a804SPeter Chen goto remove_debug; 1067a932a804SPeter Chen 1068adf0f735SAlexander Shishkin return 0; 10695f36e231SAlexander Shishkin 1070a932a804SPeter Chen remove_debug: 1071a932a804SPeter Chen dbg_remove_files(ci); 10725f36e231SAlexander Shishkin stop: 10733f124d23SPeter Chen ci_role_destroy(ci); 1074c859aa65SPeter Chen deinit_phy: 10751e5e2d3dSAntoine Tenart ci_usb_phy_exit(ci); 10767bb7e9b1SStephen Boyd ulpi_exit: 10777bb7e9b1SStephen Boyd ci_ulpi_exit(ci); 1078e443b333SAlexander Shishkin 1079e443b333SAlexander Shishkin return ret; 1080e443b333SAlexander Shishkin } 1081e443b333SAlexander Shishkin 1082fb4e98abSBill Pemberton static int ci_hdrc_remove(struct platform_device *pdev) 1083e443b333SAlexander Shishkin { 10848e22978cSAlexander Shishkin struct ci_hdrc *ci = platform_get_drvdata(pdev); 1085e443b333SAlexander Shishkin 10861f874edcSPeter Chen if (ci->supports_runtime_pm) { 10871f874edcSPeter Chen pm_runtime_get_sync(&pdev->dev); 10881f874edcSPeter Chen pm_runtime_disable(&pdev->dev); 10891f874edcSPeter Chen pm_runtime_put_noidle(&pdev->dev); 10901f874edcSPeter Chen } 10911f874edcSPeter Chen 1092adf0f735SAlexander Shishkin dbg_remove_files(ci); 1093a932a804SPeter Chen sysfs_remove_group(&ci->dev->kobj, &ci_attr_group); 10943f124d23SPeter Chen ci_role_destroy(ci); 1095864cf949SPeter Chen ci_hdrc_enter_lpm(ci, true); 10961e5e2d3dSAntoine Tenart ci_usb_phy_exit(ci); 10977bb7e9b1SStephen Boyd ci_ulpi_exit(ci); 1098e443b333SAlexander Shishkin 1099e443b333SAlexander Shishkin return 0; 1100e443b333SAlexander Shishkin } 1101e443b333SAlexander Shishkin 11021f874edcSPeter Chen #ifdef CONFIG_PM 1103961ea496SLi Jun /* Prepare wakeup by SRP before suspend */ 1104961ea496SLi Jun static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci) 1105961ea496SLi Jun { 1106961ea496SLi Jun if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && 1107961ea496SLi Jun !hw_read_otgsc(ci, OTGSC_ID)) { 1108961ea496SLi Jun hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 1109961ea496SLi Jun PORTSC_PP); 1110961ea496SLi Jun hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN, 1111961ea496SLi Jun PORTSC_WKCN); 1112961ea496SLi Jun } 1113961ea496SLi Jun } 1114961ea496SLi Jun 1115961ea496SLi Jun /* Handle SRP when wakeup by data pulse */ 1116961ea496SLi Jun static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci) 1117961ea496SLi Jun { 1118961ea496SLi Jun if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && 1119961ea496SLi Jun (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) { 1120961ea496SLi Jun if (!hw_read_otgsc(ci, OTGSC_ID)) { 1121961ea496SLi Jun ci->fsm.a_srp_det = 1; 1122961ea496SLi Jun ci->fsm.a_bus_drop = 0; 1123961ea496SLi Jun } else { 1124961ea496SLi Jun ci->fsm.id = 1; 1125961ea496SLi Jun } 1126961ea496SLi Jun ci_otg_queue_work(ci); 1127961ea496SLi Jun } 1128961ea496SLi Jun } 1129961ea496SLi Jun 11308076932fSPeter Chen static void ci_controller_suspend(struct ci_hdrc *ci) 11318076932fSPeter Chen { 11321f874edcSPeter Chen disable_irq(ci->irq); 11338076932fSPeter Chen ci_hdrc_enter_lpm(ci, true); 11341fbf4628SFabio Estevam if (ci->platdata->phy_clkgate_delay_us) 11351fbf4628SFabio Estevam usleep_range(ci->platdata->phy_clkgate_delay_us, 11361fbf4628SFabio Estevam ci->platdata->phy_clkgate_delay_us + 50); 11378076932fSPeter Chen usb_phy_set_suspend(ci->usb_phy, 1); 11381f874edcSPeter Chen ci->in_lpm = true; 11391f874edcSPeter Chen enable_irq(ci->irq); 11408076932fSPeter Chen } 11418076932fSPeter Chen 11428076932fSPeter Chen static int ci_controller_resume(struct device *dev) 11438076932fSPeter Chen { 11448076932fSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 11457bb7e9b1SStephen Boyd int ret; 11468076932fSPeter Chen 11478076932fSPeter Chen dev_dbg(dev, "at %s\n", __func__); 11488076932fSPeter Chen 11491f874edcSPeter Chen if (!ci->in_lpm) { 11501f874edcSPeter Chen WARN_ON(1); 11511f874edcSPeter Chen return 0; 11521f874edcSPeter Chen } 11538076932fSPeter Chen 11541f874edcSPeter Chen ci_hdrc_enter_lpm(ci, false); 11557bb7e9b1SStephen Boyd 11567bb7e9b1SStephen Boyd ret = ci_ulpi_resume(ci); 11577bb7e9b1SStephen Boyd if (ret) 11587bb7e9b1SStephen Boyd return ret; 11597bb7e9b1SStephen Boyd 11608076932fSPeter Chen if (ci->usb_phy) { 11618076932fSPeter Chen usb_phy_set_suspend(ci->usb_phy, 0); 11628076932fSPeter Chen usb_phy_set_wakeup(ci->usb_phy, false); 11638076932fSPeter Chen hw_wait_phy_stable(); 11648076932fSPeter Chen } 11658076932fSPeter Chen 11661f874edcSPeter Chen ci->in_lpm = false; 11671f874edcSPeter Chen if (ci->wakeup_int) { 11681f874edcSPeter Chen ci->wakeup_int = false; 11691f874edcSPeter Chen pm_runtime_mark_last_busy(ci->dev); 11701f874edcSPeter Chen pm_runtime_put_autosuspend(ci->dev); 11711f874edcSPeter Chen enable_irq(ci->irq); 1172961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1173961ea496SLi Jun ci_otg_fsm_wakeup_by_srp(ci); 11741f874edcSPeter Chen } 11751f874edcSPeter Chen 11768076932fSPeter Chen return 0; 11778076932fSPeter Chen } 11788076932fSPeter Chen 11791f874edcSPeter Chen #ifdef CONFIG_PM_SLEEP 11808076932fSPeter Chen static int ci_suspend(struct device *dev) 11818076932fSPeter Chen { 11828076932fSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 11838076932fSPeter Chen 11848076932fSPeter Chen if (ci->wq) 11858076932fSPeter Chen flush_workqueue(ci->wq); 11861f874edcSPeter Chen /* 11871f874edcSPeter Chen * Controller needs to be active during suspend, otherwise the core 11881f874edcSPeter Chen * may run resume when the parent is at suspend if other driver's 11891f874edcSPeter Chen * suspend fails, it occurs before parent's suspend has not started, 11901f874edcSPeter Chen * but the core suspend has finished. 11911f874edcSPeter Chen */ 11921f874edcSPeter Chen if (ci->in_lpm) 11931f874edcSPeter Chen pm_runtime_resume(dev); 11941f874edcSPeter Chen 11951f874edcSPeter Chen if (ci->in_lpm) { 11961f874edcSPeter Chen WARN_ON(1); 11971f874edcSPeter Chen return 0; 11981f874edcSPeter Chen } 11998076932fSPeter Chen 1200f8efa766SPeter Chen if (device_may_wakeup(dev)) { 1201961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1202961ea496SLi Jun ci_otg_fsm_suspend_for_srp(ci); 1203961ea496SLi Jun 1204f8efa766SPeter Chen usb_phy_set_wakeup(ci->usb_phy, true); 1205f8efa766SPeter Chen enable_irq_wake(ci->irq); 1206f8efa766SPeter Chen } 1207f8efa766SPeter Chen 12088076932fSPeter Chen ci_controller_suspend(ci); 12098076932fSPeter Chen 12108076932fSPeter Chen return 0; 12118076932fSPeter Chen } 12128076932fSPeter Chen 12138076932fSPeter Chen static int ci_resume(struct device *dev) 12148076932fSPeter Chen { 12151f874edcSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 12161f874edcSPeter Chen int ret; 12171f874edcSPeter Chen 1218f8efa766SPeter Chen if (device_may_wakeup(dev)) 1219f8efa766SPeter Chen disable_irq_wake(ci->irq); 1220f8efa766SPeter Chen 12211f874edcSPeter Chen ret = ci_controller_resume(dev); 12221f874edcSPeter Chen if (ret) 12231f874edcSPeter Chen return ret; 12241f874edcSPeter Chen 12251f874edcSPeter Chen if (ci->supports_runtime_pm) { 12261f874edcSPeter Chen pm_runtime_disable(dev); 12271f874edcSPeter Chen pm_runtime_set_active(dev); 12281f874edcSPeter Chen pm_runtime_enable(dev); 12291f874edcSPeter Chen } 12301f874edcSPeter Chen 12311f874edcSPeter Chen return ret; 12328076932fSPeter Chen } 12338076932fSPeter Chen #endif /* CONFIG_PM_SLEEP */ 12348076932fSPeter Chen 12351f874edcSPeter Chen static int ci_runtime_suspend(struct device *dev) 12361f874edcSPeter Chen { 12371f874edcSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 12381f874edcSPeter Chen 12391f874edcSPeter Chen dev_dbg(dev, "at %s\n", __func__); 12401f874edcSPeter Chen 12411f874edcSPeter Chen if (ci->in_lpm) { 12421f874edcSPeter Chen WARN_ON(1); 12431f874edcSPeter Chen return 0; 12441f874edcSPeter Chen } 12451f874edcSPeter Chen 1246961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1247961ea496SLi Jun ci_otg_fsm_suspend_for_srp(ci); 1248961ea496SLi Jun 12491f874edcSPeter Chen usb_phy_set_wakeup(ci->usb_phy, true); 12501f874edcSPeter Chen ci_controller_suspend(ci); 12511f874edcSPeter Chen 12521f874edcSPeter Chen return 0; 12531f874edcSPeter Chen } 12541f874edcSPeter Chen 12551f874edcSPeter Chen static int ci_runtime_resume(struct device *dev) 12561f874edcSPeter Chen { 12571f874edcSPeter Chen return ci_controller_resume(dev); 12581f874edcSPeter Chen } 12591f874edcSPeter Chen 12601f874edcSPeter Chen #endif /* CONFIG_PM */ 12618076932fSPeter Chen static const struct dev_pm_ops ci_pm_ops = { 12628076932fSPeter Chen SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume) 12631f874edcSPeter Chen SET_RUNTIME_PM_OPS(ci_runtime_suspend, ci_runtime_resume, NULL) 12648076932fSPeter Chen }; 12651f874edcSPeter Chen 12665f36e231SAlexander Shishkin static struct platform_driver ci_hdrc_driver = { 12675f36e231SAlexander Shishkin .probe = ci_hdrc_probe, 12687690417dSBill Pemberton .remove = ci_hdrc_remove, 1269e443b333SAlexander Shishkin .driver = { 12705f36e231SAlexander Shishkin .name = "ci_hdrc", 12718076932fSPeter Chen .pm = &ci_pm_ops, 1272e443b333SAlexander Shishkin }, 1273e443b333SAlexander Shishkin }; 1274e443b333SAlexander Shishkin 12752f01a33bSPeter Chen static int __init ci_hdrc_platform_register(void) 12762f01a33bSPeter Chen { 12772f01a33bSPeter Chen ci_hdrc_host_driver_init(); 12782f01a33bSPeter Chen return platform_driver_register(&ci_hdrc_driver); 12792f01a33bSPeter Chen } 12802f01a33bSPeter Chen module_init(ci_hdrc_platform_register); 12812f01a33bSPeter Chen 12822f01a33bSPeter Chen static void __exit ci_hdrc_platform_unregister(void) 12832f01a33bSPeter Chen { 12842f01a33bSPeter Chen platform_driver_unregister(&ci_hdrc_driver); 12852f01a33bSPeter Chen } 12862f01a33bSPeter Chen module_exit(ci_hdrc_platform_unregister); 1287e443b333SAlexander Shishkin 12885f36e231SAlexander Shishkin MODULE_ALIAS("platform:ci_hdrc"); 1289e443b333SAlexander Shishkin MODULE_LICENSE("GPL v2"); 1290e443b333SAlexander Shishkin MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>"); 12915f36e231SAlexander Shishkin MODULE_DESCRIPTION("ChipIdea HDRC Driver"); 1292