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 2658ce8499SPeter Chen * - CONFIG_USB_CHIPIDEA_DEBUG: enable debug facilities 27e443b333SAlexander Shishkin * - STALL_IN: non-empty bulk-in pipes cannot be halted 28e443b333SAlexander Shishkin * if defined mass storage compliance succeeds but with warnings 29e443b333SAlexander Shishkin * => case 4: Hi > Dn 30e443b333SAlexander Shishkin * => case 5: Hi > Di 31e443b333SAlexander Shishkin * => case 8: Hi <> Do 32e443b333SAlexander Shishkin * if undefined usbtest 13 fails 33e443b333SAlexander Shishkin * - TRACE: enable function tracing (depends on DEBUG) 34e443b333SAlexander Shishkin * 35e443b333SAlexander Shishkin * Main Features 36e443b333SAlexander Shishkin * - Chapter 9 & Mass Storage Compliance with Gadget File Storage 37e443b333SAlexander Shishkin * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) 38e443b333SAlexander Shishkin * - Normal & LPM support 39e443b333SAlexander Shishkin * 40e443b333SAlexander Shishkin * USBTEST Report 41e443b333SAlexander Shishkin * - OK: 0-12, 13 (STALL_IN defined) & 14 42e443b333SAlexander Shishkin * - Not Supported: 15 & 16 (ISO) 43e443b333SAlexander Shishkin * 44e443b333SAlexander Shishkin * TODO List 45e443b333SAlexander Shishkin * - Suspend & Remote Wakeup 46e443b333SAlexander Shishkin */ 47e443b333SAlexander Shishkin #include <linux/delay.h> 48e443b333SAlexander Shishkin #include <linux/device.h> 49e443b333SAlexander Shishkin #include <linux/dma-mapping.h> 50e443b333SAlexander Shishkin #include <linux/platform_device.h> 51e443b333SAlexander Shishkin #include <linux/module.h> 52fe6e125eSRichard Zhao #include <linux/idr.h> 53e443b333SAlexander Shishkin #include <linux/interrupt.h> 54e443b333SAlexander Shishkin #include <linux/io.h> 55e443b333SAlexander Shishkin #include <linux/kernel.h> 56e443b333SAlexander Shishkin #include <linux/slab.h> 57e443b333SAlexander Shishkin #include <linux/pm_runtime.h> 58e443b333SAlexander Shishkin #include <linux/usb/ch9.h> 59e443b333SAlexander Shishkin #include <linux/usb/gadget.h> 60e443b333SAlexander Shishkin #include <linux/usb/otg.h> 61e443b333SAlexander Shishkin #include <linux/usb/chipidea.h> 6240dcd0e8SMichael Grzeschik #include <linux/usb/of.h> 634f6743d5SMichael Grzeschik #include <linux/of.h> 6440dcd0e8SMichael Grzeschik #include <linux/phy.h> 651542d9c3SPeter Chen #include <linux/regulator/consumer.h> 66e443b333SAlexander Shishkin 67e443b333SAlexander Shishkin #include "ci.h" 68e443b333SAlexander Shishkin #include "udc.h" 69e443b333SAlexander Shishkin #include "bits.h" 70eb70e5abSAlexander Shishkin #include "host.h" 71e443b333SAlexander Shishkin #include "debug.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, 86987e7bc3SMarc Kleine-Budde [OP_PORTSC] = 0x44U, 87987e7bc3SMarc Kleine-Budde [OP_DEVLC] = 0x84U, 88987e7bc3SMarc Kleine-Budde [OP_OTGSC] = 0x64U, 89987e7bc3SMarc Kleine-Budde [OP_USBMODE] = 0x68U, 90987e7bc3SMarc Kleine-Budde [OP_ENDPTSETUPSTAT] = 0x6CU, 91987e7bc3SMarc Kleine-Budde [OP_ENDPTPRIME] = 0x70U, 92987e7bc3SMarc Kleine-Budde [OP_ENDPTFLUSH] = 0x74U, 93987e7bc3SMarc Kleine-Budde [OP_ENDPTSTAT] = 0x78U, 94987e7bc3SMarc Kleine-Budde [OP_ENDPTCOMPLETE] = 0x7CU, 95987e7bc3SMarc Kleine-Budde [OP_ENDPTCTRL] = 0x80U, 96e443b333SAlexander Shishkin }; 97e443b333SAlexander Shishkin 98987e7bc3SMarc Kleine-Budde static const u8 ci_regs_lpm[] = { 99987e7bc3SMarc Kleine-Budde [CAP_CAPLENGTH] = 0x00U, 100987e7bc3SMarc Kleine-Budde [CAP_HCCPARAMS] = 0x08U, 101987e7bc3SMarc Kleine-Budde [CAP_DCCPARAMS] = 0x24U, 102987e7bc3SMarc Kleine-Budde [CAP_TESTMODE] = 0xFCU, 103987e7bc3SMarc Kleine-Budde [OP_USBCMD] = 0x00U, 104987e7bc3SMarc Kleine-Budde [OP_USBSTS] = 0x04U, 105987e7bc3SMarc Kleine-Budde [OP_USBINTR] = 0x08U, 106987e7bc3SMarc Kleine-Budde [OP_DEVICEADDR] = 0x14U, 107987e7bc3SMarc Kleine-Budde [OP_ENDPTLISTADDR] = 0x18U, 108987e7bc3SMarc Kleine-Budde [OP_PORTSC] = 0x44U, 109987e7bc3SMarc Kleine-Budde [OP_DEVLC] = 0x84U, 110987e7bc3SMarc Kleine-Budde [OP_OTGSC] = 0xC4U, 111987e7bc3SMarc Kleine-Budde [OP_USBMODE] = 0xC8U, 112987e7bc3SMarc Kleine-Budde [OP_ENDPTSETUPSTAT] = 0xD8U, 113987e7bc3SMarc Kleine-Budde [OP_ENDPTPRIME] = 0xDCU, 114987e7bc3SMarc Kleine-Budde [OP_ENDPTFLUSH] = 0xE0U, 115987e7bc3SMarc Kleine-Budde [OP_ENDPTSTAT] = 0xE4U, 116987e7bc3SMarc Kleine-Budde [OP_ENDPTCOMPLETE] = 0xE8U, 117987e7bc3SMarc Kleine-Budde [OP_ENDPTCTRL] = 0xECU, 118e443b333SAlexander Shishkin }; 119e443b333SAlexander Shishkin 1208e22978cSAlexander Shishkin static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) 121e443b333SAlexander Shishkin { 122e443b333SAlexander Shishkin int i; 123e443b333SAlexander Shishkin 124e443b333SAlexander Shishkin for (i = 0; i < OP_ENDPTCTRL; i++) 1255f36e231SAlexander Shishkin ci->hw_bank.regmap[i] = 1265f36e231SAlexander Shishkin (i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) + 127e443b333SAlexander Shishkin (is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]); 128e443b333SAlexander Shishkin 129e443b333SAlexander Shishkin for (; i <= OP_LAST; i++) 1305f36e231SAlexander Shishkin ci->hw_bank.regmap[i] = ci->hw_bank.op + 131e443b333SAlexander Shishkin 4 * (i - OP_ENDPTCTRL) + 132e443b333SAlexander Shishkin (is_lpm 133e443b333SAlexander Shishkin ? ci_regs_lpm[OP_ENDPTCTRL] 134e443b333SAlexander Shishkin : ci_regs_nolpm[OP_ENDPTCTRL]); 135e443b333SAlexander Shishkin 136e443b333SAlexander Shishkin return 0; 137e443b333SAlexander Shishkin } 138e443b333SAlexander Shishkin 139e443b333SAlexander Shishkin /** 14036304b06SLi Jun * hw_read_intr_enable: returns interrupt enable register 14136304b06SLi Jun * 14236304b06SLi Jun * This function returns register data 14336304b06SLi Jun */ 14436304b06SLi Jun u32 hw_read_intr_enable(struct ci_hdrc *ci) 14536304b06SLi Jun { 14636304b06SLi Jun return hw_read(ci, OP_USBINTR, ~0); 14736304b06SLi Jun } 14836304b06SLi Jun 14936304b06SLi Jun /** 15036304b06SLi Jun * hw_read_intr_status: returns interrupt status register 15136304b06SLi Jun * 15236304b06SLi Jun * This function returns register data 15336304b06SLi Jun */ 15436304b06SLi Jun u32 hw_read_intr_status(struct ci_hdrc *ci) 15536304b06SLi Jun { 15636304b06SLi Jun return hw_read(ci, OP_USBSTS, ~0); 15736304b06SLi Jun } 15836304b06SLi Jun 15936304b06SLi Jun /** 160e443b333SAlexander Shishkin * hw_port_test_set: writes port test mode (execute without interruption) 161e443b333SAlexander Shishkin * @mode: new value 162e443b333SAlexander Shishkin * 163e443b333SAlexander Shishkin * This function returns an error code 164e443b333SAlexander Shishkin */ 1658e22978cSAlexander Shishkin int hw_port_test_set(struct ci_hdrc *ci, u8 mode) 166e443b333SAlexander Shishkin { 167e443b333SAlexander Shishkin const u8 TEST_MODE_MAX = 7; 168e443b333SAlexander Shishkin 169e443b333SAlexander Shishkin if (mode > TEST_MODE_MAX) 170e443b333SAlexander Shishkin return -EINVAL; 171e443b333SAlexander Shishkin 172727b4ddbSFelipe Balbi hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC)); 173e443b333SAlexander Shishkin return 0; 174e443b333SAlexander Shishkin } 175e443b333SAlexander Shishkin 176e443b333SAlexander Shishkin /** 177e443b333SAlexander Shishkin * hw_port_test_get: reads port test mode value 178e443b333SAlexander Shishkin * 179e443b333SAlexander Shishkin * This function returns port test mode value 180e443b333SAlexander Shishkin */ 1818e22978cSAlexander Shishkin u8 hw_port_test_get(struct ci_hdrc *ci) 182e443b333SAlexander Shishkin { 183727b4ddbSFelipe Balbi return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC); 184e443b333SAlexander Shishkin } 185e443b333SAlexander Shishkin 186864cf949SPeter Chen /* The PHY enters/leaves low power mode */ 187864cf949SPeter Chen static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable) 188864cf949SPeter Chen { 189864cf949SPeter Chen enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC; 190864cf949SPeter Chen bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm))); 191864cf949SPeter Chen 192864cf949SPeter Chen if (enable && !lpm) { 193864cf949SPeter Chen hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 194864cf949SPeter Chen PORTSC_PHCD(ci->hw_bank.lpm)); 195864cf949SPeter Chen } else if (!enable && lpm) { 196864cf949SPeter Chen hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 197864cf949SPeter Chen 0); 198864cf949SPeter Chen /* 19990893b90SPeter Chen * the PHY needs some time (less 200864cf949SPeter Chen * than 1ms) to leave low power mode. 201864cf949SPeter Chen */ 20290893b90SPeter Chen usleep_range(1000, 1100); 203864cf949SPeter Chen } 204864cf949SPeter Chen } 205864cf949SPeter Chen 2068e22978cSAlexander Shishkin static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) 207e443b333SAlexander Shishkin { 208e443b333SAlexander Shishkin u32 reg; 209e443b333SAlexander Shishkin 210e443b333SAlexander Shishkin /* bank is a module variable */ 2115f36e231SAlexander Shishkin ci->hw_bank.abs = base; 212e443b333SAlexander Shishkin 2135f36e231SAlexander Shishkin ci->hw_bank.cap = ci->hw_bank.abs; 21477c4400fSRichard Zhao ci->hw_bank.cap += ci->platdata->capoffset; 215938d323fSSvetoslav Neykov ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff); 216e443b333SAlexander Shishkin 2175f36e231SAlexander Shishkin hw_alloc_regmap(ci, false); 2185f36e231SAlexander Shishkin reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >> 219727b4ddbSFelipe Balbi __ffs(HCCPARAMS_LEN); 2205f36e231SAlexander Shishkin ci->hw_bank.lpm = reg; 221aeb2c121SChris Ruehl if (reg) 2225f36e231SAlexander Shishkin hw_alloc_regmap(ci, !!reg); 2235f36e231SAlexander Shishkin ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs; 2245f36e231SAlexander Shishkin ci->hw_bank.size += OP_LAST; 2255f36e231SAlexander Shishkin ci->hw_bank.size /= sizeof(u32); 226e443b333SAlexander Shishkin 2275f36e231SAlexander Shishkin reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >> 228727b4ddbSFelipe Balbi __ffs(DCCPARAMS_DEN); 2295f36e231SAlexander Shishkin ci->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */ 230e443b333SAlexander Shishkin 23109c94e62SRichard Zhao if (ci->hw_ep_max > ENDPT_MAX) 232e443b333SAlexander Shishkin return -ENODEV; 233e443b333SAlexander Shishkin 234864cf949SPeter Chen ci_hdrc_enter_lpm(ci, false); 235864cf949SPeter Chen 236c344b518SPeter Chen /* Disable all interrupts bits */ 237c344b518SPeter Chen hw_write(ci, OP_USBINTR, 0xffffffff, 0); 238c344b518SPeter Chen 239c344b518SPeter Chen /* Clear all interrupts status bits*/ 240c344b518SPeter Chen hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff); 241c344b518SPeter Chen 2425f36e231SAlexander Shishkin dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n", 2435f36e231SAlexander Shishkin ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); 244e443b333SAlexander Shishkin 245e443b333SAlexander Shishkin /* setup lock mode ? */ 246e443b333SAlexander Shishkin 247e443b333SAlexander Shishkin /* ENDPTSETUPSTAT is '0' by default */ 248e443b333SAlexander Shishkin 249e443b333SAlexander Shishkin /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ 250e443b333SAlexander Shishkin 251e443b333SAlexander Shishkin return 0; 252e443b333SAlexander Shishkin } 253e443b333SAlexander Shishkin 2548e22978cSAlexander Shishkin static void hw_phymode_configure(struct ci_hdrc *ci) 25540dcd0e8SMichael Grzeschik { 2563b5d3e68SChris Ruehl u32 portsc, lpm, sts = 0; 25740dcd0e8SMichael Grzeschik 25840dcd0e8SMichael Grzeschik switch (ci->platdata->phy_mode) { 25940dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_UTMI: 26040dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_UTMI); 26140dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_UTMI); 26240dcd0e8SMichael Grzeschik break; 26340dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_UTMIW: 26440dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_UTMI) | PORTSC_PTW; 26540dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_UTMI) | DEVLC_PTW; 26640dcd0e8SMichael Grzeschik break; 26740dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_ULPI: 26840dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_ULPI); 26940dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_ULPI); 27040dcd0e8SMichael Grzeschik break; 27140dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_SERIAL: 27240dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_SERIAL); 27340dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_SERIAL); 27440dcd0e8SMichael Grzeschik sts = 1; 27540dcd0e8SMichael Grzeschik break; 27640dcd0e8SMichael Grzeschik case USBPHY_INTERFACE_MODE_HSIC: 27740dcd0e8SMichael Grzeschik portsc = PORTSC_PTS(PTS_HSIC); 27840dcd0e8SMichael Grzeschik lpm = DEVLC_PTS(PTS_HSIC); 27940dcd0e8SMichael Grzeschik break; 28040dcd0e8SMichael Grzeschik default: 28140dcd0e8SMichael Grzeschik return; 28240dcd0e8SMichael Grzeschik } 28340dcd0e8SMichael Grzeschik 28440dcd0e8SMichael Grzeschik if (ci->hw_bank.lpm) { 28540dcd0e8SMichael Grzeschik hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm); 2863b5d3e68SChris Ruehl if (sts) 2873b5d3e68SChris Ruehl hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS); 28840dcd0e8SMichael Grzeschik } else { 28940dcd0e8SMichael Grzeschik hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc); 2903b5d3e68SChris Ruehl if (sts) 2913b5d3e68SChris Ruehl hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); 29240dcd0e8SMichael Grzeschik } 29340dcd0e8SMichael Grzeschik } 29440dcd0e8SMichael Grzeschik 295e443b333SAlexander Shishkin /** 296d03cccffSPeter Chen * ci_usb_phy_init: initialize phy according to different phy type 297d03cccffSPeter Chen * @ci: the controller 298d03cccffSPeter Chen * 299d03cccffSPeter Chen * This function returns an error code if usb_phy_init has failed 300d03cccffSPeter Chen */ 301d03cccffSPeter Chen static int ci_usb_phy_init(struct ci_hdrc *ci) 302d03cccffSPeter Chen { 303d03cccffSPeter Chen int ret; 304d03cccffSPeter Chen 305d03cccffSPeter Chen switch (ci->platdata->phy_mode) { 306d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_UTMI: 307d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_UTMIW: 308d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_HSIC: 309d03cccffSPeter Chen ret = usb_phy_init(ci->transceiver); 310d03cccffSPeter Chen if (ret) 311d03cccffSPeter Chen return ret; 312d03cccffSPeter Chen hw_phymode_configure(ci); 313d03cccffSPeter Chen break; 314d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_ULPI: 315d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_SERIAL: 316d03cccffSPeter Chen hw_phymode_configure(ci); 317d03cccffSPeter Chen ret = usb_phy_init(ci->transceiver); 318d03cccffSPeter Chen if (ret) 319d03cccffSPeter Chen return ret; 320d03cccffSPeter Chen break; 321d03cccffSPeter Chen default: 322d03cccffSPeter Chen ret = usb_phy_init(ci->transceiver); 323d03cccffSPeter Chen } 324d03cccffSPeter Chen 325d03cccffSPeter Chen return ret; 326d03cccffSPeter Chen } 327d03cccffSPeter Chen 328d03cccffSPeter Chen /** 329e443b333SAlexander Shishkin * hw_device_reset: resets chip (execute without interruption) 330e443b333SAlexander Shishkin * @ci: the controller 331e443b333SAlexander Shishkin * 332e443b333SAlexander Shishkin * This function returns an error code 333e443b333SAlexander Shishkin */ 3348e22978cSAlexander Shishkin int hw_device_reset(struct ci_hdrc *ci, u32 mode) 335e443b333SAlexander Shishkin { 336e443b333SAlexander Shishkin /* should flush & stop before reset */ 337e443b333SAlexander Shishkin hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); 338e443b333SAlexander Shishkin hw_write(ci, OP_USBCMD, USBCMD_RS, 0); 339e443b333SAlexander Shishkin 340e443b333SAlexander Shishkin hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST); 341e443b333SAlexander Shishkin while (hw_read(ci, OP_USBCMD, USBCMD_RST)) 342e443b333SAlexander Shishkin udelay(10); /* not RTOS friendly */ 343e443b333SAlexander Shishkin 34477c4400fSRichard Zhao if (ci->platdata->notify_event) 34577c4400fSRichard Zhao ci->platdata->notify_event(ci, 3468e22978cSAlexander Shishkin CI_HDRC_CONTROLLER_RESET_EVENT); 347e443b333SAlexander Shishkin 3488e22978cSAlexander Shishkin if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) 349758fc986SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); 350e443b333SAlexander Shishkin 3514f6743d5SMichael Grzeschik if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { 3524f6743d5SMichael Grzeschik if (ci->hw_bank.lpm) 3534f6743d5SMichael Grzeschik hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); 3544f6743d5SMichael Grzeschik else 3554f6743d5SMichael Grzeschik hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); 3564f6743d5SMichael Grzeschik } 3574f6743d5SMichael Grzeschik 358e443b333SAlexander Shishkin /* USBMODE should be configured step by step */ 359e443b333SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); 360eb70e5abSAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_CM, mode); 361e443b333SAlexander Shishkin /* HW >= 2.3 */ 362e443b333SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); 363e443b333SAlexander Shishkin 364eb70e5abSAlexander Shishkin if (hw_read(ci, OP_USBMODE, USBMODE_CM) != mode) { 365eb70e5abSAlexander Shishkin pr_err("cannot enter in %s mode", ci_role(ci)->name); 366e443b333SAlexander Shishkin pr_err("lpm = %i", ci->hw_bank.lpm); 367e443b333SAlexander Shishkin return -ENODEV; 368e443b333SAlexander Shishkin } 369e443b333SAlexander Shishkin 370e443b333SAlexander Shishkin return 0; 371e443b333SAlexander Shishkin } 372e443b333SAlexander Shishkin 37322fa8445SPeter Chen /** 37422fa8445SPeter Chen * hw_wait_reg: wait the register value 37522fa8445SPeter Chen * 37622fa8445SPeter Chen * Sometimes, it needs to wait register value before going on. 37722fa8445SPeter Chen * Eg, when switch to device mode, the vbus value should be lower 37822fa8445SPeter Chen * than OTGSC_BSV before connects to host. 37922fa8445SPeter Chen * 38022fa8445SPeter Chen * @ci: the controller 38122fa8445SPeter Chen * @reg: register index 38222fa8445SPeter Chen * @mask: mast bit 38322fa8445SPeter Chen * @value: the bit value to wait 38422fa8445SPeter Chen * @timeout_ms: timeout in millisecond 38522fa8445SPeter Chen * 38622fa8445SPeter Chen * This function returns an error code if timeout 38722fa8445SPeter Chen */ 38822fa8445SPeter Chen int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask, 38922fa8445SPeter Chen u32 value, unsigned int timeout_ms) 39022fa8445SPeter Chen { 39122fa8445SPeter Chen unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms); 39222fa8445SPeter Chen 39322fa8445SPeter Chen while (hw_read(ci, reg, mask) != value) { 39422fa8445SPeter Chen if (time_after(jiffies, elapse)) { 39522fa8445SPeter Chen dev_err(ci->dev, "timeout waiting for %08x in %d\n", 39622fa8445SPeter Chen mask, reg); 39722fa8445SPeter Chen return -ETIMEDOUT; 39822fa8445SPeter Chen } 39922fa8445SPeter Chen msleep(20); 40022fa8445SPeter Chen } 40122fa8445SPeter Chen 40222fa8445SPeter Chen return 0; 40322fa8445SPeter Chen } 40422fa8445SPeter Chen 4055f36e231SAlexander Shishkin static irqreturn_t ci_irq(int irq, void *data) 4065f36e231SAlexander Shishkin { 4078e22978cSAlexander Shishkin struct ci_hdrc *ci = data; 4085f36e231SAlexander Shishkin irqreturn_t ret = IRQ_NONE; 409b183c19fSRichard Zhao u32 otgsc = 0; 4105f36e231SAlexander Shishkin 4114dcf720cSLi Jun if (ci->is_otg) { 4120c33bf78SLi Jun otgsc = hw_read_otgsc(ci, ~0); 4134dcf720cSLi Jun if (ci_otg_is_fsm_mode(ci)) { 4144dcf720cSLi Jun ret = ci_otg_fsm_irq(ci); 4154dcf720cSLi Jun if (ret == IRQ_HANDLED) 4164dcf720cSLi Jun return ret; 4174dcf720cSLi Jun } 4184dcf720cSLi Jun } 4195f36e231SAlexander Shishkin 420a107f8c5SPeter Chen /* 421a107f8c5SPeter Chen * Handle id change interrupt, it indicates device/host function 422a107f8c5SPeter Chen * switch. 423a107f8c5SPeter Chen */ 424a107f8c5SPeter Chen if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { 425a107f8c5SPeter Chen ci->id_event = true; 4260c33bf78SLi Jun /* Clear ID change irq status */ 4270c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); 428b183c19fSRichard Zhao disable_irq_nosync(ci->irq); 4295f36e231SAlexander Shishkin queue_work(ci->wq, &ci->work); 430a107f8c5SPeter Chen return IRQ_HANDLED; 4315f36e231SAlexander Shishkin } 4325f36e231SAlexander Shishkin 433a107f8c5SPeter Chen /* 434a107f8c5SPeter Chen * Handle vbus change interrupt, it indicates device connection 435a107f8c5SPeter Chen * and disconnection events. 436a107f8c5SPeter Chen */ 437a107f8c5SPeter Chen if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { 438a107f8c5SPeter Chen ci->b_sess_valid_event = true; 4390c33bf78SLi Jun /* Clear BSV irq */ 4400c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); 441a107f8c5SPeter Chen disable_irq_nosync(ci->irq); 442a107f8c5SPeter Chen queue_work(ci->wq, &ci->work); 443a107f8c5SPeter Chen return IRQ_HANDLED; 444a107f8c5SPeter Chen } 445a107f8c5SPeter Chen 446a107f8c5SPeter Chen /* Handle device/host interrupt */ 447a107f8c5SPeter Chen if (ci->role != CI_ROLE_END) 448a107f8c5SPeter Chen ret = ci_role(ci)->irq(ci); 449a107f8c5SPeter Chen 450b183c19fSRichard Zhao return ret; 4515f36e231SAlexander Shishkin } 4525f36e231SAlexander Shishkin 4531542d9c3SPeter Chen static int ci_get_platdata(struct device *dev, 4541542d9c3SPeter Chen struct ci_hdrc_platform_data *platdata) 4551542d9c3SPeter Chen { 456c22600c3SPeter Chen if (!platdata->phy_mode) 457c22600c3SPeter Chen platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); 458c22600c3SPeter Chen 459c22600c3SPeter Chen if (!platdata->dr_mode) 460c22600c3SPeter Chen platdata->dr_mode = of_usb_get_dr_mode(dev->of_node); 461c22600c3SPeter Chen 462c22600c3SPeter Chen if (platdata->dr_mode == USB_DR_MODE_UNKNOWN) 463c22600c3SPeter Chen platdata->dr_mode = USB_DR_MODE_OTG; 464c22600c3SPeter Chen 465c2ec3a73SPeter Chen if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) { 466c2ec3a73SPeter Chen /* Get the vbus regulator */ 467c2ec3a73SPeter Chen platdata->reg_vbus = devm_regulator_get(dev, "vbus"); 468c2ec3a73SPeter Chen if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { 469c2ec3a73SPeter Chen return -EPROBE_DEFER; 470c2ec3a73SPeter Chen } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { 471c2ec3a73SPeter Chen /* no vbus regualator is needed */ 472c2ec3a73SPeter Chen platdata->reg_vbus = NULL; 473c2ec3a73SPeter Chen } else if (IS_ERR(platdata->reg_vbus)) { 474c2ec3a73SPeter Chen dev_err(dev, "Getting regulator error: %ld\n", 475c2ec3a73SPeter Chen PTR_ERR(platdata->reg_vbus)); 476c2ec3a73SPeter Chen return PTR_ERR(platdata->reg_vbus); 477c2ec3a73SPeter Chen } 478c2ec3a73SPeter Chen } 479c2ec3a73SPeter Chen 4804f6743d5SMichael Grzeschik if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL) 4814f6743d5SMichael Grzeschik platdata->flags |= CI_HDRC_FORCE_FULLSPEED; 4824f6743d5SMichael Grzeschik 4831542d9c3SPeter Chen return 0; 4841542d9c3SPeter Chen } 4851542d9c3SPeter Chen 486fe6e125eSRichard Zhao static DEFINE_IDA(ci_ida); 487fe6e125eSRichard Zhao 4888e22978cSAlexander Shishkin struct platform_device *ci_hdrc_add_device(struct device *dev, 489cbc6dc2aSRichard Zhao struct resource *res, int nres, 4908e22978cSAlexander Shishkin struct ci_hdrc_platform_data *platdata) 491cbc6dc2aSRichard Zhao { 492cbc6dc2aSRichard Zhao struct platform_device *pdev; 493fe6e125eSRichard Zhao int id, ret; 494cbc6dc2aSRichard Zhao 4951542d9c3SPeter Chen ret = ci_get_platdata(dev, platdata); 4961542d9c3SPeter Chen if (ret) 4971542d9c3SPeter Chen return ERR_PTR(ret); 4981542d9c3SPeter Chen 499fe6e125eSRichard Zhao id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); 500fe6e125eSRichard Zhao if (id < 0) 501fe6e125eSRichard Zhao return ERR_PTR(id); 502fe6e125eSRichard Zhao 503fe6e125eSRichard Zhao pdev = platform_device_alloc("ci_hdrc", id); 504fe6e125eSRichard Zhao if (!pdev) { 505fe6e125eSRichard Zhao ret = -ENOMEM; 506fe6e125eSRichard Zhao goto put_id; 507fe6e125eSRichard Zhao } 508cbc6dc2aSRichard Zhao 509cbc6dc2aSRichard Zhao pdev->dev.parent = dev; 510cbc6dc2aSRichard Zhao pdev->dev.dma_mask = dev->dma_mask; 511cbc6dc2aSRichard Zhao pdev->dev.dma_parms = dev->dma_parms; 512cbc6dc2aSRichard Zhao dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask); 513cbc6dc2aSRichard Zhao 514cbc6dc2aSRichard Zhao ret = platform_device_add_resources(pdev, res, nres); 515cbc6dc2aSRichard Zhao if (ret) 516cbc6dc2aSRichard Zhao goto err; 517cbc6dc2aSRichard Zhao 518cbc6dc2aSRichard Zhao ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); 519cbc6dc2aSRichard Zhao if (ret) 520cbc6dc2aSRichard Zhao goto err; 521cbc6dc2aSRichard Zhao 522cbc6dc2aSRichard Zhao ret = platform_device_add(pdev); 523cbc6dc2aSRichard Zhao if (ret) 524cbc6dc2aSRichard Zhao goto err; 525cbc6dc2aSRichard Zhao 526cbc6dc2aSRichard Zhao return pdev; 527cbc6dc2aSRichard Zhao 528cbc6dc2aSRichard Zhao err: 529cbc6dc2aSRichard Zhao platform_device_put(pdev); 530fe6e125eSRichard Zhao put_id: 531fe6e125eSRichard Zhao ida_simple_remove(&ci_ida, id); 532cbc6dc2aSRichard Zhao return ERR_PTR(ret); 533cbc6dc2aSRichard Zhao } 5348e22978cSAlexander Shishkin EXPORT_SYMBOL_GPL(ci_hdrc_add_device); 535cbc6dc2aSRichard Zhao 5368e22978cSAlexander Shishkin void ci_hdrc_remove_device(struct platform_device *pdev) 537cbc6dc2aSRichard Zhao { 53898c35534SLothar Waßmann int id = pdev->id; 539cbc6dc2aSRichard Zhao platform_device_unregister(pdev); 54098c35534SLothar Waßmann ida_simple_remove(&ci_ida, id); 541cbc6dc2aSRichard Zhao } 5428e22978cSAlexander Shishkin EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); 543cbc6dc2aSRichard Zhao 5443f124d23SPeter Chen static inline void ci_role_destroy(struct ci_hdrc *ci) 5453f124d23SPeter Chen { 5463f124d23SPeter Chen ci_hdrc_gadget_destroy(ci); 5473f124d23SPeter Chen ci_hdrc_host_destroy(ci); 548cbec6bd5SPeter Chen if (ci->is_otg) 549cbec6bd5SPeter Chen ci_hdrc_otg_destroy(ci); 5503f124d23SPeter Chen } 5513f124d23SPeter Chen 552577b232fSPeter Chen static void ci_get_otg_capable(struct ci_hdrc *ci) 553577b232fSPeter Chen { 554577b232fSPeter Chen if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) 555577b232fSPeter Chen ci->is_otg = false; 556577b232fSPeter Chen else 557577b232fSPeter Chen ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, 558577b232fSPeter Chen DCCPARAMS_DC | DCCPARAMS_HC) 559577b232fSPeter Chen == (DCCPARAMS_DC | DCCPARAMS_HC)); 56090893b90SPeter Chen if (ci->is_otg) 561577b232fSPeter Chen dev_dbg(ci->dev, "It is OTG capable controller\n"); 562577b232fSPeter Chen } 563577b232fSPeter Chen 56441ac7b3aSBill Pemberton static int ci_hdrc_probe(struct platform_device *pdev) 565e443b333SAlexander Shishkin { 566e443b333SAlexander Shishkin struct device *dev = &pdev->dev; 5678e22978cSAlexander Shishkin struct ci_hdrc *ci; 568e443b333SAlexander Shishkin struct resource *res; 569e443b333SAlexander Shishkin void __iomem *base; 570e443b333SAlexander Shishkin int ret; 571691962d1SSascha Hauer enum usb_dr_mode dr_mode; 572e443b333SAlexander Shishkin 573fad56745SJingoo Han if (!dev_get_platdata(dev)) { 574e443b333SAlexander Shishkin dev_err(dev, "platform data missing\n"); 575e443b333SAlexander Shishkin return -ENODEV; 576e443b333SAlexander Shishkin } 577e443b333SAlexander Shishkin 578e443b333SAlexander Shishkin res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 57919290816SFelipe Balbi base = devm_ioremap_resource(dev, res); 58019290816SFelipe Balbi if (IS_ERR(base)) 58119290816SFelipe Balbi return PTR_ERR(base); 582e443b333SAlexander Shishkin 5835f36e231SAlexander Shishkin ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL); 5845f36e231SAlexander Shishkin if (!ci) { 5855f36e231SAlexander Shishkin dev_err(dev, "can't allocate device\n"); 5865f36e231SAlexander Shishkin return -ENOMEM; 5875f36e231SAlexander Shishkin } 588e443b333SAlexander Shishkin 5895f36e231SAlexander Shishkin ci->dev = dev; 590fad56745SJingoo Han ci->platdata = dev_get_platdata(dev); 591ed8f8318SPeter Chen ci->imx28_write_fix = !!(ci->platdata->flags & 592ed8f8318SPeter Chen CI_HDRC_IMX28_WRITE_FIX); 5935f36e231SAlexander Shishkin 5945f36e231SAlexander Shishkin ret = hw_device_init(ci, base); 5955f36e231SAlexander Shishkin if (ret < 0) { 5965f36e231SAlexander Shishkin dev_err(dev, "can't initialize hardware\n"); 5975f36e231SAlexander Shishkin return -ENODEV; 5985f36e231SAlexander Shishkin } 5995f36e231SAlexander Shishkin 600c859aa65SPeter Chen if (ci->platdata->phy) 601c859aa65SPeter Chen ci->transceiver = ci->platdata->phy; 602c859aa65SPeter Chen else 603c859aa65SPeter Chen ci->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); 604c859aa65SPeter Chen 605c859aa65SPeter Chen if (IS_ERR(ci->transceiver)) { 606c859aa65SPeter Chen ret = PTR_ERR(ci->transceiver); 607c859aa65SPeter Chen /* 608c859aa65SPeter Chen * if -ENXIO is returned, it means PHY layer wasn't 609c859aa65SPeter Chen * enabled, so it makes no sense to return -EPROBE_DEFER 610c859aa65SPeter Chen * in that case, since no PHY driver will ever probe. 611c859aa65SPeter Chen */ 612c859aa65SPeter Chen if (ret == -ENXIO) 613c859aa65SPeter Chen return ret; 614c859aa65SPeter Chen 615c859aa65SPeter Chen dev_err(dev, "no usb2 phy configured\n"); 616c859aa65SPeter Chen return -EPROBE_DEFER; 617c859aa65SPeter Chen } 618c859aa65SPeter Chen 619d03cccffSPeter Chen ret = ci_usb_phy_init(ci); 62074475edeSPeter Chen if (ret) { 62174475edeSPeter Chen dev_err(dev, "unable to init phy: %d\n", ret); 62274475edeSPeter Chen return ret; 62390893b90SPeter Chen } else { 62490893b90SPeter Chen /* 62590893b90SPeter Chen * The delay to sync PHY's status, the maximum delay is 62690893b90SPeter Chen * 2ms since the otgsc uses 1ms timer to debounce the 62790893b90SPeter Chen * PHY's input 62890893b90SPeter Chen */ 62990893b90SPeter Chen usleep_range(2000, 2500); 63074475edeSPeter Chen } 63174475edeSPeter Chen 632eb70e5abSAlexander Shishkin ci->hw_bank.phys = res->start; 633eb70e5abSAlexander Shishkin 6345f36e231SAlexander Shishkin ci->irq = platform_get_irq(pdev, 0); 6355f36e231SAlexander Shishkin if (ci->irq < 0) { 636e443b333SAlexander Shishkin dev_err(dev, "missing IRQ\n"); 63742d18212SFabio Estevam ret = ci->irq; 638c859aa65SPeter Chen goto deinit_phy; 639e443b333SAlexander Shishkin } 640e443b333SAlexander Shishkin 641577b232fSPeter Chen ci_get_otg_capable(ci); 642577b232fSPeter Chen 643691962d1SSascha Hauer dr_mode = ci->platdata->dr_mode; 6445f36e231SAlexander Shishkin /* initialize role(s) before the interrupt is requested */ 645691962d1SSascha Hauer if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { 646eb70e5abSAlexander Shishkin ret = ci_hdrc_host_init(ci); 647eb70e5abSAlexander Shishkin if (ret) 648eb70e5abSAlexander Shishkin dev_info(dev, "doesn't support host\n"); 649691962d1SSascha Hauer } 650eb70e5abSAlexander Shishkin 651691962d1SSascha Hauer if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { 6525f36e231SAlexander Shishkin ret = ci_hdrc_gadget_init(ci); 653e443b333SAlexander Shishkin if (ret) 6545f36e231SAlexander Shishkin dev_info(dev, "doesn't support gadget\n"); 655691962d1SSascha Hauer } 6565f36e231SAlexander Shishkin 6575f36e231SAlexander Shishkin if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { 6585f36e231SAlexander Shishkin dev_err(dev, "no supported roles\n"); 65974475edeSPeter Chen ret = -ENODEV; 660c859aa65SPeter Chen goto deinit_phy; 661cbec6bd5SPeter Chen } 662cbec6bd5SPeter Chen 663cbec6bd5SPeter Chen if (ci->is_otg) { 66490893b90SPeter Chen /* Disable and clear all OTG irq */ 66590893b90SPeter Chen hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, 66690893b90SPeter Chen OTGSC_INT_STATUS_BITS); 667cbec6bd5SPeter Chen ret = ci_hdrc_otg_init(ci); 668cbec6bd5SPeter Chen if (ret) { 669cbec6bd5SPeter Chen dev_err(dev, "init otg fails, ret = %d\n", ret); 670cbec6bd5SPeter Chen goto stop; 671cbec6bd5SPeter Chen } 6725f36e231SAlexander Shishkin } 6735f36e231SAlexander Shishkin 6745f36e231SAlexander Shishkin if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { 675577b232fSPeter Chen if (ci->is_otg) { 6765f36e231SAlexander Shishkin ci->role = ci_otg_role(ci); 6770c33bf78SLi Jun /* Enable ID change irq */ 6780c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); 679577b232fSPeter Chen } else { 680577b232fSPeter Chen /* 681577b232fSPeter Chen * If the controller is not OTG capable, but support 682577b232fSPeter Chen * role switch, the defalt role is gadget, and the 683577b232fSPeter Chen * user can switch it through debugfs. 684577b232fSPeter Chen */ 685577b232fSPeter Chen ci->role = CI_ROLE_GADGET; 686577b232fSPeter Chen } 6875f36e231SAlexander Shishkin } else { 6885f36e231SAlexander Shishkin ci->role = ci->roles[CI_ROLE_HOST] 6895f36e231SAlexander Shishkin ? CI_ROLE_HOST 6905f36e231SAlexander Shishkin : CI_ROLE_GADGET; 6915f36e231SAlexander Shishkin } 6925f36e231SAlexander Shishkin 6935a1e1456SPeter Chen /* only update vbus status for peripheral */ 6945a1e1456SPeter Chen if (ci->role == CI_ROLE_GADGET) 6955a1e1456SPeter Chen ci_handle_vbus_change(ci); 6965a1e1456SPeter Chen 6974dcf720cSLi Jun if (!ci_otg_is_fsm_mode(ci)) { 6985f36e231SAlexander Shishkin ret = ci_role_start(ci, ci->role); 6995f36e231SAlexander Shishkin if (ret) { 7004dcf720cSLi Jun dev_err(dev, "can't start %s role\n", 7014dcf720cSLi Jun ci_role(ci)->name); 702cbec6bd5SPeter Chen goto stop; 7035f36e231SAlexander Shishkin } 7044dcf720cSLi Jun } 7055f36e231SAlexander Shishkin 7065f36e231SAlexander Shishkin platform_set_drvdata(pdev, ci); 70777c4400fSRichard Zhao ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name, 7085f36e231SAlexander Shishkin ci); 7095f36e231SAlexander Shishkin if (ret) 7105f36e231SAlexander Shishkin goto stop; 7115f36e231SAlexander Shishkin 7124dcf720cSLi Jun if (ci_otg_is_fsm_mode(ci)) 7134dcf720cSLi Jun ci_hdrc_otg_fsm_start(ci); 7144dcf720cSLi Jun 715adf0f735SAlexander Shishkin ret = dbg_create_files(ci); 716adf0f735SAlexander Shishkin if (!ret) 717adf0f735SAlexander Shishkin return 0; 7185f36e231SAlexander Shishkin 719adf0f735SAlexander Shishkin free_irq(ci->irq, ci); 7205f36e231SAlexander Shishkin stop: 7213f124d23SPeter Chen ci_role_destroy(ci); 722c859aa65SPeter Chen deinit_phy: 723c859aa65SPeter Chen usb_phy_shutdown(ci->transceiver); 724e443b333SAlexander Shishkin 725e443b333SAlexander Shishkin return ret; 726e443b333SAlexander Shishkin } 727e443b333SAlexander Shishkin 728fb4e98abSBill Pemberton static int ci_hdrc_remove(struct platform_device *pdev) 729e443b333SAlexander Shishkin { 7308e22978cSAlexander Shishkin struct ci_hdrc *ci = platform_get_drvdata(pdev); 731e443b333SAlexander Shishkin 732adf0f735SAlexander Shishkin dbg_remove_files(ci); 7335f36e231SAlexander Shishkin free_irq(ci->irq, ci); 7343f124d23SPeter Chen ci_role_destroy(ci); 735864cf949SPeter Chen ci_hdrc_enter_lpm(ci, true); 736c859aa65SPeter Chen usb_phy_shutdown(ci->transceiver); 737c859aa65SPeter Chen kfree(ci->hw_bank.regmap); 738e443b333SAlexander Shishkin 739e443b333SAlexander Shishkin return 0; 740e443b333SAlexander Shishkin } 741e443b333SAlexander Shishkin 7425f36e231SAlexander Shishkin static struct platform_driver ci_hdrc_driver = { 7435f36e231SAlexander Shishkin .probe = ci_hdrc_probe, 7447690417dSBill Pemberton .remove = ci_hdrc_remove, 745e443b333SAlexander Shishkin .driver = { 7465f36e231SAlexander Shishkin .name = "ci_hdrc", 7477cf2f861SAlexander Shiyan .owner = THIS_MODULE, 748e443b333SAlexander Shishkin }, 749e443b333SAlexander Shishkin }; 750e443b333SAlexander Shishkin 7515f36e231SAlexander Shishkin module_platform_driver(ci_hdrc_driver); 752e443b333SAlexander Shishkin 7535f36e231SAlexander Shishkin MODULE_ALIAS("platform:ci_hdrc"); 754e443b333SAlexander Shishkin MODULE_LICENSE("GPL v2"); 755e443b333SAlexander Shishkin MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>"); 7565f36e231SAlexander Shishkin MODULE_DESCRIPTION("ChipIdea HDRC Driver"); 757