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> 501e5e2d3dSAntoine Tenart #include <linux/phy/phy.h> 51e443b333SAlexander Shishkin #include <linux/platform_device.h> 52e443b333SAlexander Shishkin #include <linux/module.h> 53fe6e125eSRichard Zhao #include <linux/idr.h> 54e443b333SAlexander Shishkin #include <linux/interrupt.h> 55e443b333SAlexander Shishkin #include <linux/io.h> 56e443b333SAlexander Shishkin #include <linux/kernel.h> 57e443b333SAlexander Shishkin #include <linux/slab.h> 58e443b333SAlexander Shishkin #include <linux/pm_runtime.h> 59e443b333SAlexander Shishkin #include <linux/usb/ch9.h> 60e443b333SAlexander Shishkin #include <linux/usb/gadget.h> 61e443b333SAlexander Shishkin #include <linux/usb/otg.h> 62e443b333SAlexander Shishkin #include <linux/usb/chipidea.h> 6340dcd0e8SMichael Grzeschik #include <linux/usb/of.h> 644f6743d5SMichael Grzeschik #include <linux/of.h> 6540dcd0e8SMichael Grzeschik #include <linux/phy.h> 661542d9c3SPeter Chen #include <linux/regulator/consumer.h> 678022d3d5SPeter Chen #include <linux/usb/ehci_def.h> 68e443b333SAlexander Shishkin 69e443b333SAlexander Shishkin #include "ci.h" 70e443b333SAlexander Shishkin #include "udc.h" 71e443b333SAlexander Shishkin #include "bits.h" 72eb70e5abSAlexander Shishkin #include "host.h" 73e443b333SAlexander Shishkin #include "debug.h" 74c10b4f03SPeter Chen #include "otg.h" 754dcf720cSLi Jun #include "otg_fsm.h" 76e443b333SAlexander Shishkin 775f36e231SAlexander Shishkin /* Controller register map */ 78987e7bc3SMarc Kleine-Budde static const u8 ci_regs_nolpm[] = { 79987e7bc3SMarc Kleine-Budde [CAP_CAPLENGTH] = 0x00U, 80987e7bc3SMarc Kleine-Budde [CAP_HCCPARAMS] = 0x08U, 81987e7bc3SMarc Kleine-Budde [CAP_DCCPARAMS] = 0x24U, 82987e7bc3SMarc Kleine-Budde [CAP_TESTMODE] = 0x38U, 83987e7bc3SMarc Kleine-Budde [OP_USBCMD] = 0x00U, 84987e7bc3SMarc Kleine-Budde [OP_USBSTS] = 0x04U, 85987e7bc3SMarc Kleine-Budde [OP_USBINTR] = 0x08U, 86987e7bc3SMarc Kleine-Budde [OP_DEVICEADDR] = 0x14U, 87987e7bc3SMarc Kleine-Budde [OP_ENDPTLISTADDR] = 0x18U, 8828362673SPeter Chen [OP_TTCTRL] = 0x1CU, 8996625eadSPeter Chen [OP_BURSTSIZE] = 0x20U, 90987e7bc3SMarc Kleine-Budde [OP_PORTSC] = 0x44U, 91987e7bc3SMarc Kleine-Budde [OP_DEVLC] = 0x84U, 92987e7bc3SMarc Kleine-Budde [OP_OTGSC] = 0x64U, 93987e7bc3SMarc Kleine-Budde [OP_USBMODE] = 0x68U, 94987e7bc3SMarc Kleine-Budde [OP_ENDPTSETUPSTAT] = 0x6CU, 95987e7bc3SMarc Kleine-Budde [OP_ENDPTPRIME] = 0x70U, 96987e7bc3SMarc Kleine-Budde [OP_ENDPTFLUSH] = 0x74U, 97987e7bc3SMarc Kleine-Budde [OP_ENDPTSTAT] = 0x78U, 98987e7bc3SMarc Kleine-Budde [OP_ENDPTCOMPLETE] = 0x7CU, 99987e7bc3SMarc Kleine-Budde [OP_ENDPTCTRL] = 0x80U, 100e443b333SAlexander Shishkin }; 101e443b333SAlexander Shishkin 102987e7bc3SMarc Kleine-Budde static const u8 ci_regs_lpm[] = { 103987e7bc3SMarc Kleine-Budde [CAP_CAPLENGTH] = 0x00U, 104987e7bc3SMarc Kleine-Budde [CAP_HCCPARAMS] = 0x08U, 105987e7bc3SMarc Kleine-Budde [CAP_DCCPARAMS] = 0x24U, 106987e7bc3SMarc Kleine-Budde [CAP_TESTMODE] = 0xFCU, 107987e7bc3SMarc Kleine-Budde [OP_USBCMD] = 0x00U, 108987e7bc3SMarc Kleine-Budde [OP_USBSTS] = 0x04U, 109987e7bc3SMarc Kleine-Budde [OP_USBINTR] = 0x08U, 110987e7bc3SMarc Kleine-Budde [OP_DEVICEADDR] = 0x14U, 111987e7bc3SMarc Kleine-Budde [OP_ENDPTLISTADDR] = 0x18U, 11228362673SPeter Chen [OP_TTCTRL] = 0x1CU, 11396625eadSPeter Chen [OP_BURSTSIZE] = 0x20U, 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 2898e22978cSAlexander Shishkin static 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 } 32940dcd0e8SMichael Grzeschik 330e443b333SAlexander Shishkin /** 3311e5e2d3dSAntoine Tenart * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy 3321e5e2d3dSAntoine Tenart * interfaces 3331e5e2d3dSAntoine Tenart * @ci: the controller 3341e5e2d3dSAntoine Tenart * 3351e5e2d3dSAntoine Tenart * This function returns an error code if the phy failed to init 3361e5e2d3dSAntoine Tenart */ 3371e5e2d3dSAntoine Tenart static int _ci_usb_phy_init(struct ci_hdrc *ci) 3381e5e2d3dSAntoine Tenart { 3391e5e2d3dSAntoine Tenart int ret; 3401e5e2d3dSAntoine Tenart 3411e5e2d3dSAntoine Tenart if (ci->phy) { 3421e5e2d3dSAntoine Tenart ret = phy_init(ci->phy); 3431e5e2d3dSAntoine Tenart if (ret) 3441e5e2d3dSAntoine Tenart return ret; 3451e5e2d3dSAntoine Tenart 3461e5e2d3dSAntoine Tenart ret = phy_power_on(ci->phy); 3471e5e2d3dSAntoine Tenart if (ret) { 3481e5e2d3dSAntoine Tenart phy_exit(ci->phy); 3491e5e2d3dSAntoine Tenart return ret; 3501e5e2d3dSAntoine Tenart } 3511e5e2d3dSAntoine Tenart } else { 3521e5e2d3dSAntoine Tenart ret = usb_phy_init(ci->usb_phy); 3531e5e2d3dSAntoine Tenart } 3541e5e2d3dSAntoine Tenart 3551e5e2d3dSAntoine Tenart return ret; 3561e5e2d3dSAntoine Tenart } 3571e5e2d3dSAntoine Tenart 3581e5e2d3dSAntoine Tenart /** 3591e5e2d3dSAntoine Tenart * _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy 3601e5e2d3dSAntoine Tenart * interfaces 3611e5e2d3dSAntoine Tenart * @ci: the controller 3621e5e2d3dSAntoine Tenart */ 3631e5e2d3dSAntoine Tenart static void ci_usb_phy_exit(struct ci_hdrc *ci) 3641e5e2d3dSAntoine Tenart { 3651e5e2d3dSAntoine Tenart if (ci->phy) { 3661e5e2d3dSAntoine Tenart phy_power_off(ci->phy); 3671e5e2d3dSAntoine Tenart phy_exit(ci->phy); 3681e5e2d3dSAntoine Tenart } else { 3691e5e2d3dSAntoine Tenart usb_phy_shutdown(ci->usb_phy); 3701e5e2d3dSAntoine Tenart } 3711e5e2d3dSAntoine Tenart } 3721e5e2d3dSAntoine Tenart 3731e5e2d3dSAntoine Tenart /** 374d03cccffSPeter Chen * ci_usb_phy_init: initialize phy according to different phy type 375d03cccffSPeter Chen * @ci: the controller 376d03cccffSPeter Chen * 377d03cccffSPeter Chen * This function returns an error code if usb_phy_init has failed 378d03cccffSPeter Chen */ 379d03cccffSPeter Chen static int ci_usb_phy_init(struct ci_hdrc *ci) 380d03cccffSPeter Chen { 381d03cccffSPeter Chen int ret; 382d03cccffSPeter Chen 383d03cccffSPeter Chen switch (ci->platdata->phy_mode) { 384d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_UTMI: 385d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_UTMIW: 386d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_HSIC: 3871e5e2d3dSAntoine Tenart ret = _ci_usb_phy_init(ci); 388b82613cfSPeter Chen if (!ret) 389b82613cfSPeter Chen hw_wait_phy_stable(); 390b82613cfSPeter Chen else 391d03cccffSPeter Chen return ret; 392d03cccffSPeter Chen hw_phymode_configure(ci); 393d03cccffSPeter Chen break; 394d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_ULPI: 395d03cccffSPeter Chen case USBPHY_INTERFACE_MODE_SERIAL: 396d03cccffSPeter Chen hw_phymode_configure(ci); 3971e5e2d3dSAntoine Tenart ret = _ci_usb_phy_init(ci); 398d03cccffSPeter Chen if (ret) 399d03cccffSPeter Chen return ret; 400d03cccffSPeter Chen break; 401d03cccffSPeter Chen default: 4021e5e2d3dSAntoine Tenart ret = _ci_usb_phy_init(ci); 403b82613cfSPeter Chen if (!ret) 404b82613cfSPeter Chen hw_wait_phy_stable(); 405d03cccffSPeter Chen } 406d03cccffSPeter Chen 407d03cccffSPeter Chen return ret; 408d03cccffSPeter Chen } 409d03cccffSPeter Chen 410bf9c85e7SPeter Chen 411bf9c85e7SPeter Chen /** 412bf9c85e7SPeter Chen * ci_platform_configure: do controller configure 413bf9c85e7SPeter Chen * @ci: the controller 414bf9c85e7SPeter Chen * 415bf9c85e7SPeter Chen */ 416bf9c85e7SPeter Chen void ci_platform_configure(struct ci_hdrc *ci) 417bf9c85e7SPeter Chen { 4188022d3d5SPeter Chen bool is_device_mode, is_host_mode; 4198022d3d5SPeter Chen 4208022d3d5SPeter Chen is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC; 4218022d3d5SPeter Chen is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC; 4228022d3d5SPeter Chen 4238022d3d5SPeter Chen if (is_device_mode && 4248022d3d5SPeter Chen (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)) 4258022d3d5SPeter Chen hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); 4268022d3d5SPeter Chen 4278022d3d5SPeter Chen if (is_host_mode && 4288022d3d5SPeter Chen (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING)) 429bf9c85e7SPeter Chen hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); 430bf9c85e7SPeter Chen 431bf9c85e7SPeter Chen if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { 432bf9c85e7SPeter Chen if (ci->hw_bank.lpm) 433bf9c85e7SPeter Chen hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); 434bf9c85e7SPeter Chen else 435bf9c85e7SPeter Chen hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); 436bf9c85e7SPeter Chen } 437bf9c85e7SPeter Chen 438bf9c85e7SPeter Chen if (ci->platdata->flags & CI_HDRC_SET_NON_ZERO_TTHA) 439bf9c85e7SPeter Chen hw_write(ci, OP_TTCTRL, TTCTRL_TTHA_MASK, TTCTRL_TTHA); 440df96ed8dSPeter Chen 441df96ed8dSPeter Chen hw_write(ci, OP_USBCMD, 0xff0000, ci->platdata->itc_setting << 16); 442df96ed8dSPeter Chen 44365668718SPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_AHB_BURST) 44465668718SPeter Chen hw_write_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK, 44565668718SPeter Chen ci->platdata->ahb_burst_config); 44696625eadSPeter Chen 44796625eadSPeter Chen /* override burst size, take effect only when ahb_burst_config is 0 */ 44896625eadSPeter Chen if (!hw_read_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK)) { 44996625eadSPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_TX_BURST) 45096625eadSPeter Chen hw_write(ci, OP_BURSTSIZE, TX_BURST_MASK, 45196625eadSPeter Chen ci->platdata->tx_burst_size << __ffs(TX_BURST_MASK)); 45296625eadSPeter Chen 45396625eadSPeter Chen if (ci->platdata->flags & CI_HDRC_OVERRIDE_RX_BURST) 45496625eadSPeter Chen hw_write(ci, OP_BURSTSIZE, RX_BURST_MASK, 45596625eadSPeter Chen ci->platdata->rx_burst_size); 45696625eadSPeter Chen } 457bf9c85e7SPeter Chen } 458bf9c85e7SPeter Chen 459d03cccffSPeter Chen /** 460cdd278f2SPeter Chen * hw_controller_reset: do controller reset 461cdd278f2SPeter Chen * @ci: the controller 462cdd278f2SPeter Chen * 463cdd278f2SPeter Chen * This function returns an error code 464cdd278f2SPeter Chen */ 465cdd278f2SPeter Chen static int hw_controller_reset(struct ci_hdrc *ci) 466cdd278f2SPeter Chen { 467cdd278f2SPeter Chen int count = 0; 468cdd278f2SPeter Chen 469cdd278f2SPeter Chen hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST); 470cdd278f2SPeter Chen while (hw_read(ci, OP_USBCMD, USBCMD_RST)) { 471cdd278f2SPeter Chen udelay(10); 472cdd278f2SPeter Chen if (count++ > 1000) 473cdd278f2SPeter Chen return -ETIMEDOUT; 474cdd278f2SPeter Chen } 475cdd278f2SPeter Chen 476cdd278f2SPeter Chen return 0; 477cdd278f2SPeter Chen } 478cdd278f2SPeter Chen 479cdd278f2SPeter Chen /** 480e443b333SAlexander Shishkin * hw_device_reset: resets chip (execute without interruption) 481e443b333SAlexander Shishkin * @ci: the controller 482e443b333SAlexander Shishkin * 483e443b333SAlexander Shishkin * This function returns an error code 484e443b333SAlexander Shishkin */ 4855b157300SPeter Chen int hw_device_reset(struct ci_hdrc *ci) 486e443b333SAlexander Shishkin { 487cdd278f2SPeter Chen int ret; 488cdd278f2SPeter Chen 489e443b333SAlexander Shishkin /* should flush & stop before reset */ 490e443b333SAlexander Shishkin hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); 491e443b333SAlexander Shishkin hw_write(ci, OP_USBCMD, USBCMD_RS, 0); 492e443b333SAlexander Shishkin 493cdd278f2SPeter Chen ret = hw_controller_reset(ci); 494cdd278f2SPeter Chen if (ret) { 495cdd278f2SPeter Chen dev_err(ci->dev, "error resetting controller, ret=%d\n", ret); 496cdd278f2SPeter Chen return ret; 497cdd278f2SPeter Chen } 498e443b333SAlexander Shishkin 49977c4400fSRichard Zhao if (ci->platdata->notify_event) 50077c4400fSRichard Zhao ci->platdata->notify_event(ci, 5018e22978cSAlexander Shishkin CI_HDRC_CONTROLLER_RESET_EVENT); 502e443b333SAlexander Shishkin 503e443b333SAlexander Shishkin /* USBMODE should be configured step by step */ 504e443b333SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); 5055b157300SPeter Chen hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC); 506e443b333SAlexander Shishkin /* HW >= 2.3 */ 507e443b333SAlexander Shishkin hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); 508e443b333SAlexander Shishkin 5095b157300SPeter Chen if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { 5105b157300SPeter Chen pr_err("cannot enter in %s device mode", ci_role(ci)->name); 511e443b333SAlexander Shishkin pr_err("lpm = %i", ci->hw_bank.lpm); 512e443b333SAlexander Shishkin return -ENODEV; 513e443b333SAlexander Shishkin } 514e443b333SAlexander Shishkin 515bf9c85e7SPeter Chen ci_platform_configure(ci); 516bf9c85e7SPeter Chen 517e443b333SAlexander Shishkin return 0; 518e443b333SAlexander Shishkin } 519e443b333SAlexander Shishkin 52022fa8445SPeter Chen /** 52122fa8445SPeter Chen * hw_wait_reg: wait the register value 52222fa8445SPeter Chen * 52322fa8445SPeter Chen * Sometimes, it needs to wait register value before going on. 52422fa8445SPeter Chen * Eg, when switch to device mode, the vbus value should be lower 52522fa8445SPeter Chen * than OTGSC_BSV before connects to host. 52622fa8445SPeter Chen * 52722fa8445SPeter Chen * @ci: the controller 52822fa8445SPeter Chen * @reg: register index 52922fa8445SPeter Chen * @mask: mast bit 53022fa8445SPeter Chen * @value: the bit value to wait 53122fa8445SPeter Chen * @timeout_ms: timeout in millisecond 53222fa8445SPeter Chen * 53322fa8445SPeter Chen * This function returns an error code if timeout 53422fa8445SPeter Chen */ 53522fa8445SPeter Chen int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask, 53622fa8445SPeter Chen u32 value, unsigned int timeout_ms) 53722fa8445SPeter Chen { 53822fa8445SPeter Chen unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms); 53922fa8445SPeter Chen 54022fa8445SPeter Chen while (hw_read(ci, reg, mask) != value) { 54122fa8445SPeter Chen if (time_after(jiffies, elapse)) { 54222fa8445SPeter Chen dev_err(ci->dev, "timeout waiting for %08x in %d\n", 54322fa8445SPeter Chen mask, reg); 54422fa8445SPeter Chen return -ETIMEDOUT; 54522fa8445SPeter Chen } 54622fa8445SPeter Chen msleep(20); 54722fa8445SPeter Chen } 54822fa8445SPeter Chen 54922fa8445SPeter Chen return 0; 55022fa8445SPeter Chen } 55122fa8445SPeter Chen 5525f36e231SAlexander Shishkin static irqreturn_t ci_irq(int irq, void *data) 5535f36e231SAlexander Shishkin { 5548e22978cSAlexander Shishkin struct ci_hdrc *ci = data; 5555f36e231SAlexander Shishkin irqreturn_t ret = IRQ_NONE; 556b183c19fSRichard Zhao u32 otgsc = 0; 5575f36e231SAlexander Shishkin 5581f874edcSPeter Chen if (ci->in_lpm) { 5591f874edcSPeter Chen disable_irq_nosync(irq); 5601f874edcSPeter Chen ci->wakeup_int = true; 5611f874edcSPeter Chen pm_runtime_get(ci->dev); 5621f874edcSPeter Chen return IRQ_HANDLED; 5631f874edcSPeter Chen } 5641f874edcSPeter Chen 5654dcf720cSLi Jun if (ci->is_otg) { 5660c33bf78SLi Jun otgsc = hw_read_otgsc(ci, ~0); 5674dcf720cSLi Jun if (ci_otg_is_fsm_mode(ci)) { 5684dcf720cSLi Jun ret = ci_otg_fsm_irq(ci); 5694dcf720cSLi Jun if (ret == IRQ_HANDLED) 5704dcf720cSLi Jun return ret; 5714dcf720cSLi Jun } 5724dcf720cSLi Jun } 5735f36e231SAlexander Shishkin 574a107f8c5SPeter Chen /* 575a107f8c5SPeter Chen * Handle id change interrupt, it indicates device/host function 576a107f8c5SPeter Chen * switch. 577a107f8c5SPeter Chen */ 578a107f8c5SPeter Chen if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { 579a107f8c5SPeter Chen ci->id_event = true; 5800c33bf78SLi Jun /* Clear ID change irq status */ 5810c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); 582be6b0c1bSPeter Chen ci_otg_queue_work(ci); 583a107f8c5SPeter Chen return IRQ_HANDLED; 5845f36e231SAlexander Shishkin } 5855f36e231SAlexander Shishkin 586a107f8c5SPeter Chen /* 587a107f8c5SPeter Chen * Handle vbus change interrupt, it indicates device connection 588a107f8c5SPeter Chen * and disconnection events. 589a107f8c5SPeter Chen */ 590a107f8c5SPeter Chen if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { 591a107f8c5SPeter Chen ci->b_sess_valid_event = true; 5920c33bf78SLi Jun /* Clear BSV irq */ 5930c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); 594be6b0c1bSPeter Chen ci_otg_queue_work(ci); 595a107f8c5SPeter Chen return IRQ_HANDLED; 596a107f8c5SPeter Chen } 597a107f8c5SPeter Chen 598a107f8c5SPeter Chen /* Handle device/host interrupt */ 599a107f8c5SPeter Chen if (ci->role != CI_ROLE_END) 600a107f8c5SPeter Chen ret = ci_role(ci)->irq(ci); 601a107f8c5SPeter Chen 602b183c19fSRichard Zhao return ret; 6035f36e231SAlexander Shishkin } 6045f36e231SAlexander Shishkin 6051542d9c3SPeter Chen static int ci_get_platdata(struct device *dev, 6061542d9c3SPeter Chen struct ci_hdrc_platform_data *platdata) 6071542d9c3SPeter Chen { 608df96ed8dSPeter Chen int ret; 609df96ed8dSPeter Chen 610c22600c3SPeter Chen if (!platdata->phy_mode) 611c22600c3SPeter Chen platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); 612c22600c3SPeter Chen 613c22600c3SPeter Chen if (!platdata->dr_mode) 614c22600c3SPeter Chen platdata->dr_mode = of_usb_get_dr_mode(dev->of_node); 615c22600c3SPeter Chen 616c22600c3SPeter Chen if (platdata->dr_mode == USB_DR_MODE_UNKNOWN) 617c22600c3SPeter Chen platdata->dr_mode = USB_DR_MODE_OTG; 618c22600c3SPeter Chen 619c2ec3a73SPeter Chen if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) { 620c2ec3a73SPeter Chen /* Get the vbus regulator */ 621c2ec3a73SPeter Chen platdata->reg_vbus = devm_regulator_get(dev, "vbus"); 622c2ec3a73SPeter Chen if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { 623c2ec3a73SPeter Chen return -EPROBE_DEFER; 624c2ec3a73SPeter Chen } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { 6256629467bSMickael Maison /* no vbus regulator is needed */ 626c2ec3a73SPeter Chen platdata->reg_vbus = NULL; 627c2ec3a73SPeter Chen } else if (IS_ERR(platdata->reg_vbus)) { 628c2ec3a73SPeter Chen dev_err(dev, "Getting regulator error: %ld\n", 629c2ec3a73SPeter Chen PTR_ERR(platdata->reg_vbus)); 630c2ec3a73SPeter Chen return PTR_ERR(platdata->reg_vbus); 631c2ec3a73SPeter Chen } 632f6a9ff07SPeter Chen /* Get TPL support */ 633f6a9ff07SPeter Chen if (!platdata->tpl_support) 634f6a9ff07SPeter Chen platdata->tpl_support = 635f6a9ff07SPeter Chen of_usb_host_tpl_support(dev->of_node); 636c2ec3a73SPeter Chen } 637c2ec3a73SPeter Chen 6384f6743d5SMichael Grzeschik if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL) 6394f6743d5SMichael Grzeschik platdata->flags |= CI_HDRC_FORCE_FULLSPEED; 6404f6743d5SMichael Grzeschik 641df96ed8dSPeter Chen platdata->itc_setting = 1; 642df96ed8dSPeter Chen if (of_find_property(dev->of_node, "itc-setting", NULL)) { 643df96ed8dSPeter Chen ret = of_property_read_u32(dev->of_node, "itc-setting", 644df96ed8dSPeter Chen &platdata->itc_setting); 645df96ed8dSPeter Chen if (ret) { 646df96ed8dSPeter Chen dev_err(dev, 647df96ed8dSPeter Chen "failed to get itc-setting\n"); 648df96ed8dSPeter Chen return ret; 649df96ed8dSPeter Chen } 650df96ed8dSPeter Chen } 651df96ed8dSPeter Chen 65265668718SPeter Chen if (of_find_property(dev->of_node, "ahb-burst-config", NULL)) { 65365668718SPeter Chen ret = of_property_read_u32(dev->of_node, "ahb-burst-config", 65465668718SPeter Chen &platdata->ahb_burst_config); 65565668718SPeter Chen if (ret) { 65665668718SPeter Chen dev_err(dev, 65765668718SPeter Chen "failed to get ahb-burst-config\n"); 65865668718SPeter Chen return ret; 65965668718SPeter Chen } 66065668718SPeter Chen platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST; 66165668718SPeter Chen } 66265668718SPeter Chen 66396625eadSPeter Chen if (of_find_property(dev->of_node, "tx-burst-size-dword", NULL)) { 66496625eadSPeter Chen ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword", 66596625eadSPeter Chen &platdata->tx_burst_size); 66696625eadSPeter Chen if (ret) { 66796625eadSPeter Chen dev_err(dev, 66896625eadSPeter Chen "failed to get tx-burst-size-dword\n"); 66996625eadSPeter Chen return ret; 67096625eadSPeter Chen } 67196625eadSPeter Chen platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST; 67296625eadSPeter Chen } 67396625eadSPeter Chen 67496625eadSPeter Chen if (of_find_property(dev->of_node, "rx-burst-size-dword", NULL)) { 67596625eadSPeter Chen ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword", 67696625eadSPeter Chen &platdata->rx_burst_size); 67796625eadSPeter Chen if (ret) { 67896625eadSPeter Chen dev_err(dev, 67996625eadSPeter Chen "failed to get rx-burst-size-dword\n"); 68096625eadSPeter Chen return ret; 68196625eadSPeter Chen } 68296625eadSPeter Chen platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST; 68396625eadSPeter Chen } 68496625eadSPeter Chen 6851542d9c3SPeter Chen return 0; 6861542d9c3SPeter Chen } 6871542d9c3SPeter Chen 688fe6e125eSRichard Zhao static DEFINE_IDA(ci_ida); 689fe6e125eSRichard Zhao 6908e22978cSAlexander Shishkin struct platform_device *ci_hdrc_add_device(struct device *dev, 691cbc6dc2aSRichard Zhao struct resource *res, int nres, 6928e22978cSAlexander Shishkin struct ci_hdrc_platform_data *platdata) 693cbc6dc2aSRichard Zhao { 694cbc6dc2aSRichard Zhao struct platform_device *pdev; 695fe6e125eSRichard Zhao int id, ret; 696cbc6dc2aSRichard Zhao 6971542d9c3SPeter Chen ret = ci_get_platdata(dev, platdata); 6981542d9c3SPeter Chen if (ret) 6991542d9c3SPeter Chen return ERR_PTR(ret); 7001542d9c3SPeter Chen 701fe6e125eSRichard Zhao id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); 702fe6e125eSRichard Zhao if (id < 0) 703fe6e125eSRichard Zhao return ERR_PTR(id); 704fe6e125eSRichard Zhao 705fe6e125eSRichard Zhao pdev = platform_device_alloc("ci_hdrc", id); 706fe6e125eSRichard Zhao if (!pdev) { 707fe6e125eSRichard Zhao ret = -ENOMEM; 708fe6e125eSRichard Zhao goto put_id; 709fe6e125eSRichard Zhao } 710cbc6dc2aSRichard Zhao 711cbc6dc2aSRichard Zhao pdev->dev.parent = dev; 712cbc6dc2aSRichard Zhao pdev->dev.dma_mask = dev->dma_mask; 713cbc6dc2aSRichard Zhao pdev->dev.dma_parms = dev->dma_parms; 714cbc6dc2aSRichard Zhao dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask); 715cbc6dc2aSRichard Zhao 716cbc6dc2aSRichard Zhao ret = platform_device_add_resources(pdev, res, nres); 717cbc6dc2aSRichard Zhao if (ret) 718cbc6dc2aSRichard Zhao goto err; 719cbc6dc2aSRichard Zhao 720cbc6dc2aSRichard Zhao ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); 721cbc6dc2aSRichard Zhao if (ret) 722cbc6dc2aSRichard Zhao goto err; 723cbc6dc2aSRichard Zhao 724cbc6dc2aSRichard Zhao ret = platform_device_add(pdev); 725cbc6dc2aSRichard Zhao if (ret) 726cbc6dc2aSRichard Zhao goto err; 727cbc6dc2aSRichard Zhao 728cbc6dc2aSRichard Zhao return pdev; 729cbc6dc2aSRichard Zhao 730cbc6dc2aSRichard Zhao err: 731cbc6dc2aSRichard Zhao platform_device_put(pdev); 732fe6e125eSRichard Zhao put_id: 733fe6e125eSRichard Zhao ida_simple_remove(&ci_ida, id); 734cbc6dc2aSRichard Zhao return ERR_PTR(ret); 735cbc6dc2aSRichard Zhao } 7368e22978cSAlexander Shishkin EXPORT_SYMBOL_GPL(ci_hdrc_add_device); 737cbc6dc2aSRichard Zhao 7388e22978cSAlexander Shishkin void ci_hdrc_remove_device(struct platform_device *pdev) 739cbc6dc2aSRichard Zhao { 74098c35534SLothar Waßmann int id = pdev->id; 741cbc6dc2aSRichard Zhao platform_device_unregister(pdev); 74298c35534SLothar Waßmann ida_simple_remove(&ci_ida, id); 743cbc6dc2aSRichard Zhao } 7448e22978cSAlexander Shishkin EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); 745cbc6dc2aSRichard Zhao 7463f124d23SPeter Chen static inline void ci_role_destroy(struct ci_hdrc *ci) 7473f124d23SPeter Chen { 7483f124d23SPeter Chen ci_hdrc_gadget_destroy(ci); 7493f124d23SPeter Chen ci_hdrc_host_destroy(ci); 750cbec6bd5SPeter Chen if (ci->is_otg) 751cbec6bd5SPeter Chen ci_hdrc_otg_destroy(ci); 7523f124d23SPeter Chen } 7533f124d23SPeter Chen 754577b232fSPeter Chen static void ci_get_otg_capable(struct ci_hdrc *ci) 755577b232fSPeter Chen { 756577b232fSPeter Chen if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) 757577b232fSPeter Chen ci->is_otg = false; 758577b232fSPeter Chen else 759577b232fSPeter Chen ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, 760577b232fSPeter Chen DCCPARAMS_DC | DCCPARAMS_HC) 761577b232fSPeter Chen == (DCCPARAMS_DC | DCCPARAMS_HC)); 7622e37cfd8SPeter Chen if (ci->is_otg) { 763577b232fSPeter Chen dev_dbg(ci->dev, "It is OTG capable controller\n"); 7642e37cfd8SPeter Chen /* Disable and clear all OTG irq */ 7652e37cfd8SPeter Chen hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, 7662e37cfd8SPeter Chen OTGSC_INT_STATUS_BITS); 7672e37cfd8SPeter Chen } 768577b232fSPeter Chen } 769577b232fSPeter Chen 77041ac7b3aSBill Pemberton static int ci_hdrc_probe(struct platform_device *pdev) 771e443b333SAlexander Shishkin { 772e443b333SAlexander Shishkin struct device *dev = &pdev->dev; 7738e22978cSAlexander Shishkin struct ci_hdrc *ci; 774e443b333SAlexander Shishkin struct resource *res; 775e443b333SAlexander Shishkin void __iomem *base; 776e443b333SAlexander Shishkin int ret; 777691962d1SSascha Hauer enum usb_dr_mode dr_mode; 778e443b333SAlexander Shishkin 779fad56745SJingoo Han if (!dev_get_platdata(dev)) { 780e443b333SAlexander Shishkin dev_err(dev, "platform data missing\n"); 781e443b333SAlexander Shishkin return -ENODEV; 782e443b333SAlexander Shishkin } 783e443b333SAlexander Shishkin 784e443b333SAlexander Shishkin res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 78519290816SFelipe Balbi base = devm_ioremap_resource(dev, res); 78619290816SFelipe Balbi if (IS_ERR(base)) 78719290816SFelipe Balbi return PTR_ERR(base); 788e443b333SAlexander Shishkin 7895f36e231SAlexander Shishkin ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL); 790d0f99249SFabio Estevam if (!ci) 7915f36e231SAlexander Shishkin return -ENOMEM; 792e443b333SAlexander Shishkin 7935f36e231SAlexander Shishkin ci->dev = dev; 794fad56745SJingoo Han ci->platdata = dev_get_platdata(dev); 795ed8f8318SPeter Chen ci->imx28_write_fix = !!(ci->platdata->flags & 796ed8f8318SPeter Chen CI_HDRC_IMX28_WRITE_FIX); 7971f874edcSPeter Chen ci->supports_runtime_pm = !!(ci->platdata->flags & 7981f874edcSPeter Chen CI_HDRC_SUPPORTS_RUNTIME_PM); 7995f36e231SAlexander Shishkin 8005f36e231SAlexander Shishkin ret = hw_device_init(ci, base); 8015f36e231SAlexander Shishkin if (ret < 0) { 8025f36e231SAlexander Shishkin dev_err(dev, "can't initialize hardware\n"); 8035f36e231SAlexander Shishkin return -ENODEV; 8045f36e231SAlexander Shishkin } 8055f36e231SAlexander Shishkin 8061e5e2d3dSAntoine Tenart if (ci->platdata->phy) { 8071e5e2d3dSAntoine Tenart ci->phy = ci->platdata->phy; 8081e5e2d3dSAntoine Tenart } else if (ci->platdata->usb_phy) { 809ef44cb42SAntoine Tenart ci->usb_phy = ci->platdata->usb_phy; 8101e5e2d3dSAntoine Tenart } else { 81121a5b579SAntoine Tenart ci->phy = devm_phy_get(dev->parent, "usb-phy"); 81221a5b579SAntoine Tenart ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2); 813c859aa65SPeter Chen 8141e5e2d3dSAntoine Tenart /* if both generic PHY and USB PHY layers aren't enabled */ 8151e5e2d3dSAntoine Tenart if (PTR_ERR(ci->phy) == -ENOSYS && 8161e5e2d3dSAntoine Tenart PTR_ERR(ci->usb_phy) == -ENXIO) 8171e5e2d3dSAntoine Tenart return -ENXIO; 818c859aa65SPeter Chen 8191e5e2d3dSAntoine Tenart if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) 820c859aa65SPeter Chen return -EPROBE_DEFER; 8211e5e2d3dSAntoine Tenart 8221e5e2d3dSAntoine Tenart if (IS_ERR(ci->phy)) 8231e5e2d3dSAntoine Tenart ci->phy = NULL; 8241e5e2d3dSAntoine Tenart else if (IS_ERR(ci->usb_phy)) 8251e5e2d3dSAntoine Tenart ci->usb_phy = NULL; 826c859aa65SPeter Chen } 827c859aa65SPeter Chen 828d03cccffSPeter Chen ret = ci_usb_phy_init(ci); 82974475edeSPeter Chen if (ret) { 83074475edeSPeter Chen dev_err(dev, "unable to init phy: %d\n", ret); 83174475edeSPeter Chen return ret; 83274475edeSPeter Chen } 83374475edeSPeter Chen 834eb70e5abSAlexander Shishkin ci->hw_bank.phys = res->start; 835eb70e5abSAlexander Shishkin 8365f36e231SAlexander Shishkin ci->irq = platform_get_irq(pdev, 0); 8375f36e231SAlexander Shishkin if (ci->irq < 0) { 838e443b333SAlexander Shishkin dev_err(dev, "missing IRQ\n"); 83942d18212SFabio Estevam ret = ci->irq; 840c859aa65SPeter Chen goto deinit_phy; 841e443b333SAlexander Shishkin } 842e443b333SAlexander Shishkin 843577b232fSPeter Chen ci_get_otg_capable(ci); 844577b232fSPeter Chen 845691962d1SSascha Hauer dr_mode = ci->platdata->dr_mode; 8465f36e231SAlexander Shishkin /* initialize role(s) before the interrupt is requested */ 847691962d1SSascha Hauer if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { 848eb70e5abSAlexander Shishkin ret = ci_hdrc_host_init(ci); 849eb70e5abSAlexander Shishkin if (ret) 850eb70e5abSAlexander Shishkin dev_info(dev, "doesn't support host\n"); 851691962d1SSascha Hauer } 852eb70e5abSAlexander Shishkin 853691962d1SSascha Hauer if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { 8545f36e231SAlexander Shishkin ret = ci_hdrc_gadget_init(ci); 855e443b333SAlexander Shishkin if (ret) 8565f36e231SAlexander Shishkin dev_info(dev, "doesn't support gadget\n"); 857691962d1SSascha Hauer } 8585f36e231SAlexander Shishkin 8595f36e231SAlexander Shishkin if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { 8605f36e231SAlexander Shishkin dev_err(dev, "no supported roles\n"); 86174475edeSPeter Chen ret = -ENODEV; 862c859aa65SPeter Chen goto deinit_phy; 863cbec6bd5SPeter Chen } 864cbec6bd5SPeter Chen 86527c62c2dSPeter Chen if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) { 866cbec6bd5SPeter Chen ret = ci_hdrc_otg_init(ci); 867cbec6bd5SPeter Chen if (ret) { 868cbec6bd5SPeter Chen dev_err(dev, "init otg fails, ret = %d\n", ret); 869cbec6bd5SPeter Chen goto stop; 870cbec6bd5SPeter Chen } 8715f36e231SAlexander Shishkin } 8725f36e231SAlexander Shishkin 8735f36e231SAlexander Shishkin if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { 874577b232fSPeter Chen if (ci->is_otg) { 8755f36e231SAlexander Shishkin ci->role = ci_otg_role(ci); 8760c33bf78SLi Jun /* Enable ID change irq */ 8770c33bf78SLi Jun hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); 878577b232fSPeter Chen } else { 879577b232fSPeter Chen /* 880577b232fSPeter Chen * If the controller is not OTG capable, but support 881577b232fSPeter Chen * role switch, the defalt role is gadget, and the 882577b232fSPeter Chen * user can switch it through debugfs. 883577b232fSPeter Chen */ 884577b232fSPeter Chen ci->role = CI_ROLE_GADGET; 885577b232fSPeter Chen } 8865f36e231SAlexander Shishkin } else { 8875f36e231SAlexander Shishkin ci->role = ci->roles[CI_ROLE_HOST] 8885f36e231SAlexander Shishkin ? CI_ROLE_HOST 8895f36e231SAlexander Shishkin : CI_ROLE_GADGET; 8905f36e231SAlexander Shishkin } 8915f36e231SAlexander Shishkin 892961ea496SLi Jun if (!ci_otg_is_fsm_mode(ci)) { 8935a1e1456SPeter Chen /* only update vbus status for peripheral */ 8945a1e1456SPeter Chen if (ci->role == CI_ROLE_GADGET) 8955a1e1456SPeter Chen ci_handle_vbus_change(ci); 8965a1e1456SPeter Chen 8975f36e231SAlexander Shishkin ret = ci_role_start(ci, ci->role); 8985f36e231SAlexander Shishkin if (ret) { 8994dcf720cSLi Jun dev_err(dev, "can't start %s role\n", 9004dcf720cSLi Jun ci_role(ci)->name); 901cbec6bd5SPeter Chen goto stop; 9025f36e231SAlexander Shishkin } 9034dcf720cSLi Jun } 9045f36e231SAlexander Shishkin 90524c498dfSPeter Chen platform_set_drvdata(pdev, ci); 9064c503dd5SPeter Chen ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED, 9074c503dd5SPeter Chen ci->platdata->name, ci); 9085f36e231SAlexander Shishkin if (ret) 9095f36e231SAlexander Shishkin goto stop; 9105f36e231SAlexander Shishkin 9111f874edcSPeter Chen if (ci->supports_runtime_pm) { 9121f874edcSPeter Chen pm_runtime_set_active(&pdev->dev); 9131f874edcSPeter Chen pm_runtime_enable(&pdev->dev); 9141f874edcSPeter Chen pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); 9151f874edcSPeter Chen pm_runtime_mark_last_busy(ci->dev); 9161f874edcSPeter Chen pm_runtime_use_autosuspend(&pdev->dev); 9171f874edcSPeter Chen } 9181f874edcSPeter Chen 9194dcf720cSLi Jun if (ci_otg_is_fsm_mode(ci)) 9204dcf720cSLi Jun ci_hdrc_otg_fsm_start(ci); 9214dcf720cSLi Jun 922f8efa766SPeter Chen device_set_wakeup_capable(&pdev->dev, true); 923f8efa766SPeter Chen 924adf0f735SAlexander Shishkin ret = dbg_create_files(ci); 925adf0f735SAlexander Shishkin if (!ret) 926adf0f735SAlexander Shishkin return 0; 9275f36e231SAlexander Shishkin 9285f36e231SAlexander Shishkin stop: 9293f124d23SPeter Chen ci_role_destroy(ci); 930c859aa65SPeter Chen deinit_phy: 9311e5e2d3dSAntoine Tenart ci_usb_phy_exit(ci); 932e443b333SAlexander Shishkin 933e443b333SAlexander Shishkin return ret; 934e443b333SAlexander Shishkin } 935e443b333SAlexander Shishkin 936fb4e98abSBill Pemberton static int ci_hdrc_remove(struct platform_device *pdev) 937e443b333SAlexander Shishkin { 9388e22978cSAlexander Shishkin struct ci_hdrc *ci = platform_get_drvdata(pdev); 939e443b333SAlexander Shishkin 9401f874edcSPeter Chen if (ci->supports_runtime_pm) { 9411f874edcSPeter Chen pm_runtime_get_sync(&pdev->dev); 9421f874edcSPeter Chen pm_runtime_disable(&pdev->dev); 9431f874edcSPeter Chen pm_runtime_put_noidle(&pdev->dev); 9441f874edcSPeter Chen } 9451f874edcSPeter Chen 946adf0f735SAlexander Shishkin dbg_remove_files(ci); 9473f124d23SPeter Chen ci_role_destroy(ci); 948864cf949SPeter Chen ci_hdrc_enter_lpm(ci, true); 9491e5e2d3dSAntoine Tenart ci_usb_phy_exit(ci); 950e443b333SAlexander Shishkin 951e443b333SAlexander Shishkin return 0; 952e443b333SAlexander Shishkin } 953e443b333SAlexander Shishkin 9541f874edcSPeter Chen #ifdef CONFIG_PM 955961ea496SLi Jun /* Prepare wakeup by SRP before suspend */ 956961ea496SLi Jun static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci) 957961ea496SLi Jun { 958961ea496SLi Jun if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && 959961ea496SLi Jun !hw_read_otgsc(ci, OTGSC_ID)) { 960961ea496SLi Jun hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 961961ea496SLi Jun PORTSC_PP); 962961ea496SLi Jun hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN, 963961ea496SLi Jun PORTSC_WKCN); 964961ea496SLi Jun } 965961ea496SLi Jun } 966961ea496SLi Jun 967961ea496SLi Jun /* Handle SRP when wakeup by data pulse */ 968961ea496SLi Jun static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci) 969961ea496SLi Jun { 970961ea496SLi Jun if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && 971961ea496SLi Jun (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) { 972961ea496SLi Jun if (!hw_read_otgsc(ci, OTGSC_ID)) { 973961ea496SLi Jun ci->fsm.a_srp_det = 1; 974961ea496SLi Jun ci->fsm.a_bus_drop = 0; 975961ea496SLi Jun } else { 976961ea496SLi Jun ci->fsm.id = 1; 977961ea496SLi Jun } 978961ea496SLi Jun ci_otg_queue_work(ci); 979961ea496SLi Jun } 980961ea496SLi Jun } 981961ea496SLi Jun 9828076932fSPeter Chen static void ci_controller_suspend(struct ci_hdrc *ci) 9838076932fSPeter Chen { 9841f874edcSPeter Chen disable_irq(ci->irq); 9858076932fSPeter Chen ci_hdrc_enter_lpm(ci, true); 9868076932fSPeter Chen usb_phy_set_suspend(ci->usb_phy, 1); 9871f874edcSPeter Chen ci->in_lpm = true; 9881f874edcSPeter Chen enable_irq(ci->irq); 9898076932fSPeter Chen } 9908076932fSPeter Chen 9918076932fSPeter Chen static int ci_controller_resume(struct device *dev) 9928076932fSPeter Chen { 9938076932fSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 9948076932fSPeter Chen 9958076932fSPeter Chen dev_dbg(dev, "at %s\n", __func__); 9968076932fSPeter Chen 9971f874edcSPeter Chen if (!ci->in_lpm) { 9981f874edcSPeter Chen WARN_ON(1); 9991f874edcSPeter Chen return 0; 10001f874edcSPeter Chen } 10018076932fSPeter Chen 10021f874edcSPeter Chen ci_hdrc_enter_lpm(ci, false); 10038076932fSPeter Chen if (ci->usb_phy) { 10048076932fSPeter Chen usb_phy_set_suspend(ci->usb_phy, 0); 10058076932fSPeter Chen usb_phy_set_wakeup(ci->usb_phy, false); 10068076932fSPeter Chen hw_wait_phy_stable(); 10078076932fSPeter Chen } 10088076932fSPeter Chen 10091f874edcSPeter Chen ci->in_lpm = false; 10101f874edcSPeter Chen if (ci->wakeup_int) { 10111f874edcSPeter Chen ci->wakeup_int = false; 10121f874edcSPeter Chen pm_runtime_mark_last_busy(ci->dev); 10131f874edcSPeter Chen pm_runtime_put_autosuspend(ci->dev); 10141f874edcSPeter Chen enable_irq(ci->irq); 1015961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1016961ea496SLi Jun ci_otg_fsm_wakeup_by_srp(ci); 10171f874edcSPeter Chen } 10181f874edcSPeter Chen 10198076932fSPeter Chen return 0; 10208076932fSPeter Chen } 10218076932fSPeter Chen 10221f874edcSPeter Chen #ifdef CONFIG_PM_SLEEP 10238076932fSPeter Chen static int ci_suspend(struct device *dev) 10248076932fSPeter Chen { 10258076932fSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 10268076932fSPeter Chen 10278076932fSPeter Chen if (ci->wq) 10288076932fSPeter Chen flush_workqueue(ci->wq); 10291f874edcSPeter Chen /* 10301f874edcSPeter Chen * Controller needs to be active during suspend, otherwise the core 10311f874edcSPeter Chen * may run resume when the parent is at suspend if other driver's 10321f874edcSPeter Chen * suspend fails, it occurs before parent's suspend has not started, 10331f874edcSPeter Chen * but the core suspend has finished. 10341f874edcSPeter Chen */ 10351f874edcSPeter Chen if (ci->in_lpm) 10361f874edcSPeter Chen pm_runtime_resume(dev); 10371f874edcSPeter Chen 10381f874edcSPeter Chen if (ci->in_lpm) { 10391f874edcSPeter Chen WARN_ON(1); 10401f874edcSPeter Chen return 0; 10411f874edcSPeter Chen } 10428076932fSPeter Chen 1043f8efa766SPeter Chen if (device_may_wakeup(dev)) { 1044961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1045961ea496SLi Jun ci_otg_fsm_suspend_for_srp(ci); 1046961ea496SLi Jun 1047f8efa766SPeter Chen usb_phy_set_wakeup(ci->usb_phy, true); 1048f8efa766SPeter Chen enable_irq_wake(ci->irq); 1049f8efa766SPeter Chen } 1050f8efa766SPeter Chen 10518076932fSPeter Chen ci_controller_suspend(ci); 10528076932fSPeter Chen 10538076932fSPeter Chen return 0; 10548076932fSPeter Chen } 10558076932fSPeter Chen 10568076932fSPeter Chen static int ci_resume(struct device *dev) 10578076932fSPeter Chen { 10581f874edcSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 10591f874edcSPeter Chen int ret; 10601f874edcSPeter Chen 1061f8efa766SPeter Chen if (device_may_wakeup(dev)) 1062f8efa766SPeter Chen disable_irq_wake(ci->irq); 1063f8efa766SPeter Chen 10641f874edcSPeter Chen ret = ci_controller_resume(dev); 10651f874edcSPeter Chen if (ret) 10661f874edcSPeter Chen return ret; 10671f874edcSPeter Chen 10681f874edcSPeter Chen if (ci->supports_runtime_pm) { 10691f874edcSPeter Chen pm_runtime_disable(dev); 10701f874edcSPeter Chen pm_runtime_set_active(dev); 10711f874edcSPeter Chen pm_runtime_enable(dev); 10721f874edcSPeter Chen } 10731f874edcSPeter Chen 10741f874edcSPeter Chen return ret; 10758076932fSPeter Chen } 10768076932fSPeter Chen #endif /* CONFIG_PM_SLEEP */ 10778076932fSPeter Chen 10781f874edcSPeter Chen static int ci_runtime_suspend(struct device *dev) 10791f874edcSPeter Chen { 10801f874edcSPeter Chen struct ci_hdrc *ci = dev_get_drvdata(dev); 10811f874edcSPeter Chen 10821f874edcSPeter Chen dev_dbg(dev, "at %s\n", __func__); 10831f874edcSPeter Chen 10841f874edcSPeter Chen if (ci->in_lpm) { 10851f874edcSPeter Chen WARN_ON(1); 10861f874edcSPeter Chen return 0; 10871f874edcSPeter Chen } 10881f874edcSPeter Chen 1089961ea496SLi Jun if (ci_otg_is_fsm_mode(ci)) 1090961ea496SLi Jun ci_otg_fsm_suspend_for_srp(ci); 1091961ea496SLi Jun 10921f874edcSPeter Chen usb_phy_set_wakeup(ci->usb_phy, true); 10931f874edcSPeter Chen ci_controller_suspend(ci); 10941f874edcSPeter Chen 10951f874edcSPeter Chen return 0; 10961f874edcSPeter Chen } 10971f874edcSPeter Chen 10981f874edcSPeter Chen static int ci_runtime_resume(struct device *dev) 10991f874edcSPeter Chen { 11001f874edcSPeter Chen return ci_controller_resume(dev); 11011f874edcSPeter Chen } 11021f874edcSPeter Chen 11031f874edcSPeter Chen #endif /* CONFIG_PM */ 11048076932fSPeter Chen static const struct dev_pm_ops ci_pm_ops = { 11058076932fSPeter Chen SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume) 11061f874edcSPeter Chen SET_RUNTIME_PM_OPS(ci_runtime_suspend, ci_runtime_resume, NULL) 11078076932fSPeter Chen }; 11081f874edcSPeter Chen 11095f36e231SAlexander Shishkin static struct platform_driver ci_hdrc_driver = { 11105f36e231SAlexander Shishkin .probe = ci_hdrc_probe, 11117690417dSBill Pemberton .remove = ci_hdrc_remove, 1112e443b333SAlexander Shishkin .driver = { 11135f36e231SAlexander Shishkin .name = "ci_hdrc", 11148076932fSPeter Chen .pm = &ci_pm_ops, 1115e443b333SAlexander Shishkin }, 1116e443b333SAlexander Shishkin }; 1117e443b333SAlexander Shishkin 11182f01a33bSPeter Chen static int __init ci_hdrc_platform_register(void) 11192f01a33bSPeter Chen { 11202f01a33bSPeter Chen ci_hdrc_host_driver_init(); 11212f01a33bSPeter Chen return platform_driver_register(&ci_hdrc_driver); 11222f01a33bSPeter Chen } 11232f01a33bSPeter Chen module_init(ci_hdrc_platform_register); 11242f01a33bSPeter Chen 11252f01a33bSPeter Chen static void __exit ci_hdrc_platform_unregister(void) 11262f01a33bSPeter Chen { 11272f01a33bSPeter Chen platform_driver_unregister(&ci_hdrc_driver); 11282f01a33bSPeter Chen } 11292f01a33bSPeter Chen module_exit(ci_hdrc_platform_unregister); 1130e443b333SAlexander Shishkin 11315f36e231SAlexander Shishkin MODULE_ALIAS("platform:ci_hdrc"); 1132e443b333SAlexander Shishkin MODULE_LICENSE("GPL v2"); 1133e443b333SAlexander Shishkin MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>"); 11345f36e231SAlexander Shishkin MODULE_DESCRIPTION("ChipIdea HDRC Driver"); 1135